OpenCVで、重なりのある物体の領域分割をやってみた。 以下の関数では領域分割を行い、その領域をクロッピングして返却する。 領域分割がうまくいかない場合は、dist_factorを調整する。これは重なりが大きい場合は小さく、重なりが小さい場合は大きい値にする。
参考↓
def watershed_segmentation_with_bounding_boxes(image, dist_factor=0.35): mask = image.copy().astype(np.uint8) # 画像をグレースケールに変換 gray = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY) # 画像の前処理(例:ノイズ除去やコントラスト調整) blurred = cv2.GaussianBlur(gray, (5, 5), 0) # 画像の閾値処理を適用して、物体と背景を区別 _, thresh = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 領域分割を行う前に、距離変換を計算 dist_transform = cv2.distanceTransform(thresh, cv2.DIST_L2, 5) _, sure_fg = cv2.threshold(dist_transform, dist_factor * dist_transform.max(), 255, 0) # sure_fgをもとにマーカーを作成 sure_fg = np.uint8(sure_fg) unknown = cv2.subtract(thresh, sure_fg) # マーカーにラベルを付ける _, markers = cv2.connectedComponents(sure_fg) # マーカーの背景を1に設定 markers = markers + 1 markers[unknown == 255] = 0 # Watershedアルゴリズムを適用 cv2.watershed(mask, markers) #show_img(markers) labels = np.unique(markers) contours_all = [] # 各領域の輪郭を検出 for label in labels[2:]: target = np.where(markers == label, 255, 0).astype(np.uint8) contours, _ = cv2.findContours(target, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for c in contours: contours_all.append(c) # 輪郭を囲むバウンディングボックスを取得 bounding_boxes = [cv2.boundingRect(contour) for contour in contours_all] # バウンディングボックスを描画 #for x, y, w, h in bounding_boxes: # cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2) # 緑のバウンディングボックスを描画 croped = [] for (x, y, w, h), contour in zip(bounding_boxes, contours_all): mask = np.zeros(image.shape) mask = cv2.drawContours(mask, [contour], -1, (1, 1, 1), -1).astype(np.uint8) area = w*h if area > 4000 and area < 13000: # あるしきい値より大きな領域のみを描画 croped.append(cv2.resize((np.multiply(image.copy(), mask))[y:y+h, x:x+w, :], (16, 16))) return bounding_boxes, croped