LoginSignup
0
3

ガンマ補正を用いたリアルタイム映像の明るさ調整【Python】

Posted at

目次

はじめに
実行環境
webカメラの幅と高さの取得方法
メインウィンドウのサイズの設定方法
リアルタイム映像の表示領域の設定方法
スライドバーで小数を扱う方法
スライドバーの設定方法
ガンマ補正の実装方法
ルックアップテーブルの作成方法
ガンマ補正の処理方法
リアルタイム画像のtkinterでの表示方法
定期的に処理を実行する方法
ソースコード
結果
まとめ

はじめに

今回は、前回のTwitter(X)のアンケートでトップだった動画像処理についての記事です。
そのジャンルの中から、webカメラから得られた映像にγ変換をかけて明るくしたリアルタイムの動画をTkinterを用いて表示させγの値をTkintertkinter.Scaleで調整させるプログラムを作成したのでそれについて紹介したいと思います。


マイページについて

主にPythonについての記事を投稿してます!!

マイページはこちら

X(Twitter)について

Qiitaの新着記事の通知おススメの理工学書の紹介をツイートしてます!!

X(Twitter)はこちら

Zennについて

主に自分の書いたQiitaの記事の転載Pythonを使っていて遭遇したエラーの対処法のような記事を投稿していく予定です!!

Zennはこちら

実行環境

実行環境は次の通りです。

実行環境
  • 環境

    • 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より大きくしました。

これらの情報をもとにγ補正を実装しました。

ルックアップテーブルの作成方法

γ補正の計算結果を格納する配列の作成方法

γ補正の計算結果を格納する変数を作成するために
numpyzeros 関数を利用しました。

こちらのソース

によると、numpyzeros関数は、

配列の全要素が0で初期化されている

関数だそうです。
この関数を使って、γ補正の計算結果を格納する配列を作成しました。

OpenCVのカラー画像では、値の範囲が0~255の8bitです。そのため、要素の型の指定ができるパラメータであるdtypenumpy で8bitデータを指している np.uint8 を指定しました。

γ補正からのルックアップテーブルの作成方法

ガンマ補正の方法で紹介したγ補正の公式である

Y=255\times\Bigl(\frac{X}{255}\Bigl)^\frac{1}{\gamma}

ここで、

Y:γ補正後のピクセル値
X:γ補正前のピクセル値
γ:ガンマ補正値

の公式を使って、 γ補正の計算結果を格納する配列の作成方法で紹介した配列に計算結果を0~255までループ処理で逐次代入して作成しました。

ガンマ補正の処理方法

こちらのソース

によると、OpenCVcv2.LUTを使うことでルックアップテーブルをもとにした階調変換ができるそうです。
この方法でルックアップテーブルを使ってリアルタイム画像の階調変換を行いました。

リアルタイム画像のtkinterでの表示方法

OpenCVの画像のtkinterでの使用方法

こちらのソース

によると、PIL(pillow)ImageTkモジュールのPhotoImageクラスを使うことでtkinterの画像オブジェクトに変換できるそうです。

また、こちらのソース

によると、次の手順でOpenCVのデータをtkinterで使えるデータに変換できるそうです。

  1. OpenCVの画像データをBGR色空間からRGB色空間にcv2.cvtColorで変換
  2. OpenCVの画像データのようなnumpy配列のデータをPIL(pillow)Imageモジュールのfromarrayクラスを使ってPIL(pillow)の画像データに変換

この手順でOpenCVのデータをPIL(pillow)のデータにすることができるそうです。

これらの方法を使って、OpenCVで得られたリアルタイム画像をtkinterで表示できるように設定しました。

変換した画像の表示方法

OpenCVの画像のtkinterでの使用方法OpenCV の画像を tkinter で使えるように変換できました。この変換された画像を

こちらのソース

によると、tkinterCanvasクラスのcreate_imageメソッドを使うと画像をtkinterに表示できるそうです。
このメソッドを使って、画像をtkinter上に表示させました。

また、こちらのソース

によると、anchorオプションを使うことで、画像の基準位置を設定できるそうです。
このオプションのtkinter.NW
を指定しました。この値を指定することで画像の左上を基準位置として設定しました。

座標(0,0) に基準位置が来るように設定したため画像の左上を基準位置として設定しました。
こうすることで、webカメラの幅と高さに依存しない位置に画像を配置できました。

定期的に処理を実行する方法

こちらのソース

によると、afterオプションを関数内で使うことで定期的に処理を実行させることができるそうです。
このメソッドを使って、処理を定期的に実行させました。

ソースコード

下にソースコードを示します。おそらく実行環境で示した環境では動くはず。

ソースコード
img_gamma.py
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を用いて表示させγの値をTkintertkinter.Scaleで調整させるプログラムを作成しました。
この記事が実際に役に立つかどうかは分かりませんが、誰かの役に立ってくれると嬉しいです。
記事を執筆する余力があれば、次回も記事を投稿する予定です。
次回の予定としては、今回のTwitter(X)のアンケートでトップだったファイル操作から HTML で書かれた文字列から HTML タグを取り除いて Windows のメモ帳に HTML タグのない文章を書き込む(テキストファイル名には、生成時の日付が入っている)プログラムを作成できたのでそれに関する記事を投稿予定です!!

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3