pythonで一から画像処理 (3)エッジ検出、モルフォロジー変換

基本となる画像処理を一から勉強していくシリーズ (3)。

OpenCV-Pythonチュートリアルを参考に、
画像認識本　https://www.amazon.co.jp/dp/4061529129/
でやっている処理の理解を進める方針です。

#目次
1. 環境
2. Sobel、Laplacianフィルタ
3. Cannyエッジ検出
4. モルフォロジー変換
5. 輪郭抽出トライ

#1. 環境
Python 3.7.0
OpenCV 4.1.0
Jupyter Notebook

#2. Sobel、Laplacianフィルタ
Sobelフィルタは1次微分によるエッジの検出で、注目画素を重要視したもの。
Laplacianフィルタは2次微分によるエッジの検出。

```python:Sobel_LaplacianFilter.py
img = cv2.imread('brabra/1.jpg',0)

#Laplacianフィルタ
laplacian = cv2.Laplacian(img,cv2.CV_64F)
#Sobelフィルタx方向
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=15)
#Sobelフィルタy方向
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=15)

plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])

plt.show()
```
![SobelLaplacian.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480516/18d039f1-f2fa-4cb6-7ca1-877af1ae3151.png)

Sobelフィルタではx,y方向のエッジに反応している。

#3. Cannyエッジ検出
・ガウシアンフィルタにより平滑化後（5x5のカーネル）、
・Sobelフィルタによってx,y方向の1次微分(Gx,Gy)を取得して
　√(Gx^2 + Gy^2)のgradientを得る。勾配方向も(Gx,Gy)から得ます。
・次に、その注目画素の勾配方向の両隣の画素と比較し、
　注目画素が極大値でなければエッジとしない処理（細線化）を行う。
・次に、minVal,maxValを設定し、minValより小さければ除去、
　maxValより大きければ残す、minVal~maxValでは隣がmaxValより大きければ
　残す処理を行う。これにより輪郭を繋げることと、誤検出を取り除くそう。

```python:Canny.py
img = cv2.imread('brabra/1.jpg',0)
minVal = 50
maxVal = 250
SobelSize = 10

edges = cv2.Canny(img,minVal,maxVal,SobelSize)

plt.subplot(251),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(252),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])

plt.show()
```
![CAnny.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480516/659dba38-ed5e-c2d7-b212-bc8594fc1f63.png)

輪郭はうまく繋がってはいないが、形状は認識できている感じ。

#4. モルフォロジー変換
モルフォロジー=形態。全体的に太くしたり細くしたりする。
・Erosion(収縮)
・Dilation(膨張)
・Opening(収縮⇒膨張　小さい点のようなノイズ除去)
・Closing(膨張⇒収縮　小さい箇所の穴埋め)
・勾配（膨張-収縮で輪郭を得る）
・トップハット（入力-Opening）
・ブラックハット（入力-Closing）

演習には2値化されたパンダを使用。

```python:Mol.py
img = cv2.imread('brabra/6.jpg',0)
#大津の2値化されたパンダ
abc,two = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
#白黒反転（白に適用されるため）
two = cv2.bitwise_not(two)

#Erositon
kernel = np.ones((35,35),np.uint8)
erosion = cv2.erode(two,kernel,iterations = 1)
#Dilation
kernel = np.ones((35,35),np.uint8)
dilation = cv2.dilate(two,kernel,iterations = 1)
#Opening
kernel = np.ones((35,35),np.uint8)
opening = cv2.morphologyEx(two, cv2.MORPH_OPEN, kernel)
#Closing
kernel = np.ones((35,35),np.uint8)
closing = cv2.morphologyEx(two, cv2.MORPH_CLOSE, kernel)
#Gradient
kernel = np.ones((35,35),np.uint8)
gradient = cv2.morphologyEx(two, cv2.MORPH_GRADIENT, kernel)
#Tophat
kernel = np.ones((35,35),np.uint8)
tophat = cv2.morphologyEx(two, cv2.MORPH_TOPHAT, kernel)
#Blackhat
kernel = np.ones((35,35),np.uint8)
blackhat = cv2.morphologyEx(two, cv2.MORPH_BLACKHAT, kernel)

plt.subplot(251),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(252),plt.imshow(two,cmap = 'gray')
plt.title('2value Image'), plt.xticks([]), plt.yticks([])
plt.subplot(253),plt.imshow(erosion,cmap = 'gray')
plt.title('Erosion Image'), plt.xticks([]), plt.yticks([])
plt.subplot(254),plt.imshow(dilation,cmap = 'gray')
plt.title('Dilation Image'), plt.xticks([]), plt.yticks([])
plt.subplot(255),plt.imshow(opening,cmap = 'gray')
plt.title('Opening Image'), plt.xticks([]), plt.yticks([])
plt.subplot(256),plt.imshow(closing,cmap = 'gray')
plt.title('Closing Image'), plt.xticks([]), plt.yticks([])
plt.subplot(257),plt.imshow(gradient,cmap = 'gray')
plt.title('Gradient Image'), plt.xticks([]), plt.yticks([])
plt.subplot(258),plt.imshow(tophat,cmap = 'gray')
plt.title('Tophat Image'), plt.xticks([]), plt.yticks([])
plt.subplot(259),plt.imshow(blackhat,cmap = 'gray')
plt.title('Blackhat Image'), plt.xticks([]), plt.yticks([])

plt.show()
```
![mol.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480516/ec44e57a-44d7-20ef-2d64-6984f5771b33.png)

#5. 輪郭抽出トライ

Cannyでエッジ検出した後、Closingで中空部をくっつけてガイコツの領域を抽出してみた。

```python:Rinkaku.py
img = cv2.imread('brabra/1.jpg',0)
edges = cv2.Canny(img,50,250,10)

#Closing
kernel = np.ones((5,5),np.uint8)
closing = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
kernel = np.ones((35,35),np.uint8)
closing2 = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
kernel = np.ones((135,135),np.uint8)
closing3 = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)

plt.subplot(251),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(252),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.subplot(253),plt.imshow(closing,cmap = 'gray')
plt.title('Closing Image'), plt.xticks([]), plt.yticks([])
plt.subplot(254),plt.imshow(closing2,cmap = 'gray')
plt.title('Closing2 Image'), plt.xticks([]), plt.yticks([])
plt.subplot(255),plt.imshow(closing3,cmap = 'gray')
plt.title('Closing3 Image'), plt.xticks([]), plt.yticks([])

plt.show()
```
![rinkaku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480516/975d37d7-e06e-c95b-0592-62869a4a35b4.png)

#まとめ

エッジ検出とモルフォロジー変換について理解を深めた。

#参考文献
- OpenCVを使った画像処理（OpenCV-Pythonチュートリアル）
http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_table_of_contents_imgproc/py_table_of_contents_imgproc.html#py-table-of-content-imgproc

- 画像認識（機械学習プロフェッショナルシリーズ）
https://www.amazon.co.jp/dp/4061529129/

- https://qiita.com/shim0mura/items/f222082b7ef08de3732e
- Cannyエッジ検出
https://algorithm.joho.info/image-processing/canny-edge-detecter/