SAMを使ったPoint指定とBox指定での画像セグメンテーション

前回、[Segment Anything Model (SAM)を用いた画像セグメンテーションの試み](https://qiita.com/SChen/items/f2671e5a95afa82999fb)をご紹介しましたが、今回は「Point指定」と「Box指定」を活用したセグメンテーション方法について共有します！

それぞれの方法のコード例を用いて、対象物の領域を効率的に抽出するプロセスを説明します。

## 1. Point指定によるセグメンテーション
### 画像の準備とポイント指定
画像を読み込み、対象のポイント座標を指定します。

```ruby:title.rb
import cv2
import matplotlib.pyplot as plt
import numpy as np

image_path = "/path/to/image.jpg"
coordinates = [
    [150, 300],  # Point 1
    [500, 300],  # Point 2
    [590, 300]   # Point 3
]

image = cv2.imread(image_path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

plt.figure(figsize=(4, 4))
plt.imshow(image)
plt.title("Original Image")
plt.grid(True)
plt.axis("on")
plt.show()

```
### Point指定でセグメント化
指定したポイントでマスクを生成します。

```ruby:title.rb
predictor.set_image(image)

def segment_multiple_points(predictor, image, coordinates):
    n_points = len(coordinates)
    n_cols = min(3, n_points)  # 1行あたり最大3つの画像
    n_rows = (n_points + n_cols - 1) // n_cols
    
    plt.figure(figsize=(6*n_cols, 6*n_rows))
    
    for idx, coord in enumerate(coordinates):
        input_point = np.array([coord])
        input_label = np.array([1])  # 1:対象物、0:背景
        
        masks, _, _ = predictor.predict(
            point_coords=input_point,
            point_labels=input_label,
            multimask_output=False
        )
        
        plt.subplot(n_rows, n_cols, idx + 1)
        plt.imshow(image)
        plt.imshow(masks[0], alpha=0.5)
        plt.scatter(input_point[:, 0], input_point[:, 1], color="red", s=100, label="Input Point")
        plt.legend(loc="upper right")
        plt.title(f"Point {idx+1}: ({coord[0]}, {coord[1]})")
        plt.axis("on")
        
        # マスク情報を表示
        print(f"\nPoint {idx+1} ({coord[0]}, {coord[1]}):")
        print(f"Mask Shape: {masks[0].shape}")
        print(f"Non-zero Pixels in Mask: {np.sum(masks[0])}")
    
    plt.tight_layout()
    plt.show()

segment_multiple_points(predictor, image, coordinates)

```
## 2. Box指定によるセグメンテーション
### Boxの指定と画像準備
次に、矩形（バウンディングボックス）を指定してマスクを生成します。

```ruby:title.rb
boxes = [
    [50, 150, 610, 350],  # Box 1: [x_min, y_min, x_max, y_max]
]

image = cv2.imread(image_path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

plt.figure(figsize=(4, 4))
plt.imshow(image)
plt.title("Original Image with Box")
plt.grid(True)
plt.axis("on")
plt.show()

```

### Box指定でセグメント化
指定したBoxでマスクを生成します。
```ruby:title.rb
def segment_multiple_boxes(predictor, image, boxes):
    n_boxes = len(boxes)
    n_cols = 1
    n_rows = (n_boxes + n_cols - 1) // n_cols
    
    plt.figure(figsize=(6*n_cols, 6*n_rows))
    
    for idx, box in enumerate(boxes):
        input_box = np.array([box])
        
        masks, _, _ = predictor.predict(
            box=input_box,
            multimask_output=False
        )
        
        plt.subplot(n_rows, n_cols, idx + 1)
        plt.imshow(image)
        plt.imshow(masks[0], alpha=0.5)
        x_min, y_min, x_max, y_max = box
        plt.gca().add_patch(
            plt.Rectangle(
                (x_min, y_min), x_max - x_min, y_max - y_min,
                edgecolor="red", facecolor="none", linewidth=2, label="Input Box"
            )
        )
        plt.legend(loc="upper right")
        plt.title(f"Box {idx+1}: [{x_min}, {y_min}, {x_max}, {y_max}]")
        plt.axis("on")
        
        # マスク情報を表示
        print(f"\nBox {idx+1} [{x_min}, {y_min}, {x_max}, {y_max}]:")
        print(f"Mask Shape: {masks[0].shape}")
        print(f"Non-zero Pixels in Mask: {np.sum(masks[0])}")
    
    plt.tight_layout()
    plt.show()

segment_multiple_boxes(predictor, image, boxes)

```

## 実行結果
Point指定
指定した座標を中心にセグメント化されたマスクが生成されます。

Box指定
指定した矩形領域内で対象物の形状に沿ったマスクが生成されます。

## 調整可能なパラメータ
1\.coordinates: セグメント化したいポイント座標のリスト。
2\.boxes: 矩形領域を定義する[x_min, y_min, x_max, y_max]形式のリスト。
3\.multimask_output: Trueにすると、複数の候補マスクを生成します。

Point指定とBox指定はSAMを使って柔軟に画像セグメンテーションを行うための強力なツールです。
用途に応じてこれらの方法を組み合わせ、精度や処理時間を調整してください。