別の方法も思い出したので参考までに。
成果物

スケルトン
Scikit-imageという、顕微鏡で観察した結果などをイイ感じに加工するライブラリがあります。そこにスケルトンというのがあり、二値化画像の「骨」を抽出できます。

スケルトンの身を探る
が、スケルトンだと本当に骨しか出てこず、ほしいものとちょっと違います。本当は「身」がほしいので、ここから身を取り出す関数を使います。具体的には、thin()
という関数です。
※thinは先のスケルトンと同じチュートリアルのURLで紹介されていますのでURLは省略します。

余分な骨を消す
ここで、thinで身を取り出せましたが、残った骨が邪魔になりました。残った骨はOpenCVのモルフォロジーのopen()
で片づけます。
加工後 | 生画像 |
---|
 |  |
※元画像と比べると分かりやすいですが、このままではやせすぎています。
やせ過ぎた分太らせる
先のOpenCVの別の関数、モルフォロジーのdilate()
で片づけます。
という感じです。
「どれだけ元画像からゴミを取り除いた/取り除きすぎたんだ?」という疑問をお持ちの方は、一番上の画像を参照ください。まぁこれが妥協点だろう、と思っていただけるのではないでしょうか?
課題
枝葉のように生えた部分の大きさやskeletonのiterによっては、枝葉の部分と本体とに分割されます。こういったときには、以下のような感じで処理すると良いと思います。
- iterを機械学習でいうハイパーパラメータとして割り切って適当にやり過ごす
- それでも何とかなるようにblob検出をして一番大きい本体だけに注目する
import sys
import os
import numpy as np
import cv2
from skimage.morphology import skeletonize, thin
# グレースケールとして読み込み
img_path = os.path.split(os.path.abspath(__file__))[0] + "/img.png"
img_raw = cv2.imread(img_path,0)
cv2.imshow("img_raw",img_raw)
# skimage用に二値化
_,img = cv2.threshold(img_raw ,127,255,cv2.THRESH_BINARY)
img[0 < img] = 1
# # スケルトン
# # https://scikit-image.org/docs/stable/auto_examples/edges/plot_skeleton.html
# img_skeleton= skeletonize(img).astype(np.uint8)
# img_skeleton[0<img_skeleton] = 255
# cv2.imshow("img_skeleton",img_skeleton)
# cv2.imwrite("img_skeleton.png",img_skeleton)
# スケルトン(塗りつぶし)
iter = 5
# img_thin = thin(img, max_num_iter=iter).astype(np.uint8) # <--チュートリアルに沿った表記
img_thin = thin(img, max_iter=iter).astype(np.uint8) # <--手元のskimageはこれでないと動かない
img_thin[0<img_thin] = 255
cv2.imshow("img_thin",img_thin )
# cv2.imwrite("img_thin.png",img_thin)
# スケルトン(塗りつぶし)にモルフォロジー(オープニング)
kernel = np.ones((3,3),np.uint8) #可能な限り小さいカーネルでスケルトンの余分なところを潰す
img_thin_opened = cv2.morphologyEx(img_thin, cv2.MORPH_OPEN, kernel)
cv2.imshow("img_thin_opened",img_thin_opened)
# cv2.imwrite("img_thin_opened.png",img_thin_opened)
# やせた分太らせる
img_thin_opened_dil = cv2.dilate(img_thin_opened ,kernel,iterations = 4)
# cv2.imwrite("img_thin_opened_dil.png",img_thin_opened_dil)
# 差分を出す
img_diff = img_raw.copy()
img_diff[img_thin_opened_dil ==255] = 128
cv2.imshow("img_diff",img_diff)
# cv2.imwrite("img_diff.png",img_diff)
cv2.waitKey(0)