目次
はじめに
実行環境
webカメラの幅と高さの取得方法
メインウィンドウのサイズの設定方法
リアルタイム映像の表示領域の設定方法
スライドバーで小数を扱う方法
スライドバーの設定方法
ガンマ補正の実装方法
ルックアップテーブルの作成方法
ガンマ補正の処理方法
リアルタイム画像のtkinterでの表示方法
定期的に処理を実行する方法
ソースコード
結果
まとめ
はじめに
今回は、前回のTwitter(X)のアンケートでトップだった動画像処理についての記事です。
そのジャンルの中から、webカメラから得られた映像にγ変換
をかけて明るくしたリアルタイムの動画をTkinter
を用いて表示させγ
の値をTkinter
のtkinter.Scale
で調整させるプログラムを作成したのでそれについて紹介したいと思います。
実行環境
実行環境は次の通りです。
実行環境
-
環境
Windows10
Python 3.10.5
-
ライブラリ
OpenCV 4.8.0
numpy 1.22.4
pillow 9.2.0
tkinter
は、標準ライブラリなので特に気にしなくても大丈夫です!!
処理内容とライブラリの対応を分かりやすく表にすると次の通りです。
説明
処理内容 | モジュール ライブラリ |
---|---|
webカメラの幅と高さの取得 webカメラの映像の取得・反転 ルックアップテーブル(LUT)の実装 |
OpenCV |
γ 変換の初期値の設定 |
numpy |
γ 変換のGUIでの操作 |
tkinter |
OpenCV で得られた画像をTkinter で使えるように変換 |
pillow |
webカメラの幅と高さの取得方法
こちらのソース
によると、cv2.CAP_PROP_FRAME_WIDTH
でwebカメラの幅をcv2.CAP_PROP_FRAME_HEIGHT
でwebカメラの高さを取得できるそうです。
この方法を使ってwebカメラの幅と高さを取得しました。
メインウィンドウのサイズの設定方法
こちらのソース
によると、geometry
メソッドを使うことでメインウィンドウのサイズを設定できるそうです。
このメソッドを使って、メインウィンドウのサイズを設定しました。
メインウィンドウの幅は、cv2.CAP_PROP_FRAME_WIDTH
で取得したwebカメラの幅を整数型に型変換し、その値を文字列型に型変換して設定しました。
メインウィンドウの高さは、cv2.CAP_PROP_FRAME_HEIGHT
で取得したwebカメラの高さを整数型に型変換し、その値を文字列型に型変換して設定しました。
リアルタイム映像の表示領域の設定方法
こちらのソース
によると、Canvas
ウィジェットを使うことで図形を描画できるそうです。
このウィジェットを使ってリアルタイム映像を表示させました。
表示領域の幅の設定方法
こちらのソース
によると、Canvas
ウィジェットのwidth
キーワード引数で表示領域の幅を設定できるそうです。
このキーワード引数を使って、表示領域の幅を設定しました。
表示領域の高さの設定方法
こちらのソース
によると、Canvas
ウィジェットのheight
キーワード引数で表示領域の高さを設定できるそうです。
このキーワード引数を使って、表示領域の高さを設定しました。
スライドバーで小数を扱う方法
こちらのソース
によると、DoubleVar
というウィジェット変数を用いることで tkinter
のスライドバーで浮動小数点数(小数)を使うことができるそうです。
このDoubleVar
というウィジェット変数を用いてスライドバーで小数を扱えるようにしました。
スライドバーの設定方法
こちらのソース
によると、Scale
ウィジェットを使うことでスライドバーを表示できるそうです。
向きの設定方法
こちらのソース
によると、orient
オプションのtkinter.HORIZONTAL
を使うことでスライドバーの向きを水平方向(横向き)にすることができるそうです。
このオプションを使って、向きを設定しました。
最小値と最大値の設定方法
こちらのソース
によると、from_
オプションで最小値をto
オプションで最大値を設定できるそうです。
このオプションを使って、最小値と最大値を設定しました。
最小値と最大値の説明
-
最小値について
ガンマ補正の方法の項で紹介するように画像を明るく表示させたいときには1よりも大きい値に設定する必要があるそうなので1より大きくしました。 -
最大値について
実行環境で実験をしたところ、3で問題なかったので3までとしました。
最大値については、お使いの際は適宜変更してください。
分解能の設定方法
こちらのソース
によると、resolution
オプションで分解能を設定できるそうです。
このオプションを使って、分解能を設定しました。
分解能についての説明
resolution
オプションは小数で指定してください。そうしないと、スライドバーで小数を設定できません。
ウィジェット変数の指定方法
こちらのソース
によると、variable
オプションでウィジェット変数を指定できるそうです。
このオプションを使って、ウィジェット変数を指定しました。
スライドバーで小数を扱う方法で示したDoubleVar
というウィジェット変数をvariable
オプションで指定しました。
目盛の設定方法
こちらのソース
によると、tickinterval
オプションで目盛の表示・間隔を設定できるそうです。
このオプションを使って、目盛の表示と目盛の間隔を設定しました。
長さの設定方法
こちらのソース
によると、length
オプションで長さを設定できるそうです。
このオプションを使って、長さを設定しました。
スライドバーを画面の横全体に表示させたかったので、webカメラの幅と高さの取得方法で紹介したcv2.CAP_PROP_FRAME_WIDTH
を長さに設定しました。
表示文字色の設定方法
こちらのソース
によると、fg
オプションで表示文字色を設定できるそうです。
このオプションを使って、表示文字色を設定しました。
背景、選択してない状態でのスライドバーの色の設定方法
こちらのソース
によると、bg
オプションで背景、選択してない状態でのスライドバーの色を設定できるそうです。
このオプションを使って、背景、選択してない状態でのスライドバーの色を設定しました。
ウィジェットの枠線色の設定方法
こちらのソース
によると、highlightbackground
オプションでフォーカスが外れた時のスケールウィジェットの色を設定できるそうです。
このオプションを使って、フォーカスが外れた時のスケールウィジェットの色を設定しました。
このオプションを指定しないと、外枠線の色がデフォルト(灰色に似た色)のままになります。
ガンマ補正の実装方法
こちらのソース
によると、γ
補正の公式は
Y=255\times\Bigl(\frac{X}{255}\Bigl)^\frac{1}{\gamma}
ここで、
Y:γ補正後のピクセル値
X:γ補正前のピクセル値
γ:ガンマ補正値
で表現できるそうです。
また、こちらのソース
では、画像を明るく表示したい場合には
γ
の値を1より大きい値に設定する
必要があるそうなので、γ
の値の範囲を1より大きくしました。
これらの情報をもとにγ
補正を実装しました。
ルックアップテーブルの作成方法
γ
補正の計算結果を格納する配列の作成方法
γ
補正の計算結果を格納する変数を作成するために
numpy
の zeros
関数を利用しました。
こちらのソース
によると、numpy
のzeros
関数は、
配列の全要素が0で初期化されている
関数だそうです。
この関数を使って、γ
補正の計算結果を格納する配列を作成しました。
OpenCVのカラー画像では、値の範囲が0~255の8bitです。そのため、要素の型の指定ができるパラメータであるdtype
で numpy
で8bitデータを指している np.uint8
を指定しました。
γ
補正からのルックアップテーブルの作成方法
ガンマ補正の方法で紹介したγ
補正の公式である
Y=255\times\Bigl(\frac{X}{255}\Bigl)^\frac{1}{\gamma}
ここで、
Y:γ補正後のピクセル値
X:γ補正前のピクセル値
γ:ガンマ補正値
の公式を使って、 γ
補正の計算結果を格納する配列の作成方法で紹介した配列に計算結果を0~255までループ処理で逐次代入して作成しました。
ガンマ補正の処理方法
こちらのソース
によると、OpenCV
のcv2.LUT
を使うことでルックアップテーブルをもとにした階調変換ができるそうです。
この方法でルックアップテーブルを使ってリアルタイム画像の階調変換を行いました。
リアルタイム画像のtkinterでの表示方法
OpenCV
の画像のtkinter
での使用方法
こちらのソース
によると、PIL(pillow)
のImageTk
モジュールのPhotoImage
クラスを使うことでtkinter
の画像オブジェクトに変換できるそうです。
また、こちらのソース
によると、次の手順でOpenCV
のデータをtkinter
で使えるデータに変換できるそうです。
-
OpenCV
の画像データをBGR色空間からRGB色空間にcv2.cvtColor
で変換 -
OpenCV
の画像データのようなnumpy
配列のデータをPIL(pillow)
のImage
モジュールのfromarray
クラスを使ってPIL(pillow)
の画像データに変換
この手順でOpenCV
のデータをPIL(pillow)
のデータにすることができるそうです。
これらの方法を使って、OpenCV
で得られたリアルタイム画像をtkinter
で表示できるように設定しました。
変換した画像の表示方法
OpenCV
の画像のtkinter
での使用方法 で OpenCV
の画像を tkinter
で使えるように変換できました。この変換された画像を
こちらのソース
によると、tkinter
のCanvas
クラスのcreate_image
メソッドを使うと画像をtkinter
に表示できるそうです。
このメソッドを使って、画像をtkinter
上に表示させました。
また、こちらのソース
によると、anchor
オプションを使うことで、画像の基準位置を設定できるそうです。
このオプションのtkinter.NW
を指定しました。この値を指定することで画像の左上を基準位置として設定しました。
座標(0,0) に基準位置が来るように設定したため画像の左上を基準位置として設定しました。
こうすることで、webカメラの幅と高さに依存しない位置に画像を配置できました。
定期的に処理を実行する方法
こちらのソース
によると、after
オプションを関数内で使うことで定期的に処理を実行させることができるそうです。
このメソッドを使って、処理を定期的に実行させました。
ソースコード
下にソースコードを示します。おそらく実行環境で示した環境では動くはず。
ソースコード
import cv2 #webカメラの幅と高さの取得、webカメラの映像を反転させるためにインポート
import numpy as np #γ変換の初期値を設定するためにインポート
import tkinter #γ変換をGUIで操作できるようにインポート
import PIL.Image,PIL.ImageTk #OpenCVで得られた画像をTkinterで扱えるようにインポート
cap = cv2.VideoCapture(0) #webカメラを設定
app = tkinter.Tk() #tkinterのセットアップ
app.title("画像明るさ調整") #「画像明るさ調整」という名前のウインドウを作成
app.geometry(str(int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)))+"x"+str(int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))) #Tkinterのウインドウの初期サイズをwebカメラの幅×高さに設定
canvas = tkinter.Canvas(app,width=cap.get(cv2.CAP_PROP_FRAME_WIDTH),height=cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) #webカメラの映像を表示させる範囲の設定(大きさ:webカメラの幅×
#高さに設定)
canvas.pack() #webカメラの映像を表示させる範囲の表示
var_scale = tkinter.DoubleVar() #Double(浮動小数点数(実数)型でスライドバーの値を取得
#最小値1.0、最大値3.0、刻み幅0.01、値がvar_scale、向きが横向き、背景色が青色、文字色が黄色、枠線の色が青色、目盛の間隔が0.1、長さがwebカメラの幅のスライドバーをappの(x=0,y=0)に設置
#夜間にプログラムを走らせ実装したところ3.0以上は必要ないと考えたため最大値3.0とした
scale = tkinter.Scale(app,from_=1.0,to=3.0,resolution=0.01,variable=var_scale,orient=tkinter.HORIZONTAL,bg='blue',fg='yellow',highlightbackground='blue',tickinterval=0.1,length=cap.get(cv2.CAP_PROP_FRAME_WIDTH)).place(x=0,y=0)
def gamma_trans(event=None): #Tkinterの関数の定義(関数の定義をすることで再帰的に処理を実行(こうすることでfor文でのループ処理ができるようになる))
_,img = cap.read() #webカメラから1フレーム取得
img = cv2.flip(img,1) #取得したフレームを左右反転
gamma = var_scale.get() #スライドバーで設定した値を取得
img2gamma = np.zeros((256,1),dtype=np.uint8) #256行1列の値がすべて0のリストを作成(OpenCVの色空間の値の範囲は0~255の符号なし8bitの数値であるためdtypeにnp.uint8を指定)
for i in range(256): #0~255まで処理を実行
'''
γ補正の式
γ_new=255×(X/255)^(1/γ)
γ_new:γ補正後の画素値
X:ガンマ補正前の画素値
γ:γ補正値
をもとにルックアップテーブル(LUT)を作成
'''
img2gamma[i][0] = 255 * (float(i)/255) ** (1.0 /gamma) #img2gammaの先頭の値に255×(iをfloat(浮動小数点数(実数)型に型変換)したもの/255)^(1.0/スライドバーで指定したγの値)
#小数部分が切り捨てられた状態(整数)でimg2gammaが取得される(dtypeをnp.uint8にしているため)
gamma_img = cv2.LUT(img,img2gamma) #webカメラから得られたフレームをimg2gammaをルックアップテーブル(LUT)として画像処理
app.img = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(cv2.cvtColor(gamma_img, cv2.COLOR_BGR2RGB))) #Tkinterのウインドウのwebカメラを表示させる範囲にgamma_imgを設定
#PIL.Image.fromarrayで画像を指定(PillowとOpenCVの色空間が違うので
#変換)
canvas.create_image(0,0,image=app.img,anchor=tkinter.NW) #画像の座標を(0,0)(原点は画像の左上端)に表示する画像をapp.imgに
#基準位置を画像の左上に設定
app.after(1,gamma_trans) #1ms間隔でgamma_transを実行(この処理がないとリアルタイムなので
#処理が重すぎて固まる)
gamma_trans() #この部分がないとTkinter上にwebカメラからのリアルタイムの映像が表示されない(関数内でTkinterでの画像表示も組み込んでいるため)
app.mainloop() #GUIの表示(この処理がないと一瞬表示されて消えるので表示されていないように見える)
結果
下に結果を示します。
実行結果
まとめ
今回は、前回のTwitter(X)のアンケートでトップだった動画像処理からwebカメラから得られた映像にγ変換
をかけて明るくしたリアルタイムの動画をTkinter
を用いて表示させγ
の値をTkinter
のtkinter.Scale
で調整させるプログラムを作成しました。
この記事が実際に役に立つかどうかは分かりませんが、誰かの役に立ってくれると嬉しいです。
記事を執筆する余力があれば、次回も記事を投稿する予定です。
次回の予定としては、今回のTwitter(X)のアンケートでトップだったファイル操作から HTML
で書かれた文字列から HTML
タグを取り除いて Windows
のメモ帳に HTML
タグのない文章を書き込む(テキストファイル名には、生成時の日付が入っている)プログラムを作成できたのでそれに関する記事を投稿予定です!!