0
1

More than 1 year has passed since last update.

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

Last updated at Posted at 2023-08-02

目次

はじめに
実行環境
画像の画素値へのアクセス方法
画素値の演算方法
ソースコード
結果
特徴
まとめ

はじめに

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

分かりやすくするためにBGRでの実装では注目色以外の色の値を0にしています


前回の記事はこちら


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

マイページについて

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

マイページはこちら


X(Twitter)について

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

X(Twitter)はこちら


Zennについて

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

Zennはこちら


実行環境

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

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

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

使用した画像

使用した画像

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

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

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

上の参照元によると、

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

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

第3引数の整数値の説明
整数値の説明
整数値
0 青(B)
1 緑(G)
2 赤(R)

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

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

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

画素値は0~255で返されます

画素値の演算方法

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

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

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

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

具体例

例えば、青(B)の場合は

f'(img[y,x,0])= \frac{f(img[y,x,0]+h)-f(img[y,x,0]-h)}{2h}

として演算します。

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

理由

$f(x)=x^2$とすることで、$f'(x)=2x$となるので微分した式が線形となり考えやすくなるためです。

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

f'(img[y,x,0])= \frac{(img[y,x,0]+h)^2-(img[y,x,0]-h)^2}{2h} \label{a}\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)の処理結果(画像)


緑(G)の処理結果

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


赤(R)の処理結果

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


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

左がオリジナルのグレースケール画像
右が変換後の画像です。

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


次に、それぞれのヒストグラムが

青(B)のヒストグラム

青(B)のヒストグラム


緑(G)のヒストグラム

緑(G)のヒストグラム


赤(R)のヒストグラム

赤(R)のヒストグラム


グレースケールのヒストグラム

左がオリジナルのグレースケールのヒストグラム
右が変換後のグレースケールのヒストグラムです。

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

となります。

特徴

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

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

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

処理画像についての特徴

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

まとめ

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

0
1
1

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
1