OpenCVで重なりのある物体の領域分割

OpenCVで、重なりのある物体の領域分割をやってみた。 以下の関数では領域分割を行い、その領域をクロッピングして返却する。 領域分割がうまくいかない場合は、dist_factorを調整する。これは重なりが大きい場合は小さく、重なりが小さい場合は大きい値にする。

参考↓

labs.eecs.tottori-u.ac.jp

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