つれづれなる備忘録

日々の発見をあるがままに綴る

OpenCVの使い方31 ~ ヒストグラム逆投影法

今回はOpenCVヒストグラム逆投影法を用いた領域の切り出し方法について紹介する。

atatat.hatenablog.com

1. 画像読み込み

今回は標準画像データベースのうちPepperを利用する。

import cv2
import numpy as np
from matplotlib import pyplot as plt

import cv2
import numpy as np
from matplotlib import pyplot as plt

orig = cv2.imread(uploaded_file_name)
src = cv2.cvtColor(orig, cv2.COLOR_BGR2RGB)
plt.imshow(src)
plt.title('Original')

以下のように読み込んだ画像から緑の領域を切り出すことを考える。

"Pepper"
Pepper

画像全体origをもとのBGRからHSVに変換してhsvtとしておく。 中央の緑のペッパーのヒストグラムを用いて、画像全体の緑の領域を切り出す。 とりあえずX:125~170, Y:150~200の領域を切り出して表示させてみる。

hsvt=cv2.cvtColor(orig, cv2.COLOR_BGR2HSV)
roi=src[150:200,125:170,:]
plt.imshow(roi)

"Green Pepper"
Green Pepper

2. ヒストグラム計算

配列srcはカラーがBGRからRGBに変換されているので、もとのBGRからHSVに変換するため配列origを用いて上で確認した領域 X:125~170, Y:150~200を切り出してhsvに変換してからヒストグラムを計算する。ヒストグラムを計算するにはcv2.calcHist([hsv],[0,1],None,[180,255],[0,180,0,256])を用いる。引数[hsv]は入力配列、[0,1]ヒストグラムを計算するカラーチャネルで色相Hと彩度Sを指定する。Noneはマスクオプションなし、[180, 256]はHとSのヒストグラムのサイズ(Bin数)、[0,180, 0, 256]ヒストグラムの出力レンジで最小と最大を指定する。

roi=orig[150:200,125:170,:]
hsv=cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
roihist=cv2.calcHist([hsv],[0,1],None,[180,255],[0,180,0,256])

3. 逆投影計算

 逆投影の前にヒストグラム計算の結果roihistcv2.normalize()で0~255に正規化しておく。次に切り出しを行うPepperのHSV画像hsvtと緑の領域の正規化したヒストグラムroihistを用いて逆投影dstcv2.calcBackProject([hsvt],[0,1],roihist,[0,180,0,256],1)によって計算する。引数[0, 1]はカラーチャネルでcalcHistに用いた[0, 1]を指定、[0,189,0,256]ヒストグラムのレンジで、カラーチャネルと同じくcalcHistに用いた[0,189,0,256]を指定する。最後の引数1は逆投影の出力スケールでとりあえず1を指定する。逆投影の結果dstを表示すると緑色の部分に強度があり、赤色の部分はほぼ0になっている。

cv2.normalize(roihist,roihist,0,255,cv2.NORM_MINMAX)
dst=cv2.calcBackProject([hsvt],[0,1],roihist,[0,180,0,256],1)
plt.imshow(dst,cmap='gray',vmin=0,vmax=255)
plt.colorbar()

"逆投影の結果"
逆投影の結果

4. 領域の切り出し

 逆投影の結果dstを元にしたマスクを作成することで、最終的にPepper画像から緑の領域を切り出す。下処理として円形領域を用いてフィルタをかけて少し滑らかに領域が選択できるようにする。次にcv2.threshold(dst,150,255,0)で強度150を閾値としたバイナリマスクを作成し、cv2.merge((thresh,thresh,thresh))として3色分のバイナリマスクになるようにする。最後にcv2.bitwise_and(src,thresh)としてRGBのPepper画像であるsrcにバイナリマスクthreshを適用して、緑の領域を切り出した画像resを生成する。 元の画像src,バイナリマスクthresh, 緑の領域を切り出したres, をnp.hstack()で1つの配列(画像)にまとめて表示して比較できるようにした。

disc=cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(2,2))
cv2.filter2D(dst,-1,disc,dst)
ret,thresh=cv2.threshold(dst,150,255,0)
thresh=cv2.merge((thresh,thresh,thresh))
res=cv2.bitwise_and(src,thresh)
res3=np.hstack((src,thresh,res))
plt.figure(figsize=(15,8))
plt.imshow(res3)res=np.hstack((src,thresh,res))
plt.figure(figsize=(15,8))
plt.imshow(res)

虫食いにようになっているところは、HSVヒストグラムroihistと部分的にずれているためだが、より滑らかにするにはフィルタ処理やerosion, dilationで虫食いの領域をつぶすなどが考えられる。

"逆投影法により切り出し結果"
逆投影法により切り出し結果

5. まとめ

今回はヒストグラム逆投影を用いた領域の切り出し処理について紹介した。