📝

PythonのライブラリであるOpenCVを用いた中心差分による画素値ごとの微分

2023/08/02に公開
1

※この記事は自分のQiitaの記事の転載です。
Qiitaの記事はこちら

https://qiita.com/blueman/items/8b9b8a719d59c6f32979

前回の記事はこちら

https://zenn.dev/blueman/articles/e30de15f0c9bf3


はじめに

前回の記事では、数値微分の中で最も精度が高いのは中心差分らしいということが分かりました。
今回は、中心差分を用いて画像の画素値ごとに中心差分を施していきOpenCVを用いて表示させようと思います。また、matplotlibを用いてRGBのそれぞれのヒストグラムを表示させようと思います。

前回の記事はこちら

https://qiita.com/blueman/items/542064585cd44f894494


よろしければこちらもどうぞ!!

マイページについて

https://qiita.com/blueman


X(Twitter)について

https://twitter.com/0ca00118726208m


Qiitaについて

実行環境

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

実行環境
  • 環境
    • Windows 10
    • Python 3.10.5
  • ライブラリ
    • OpenCV 4.8.0
    • matplotlib 3.6.1

画像はこちらを使います。

使った画像
使った画像

画像の引用元はこちらです

http://www.ess.ic.kanagawa-it.ac.jp/app_images_j.html

画像の画素値へのアクセス方法

https://biotech-lab.org/articles/872

BGRでの画素値へのアクセス方法

上の参照元によると、

画像の画素値へのアクセスは画像の配列に画像のy座標を第1引数、画像のx座標を第2引数、整数値を第3引数に指定するとBGRそれぞれの画素値が整数値で返される

らしいのでその方法でRGBの画素値へアクセスします。
ちなみに、第3引数に指定する整数値は次の通りです。

第3引数の整数値の説明

整数値の説明

整数値
0 青(B)
1 緑(G)
2 赤(R)

グレースケールでの画素値へのアクセス方法

また、グレースケール画像では第1引数に画像のy座標、第2引数に画像のx座標を指定すると画素値が整数値で返される

らしいのでその方法でグレースケールの画素値へアクセスします。

画素値の演算方法

画像の画素値へのアクセス方法で紹介した方法で画像の画素値を取得します。

その次に、前回の記事で紹介した中心差分を求める式である下の式

f'(x)\approx \frac{f(x+h)-f(x-h)}{2h}

xの部分を画素値に置き換えて演算します。

具体例

今回は、f(x)=x^2としました。

理由

以上のことから、画像の画素値の中心差分を用いた式は次のように書き換えられます。

f'(img[y,x,0])=\dfrac{(img[y,x,0]+h)^2-(img[y,x,0]-h)^2}{2h} \tag{1}

(1)式と同様に考えるとほかの色についても実装できるので(1)式のimg[y,x,0]の部分をそれぞれの色の引数に変えて実装しました。

ソースコード

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

ソースコード(青(B)の処理)
B_img_transform.py
import cv2 #OpenCVをインポート
import matplotlib.pyplot as plt #matplotlibをpltという名前でインポート

h = 10**(-4) #中心差分のhの定義
blue_val=[] #青(B)の画素値を格納する配列の定義
green_val=[] #緑(G)の画素値を格納する配列の定義
red_val=[] #赤(R)の画素値を格納する配列の定義

img = cv2.imread('lena_std.jpg') #画像の読み込み

for i in range(img.shape[0]): #画像のy座標iを画像の高さの分imgの配列の第1引数に入力
	for j in range(img.shape[1]): #画像のx座標jを画像の幅の分imgの配列の第2引数に入力
		img[i,j,0] = ((img[i,j,0]+h)**2-(img[i,j,0]-h)**2)/(2*h) #画像上にあるすべての青(B)の画素値に中心差分を施す
		img[i,j,1]=0 #画像上にあるすべての緑(G)の画素値を0にする
		img[i,j,2]=0 #画像上にあるすべての赤(R)の画素値を0にする
		
		blue_val.append(img[i,j,0]) #blue_valに新しくimg[i,j,0]の計算結果を代入
		green_val.append(img[i,j,1]) #green_valに新しく値0を代入
		red_val.append(img[i,j,2]) #red_valに新しく値0を代入

cv2.imshow("tyuusinsabun",img) #処理結果を出力
cv2.waitKey() #window closeボタンが押されるまで待機

plt.figure(tight_layout=True) #ヒストグラムが重ならないように調整

