OpenCVを使ったPythonでの画像処理について、ここではヒストグラムを求める方法について見ていこうと思います。
ヒストグラムとは、縦軸に度数、横軸に階級をとった統計グラフの1つです。データの分布状況を視覚的に認識するために用いられ、度数分布図などとも言われます。
画像処理におけるヒストグラムは、横軸に画素値、縦軸に各画素値の頻度をとったヒストグラムとなります。
OpenCVによる画像ヒストグラム
まず画像を読み込んでいくことからはじめましょう。jupyter notebookを使って作業していきます。
各種ライブラリーをインポートします。
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
ここで利用する画像を3つ用意して読み込んでいきましょう。
white_eye = cv2.imread('images/white-eye.jpg')
show_bird = cv2.cvtColor(white_eye, cv2.COLOR_BGR2RGB)
color_img = cv2.imread('images/color.jpg')
show_color =cv2.cvtColor(color_img, cv2.COLOR_BGR2RGB)
wall = cv2.imread('images/wall.jpg')
show_wall = cv2.cvtColor(wall, cv2.COLOR_BGR2RGB)
作業ディレクトリにimagesフォルダを用意して、3つのjpgファイルを入れています。white-eye.jpg以外は以前に使った画像です。
imread()でそれぞれの画像を読み込み、cvtColor()を使ってカラーモデルをBGRからRGBに変換しています。Matplotlibで読み込むと色合いが変わってしまうので、RGBにする必要があります。
画像を表示します。
plt.imshow(show_bird)
plt.imshow(show_color)
plt.imshow(show_wall)
imshow()でそれぞれの画像を表示してみます。
calcHist()
calcHist()を使ってヒストグラムを計算してみます。
hist_values = cv2.calcHist([wall],channels=[0],mask=None,histSize=[256],ranges=[0,256])
hist_values.shape
plt.plot(hist_values)
関数の使い方としては次のようになります。
cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])
- imagesは、入力画像でuint8またはfloat32のデータ型で、角括弧を使って[img]で指定します。
- channelsは、ヒストグラムを計算する対象となるチャネルのインデックスで角括弧で指定します。たとえば、グレースケールの場合は[0]です。カラー画像の場合は、[0]、[1]、または[2]を渡して、それぞれ青(B)、緑(G)、または赤(R)の計算をします。
- maskはマスク画像です。フルイメージのヒストグラムを見つけるためには、Noneを指定します。しかし、画像の特定の領域のヒストグラムを計算する場合は、そのためのマスク画像を作成し、それをマスクとして指定します。
- histSizeはビンの数で、角括弧で指定します。フルスケールでは[256]を渡します。
- rangesはヒストグラムを計算したい画像値の範囲です。通常は[0,256]です。
先ほどのコードを実行してみます。
暗い画像であると、左側の0の部分の値が大きくなるはずです。
他の画像でも同様に試してみるとよいでしょう。
3色のヒストグラムの表示
上では1色についてのヒストグラムを表示して見ましたが、B、G、Rの3色についても計算してみようと思います。
img = wall
color = ('b','g','r')
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
plt.title('Wall Image')
plt.show()
読み込んだ画像をimgに格納し、colorにタプルでb、g、rを格納しています。このcolorをfor-inループを使ってenumrate()で取り出します。取り出されたインデックスを使ってcalcHist()を計算し、plotで色を指定して表示します。xlim()で範囲の最小値、最大値を指定しています。あとはタイトルをつけて全体の表示です。
同様に他の画像もやってみましょう。
img = white_eye
color = ('b','g','r')
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
plt.title('Mejiro')
plt.show()
img = color_img
color = ('b','g','r')
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
plt.title('Color Image')
plt.show()
それぞれの画像の特徴をヒストグラムから読み取ってみるともよいと思います。
マスク
画像の特定の部分だけにマスクをかけてみます。そしてヒストグラムを計算してみてることにします。
マスクを作っていきましょう。
img = color_img
img.shape
マスクをかける画像をimgに格納して、shapeで画像のサイズをチェックします。
このデータを利用してマスクを作っていきます。
mask = np.zeros(img.shape[:2], np.uint8)
mask[300:400, 100:400] = 255
zeros()を使って黒いマスクを作っています。shape[:2]としているのは、先ほどのshapeで求めた(450,600,3)の左側2つ目までを使ってshapeを指定するためです。これで全体が黒色のマスクになりました。
これをmask[300:400, 100:400]と範囲指定したところに255の値を入れることで、この部分のみが白くなるマスクになります。
imshow()を使ってマスクを表示します。
plt.imshow(mask,cmap='gray')
表示するとこうなります。
このマスクを使って、画像に被せてみます。
# masked_img = cv2.bitwise_and(img,img,mask = mask)
show_masked_img = cv2.bitwise_and(show_color,show_color,mask = mask)
bitwise_and()を使ってマスクをかけてみます。上側は元の画像に、下側はRGBで表示したものにマスクをかけています。画像を合成する訳ではないので、第1引数と第2引数は同じ値で構いません。
plt.imshow(show_masked_img)
表示してみましょう。
マスクのかけられた画像が表示されました。
bitwise_and()とマスクについてはこちらも参照ください。
マスクをかけた画像のヒストグラムを作ってみましょう。
hist_mask_values_red = cv2.calcHist([color_img],channels=[2],mask=mask,histSize=[256],ranges=[0,256])
hist_full_values_red = cv2.calcHist([color_img],channels=[2],mask=None,histSize=[256],ranges=[0,256])
上側がマスクをかけた画像について、下側は元の画像についてです。
channelsが[2]となっているのは、BGRのRについて計算するためです。下側は元画像なのでmaskをNoneにしています。
plt.plot(hist_mask_values_red)
plt.title('Histogram for RED values for the Masked Area')
マスク画像のヒストグラムを表示します。
元画像のヒストグラムを表示します。
plt.plot(hist_full_values_red)
plt.title('Histogram for RED values of the full image')
マスクの有無での違いが確認できるのがわかります。
ヒストグラム平坦化
次は、ヒストグラム平坦化の処理についてみていきます。
ヒストグラム平坦化とは、画素値が特定の部分に偏っている場合に、この部分を満遍なく広げるようにすることで、画像を調整することです。例えば、明るい画像は画素値が高い部分に集中してしまっているので、それをなだらかにすることによって、見やすい画像にすることができます。
画像のコントラストを調整することができる処理となります。
ここでは次の画像を使ってみます。(images/golden-retriever.jpg)
シングルチャンネル(グレースケール)
まずはグレースケール画像を読み込むところから始めましょう。
dog = cv2.imread('images/golden-retriever.jpg',0)
imread()を使って画像を読み込みます。グレースケールで読み込むので0を指定しています。
def display(img,cmap=None):
fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111)
ax.imshow(img,cmap)
画像表示の関数をdisplay()で定義します。この時点ではcmapを指定していません。figure()で表示のアスペクト比を指定して、add_subplot()で111を指定して領域の画像左上を基準に表示するようにします。imshow()で実際に表示します。
display(dog,cmap='gray')
cmapをグレースケールに指定して、dogの画像を表示してみましょう。
これまでと同様に、calcHist()でヒストグラムを計算していきましょう。
hist_values = cv2.calcHist([dog],channels=[0],mask=None,histSize=[256],ranges=[0,256])
plt.plot(hist_values)
ヒストグラムを表示してみます。
画像のコントラストがどうなっているのかわかります。
これに対してヒストグラム平坦化を行っていきます。それには、equalizeHist()を使います。
eq_dog = cv2.equalizeHist(dog)
display(eq_dog,cmap='gray')
equalizeHist()にdogの画像をして平坦化しています。それをグレースケールで先ほど定義したdisplay()関数で表示しています。
最初に表示した元画像よりも平坦化によりコントラストが違っているのがわかると思います。
この平坦化した画像のヒストグラムを計算してみましょう。
hist_values = cv2.calcHist([eq_dog],channels=[0],mask=None,histSize=[256],ranges=[0,256])
plt.plot(hist_values)
計算はこれまでと同様にcalcHist()です。
ヒストグラムを表示してみましょう。
ヒストグラム平坦化による違いがわかると思います。両端の暗い値と明るい値の部分が上にかさ上げされていますし、間の部分細かく値を下げているのがわかります。これで平坦化されているというのが数値上もわかるわけです。
カラー画像
グレースケールの画像を扱ったので、今度はカラーの画像について処理してみます。
color_dog = cv2.imread('images/golden-retriever.jpg')
show_dog = cv2.cvtColor(color_dog,cv2.COLOR_BGR2RGB)
hsv = cv2.cvtColor(color_dog, cv2.COLOR_BGR2HSV)
imread()でこれまでと同様に画像を読み込みます。cvtColor()でカラーを変換しますが、BGRをRGBに変換し、BGRをHSVにも変換しています。
HSVについてはこちらも参照ください。
画像を表示していきましょう。
display(show_dog)
RGB形式で元画像が表示されることになります。
HSVに変換した画像の明度のチャンネルを指定していきましょう。
hsv[:,:,2]
ここで、2を指定しています。これが明度を示すチャンネルです。ここを0にすると色相、1にすると彩度になります。
これをequalizeHist()で平坦化します。
hsv[:,:,2] = cv2.equalizeHist(hsv[:,:,2])
この平坦化したHSVの画像をRGBに変換して表示します。
eq_color_dog = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
display(eq_color_dog)
cvtColor()にCOLOR_HSV2RGBを指定して、hsvを変換しています。
表示してみましょう。
グレースケールでやった時と同様に、コントラストが調整されているのがわかります。
最後に
ここでは、OpenCVを使ったPythonでの画像処理について、ヒストグラムを扱ってきました。
画像処理におけるヒストグラムは、横軸に画素値、縦軸に各画素値の頻度をとったヒストグラムとなります。
ヒストグラムを求めるにはcalcHist()を使います。
画像のコントラストを調整するのにヒストグラム平坦化があります。ヒストグラム平坦化は画像の暗い部分に偏った部分、明るい部分に偏った部分を全体でならす処理です。これはequalizeHist()で処理します。