plt.subplot(221,title="Blue") #2×2のグラフ領域の左上に青(B)のヒストグラムを表示
plt.hist(blue_val,ec='black') #青(B)のヒストグラムのグラフの枠線を黒(black)に設定
plt.subplot(222,title="Green") #2×2のグラフ領域の右上に緑(G)のヒストグラムを表示
plt.hist(green_val,ec='black') #緑(G)のヒストグラムのグラフの枠線を黒(black)に設定
plt.subplot(223,title="Red") #2×2のグラフ領域の左下に赤(R)のヒストグラムを表示
plt.hist(red_val,ec='black') #赤(R)のヒストグラムのグラフの枠線を黒(black)に設定
plt.show() #全体のヒストグラムを表示
ソースコード(緑(G)の処理)
G_img_transform.py
import cv2 #OpenCVをインポート
import matplotlib.pyplot as plt #matplotlibをpltという名前でインポート

h = 10**(-4) #中心差分のhの定義
blue_val=[] #青(B)の画素値を格納する配列の定義
green_val=[] #緑(G)の画素値を格納する配列の定義
red_val=[] #赤(R)の画素値を格納する配列の定義

img = cv2.imread('lena_std.jpg') #画像の読み込み

for i in range(img.shape[0]): #画像のy座標iを画像の高さの分imgの配列の第1引数に入力
	for j in range(img.shape[1]): #画像のx座標jを画像の幅の分imgの配列の第2引数に入力
		img[i,j,0]=0 #画像上にあるすべての青(B)の画素値を0にする
		img[i,j,1] = ((img[i,j,1]+h)**2-(img[i,j,1]-h)**2)/(2*h) #画像上にあるすべての緑(G)の画素値に中心差分を施す
		img[i,j,2]=0 #画像上にあるすべての赤(R)の画素値を0にする
		
		blue_val.append(img[i,j,0]) #blue_valに新しく値0を代入
		green_val.append(img[i,j,1]) #green_valに新しくimg[i,j,1]の計算結果を代入
		red_val.append(img[i,j,2]) #red_valに新しく値0を代入

cv2.imshow("tyuusinsabun",img) #処理結果を出力
cv2.waitKey() #window closeボタンが押されるまで待機

plt.figure(tight_layout=True) #ヒストグラムが重ならないように調整

plt.subplot(221,title="Blue") #2×2のグラフ領域の左上に青(B)のヒストグラムを表示
plt.hist(blue_val,ec='black') #青(B)のヒストグラムのグラフの枠線を黒(black)に設定
plt.subplot(222,title="Green") #2×2のグラフ領域の右上に緑(G)のヒストグラムを表示
plt.hist(green_val,ec='black') #緑(G)のヒストグラムのグラフの枠線を黒(black)に設定
plt.subplot(223,title="Red") #2×2のグラフ領域の左下に赤(R)のヒストグラムを表示
plt.hist(red_val,ec='black') #赤(R)のヒストグラムのグラフの枠線を黒(black)に設定
plt.show() #全体のヒストグラムを表示
ソースコード(赤(R)の処理)
R_img_transform.py
import cv2 #OpenCVをインポート
import matplotlib.pyplot as plt #matplotlibをpltという名前でインポート

h = 10**(-4) #中心差分のhの定義
blue_val=[] #青(B)の画素値を格納する配列の定義
green_val=[] #緑(G)の画素値を格納する配列の定義
red_val=[] #赤(R)の画素値を格納する配列の定義

img = cv2.imread('lena_std.jpg') #画像の読み込み

for i in range(img.shape[0]): #画像のy座標iを画像の高さの分imgの配列の第1引数に入力
	for j in range(img.shape[1]): #画像のx座標jを画像の幅の分imgの配列の第2引数に入力
		img[i,j,0]=0 #画像上にあるすべての青(B)の画素値を0にする
		img[i,j,1]=0 #画像上にあるすべての緑(G)の画素値を0にする
		img[i,j,2] = ((img[i,j,2]+h)**2-(img[i,j,2]-h)**2)/(2*h) #画像上にあるすべての赤(R)の画素値に中心差分を施す
		
		blue_val.append(img[i,j,0]) #blue_valに新しく値0を代入
		green_val.append(img[i,j,1]) #green_valに新しく値0を代入
		red_val.append(img[i,j,2]) #red_valに新しくimg[i,j,2]の計算結果を代入

cv2.imshow("tyuusinsabun",img) #処理結果を出力
cv2.waitKey() #window closeボタンが押されるまで待機

plt.figure(tight_layout=True) #ヒストグラムが重ならないように調整

plt.subplot(221,title="Blue") #2×2のグラフ領域の左上に青(B)のヒストグラムを表示
plt.hist(blue_val,ec='black') #青(B)のヒストグラムのグラフの枠線を黒(black)に設定
plt.subplot(222,title="Green") #2×2のグラフ領域の右上に緑(G)のヒストグラムを表示
plt.hist(green_val,ec='black') #緑(G)のヒストグラムのグラフの枠線を黒(black)に設定
plt.subplot(223,title="Red") #2×2のグラフ領域の左下に赤(R)のヒストグラムを表示
plt.hist(red_val,ec='black') #赤(R)のヒストグラムのグラフの枠線を黒(black)に設定
plt.show() #全体のヒストグラムを表示
ソースコード(グレースケールの処理)
grayscale_img_transform.py
import cv2 #OpenCVをインポート
import matplotlib.pyplot as plt #matplotlibをpltという名前でインポート

h = 10**(-4) #中心差分のhの定義
grayscale_val=[] #グレースケールの画素値を格納する配列の定義

img = cv2.imread('lena_std.jpg') #画像の読み込み
img_2 = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #読み込んだ画像をグレースケールに変換

for i in range(img_grayscale.shape[0]): #画像のy座標iを画像の高さの分imgの配列の第1引数に入力
	for j in range(img_grayscale.shape[1]): #画像のx座標jを画像の幅の分imgの配列の第2引数に入力
		img_grayscale[i,j] = ((img_grayscale[i,j]+h)**2-(img_grayscale[i,j]-h)**2)/(2*h) #画像上にあるすべてのグレースケールの画素値に中心差分を施す
		
		grayscale_val.append(img_grayscale[i,j]) #graycale_valに新しくimg[i,j]の計算結果を代入

cv2.hconcat("tyuusinsabun",[img_2,img_grayscale]) #処理結果を出力
cv2.waitKey() #window closeボタンが押されるまで待機

plt.hist(grayscale_val,ec='black') #グレースケールのヒストグラムを表示
plt.show() #グレースケールのヒストグラムを表示

結果

まず、画像の処理結果が

青(B)の処理結果
画像

青(B)の処理結果(画像)
青(B)の処理結果(画像)

ヒストグラム

青(B)のヒストグラム
青(B)のヒストグラム

緑(G)の処理結果
画像

緑(G)の処理結果(画像)
緑(G)の処理結果(画像)

ヒストグラム

緑(G)のヒストグラム
緑(G)のヒストグラム

赤(R)の処理結果
画像

赤(R)の処理結果(画像)
赤(R)の処理結果(画像)

ヒストグラム

赤(R)のヒストグラム
赤(R)のヒストグラム

グレースケールの処理結果
画像

グレースケール画像
オリジナルのグレースケール画像
グレースケールの処理結果(画像)
変換後の画像

ヒストグラム

グレースケール(オリジナル)のヒストグラム
グレースケール(オリジナル)のヒストグラム
グレースケール(変換後)のヒストグラム
グレースケール(変換後)のヒストグラム

となります。

特徴

画像処理で微分と聞くと微分フィルタを思い浮かべられると思いますが、微分フィルタでは隣り合う画素の差分を取ることで微分を表現しています。
この手法では、画素ごとに中心差分を取ることで画像の画素単体での微分を行うことができます。

手法についての特徴
従来 この手法
隣り合う画素の差分を取る 画素ごとの中心差分を計算
ヒストグラムについての特徴

各処理結果のヒストグラムを見ると、ヒストグラムの先端が2次関数で近似できそうなところがあります!!

処理画像についての特徴

処理結果の画像を見ると、ところどころ共通な部分がありそうです!!

まとめ

今回は、Pythonのライブラリである OpenCV を用いてRGB画像やグレースケール画像の画像上のすべての画素値を取得しました。また、それらの画素値を用いて画素値ごとの中心差分を行うことで画像の微分を実装しました。
そして、画像のRGBの画素値やグレースケールの画素値からヒストグラムを作成しました。
この記事が実際に役に立つかどうかは分かりませんが、誰かの役に立ってくれると嬉しいです。
記事を執筆する余力があれば、次回も記事を投稿する予定です。
次回の予定としては、動画から手の姿勢を推論するライブラリである MediaPipe Hands を用いて指の先端部分をマークすることができたのでそのことに関しての記事を投稿する予定です。

Discussion