【画像解析】Mask R-CNNの使い方|COCO形式のインスタンスセグメンテーションをPythonで体験【コード例付き】

Mask R-CNNを使って、自作の粒子画像データ(マスク付きPNG)をCOCO形式で学習・推論する方法をまとめました。
 Python+Matterport実装ベースで、Kaggle形式やnucleus.py、coco.pyの応用にも対応しています。

本記事では、「Mask R-CNNを使って粒子レベルのインスタンスセグメンテーションを行う」 というテーマを扱います。具体的には、GitHubmatterport/Mask_RCNN で提供されている nucleps.py を活用し、手軽な1クラス分類(1種類の物体を分類可能)にトライします。
そのあと、COCO形式という独自のフォーマットを利用して coco.py を活用し、同時に多クラス分類することに挑戦します。
自作マスキング画像データセットに対してどのようにCOCO形式Jsonファイルを作成し、学習や推論(推定)まで持ち込むのかを、初心者にも分かりやすい流れで徹底解説してみたいと思います。

そもそもMask R-CNNって何?」という方から、「論文はちらっと読んだことあるけど、実際どうやって使うの?」という方まで、幅広く楽しめるように構成しています。

§本記事の構成

  1. Mask R-CNNとは?粒子画像を分割する仕組みと論文の要点
  2. GitHubのMask_RCNNレポジトリへ:実際のソースコードのポイント
  3. nucleus.pyに注目:粒子(nuclei)インスタンスセグメンテーションの原理
  4. COCO形式でマスク画像からJsonを作成する方法(自作データ対応)
  5. 実際の学習(Fine-Tuning)手順:コマンドやポイントを徹底解説
  6. 応用展望:他のタスクへの広がり、未来の進化

それでは、さっそく見ていきましょう!

※本記事では論文「Mask R-CNN(arXiv:1703.06870)」及びGitHub「matterport/Mask_RCNN」から図や説明を引用しています。


§0. 画像セグメンテーションとは?

本題に入る前に、画像処理について専門家じゃない方に向けて、そもそもセグメンテーションとかマスクって何?という基礎を、直感的に説明しますね。

「画像セグメンテーション」という言葉を聞いても、ピンとこない人も多いかもしれません。
これは簡単に言えば 「画像の中の特定の部分を切り取る技術」 です。

たとえば、スマホの写真編集アプリで「背景をぼかす」機能を使ったことはありませんか?
あれは、人の部分を 「前景」、その他の部分を 「背景」 に分けることで実現されています。

もう少し具体的に見てみましょう。


画像セグメンテーションのイメージ

👇 下の画像を見てください。

左側 :元の写真(白い花が背景の前にある)
右側 :花の部分だけを白く、それ以外を黒くした「セグメンテーションマスク」

セグメンテーションマスクとは?

  • 白い部分 → 「切り取りたい対象」(この場合は花)
  • 黒い部分 → 「それ以外の背景」

こうすることで、たとえば「花だけを抜き出して背景を透明にする」といった画像編集ができるわけです。

マスクの役割とは?

  •  Photoshopなどで「レイヤーマスク」を使ったことがある人は、それと同じイメージ。
  • マスク = どの部分を使うか / どの部分を無視するか を指定するフィルターのようなもの
  • 背景を削除する、特定の物体だけ色を変える、AIに「この部分を編集して!」と指示する… などに使える

セグメンテーションと物体検出の違い
よく「物体検出」と「セグメンテーション」は混同されがちですが、ざっくりいうとこんな違いがあります。

技術名 何をする? 出力例
物体検出 (Object Detection) 画像内の特定の物体に「四角い枠(バウンディングボックス)」をつける 🟥 物体が四角で囲まれる
セグメンテーション (Segmentation) 物体の形状にピッタリ沿ったマスクを作る 🌿 物体の輪郭が正確に切り取られる

 
たとえば、👆の猫の画像なら…
(左)物体検出 → 「この四角の中に猫がいるよ!」
(右)セグメンテーション → 「この猫の形にぴったり沿って切り取るよ!」

画像セグメンテーションは、物体検出よりも 「より細かく形状を捉えられる」 のがポイントです。


1.Mask R-CNNとは?粒子画像を分割する仕組みと論文の要点

1.1 論文の背景

Mask R-CNNは、2017年にKaiming HeらFacebook AI Research (FAIR)の研究チームが発表した 「Object Instance Segmentation」 の手法です。論文タイトルは

"Mask R-CNN" (arXiv:1703.06870)

この論文が目指したのは、「物体を見つけつつ、各物体のピクセル単位マスクを得る」 というタスクの高精度化と高効率化でした。

  • 単なるバウンディングボックス(境界矩形)ではなく、対象物の形状(マスク)をピクセルレベルで得たい。
  • かつ、画像内の複数オブジェクトを「インスタンス」として識別したい(同じクラスでも個体が複数あるなら、個別にマスクを出したい)。

論文では、Faster R-CNN(2段構成で、高速に物体検出を行う手法)の思想を拡張し、そこに「マスクを予測するブランチ」を並列的に追加する形で、物体領域の推定を実現しています。

1.2 Mask R-CNNの基本構造

論文:図1より引用

ざっくり構造を図示すると、以下のようになります(論文のFigure 1が典型的)。

  1. Backbone: ResNet101やResNeXtなどの高性能CNNを利用し、画像特徴マップを抽出。
  2. Region Proposal Network (RPN): 提案領域(RoIと呼ばれる物体候補)を見つける。
  3. RoIAlign: 提案領域(RoI)に対して、ピクセル単位での特徴切り出しを厳密に行う仕組み。
  4. Bounding Box Branch: RoIを分類し、バウンディングボックスをさらに精密に回帰(調整)する。
  5. Mask Branch: それぞれのRoIに対して二次元マスクを推定する。

RoIAlignのポイント

Mask R-CNNが特に強調するのは「RoIAlign」というレイヤーです。
先行手法であるFaster R-CNNはRoIPoolを使うのですが、座標の量子化(プーリング時のずれ) が生じるため、ピクセル単位のマスク精度を下げやすいという欠点がありました。
RoIAlignでは、この量子化を廃し、バイリニア補間 によって微妙な位置まで正確に特徴マップをサンプリングします。
これにより、マスクの境界がより正確に復元 できるという利点があります。

マルチタスク学習

Mask R-CNNは、「分類」+「バウンディングボックス回帰」+「マスク推定」 を同時学習します。
ただし、マスク推定は「各クラスごとに独立なバイナリマスクを予測」させる仕組みを採用。
これは、論文で述べられるように、ピクセルごとにクラスを競合させるsoftmax方法よりも精度が高いとされています。

1.3 成果とインパク

  • COCOデータセットインスタンスセグメンテーション物体検出キーポイント検出 の各競技タスクで、当時のSOTA性能を達成。
  • しかも、5fps 程度のリアルタイムに近い速度で動く(ResNet-101を使った場合)というスピードも実現。
  • 拡張容易:人体姿勢推定(keypoints)などにも流用しやすく、インスタンス単位で何かを検出・分割する』 多様な応用に繋がるフレームワークとなった。

論文の考察にあるように、「ピクセル精度が要る場合でも、Faster R-CNNをベースに少し手を加えるだけでうまくいく」発想が、研究コミュニティに大きなインパクトを与えました。


2.GitHubのMask_RCNNレポジトリ:実装の全体像

Mask R-CNNは論文だけでなく、GitHubリポジトリ上に公式実装的なコード も公開されています。
本記事では特に有名な、Matterport社が公開したKeras+TensorFlow実装 matterport/Mask_RCNN を取り上げます。

2.1 matterport/Mask_RCNNの特徴

  • ResNet101+Feature Pyramid Network(FPN をベースとした構成
  • MS COCOで事前学習された重み(mask_rcnn_coco.h5)が提供されており、すぐ試せる
  • Jupyter Notebookのサンプル が豊富(検出パイプラインの可視化、インスペクションなど)
  • マルチGPU 対応のParallelModelクラスなども準備
  • 自分のデータセットに合わせた「Datasetクラス」と「Configクラス」を定義するだけで、マスク生成まで実現 できる
  • COCOフォーマット対応:COCOデータの学習・評価スクリプトもサンプルに含まれている

GitHubリポジトリより引用

2.2 リポジトリ内のファイル構成(主要部分)

  • model.py, utils.py, config.py: Mask R-CNNのコア実装
  • samples/: いくつかのサンプルデモ・学習スクリプト
    • coco/coco.py:COCOデータセットでの学習・推論用スクリプト
    • shapes/train_shapes.ipynb:おもちゃデータ(幾何形状)での学習例
    • balloon/balloon.py:風船画像を使ったインスタンスセグメンテーション例(色抽出など)
    • nucleus/nucleus.py:今回注目する「粒子(nuclei)向け」学習用スクリプト
  • demo.ipynb: MS COCOの事前学習モデルを使って、任意画像で物体を検出&マスクするサンプル
  • inspect_data.ipynb, inspect_model.ipynb, inspect_weights.ipynb:各ステップを可視化し、問題をデバッグしやすくするためのノートブック群

3.nucleus.pyをピックアップして解説:粒子インスタンスセグメンテーションの仕組み

mask_rcnnには様々な使い方 がサンプルとして同梱されています。たとえば:

  • COCOデータセットの80クラスに対する物体検出・マスク生成
  • 風船(balloon)だけをマスクする例
  • 幾何図形(shapes)でのトイデータ学習

今回はまず手軽に扱えるnucleus.py」を使い、粒子(nuclei)をセグメンテーション するケースを重点的に見ていきます。

「粒子」と一言でいっても、顕微鏡画像中の細胞核(Nuclei)や、あるいは金属・材料工学での微粒子など様々。
nucleus.pyでは、Kaggleの「Data Science Bowl 2018」(細胞核分割)を想定したスクリプトが用意されており、インスタンスとして多数の「粒子」を一括セグメンテーション する流れがまとまっています。

GitHubリポジトリより引用

👆nucleus.pyを使って粒子を認識したイメージ写真です。公式から引用しました。

3.1 nucleus.pyの役割:なぜ粒子のセグメンテーションができる?

nucleus.py の冒頭を眺めると、以下のようなことが書かれています(大意):

"""
Mask R-CNN
Train on the nuclei segmentation dataset from the
Kaggle 2018 Data Science Bowl
...
Usage:
python3 nucleus.py train --dataset=/path/to/dataset --subset=train --weights=imagenet
python3 nucleus.py detect --dataset=/path/to/dataset --subset=train --weights=/path/to/weights.h5
"""
  • つまり、Kaggleの細胞核データをもとに学習・推論を行うスクリプト
  • NucleusConfig クラスと NucleusDataset クラスが定義されており、自作データセットを読み込む時の処理が具体的 に書かれている

NucleusConfig

  • “粒子(nucleus)”を1クラス追加 する形で、NUM_CLASSES = 1 + 1 となっている(背景クラス+核クラスの合計2クラス)
  • 入力画像サイズやアンカーサイズ、ミニマスク(使うかどうか)など、Mask R-CNN特有のパラメータが最適化済み

NucleusDataset

  • マスク画像(白黒png)を探し、それぞれのピクセルを「核オブジェクト」として認識している
  • 画像ごとに複数マスクがあれば、それらをスタック してマルチインスタンスとして読み込む
  • 1つのpngマスクが1インスタンスを意味するため、「どこがどの粒子か」が個別に区別されて学習可能

3.2 「単一クラス×多数インスタンス」への対応

COCOのように80クラスある場合にはNUM_CLASSES = 81(背景を含む)としますが、nucleus.pyでは1クラス(粒子)しかない のでクラス数は2(背景+1クラス)になっています。

ただし、複数個の粒子が画面に散在 しているとき、Mask R-CNNはクラスは同じでもインスタンスごとに別々のマスク を出力できる仕組みがあるため、「同じ粒子クラスが多数ある画像」を同時分割 できるわけです。


3.3 実際にnucleus.pyを使ってセグメンテーションAIをFine-Tuningしてみましょう!

👇nucleus.pyをFine-Tuningするには、下記のフォルダ構成をカスタムデータセットで作成します。

フォルダ構造例(Kaggle細胞核形式)

my_nucleus_dataset/
 ├─ stage1_train/
 │    ├─ 0001/
 │    │    ├─ images/0001.png
 │    │    └─ masks/...
 │    ├─ 0002/
 │    │    ├─ images/0002.png
 │    │    └─ masks/...
 │    ...
 ├─ stage1_test/
 │    ├─ ...
 └─ ...
  • 1つのディレクトリ内に、「元画像ファイル(images/xxx.png)」と、「インスタンスマスクが複数枚(masks/*.png)」という形。
  • マスクPNGは、各粒子ごとに白で塗った2値画像 として分割されており、1粒子1ファイル。

もちろん、自前のマスキング画像 さえ同様に準備しておけば、この nucleus.py の枠組みを流用できます。


3.4 データセットクラスとマスク読み込み

nucleus.pyNucleusDataset クラスでは、以下のようにPNGマスクを複数枚重ねて1画像のインスタンス全体を生成します。

class NucleusDataset(utils.Dataset):
    def load_nucleus(self, dataset_dir, subset):
        self.add_class("nucleus", 1, "nucleus")
        ...
        # subsetが"train"か"val"かでフォルダを切り替え
        # 画像フォルダを走査してself.add_image(...)
    def load_mask(self, image_id):
        # mask_dir = ... # (例) /.../masks/
        # PNGファイルを読み込み、粒子ごとに二値配列へ
        mask = []
        for f in mask_files:
            m = skimage.io.imread(f).astype(np.bool)
            mask.append(m)
        mask = np.stack(mask, axis=-1)
        # すべて「粒子クラス1」で統一
        return mask, np.ones([mask.shape[-1]], dtype=np.int32)

これがポイント:

  • 1粒子 = 1枚のPNGに白で塗られているので、それをスタック(np.stack)していくと、[height, width, instance数] の3次元配列ができる。
  • インスタンスのクラスIDは「1(nucleus)」のみ。

3.5 学習・推論の実行:コマンドライン

nucleus.py は、次のようなコマンドで動かします。

# 学習モード (ImageNet重みを初期値に)
python3 nucleus.py train \
  --dataset /path/to/my_nucleus_dataset \
  --subset train \
  --weights=imagenet

# あるいはCOCO重みを使うなら
python3 nucleus.py train \
  --dataset /path/to/my_nucleus_dataset \
  --subset train \
  --weights=coco

# 学習再開 (最後に保存されたweightを読み込む)
python3 nucleus.py train \
  --dataset /path/to/my_nucleus_dataset \
  --subset train \
  --weights=last

# 推論モード: 学習済み重みで粒子マスクを生成
python nucleus.py detect \
  --dataset /path/to/my_nucleus_dataset \
  --subset val \
  --weights=/path/to/mask_rcnn_nucleus_00xx.h5

学習の流れ (train() 関数)

def train(model, dataset_dir, subset):
    dataset_train = NucleusDataset()
    dataset_train.load_nucleus(dataset_dir, subset="train")
    dataset_train.prepare()

    dataset_val = NucleusDataset()
    dataset_val.load_nucleus(dataset_dir, subset="val")
    dataset_val.prepare()

    # Augmentationなど設定
    augmentation = iaa.SomeOf((0, 2), [...])

    # 頭(クラス分類やマスク生成など上位層)のみ学習
    model.train(dataset_train, dataset_val,
                learning_rate=config.LEARNING_RATE,
                epochs=20,
                layers='heads')

    # 全層を学習
    model.train(dataset_train, dataset_val,
                learning_rate=config.LEARNING_RATE,
                epochs=40,
                layers='all')

最初はヘッド層だけ、次に全層という2段階で微調整(Fine-Tuning)します。
実際にはエポック数やAugmentationなどを好みに合わせて変更してください。

推論の流れ (detect() 関数)

def detect(model, dataset_dir, subset):
    dataset = NucleusDataset()
    dataset.load_nucleus(dataset_dir, subset)
    dataset.prepare()
    for image_id in dataset.image_ids:
        image = dataset.load_image(image_id)
        result = model.detect([image], verbose=0)[0]
        # r["rois"], r["masks"], r["class_ids"], r["scores"] ...
        # ここで推定結果が得られる
        # さらに可視化 or RLE出力 etc.

推定された各粒子の位置が masks として出力されるので、粒子ごとに個別のマスク が取り出せます。
実際にFine-Tuningした所感として、1クラス検知に特化した調整がされているのか、例えばUnetなど他のセグメンテーションAIと比較しても高精度なマスキングが可能な印象でした。

4.COCO形式でマスク画像からJsonを作成する方法(自作データ対応)

さて、nucleus.pyを使って1クラスをマスキング出来るようになりましたが、実際のタスクでは同時に多クラス分類をしたいという場面も多いでしょう。
その時こそ、多クラス分離が可能な、coco.pyを用いたインスタンスセグメンテーションの出番です。

ここからは、coco.pyを使って多クラス分類が可能なインスタンスセグメンテーションAIをFine-Tuningするハウツーを説明いたしますね。

4.0 はじめに

インスタンスセグメンテーションを実施する時は、COCO形式(Json)と呼ばれるフォーマットを用いることが多いです。
Jsonファイルとは、ざっくり言うと、AIモデルに与える「教科書」の情報を数値データとして詰め込んだ設定ファイルのようなイメージです。
COCO形式のJsonファイルを作成するためには、下記の2点データが必要です。

(例)

  • 自分で撮影した粒子の顕微鏡画像(1枚に複数粒子が写っている)
  • その粒子領域をマスク(ピクセル)で塗りつぶした画像(白黒2値PNGなど)がある

Matterport版Mask_RCNN は、COCO形式(Json) に対応しています。

「COCO形式って何?」 という方は、マイクロソフトの論文 “Microsoft COCO: Common Objects in Context” から始まった物体認識用アノテーションの標準フォーマットと思えばOK。
「images」「annotations」「categories」などの構造をもつJsonファイルに、各画像に含まれるインスタンスピクセル領域を格納し、使い回しがしやすい 大規模データセット形式です。

4.1 マスキング画像からCOCO形式Jsonを作る方法

マスキングした画像から、COCO形式のJsonファイルを自動作成するコードは、下記の記事の最後の方に記述しているのでご参考ください。

yuzukaki-chemical.hateblo.jp

👆の記事に記載の、「マスキング画像を見て輪郭を抽出→COCO形式でJson書き出し」 という流れの Pythonコードを用いれば、自前で撮影・作成したデータから比較的簡単にJsonを生成できます。
COCO形式のJsonファイルを作れるかどうかでcoco.pyをFine-Tuningできるかどうか変わってくるので、閲覧推奨です。

  1. マスキング画像フォルダ: 例えば “mask/class_name/” の下に、各画像IDに対応するpngマスクを用意
  2. 元画像フォルダ: 例えば “images/” の下に、対応するjpgやpng画像を置いておく
  3. スクリプトを実行して、COCO構造の“annotations/instances_train2017.json”のようなJsonを作る
  4. train2017/val2017などに分割 してデータを用意(学習用・検証用)

こうして得られたCOCO形式データセットを、Mask R-CNNに読み込ませる ことで、粒子インスタンスを検出・マスク生成できるようになります。

COCO形式Jsonのポイント

  • images : 画像ごとのidfile_namewidth,heightなど
  • annotations : 各インスタンスsegmentation(ポリゴン点列やRLE), bbox, category_id, image_idなど
  • categories : カテゴリ情報(今回なら1カテゴリ nucleus だけ)

(例)COCO形式のJsonファイルの一部

👆のように、画像のアノテーション情報が数値データとして格納されています。

5.coco.pyの学習(Fine-Tuning)手順:コマンドやポイントを徹底解説

では、coco.pyを使って粒子向けのインスタンスセグメンテーションモデルを学習 するまでを、具体的にステップで示します。

5.0 準備:必要なもの

  1. Python3, TensorFlow, Keras などの環境(matterport/Mask_RCNNの requirements.txt を参照)
  2. Mask_RCNNリポジトリのクローン
    bash git clone https://github.com/matterport/Mask_RCNN.git
  3. 事前学習ウェイト mask_rcnn_coco.h5リポジトリのReleaseページから入手可)
    • もしくはimagenet初期化で学習を始めてもよいが、COCOでpretrainした重み があると収束が速い
  4. 自作データセット
    • ファイル構造例:
      /path/to/dataset/ ├─ train2017/ ...(train用の画像データ) ├─ val2017/ ...(val用の画像データ) └─ annotations/ ├─ instances_train2017.json └─ instances_val2017.json
    • あるいはKaggle Data Science Bowlのデータ形式

5.1 CocoConfig でクラス数を調整する

coco.py には以下のように CocoConfig が定義されています。(一部抜粋)

class CocoConfig(Config):
    """Configuration for training on MS COCO."""
    NAME = "coco"
    IMAGES_PER_GPU = 2
    # ここが重要: COCOは 1 + 80 クラス (背景 + 80)
    NUM_CLASSES = 1 + 80

自作のCOCO形式データセットが、背景を除いて X クラスある場合、NUM_CLASSES = 1 + X に書き換えてください。

たとえばクラスが5種類(cat, dog, bird, car, bike)なら、こうなります:

class MyCocoConfig(Config):
    NAME = "my_coco"
    IMAGES_PER_GPU = 2
    NUM_CLASSES = 1 + 5  # 背景 + 5クラス
    # あとは適宜パラメータを調整

あとは model.pyutils.py などの他ファイルは、matterport/Mask_RCNN 側のままでもOKです。

5.2 コマンドの基本

coco.pyは以下のようにコマンドラインで実行します:

# 新規学習 (COCO重みを使う)
python coco.py train --dataset=/path/to/dataset --subset=train --weights=coco

# 新規学習 (ImageNet重みを使う)
python coco.py train --dataset=/path/to/dataset --subset=train --weights=imagenet

# 途中で中断した学習の続き (last)
python coco.py train --dataset=/path/to/dataset --subset=train --weights=last

# 推論モード (検出・マスク生成)
python coco.py detect --dataset=/path/to/dataset --subset=val --weights=/path/to/weights.h5

👆コマンドラインにおいて、 pythonで動かなかったら、python3 を試してみてください。この辺り環境によって違ってきますので。
筆者はいつも、 python で動かしています。

trainコマンドの詳細

  • --dataset=/path/to/dataset : データセットのルートパス
  • --subset=train : datasetの下にある「train」サブフォルダや「train.json」などを読み込む指定
  • --weights=coco : COCOで学習済みウェイトを初期値にする
    • imagenetlast や、実際のファイルパス .h5 も指定可能

スクリプトの中を見ると、train() 関数で最初に"heads"だけ学習→その後"all"層を学習という2段階学習をしている例が掲載されています。

Heads層とは?

  • ResNetなどのbackbone(下層部)は凍結し、RPNや分類・マスク生成などの上位層を先に学習 するやり方
  • 大規模データセット(COCO)でpretrainされているため、backboneをすぐには書き換えずに、まずはheads層だけfine-tuneする

学習が安定したら、「all」層 も含めて全体を微調整する流れが推奨されています。

5.3 学習データのパス指定方法

--dataset には、自作のCOCO形式フォルダへのパスを設定します。
--model は重みの指定で、coco と書くとMatterportが公開している mask_rcnn_coco.h5 が自動的に読み込まれます(事前にダウンロード必要)。

COCO形式のJsonを読み込む仕組み

class CocoDataset(utils.Dataset):
    def load_coco(self, dataset_dir, subset, year=2014, ...):
        ...
        # annotations/instances_{subset}{year}.json を読み込む
        coco = COCO("{}/annotations/instances_{}{}.json".format(dataset_dir, subset, year))
        ...
        self.add_class("coco", i, coco.loadCats(i)[0]["name"])
        ...
        self.add_image("coco", image_id=i, path=..., annotations=...)
        ...

上記のようにpycocotoolsを使って、Jsonファイルから annotations を取り出しているので、標準的なCOCOフォーマット であれば何もしなくても OK。
年号は 20142017 を想定していますが、自作の場合は subset=train2017, subset=val2017 など好きなフォルダ名で合わせれば大丈夫です。

👆これは好みの問題ですが、筆者は「出来るだけ新しい方が良さそう!」とはじめに思い、以降ずっと2017を使用しています。😂

結果として、複数クラス(例えば cat, dog, bird, ...)を一度に学習&インスタンスセグメンテーションが可能になります。


5.4 学習が完了すると…

学習が進むと、DEFAULT_LOGS_DIR(デフォルトはMask_RCNN/logs/)にエポックごとの学習済みウェイトが.h5ファイルとして保存されます。
例えば

mask_rcnn_nucleus_0001.h5
mask_rcnn_nucleus_0002.h5
...

のようにエポック番号が付いた重みファイルが作られます。

その中で性能が良いものを、本番推論に使うのが一般的です。(もしくは最後のlastを使う)

5.5 推論(Detectモード)でセグメンテーション結果を得る

学習が一段落したら、今度は detect コマンドを試しましょう。例えば:

python coco.py detect \
  --dataset=/path/to/dataset \
  --subset=val \
  --weights=/path/to/mask_rcnn_coco_0120.h5
  • これにより、対応する画像に対して粒子マスク推定 が実行され、coco.pydetect() 関数でマスクを合成した画像などが出力されます。
  • コードを見ると、RLE(Run Length Encoding) での出力なども実装されており、Kaggleの提出形式 に合わせてCSVを作る処理が書かれています。

こうしたRLE出力は、COCO形式のアノテーション でもしばしば使われる手法で、マスク領域を圧縮して表す 時に便利。


6.応用展望:他のタスクへの広がり、未来の進化

6.1 Mask R-CNNの汎用性

先ほど触れたように、Mask R-CNNは“インスタンス単位で領域を切り分けたい” 場面に幅広く応用可能 です。

  • 細胞核や微粒子検出(nucleus.py)
  • 人物の姿勢推定(keypoint)
  • 建物の領域分割(地理情報など)
  • ロボットアームのパーツ分割 … など

「1つのクラスで粒子を区別したい」 でも「複数クラスを同時に分割したい」 でも、同じMask R-CNNの流れを踏襲できるのが大きなメリットといえます。

6.2 未来の展望:さらなる高精度化と新しいパラダイム

  • パノプティックセグメンテーション:背景(stuff)も含めてすべてのピクセルをラベルづけ
  • 多クラスのインスタンスを一度に区別(しつつ超小さなオブジェクトも見逃さない)
  • 3D空間への発展(点群や3Dボクセルを扱うMask R-CNNの派生手法)
  • 大規模モデルへの適用(Swin Transformerなどのバックボーンと合わせてさらに高精度化)

ここ数年でセグメンテーション技術は急激に進化しており、Segment Anything Model (SAM) のようにゼロショットで何でもセグメントする、という大きな動きも登場しています。
しかし、特定ドメインの粒子分割などは、まだまだMask R-CNN系の「教師ありインスタンスセグメンテーション」が有力 です。
例えば細かい粒子をしっかり検出してくれる等、実用の場面は多いでしょう。

yuzukaki-chemical.hateblo.jp yuzukaki-chemical.hateblo.jp

👆SAM系統の汎用基盤モデルに焦点を当てた記事も用意してあります!
最新のモデルにも興味のある方はぜひご覧ください。


まとめ:Mask R-CNNで物体(インスタンス)を自由自在に分割しよう!

ここまでのポイントをまとめると、以下のようになります。

  1. Mask R-CNNとは

    • Faster R-CNNにマスク生成ブランチを加えたインスタンスセグメンテーション手法
    • RoIAlignでピクセル精度を高め、マスクを高品質に予測
  2. matterport/Mask_RCNNリポジトリの構造

    • nucleus.pycoco.py など用途別サンプルが用意されている
    • コア部分は model.py, config.py, utils.py
  3. nucleus.pyの活用

  4. COCO形式Jsonとの連携

    • 別記事で紹介のスクリプトなどを用いて「マスク画像→COCO Json」生成
    • Mask R-CNNで読み込めば、学習&推論がスムーズに行える
  5. Fine-Tuningの流れ

    • python coco.py train --dataset=... --weights=coco
    • 最初はheads層のみ→慣れてきたらall層の微調整
    • 学習終了後は detect モードで推論&可視化
  6. 今後の展望

    • Instance Segmentationは様々な応用が可能
    • 大規模モデルとの組み合わせや3D展開なども期待される

おわりに

「Mask R-CNNを使って粒子(nuclei)をインスタンス分割するには?」 というテーマで解説してきました。

  • 「セグメンテーションって難しそう…」 と思われがちですが、Mask R-CNNを活用すれば、比較的簡単に一枚の画像に写る複数粒子を一挙に分割できます。
  • GitHubnucleus.py 及び coco.py を少し読み解くだけで、自作データの粒子解析にも応用できる のが大きな魅力です。
  • 加えて、COCO形式のJsonデータ を用意すると、より一般的なワークフロー(pycocotoolsの活用など)が組みやすくなります。

もし今後、さらに大量の粒子データセットを扱ったり、新しいバックボーン(Swin TransformerやConvNeXtなど)を組み合わせたい場合も、Mask R-CNNの設計は拡張しやすいので、ぜひ色々試してみてください。

Mask R-CNNは2025年現在では古典的な手法になりつつありますが、当時セグメンテーションAI界隈に与えた衝撃は非常に大きく、その軽量さに対する性能の高さは現在でも最新AIに引けを取らないです。

というわけで今回は、初心者の方にもなるべくイメージしやすく、かつ技術的にも奥行きを感じられるように、論文+GitHubリポジトリの視点から Mask R-CNNの粒子インスタンスセグメンテーション をまとめてみました。

最後までお読みいただきありがとうございました!


🔑この記事のポイントまとめ

  • Mask R-CNN:Faster R-CNNに「マスク予測ブランチ」を追加し、RoIAlignでピクセル精度を高めたインスタンスセグメンテーション手法
  • matterport/Mask_RCNN:Keras/TensorFlow実装を公開。nucleus.pyなど用途別サンプルが充実していて、自作データ対応が簡単
  • nucleus.py:単一クラス(背景+粒子)設定で、顕微鏡下の粒子などを簡単に大量分割できる例を提供
  • COCO形式Json:独自のマスク画像データをCOCOフォーマットに変換すると、Mask R-CNNでの学習・推論がスムーズ
  • Fine-Tuning手順python nucleus.py train/detect コマンドを使うだけ。weightsにcocoを指定すると学習済みモデルを継承できる

参考文献・関連リンク


ライセンス表記:
本記事におけるスクリーンショットはMetaの公開論文からの引用です。
Courtesy of Meta AI (CC BY-NC-ND 4.0)

【画像解析】Segment Anything 2 (SAM2)の使い方|Pythonで画像・動画を自動セグメントする手順まとめ【コード例付き】

こんにちは、ゆずかきです。

以前、Segment Anything Model(SAM)の入門向け解説記事を書きました。

yuzukaki-chemical.hateblo.jp

本記事では、「Segment Anything Model 2(SAM 2)」 という、画像・動画の両方で使える最新のセグメンテーション基盤モデルについてじっくり解説してみたいと思います。

動画でも物体を" 塗れる "AIモデル

「背景を消すだけならフォトレタッチツールで十分かもだけど、動画で動き回る対象物を“自動で追跡しながら切り抜き”できるってすごくない?」

――そうなんです、このSAM 2は、クリック(あるいはバウンディングボックスやマスク)といったプロンプトを入力するだけで、画像や動画上の任意のオブジェクトを自動でセグメンテーションし、しかも動画なら時系列で追跡(トラッキング)までしてくれるんです。

「Segment Anything」という言葉を初耳の方も、また“前作”であるSAM(Segment Anything Model 1)をご存知の方も、きっと今回のSAM 2は興味深い存在だと思います。では、一緒に踏み込んでみましょう!

※中盤からコード書く人向けの実装パートがあります!SAM2の概要をフワッと知りたい方は、前半までご覧ください。

※記事中では論文「SAM 2: Segment Anything in Images and Videos (arXiv:2408.00714)」より図や説明を引用しています。


§本記事の構成

  1. はじめに: Segment Anything Model 2(SAM 2)とは?
  2. 導入事例:画像編集から動画追跡まで
  3. SAM 2のアーキテクチャ:ストリーミングメモリが鍵?
  4. データセット: SA-V Datasetの圧倒的スケール
  5. インストール~基本の使い方:画像編・動画編
  6. 従来版SAMとの比較、どこが進化?
  7. よくある疑問Q&A・実運用上の注意点
  8. 今後の展望

それでは、一つずつ順を追って見ていきましょう。


§0. 画像セグメンテーションとは?

本題に入る前に、画像処理について専門家じゃない方に向けて、そもそもセグメンテーションとかマスクって何?という基礎を、直感的に説明しますね。

「画像セグメンテーション」という言葉を聞いても、ピンとこない人も多いかもしれません。
これは簡単に言えば 「画像の中の特定の部分を切り取る技術」 です。

たとえば、スマホの写真編集アプリで「背景をぼかす」機能を使ったことはありませんか?
あれは、人の部分を 「前景」、その他の部分を 「背景」 に分けることで実現されています。

もう少し具体的に見てみましょう。


画像セグメンテーションのイメージ

👇 下の画像を見てください。

左側 :元の写真(白い花が背景の前にある)
右側 :花の部分だけを白く、それ以外を黒くした「セグメンテーションマスク」

セグメンテーションマスクとは?

  • 白い部分 → 「切り取りたい対象」(この場合は花)
  • 黒い部分 → 「それ以外の背景」

こうすることで、たとえば「花だけを抜き出して背景を透明にする」といった画像編集ができるわけです。

マスクの役割とは?

  • Photoshopなどで「レイヤーマスク」を使ったことがある人は、それと同じイメージ。
  • マスク = どの部分を使うか / どの部分を無視するか を指定するフィルターのようなもの
  • 背景を削除する、特定の物体だけ色を変える、AIに「この部分を編集して!」と指示する… などに使える

セグメンテーションと物体検出の違い
よく「物体検出」と「セグメンテーション」は混同されがちですが、ざっくりいうとこんな違いがあります。

技術名 何をする? 出力例
物体検出 (Object Detection) 画像内の特定の物体に「四角い枠(バウンディングボックス)」をつける 🟥 物体が四角で囲まれる
セグメンテーション (Segmentation) 物体の形状にピッタリ沿ったマスクを作る 🌿 物体の輪郭が正確に切り取られる

 
たとえば、👆の猫の画像なら…
(左)物体検出 → 「この四角の中に猫がいるよ!」
(右)セグメンテーション → 「この猫の形にぴったり沿って切り取るよ!」

画像セグメンテーションは、物体検出よりも 「より細かく形状を捉えられる」 のがポイントです。


Segment Anything Model2 (SAM2) が凄い理由

従来、セグメンテーションには「この物体は何?」というカテゴリ(例:犬、猫、車)を学習させる必要がありました。
でもSAM2は違います。

「どんな画像でも、どんなオブジェクトでも、自動でマスクを作れる!」
つまり、事前に学習されていないモノ(たとえば「初めて見る変わった形の花」)でも、形状に沿ってセグメンテーションできる。
しかも、ワンクリックや矩形選択など「超簡単な指示」だけで動く!

これが、SAM2が「Segment Anything(何でも切り取れる)」と言われる理由です。


このあとは、Segment Anything Model2 (SAM2) がどのようにして 「あらゆる画像を簡単にセグメンテーションできるのか?」 を掘り下げていきます。

「セグメンテーションって何?」という疑問がスッキリしたところで、次のセクションへ進んでみましょう! 🌸


§1 はじめに: Segment Anything Model 2(SAM 2)とは?

SAM 2は、Meta(旧Facebook)の研究チームが提案した「プロンプト可能なビジュアルセグメンテーション」を実現するための最新モデルです。前作であるSAM(Segment Anything Model)は、

という点で大きな注目を集めました。

しかし、実際の映像データの多くは「動画」ですよね。
そこで「画像だけでなく、動画でもどんなオブジェクトでも切り抜けたら便利!」という発想から生まれたのがSAM 2です。
論文では、「Promptable Visual Segmentation (PVS)」 というタスクを定義し、画像だけでなく時系列で動くオブジェクトにも対応しようという試みが報告されています。

さらに今回、大規模な動画データセット「SA-V」約50.9K本の動画35.5M(3,550万)ものマスク)を収集・学習に使ったことで、幅広い物体や背景に対して頑丈な性能を発揮できるようになっています。


§2 導入事例:画像編集から動画追跡まで

ここで少し直感的なイメージをつかむため、具体例を見てみましょう。

  • 画像編集の例

    たとえば、静止画像の中で「この花を切り抜いて背景を黒くしたい」「ペットのワンちゃんを画像から取り出したい」といった用途。
    前作SAMでは、ポイント(クリック)や矩形で対象物を指定するだけで、すぐに高精度のマスクを得られました。
    SAM 2でも、画像は1フレームの動画と見なして動作するため、同じようにクリックだけでパッとセグメンテーションが可能。

  • 動画編集の例

    たとえば、SNSに投稿する短いムービーで「特定の人だけ明るくして背景を暗くしたい」「犬の舌の部分だけ色を変えたい」といった要望があった場合に、動画でのオブジェクト追跡が必要ですよね。
    SAM 2では、動画のあるフレームをクリックし、そこから得たマスクを時系列全体に伝搬(プロパゲーション)してくれます。
    途中でマスクがズレても、別のフレームで1クリックほど修正してあげれば、“記憶(メモリ)”を活かして全フレームを補正してくれる仕組みです。

例えば、犬🐕の舌の位置を追跡可能

こうした例からも分かるように、「動画で動く物体も、数クリックでスパッと切り抜ける」って、なかなかインパクトが大きいですよね。


§3 SAM 2のアーキテクチャ:ストリーミングメモリが鍵?

SAM 2の論文では、以下のような構成が提案されています。

SAM 2アーキテクチャ

  • Image Encoder

    • 従来のSAM同様、MAEで事前学習された「Hiera」エンコーダを採用

    • 画像をトークン化し、高次元特徴ベクトルを出力

  • Memory Attention

    • ポイント:動画処理を想定し、“ストリーミングメモリ” という概念を追加

    • これまでのフレームで得たマスク情報やプロンプト情報を「FIFOキュー」に格納し、後続のフレーム処理で参照できる

    • 時系列でフレームを1つずつ読み込むたびに、過去メモリとクロスアテンションを行いながら対象物を追跡

  • Prompt Encoder

    • クリック、バウンディングボックス、マスクなどのプロンプトをエンコードし、「このフレームでどの物体が欲しいのか」をモデルに指示
  • Mask Decoder

    • 最終的に、現在のフレームにおける対象物のマスクを出力(多重マスクの場合もあり)

    • また「オブジェクトがフレーム内に存在しない」(例えば完全に画面外・オクルージョンなど)の判定も担う

要は「SAMの推論プロセスを、動画の連続フレーム全体へ拡張」したような構成で、その際に過去フレーム情報を記憶するモジュールが追加されているのが特徴です。


§4 データセット: SA-V Datasetの圧倒的スケール

SAM 2の開発にあたり、著者たちは「SA-V(Segment Anything Video)Dataset」を構築しました。

  • 50.9K(約5万本)の動画

  • 計35.5M(3,550万)以上のマスク

  • 屋外・屋内、ペット、乗り物、雑貨、人体、果物、家具…ありとあらゆる対象をカバー

このデータセットは、「何でもアノテーションする」というSegment Anythingコンセプトを動画でも実践したもの。
しかも、SAM 2をアノテーション支援ツールとして使う「人間との対話型データ収集」(Data Engine)を行い、どんどんカバレッジを広げたとのことです。

SA-V Datasetの例

また、SA-V には自動生成マスクも含まれますが、それらは人間が検証し、失敗したマスクは追加で修正するという洗練プロセスを経ています。
結果として、既存の動画セグメンテーション・データセットYouTube-VOSなど)をはるかに超える量のマスクが得られたとのこと。


§5 インストール~基本の使い方:画像編・動画編

では、ここからは実際にSAM 2を使う方法について、GitHubのREADMEに沿った形でまとめていきます。

5.1 セットアップ

Python環境】

  •  Pythonバージョン:>=3.10

  • PyTorchバージョン:>=2.5.1

  • CUDA対応のGPU環境が望ましい

git clone https://github.com/facebookresearch/sam2.git
cd sam2
pip install -e .

上記コマンドで、SAM 2をインストールできます。
WindowsならWSL推奨、CUDAツールキットを別途入れておくと良いです)

【チェックポイントのダウンロード】

モデルの重み(SAM 2.1版など)を使うには、以下のようにチェックポイントをダウンロードします。

cd checkpoints
./download_ckpts.sh
cd ..

個別に .pt ファイルを落としてもOKです。


5.2 画像に対して使う(SAM 2はそのままSAMとして機能)

画像セグメンテーションのデモ図

import torch
from sam2.build_sam import build_sam2
from sam2.sam2_image_predictor import SAM2ImagePredictor

checkpoint = "./checkpoints/sam2.1_hiera_large.pt"
model_cfg = "configs/sam2.1/sam2.1_hiera_l.yaml"
predictor = SAM2ImagePredictor(build_sam2(model_cfg, checkpoint))

with torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16):
    predictor.set_image(<your_image>)      # 画像をセット
    masks, scores, logits = predictor.predict(<input_prompts>)
  • <input_prompts>には、クリックやバウンディングボックス、マスクなどを指定

  • masksがセグメンテーション結果で、いくつかの候補マスク(複数)やスコアが返ってきます

「Automatic Mask Generation」 も可能で、画像内にあるオブジェクトを一斉に推定するモードがあります。


5.3 動画に対して使う(SAM 2最大の特徴)

動画セグメンテーションのUI例

import torch
from sam2.build_sam import build_sam2_video_predictor

checkpoint = "./checkpoints/sam2.1_hiera_large.pt"
model_cfg = "configs/sam2.1/sam2.1_hiera_l.yaml"
predictor = build_sam2_video_predictor(model_cfg, checkpoint)

with torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16):
    # 1) stateを初期化(動画を読み込む)
    state = predictor.init_state(<your_video>)

    # 2) あるフレームでクリックやボックス入力などのプロンプト
    frame_idx, object_ids, masks = predictor.add_new_points_or_box(
        state, <your_prompts>)

    # 3) 全フレームにマスクを伝搬
    for frame_idx, object_ids, masks in predictor.propagate_in_video(state):
        # フレームごとにmaskが取れる
        pass
  1. init_state() で動画をセットし、初期化

  2. add_new_points_or_box() で指定フレームのオブジェクトをクリックやボックスで指定

  3. すると、そのフレームのマスクが即時取得され、それを元にpropagate_in_video()で全フレームを自動追跡

途中でマスクがズレてきたら、また別フレームでadd_new_points_or_box()を呼び、誤った部分に正解を指示して再伝搬。これを繰り返して最終的に全フレームの高精度マスクを得られる仕組みです。


§6 従来版SAMとの比較、どこが進化?

前述の通り、SAM 2は「動画対応」が一番の目玉です。具体的に進化した点としては:

  1. 動画の連続フレームにまたがる「ストリーミングメモリ」

    • これにより、1度取得したオブジェクト情報を次フレームにも活かす

    • 逆に、別フレームでの修正クリックが過去のマスクとも整合性をとりつつ、最小限の操作で誤りを一気に補正

  2. 圧倒的に大規模な動画データ(SA-V)の学習

    • 実際の性能向上は「データ」がモノをいう、というのは前作SAMのSA-1Bデータセットと同様
  3. 処理速度の向上

    • メモリを利用しながらも、リアルタイム近い速度(推論時30~60FPS程度を達成する実装もあり)

    • 画像単体でも6倍近く高速化(特にHiera-B+モデル)という報告


§7 よくある疑問Q&A・実運用上の注意点

Q1: どんな動画でも完全にセグメンテーションできる?
A1: 万能ではありません。被写体が激しく動き過ぎる場合や、ほぼ同一の外観のモノが複数ある場合には混同リスクがあります。オクルージョン(重なり)やカメラのパン・ズームが大きいケースでは、追加フレームでの修正クリックが必須になることも多いです。

Q2: 複数オブジェクトを同時に追跡できる?
A2: できます。ただし、現状の仕組みでは1オブジェクトずつ処理を回す形が基本です(ただし背後でフレーム埋め込みは共有可能)。複数オブジェクトを一気に自動生成する手法もありますが、高精度化には要検討とされています。

Q3: 学習済みチェックポイントだけで十分? 自前データで再学習できる?
A3: 公式GitHubにはレーニング用コードも公開されています(training/README.md参照)。自前の特殊な動画データがあるなら、部分的にfine-tuningしてみるのもアリでしょう。ただし、学習コストはかなり大きいので要GPUです。


§8 今後の展望

SAM 2は、画像・動画のあらゆる対象をインタラクティブかつ高速にセグメンテーションできるという点で、大いに期待が高まっています。今後、考えられる発展としては:

  1. さらなるスケーラビリティ

    • より大型モデルや更なる大規模ビデオデータ(ビリオン級?)で、「より精細な動画編集」「更なる汎用性」が期待
  2. LLMとの統合

    • 自然言語で“この映像の中でスポーツカーだけ切り抜いて”と言う」といったマルチモーダル統合
  3. 産業用途(医療・監視・自動運転など)

    • 医療映像や、店舗監視映像、自動運転システムでのリアルタイム物体追跡など幅広い応用が見込まれる
  4. 動画生成モデルと組み合わせ

    • セグメンテーション後の領域をGANやDiffusionモデルで置き換える、あるいは動的に編集する――夢が広がりますよね。

現状では、「物体が一瞬フレーム外に消え、再び出現したときの追跡」など、課題もいくつか指摘されていますが、わずかな追加クリックで補正できる設計は非常に実用的です。

「1~2クリックだけで動画編集のハードルを一気に下げる技術」として、今後さらに注目度が高まるだろうと感じます。


まとめ&おわりに

今回は、「Segment Anything Model 2 (SAM 2)」を大ボリュームで紹介しました。

  • 画像・動画両対応のプロンプト可能セグメンテーション

  • “ストリーミングメモリ” を用いて、いったん得たオブジェクト情報を時系列伝搬できる

  • 一度ずれたマスクも、別フレームで1クリック修正するだけで動画全体を再補正可能

  • SA-V Dataset(約3,550万マスク!)での大規模学習により、多種多様なオブジェクトに強い

  • 従来SAMとの比較で、動画編集がかなり楽になっただけでなく、画像でも速度面で進化

例えば、「ペットの動画で猫だけ切り抜く」「スポーツ映像でボールの動きだけ着色する」など、動画編集をグッと容易化できる技術ではないでしょうか。

最後までお読みいただき、ありがとうございました。ぜひ公式GiuHub(GitHubリポジトリ)やデモも覗いてみてくださいね。


§参考リンク

以下より、写真や図、文章を引用


🔑この記事のポイントまとめ
1. 動画セグメンテーションが超手軽に:数クリックでトラッキング付きマスクが出る
2. メモリ機構を活かした少ない操作量:修正クリックが全フレームに即時反映
3. SA-V Datasetの圧倒的規模:扱えるオブジェクトの幅が格段に広い
4. 画像だけでも高速&高精度:前作SAMより6倍速になる事例も
5. 応用範囲:AR/VR、動画編集、医療、監視、ロボティクスなど多岐にわたる可能性


ライセンス表記:
本記事におけるスクリーンショットはMetaの公開論文からの引用です。
Courtesy of Meta AI (CC-BY-4.0)

【画像解析】Segment Anything (SAM) の使い方|Pythonで画像を自動セグメントする手順まとめ【コード例あり】

こんにちは、ゆずかきです。

今回は、「Segment Anything(略称:SAM)」 と呼ばれる最新の画像セグメンテーション技術について、入門者向けにできるだけ噛み砕いて解説してみたいと思います。

「Segment Anything」は、Meta AI Research(旧FaceBook AI Research)によって2023年に発表された、大規模言語モデル(LLM)ならぬ「大規模視覚モデル」の草分け的存在です。

画像上で「なんでもセグメンテーションできる」ことを目指しており、なんと1100万枚の画像と11億(1.1B)ものマスク(白黒画像)からなる膨大なデータセットSA-1Bを使い、言葉で指示できるAI(Promptable Segmentation)という発想で学習されたモデルなんですね。

さらにこの度、「SAM 2」というアップデートが公開され、静止画だけでなく動画にも適用できる汎用的な拡張モデルが登場。
まさに画像・動画セグメンテーションの基盤となるAIモデル(Foundation Model)を目指しているわけです。

今回は、

  • 論文 "Segment Anything" (arXiv:2304.02643) で提唱されている内容をベースに、
  •  GitHubリポジトリ(公式のReadMe情報)を活用しながら、
  • 具体的な使い方・導入手順、コード例、応用範囲 を網羅的に解説

していきます。
前後編に分けてもいいくらいのボリュームですが、セグメンテーション技術に馴染みのない方も気軽に読み進めてみてください!
※中盤からコード書く人向けの実装フェーズに入ります!SAMの雰囲気をフワッと知りたいという方は、前半部分をご覧ください。

もしSegment Anythingの出力例をイメージしたい方は、下記写真群を参照してみてください。

👆入力画像に対してSAMが自動でマスキングする処理イメージ
👆色んなものを1発でセグメンテーションする様子
👆プロンプト次第でピンポイント指定も可能

どんな感じでマスクが生成されるかイメージできますでしょうか。
今回は、このようなすごいことが出来るSAMという技術について、詳細まで解説してみようかな、と思います。

※記事中では論文:「Segment Anything (arXiv:2304.02643)」から図や説明を引用しています。


§本記事の構成

  1. Segment Anythingとは?
  2. 論文の概要(arXiv:2304.02643)
  3. 従来手法との比較&画期的ポイント
  4. SAMのアーキテクチャ:画像セグメンテーションが"Promptable"に
  5. 1.1Bマスク&11M画像!世界最大級のSA-1Bデータセット
  6. SAM 2:動画対応への新アプローチ
  7. 実際に使ってみる:インストール~推論コード例
  8. 主要な実験結果と考察:Zero-Shot性能はどこまで通用?
  9. 応用範囲と今後の展望
  10. まとめ・参考文献

途中に写真を挟んでより分かりやすくしてみたいと思います。必要そうなところで差し込みますね。


§0. 画像セグメンテーションとは?

本題に入る前に、画像処理について専門家じゃない方に向けて、そもそもセグメンテーションとかマスクって何?という基礎を、直感的に説明しますね。

「画像セグメンテーション」という言葉を聞いても、ピンとこない人も多いかもしれません。
これは簡単に言えば 「画像の中の特定の部分を切り取る技術」 です。

たとえば、スマホの写真編集アプリで「背景をぼかす」機能を使ったことはありませんか?
あれは、人の部分を 「前景」、その他の部分を 「背景」 に分けることで実現されています。

もう少し具体的に見てみましょう。


画像セグメンテーションのイメージ

👇 下の画像を見てください。

左側 :元の写真(白い花が背景の前にある)
右側 :花の部分だけを白く、それ以外を黒くした「セグメンテーションマスク」

セグメンテーションマスクとは?

  • 白い部分 → 「切り取りたい対象」(この場合は花)
  • 黒い部分 → 「それ以外の背景」

こうすることで、たとえば「花だけを抜き出して背景を透明にする」といった画像編集ができるわけです。

マスクの役割とは?

  • Photoshopなどで「レイヤーマスク」を使ったことがある人は、それと同じイメージ。
  • マスク = どの部分を使うか / どの部分を無視するか を指定するフィルターのようなもの
  • 背景を削除する、特定の物体だけ色を変える、AIに「この部分を編集して!」と指示する… などに使える

セグメンテーションと物体検出の違い
よく「物体検出」と「セグメンテーション」は混同されがちですが、ざっくりいうとこんな違いがあります。

技術名 何をする? 出力例
物体検出 (Object Detection) 画像内の特定の物体に「四角い枠(バウンディングボックス)」をつける 🟥 物体が四角で囲まれる
セグメンテーション (Segmentation) 物体の形状にピッタリ沿ったマスクを作る 🌿 物体の輪郭が正確に切り取られる

 
たとえば、👆の猫の画像なら…
(左)物体検出 → 「この四角の中に猫がいるよ!」
(右)セグメンテーション → 「この猫の形にぴったり沿って切り取るよ!」

画像セグメンテーションは、物体検出よりも 「より細かく形状を捉えられる」 のがポイントです。


Segment Anything (SAM) が凄い理由

従来、セグメンテーションには「この物体は何?」というカテゴリ(例:犬、猫、車)を学習させる必要がありました。
でもSAMは違います。

「どんな画像でも、どんなオブジェクトでも、自動でマスクを作れる!」
つまり、事前に学習されていないモノ(たとえば「初めて見る変わった形の花」)でも、形状に沿ってセグメンテーションできる。
しかも、ワンクリックや矩形選択など「超簡単な指示」だけで動く!

これが、SAMが「Segment Anything(何でも切り取れる)」と言われる理由です。


このあとは、Segment Anything (SAM) がどのようにして 「あらゆる画像を簡単にセグメンテーションできるのか?」 を掘り下げていきます。

「セグメンテーションって何?」という疑問がスッキリしたところで、次のセクションへ進んでみましょう! 🌸


§1. Segment Anythingとは?

Segment Anythingは、「画像上のあらゆる物体や領域を、一つのモデルですべて『切り取る(セグメンテーションする)』」という壮大なビジョンで進められているプロジェクトです。
2023年4月に論文サイト:rXivへ登場し、大きな反響を呼びました。

特徴としては、

  • 大規模データセット SA-1B (Segment Anything 1-Billion Mask Dataset) を構築し、
  • Promptable Segmentation(ユーザが与える"Prompt"に応じて、任意のマスクをリアルタイム生成)を実現し、
  • "SAM" (Segment Anything Model) と呼ばれる統一モデルで、画像上のどんな物体でも分割可能にしよう

という点です。

従来の画像セグメンテーション手法との違いは、「少数のクリック(例:1点指定)やボックスの入力だけで、有効なセグメンテーションマスクが得られる」というところ。
さらに、マスクを後から refined(微修正)しながら再度Promptとして入力すれば、モデルがそれを反映してより精密なマスクを吐き出す「対話的」な仕組みまで備えています。

実際、下記のようなイメージがあると分かりやすいですね。

👆 画像上で1点をクリックしただけで、「こういうオブジェクトだよね?」というマスクが出力されている様子


§2. 論文の概要(arXiv:2304.02643)

論文タイトルは、

Segment Anything
Alexander Kirillov et al., Meta AI Research, FAIR, 2023

論文内容の骨子は以下のようになっています:

  1. タスク定義:Promptable Segmentation

    • 「1点クリック」や「バウンディングボックス」、「任意のマスクの一部を再提示」、あるいはテキスト入力("a wheel"など)といったプロンプトを与えると、それに対応する領域がセグメンテーションされる。
    • 不完全・曖昧な入力であっても「有効なマスク」を返す必要がある(例えば1点だけのクリックが、複数オブジェクトに跨るような曖昧さを含んでいたとしても、有効な1つの(または複数の)マスクを生成)。
  2. モデル構造(SAM)

    • 高価な「画像エンコーダ」は1枚の画像につき1回だけ処理し、あとは軽量な「プロンプトエンコーダ」&「マスクデコーダ」を高速に動かすことで、リアルタイム応答を目指している。
    • 実際、学習済みのSAMではViT-Hと呼ばれる巨大なビジョントランスフォーマーモデルを使い、生成されるマスクの品質を追求している。
    • 「マスクを複数同時に出力し、最も適合度の高いマスクをスコアリングする」アプローチで曖昧性に対処する。
  3. データ収集ループ(Data Engine)

    • 通常、セグメンテーションに必要なマスク教師データは膨大なアノテーション作業を要する。
      しかし本プロジェクトでは「モデルを使ってアノテーションを半自動 or 全自動化し、そのアノテーションをさらに学習に使ってモデルを強化する」というループを回した。
    • その結果、11M(1100万)枚の画像から1.1B(11億)マスクという前代未聞のデータセット SA-1B を構築し、それをモデル学習に活用した。
  4. 実験結果

    • 通常のセグメンテーションタスクや物体検出をゼロショット(追加学習なし)でこなせることを確認。
    • たとえばCOCOのインスタンスセグメンテーションに対し、Bounding Boxだけを外部検出器に任せる + SAMでマスク生成するだけでも、従来の学習済みモデルに迫る性能を達成。
    • 加えて、BSDS500のエッジ検出オブジェクトプロポーザル生成など、多様な下流タスクにも汎用的に転用可能。
  5. 結論と今後の展望

    • 「大規模データ × 大規模モデル × Promptableな設計」で、画像セグメンテーションの新しい潮流を生む。
    • 今後はさらなる大規模化やテキストや動画などへの応用が期待される。

こうした内容になっています。
特に「Data Engine」で1.1Bという膨大なマスクを自動収集してしまうスケールの大きさ、それを元にしたモデルがゼロショットで幅広いドメインに対応する汎用性を示したのが、この論文の面白いところです。


§3. 従来手法との比較&画期的ポイント

従来手法

これまでも「インタラクティブ セグメンテーション」などの研究はありましたが、ユーザは多くのクリックを行い、モデルをその都度アップデート(fine-tune)する、といった手間が必要でした。

また、物体検出+マスク生成の組み合わせ手法(Mask R-CNN等)も定番ですが、基本は対象のクラス(カテゴリ)を学習時に想定しておく必要があります。

例えば「犬」や「猫」は検出してくれるけど、「鍵」や「ペットボトルのラベル」などクラス外のものは扱えない場合が多いです。

SAMの画期的要素

  1. 事前学習のスケール

    • 11億ものマスクを使って学習したという規模は他に例を見ない。
    • しかも様々なオブジェクト・シーンが含まれ、オープンワールド的に「カテゴリを問わず切り取り可能」な汎用性を持つ。
  2. Promptable

    • 「ポイント1個」「ボックス1つ」「すでに生成済みのマスク」「テキストによるキーワード指定」など自由な手段で「こういう領域が欲しい」と指示すれば、モデルがマスクを出力。
    • 曖昧な指定であっても複数マスクを一度に出力し、その中からユーザが選んだり(または自動的にスコアで絞ったり)できる。
  3. リアルタイム応答を重視

    • 大きなViTで画像をエンコードしたら、あとは「軽量デコーダ」で素早くマスク生成可能(ブラウザ上でも動く実装例が公開されている)。

※ViT = Vision Transformerの略で、画像を言語のように処理できるやつ、のイメージです! chatGPTって画像も読めますよね?ざっくり言うとあんな感じですかね。


§4. SAMのアーキテクチャ:画像セグメンテーションが"Promptable"に

論文&GitHub情報を踏まえると、SAMの内部はざっくり以下の3つのコンポーネントで構成されています:

  1. Image Encoder

    • ViT-Hなど大規模Vision Transformerで画像を一回だけエンコードし、画像埋め込み(image embedding) を生成。
    • 解像度は 1024 x 1024 あたりを想定。
    • この処理は重いものの、1度実行すればOK(後のプロンプト変更時には再計算しない)。
  2. Prompt Encoder

    • ユーザからの入力(ポイント、ボックス、既存マスク、テキストなど)をベクトル表現に変換。
    • テキストはCLIPのtext encoderを利用するなど、モジュールごとに拡張可能。
  3. Mask Decoder

    • 画像埋め込みとプロンプト埋め込みを統合して、最終的にマスクを生成。
    • 数十ミリ秒程度で計算できるよう設計(これが対話的な使用を可能にしている)。
    • “曖昧性”に対応するため、複数マスクを一度に出力し、さらにそれらのマスクに対してモデルが「自信度(IoU予測スコア)」を付与している。

実際のフローチャートはこんな感じです:

👆SAMの構造図(Image Encoder, Prompt Encoder, Mask Decoder)が図解されているもの

「画像をEmbed化 → PromptをEmbed化 → Decoderで合体 → マスク出力」というパイプラインですね。
特に注目は「曖昧性対策として、3つのマスクを同時出力する」ところ。
論文や公式実装を見ても、このアイデアがシンプルかつ効いているようです。


§5. 1.1Bマスク&11M画像!世界最大級のSA-1Bデータセット

Segment Anythingの学習を支えるのが、SA-1B (Segment Anything 1-Billion mask) dataset です。その名の通り、11億個ものマスクが用意されています。

  • 画像数は約 11M(1100万)
  • マスク数は 1.1B(11億)
  • ライセンスおよびプライバシー保護が行き届いた画像を使用
  • マスクは「自動生成」段階が9割以上(人手アノテーション+モデル補助で多様性&品質を確保)

Mask品質 も大きなポイントで、ランダムに抽出したサンプルをさらに人間が最終確認したところ、75~90% IoUレベルの高品質が得られているそう。

👆 SA-1Bデータセットのサンプル画像に大量のマスクがオーバーレイされているイメージ

巨大言語モデルがWebテキストをかき集めるように、画像セグメンテーションにおいても「生データをとにかく集める」→「モデルがAnnotationを補助」→「さらにモデルが良くなる」というループを回す時代になったという印象です。


§6. SAM 2:動画対応への新アプローチ

2024年夏、Segment Anything Model 2 (SAM 2) がリリースされました。
これは、「画像は動画の1フレームに過ぎない」という視点で、従来の静止画セグメンテーションから動画へ拡張するモデルです。

SAM 2の詳細は以下を参照:

画像にも動画にも同じ文法で使える というのは非常に強力ですよね。
静止画をセグメンテーションできるなら、それをフレーム系列に適用すれば動画セグメンテーションもできるのでは…?という発想です。

このあたり、最近のトレンドである「マルチモーダルの大規模モデル」とも関連し、LLM + SAM 2 のような統合活用が期待されます。

yuzukaki-chemical.hateblo.jp

👆SAM 2についても解説記事を書きました!動画もセグメントしたい方はぜひご覧ください!


§7. 実際に使ってみる:インストール~推論コード例

さて、ここからは実運用でどう使うかを見ていきましょう。GitHub上のReadMe(facebookresearch/segment-anything)では具体的に以下のような案内がされています。

7.1 インストール

まずはPyTorch環境があるのが前提です(python>=3.8, pytorch>=1.7)。CUDA有効化推奨。
以下のコマンドで簡単にinstallできます:

pip install git+https://github.com/facebookresearch/segment-anything.git

またはクローンしてローカルインストール:

git clone git@github.com:facebookresearch/segment-anything.git
cd segment-anything
pip install -e .

オプションとして以下があると便利:

pip install opencv-python pycocotools matplotlib onnxruntime onnx

これにより、マスクの可視化やCOCO形式での保存などがスムーズになります。

7.2 モデルチェックポイントの入手

公式リポジトリには3種類のチェックポイントが公開されています:

  • default (or vit_h): ViT-H SAMモデル
  • vit_l: ViT-L SAMモデル
  • vit_b: ViT-B SAMモデル

サイズが大きいほど性能は高い傾向ですが、メモリ使用量などとのトレードオフがあるので用途に合わせて選びましょう。

ダウンロードが完了したら、以下のようにしてロードします(model_typeには"vit_h", "vit_l", "vit_b"などを指定):

from segment_anything import sam_model_registry
sam = sam_model_registry["vit_h"](checkpoint="<path/to/checkpoint>")

7.3 プロンプト付き推論の例

Pythonスクリプト上で"この画像を読み込んで、ここにポイントを打ったらマスクを生成" という流れを簡単に試せます。

from segment_anything import SamPredictor, sam_model_registry
import cv2

# モデルをロード
sam = sam_model_registry["vit_h"](checkpoint="sam_vit_h_4b8939.pth")
predictor = SamPredictor(sam)

# 画像の読み込み
image = cv2.imread("test_image.jpg")
predictor.set_image(image)

# 例: 前景ポイント(x,y)だけを与える
input_point = [[500, 300]]
input_label = [1]   # 1は"前景"を示すラベル

masks, scores, logits = predictor.predict(
    point_coords=input_point,
    point_labels=input_label,
    multimask_output=True  # Trueで複数マスク出力
)

print("生成されたマスク数:", len(masks))
for i, mask in enumerate(masks):
    print(f"マスク{i}のスコア:", scores[i])
    # maskは高さ×幅の2次元配列(True/False)

ここで masks は例えば3つのマスク(多層の曖昧性を含む)が得られるので、「scores のうち最も高いもの」を選ぶなどして使えます。

👆実行後のマスクを可視化した結果の例

7.4 画像全体のマスクを一括生成

さらに「画像に映っているすべての物体(または領域)をまとめてセグメンテーションしたい!」という場合には、SamAutomaticMaskGenerator を使います。

from segment_anything import SamAutomaticMaskGenerator, sam_model_registry
import cv2

sam = sam_model_registry["vit_h"](checkpoint="sam_vit_h_4b8939.pth")
mask_generator = SamAutomaticMaskGenerator(sam)

image = cv2.imread("test_image.jpg")
masks = mask_generator.generate(image)

print("生成されたマスク数:", len(masks))
# masksは各マスクのバウンディングボックスやスコアなどを持つ辞書のリスト
# "segmentation", "bbox", "area", "predicted_iou"などの情報を含む

これで、その画像に含まれる主要なマスクを片っ端から生成してくれます。
NMSの閾値点のサンプリング間隔など、パラメータを細かく設定すると、より細かく調整可能です。

7.5 コマンドラインでもOK

GUIなしでも、CLIで画像フォルダを渡して自動マスク生成も可能です:

python scripts/amg.py \
    --checkpoint sam_vit_h_4b8939.pth \
    --model-type vit_h \
    --input path/to/image_or_folder \
    --output path/to/output_folder

バックエンドで同じSamAutomaticMaskGeneratorを使っているため、一括処理に便利です。


§8. 主要な実験結果と考察:Zero-Shot性能はどこまで通用?

論文でも触れられている通り、Segment Anythingは下流タスクへゼロショット転用できるのが強みです。

  • 1点を打っただけのセグメンテーション
    従来のインタラクティブセグメンテーション手法(RITMなど)と比較しても高品質マスクを生成。

  • オブジェクトプロポーザル(AR@1000)
    LVISデータセットで、専用のオブジェクト検出器を利用した場合に匹敵する高リコール率。

  • Edge Detection (BSDS500)
    Sobelフィルタなど他の汎用ゼロショット手法より高い再現率を叩き出す。

  • テキスト→マスク
    テキストと画像エンコーダがCLIPで整合性がとれていれば、テキストプロンプトでもおおまかなマスクが得られる。

但し、細かい形状の再現や超高精度の輪郭抽出は、最先端の専用モデルにまだ及ばないケースもあるようです。
実際、私が手元で試したところ、細かいところはマスクできなかったり、変わった形状の物体は認識できないという弱点はありました。

また、データセット固有のアノテーションルール(例:COCOだと穴を持たないポリゴン形式、LVISだとアモーダル等)に合わせて学習したモデルに比べると、評価指標で差が出ることもあります。
ただし、その分バイアスに囚われず、より汎用的なマスクを生み出せるというメリットがあるわけですね。


§9. 応用範囲と今後の展望

  • 画像上の任意物体セグメンテーション:
    どんなクラスでもセグメンテーションできるので、写真整理やフォトレタッチ補助に応用。

  • 動画セグメンテーション (SAM 2):
    各フレームに対してSAMを適用、あるいは最新のストリーミング設計でリアルタイム解析。

  • アノテーション支援:
    データセット作成において下絵を自動生成し、人間が微修正して効率化するループを回せる。

  • ロボット視覚:
    ロボットが環境内のあらゆるオブジェクトを素早くマスク化できれば、把持(Grasping)や衝突回避などもより柔軟になる。

  • LLMとの統合:
    画像上のオブジェクト情報を自然言語で扱う(ChatGPT的な対話で「この画像のテーブルの上のコップの部分を切り取って」などを指示すると自動分割…)のも近未来に実現しそう。

今後、テキストや音声などと組み合わせた真の「マルチモーダル大規模モデル」化や、3D/点群データへの拡張、さらには産業界の具体的用途へ展開が進むと思われます。


§10. まとめ・参考文献

Segment Anythingは、「Promptable Segmentation」という新しい概念を提案し、1.1Bマスクに及ぶ大規模データセットを武器にして汎用セグメンテーションの可能性を切り拓きました。
SAM 2によって動画セグメンテーションを含むさらなる拡張が進んでおり、今後の発展が非常に楽しみです。

  • 大規模データによる「オープンワールド」なセグメンテーション
  • Prompt(ポイント、ボックス、マスク、テキスト等)を柔軟に受け付けるモデル設計
  • リアルタイム推論の可能性
  • 画像以外への波及(動画、3Dなど)

私自身も試しに、自前データセットで簡単にSAMを走らせてみましたが、ビックリするほど汎用的に切り取ってくれる印象でした。
ぜひみなさんもトライしてみてください!


🔑この記事のポイントまとめ

  • Segment Anything (SAM):Promptable Segmentationを1つのモデルで実現したMeta AIの研究プロジェクト
  • SA-1B:11億マスク規模の巨大データセットにより、カテゴリ非依存でのセグメンテーションが可能
  • 対話的操作に強い:1点やボックスを指定するだけでマスクが生成される。曖昧な場合は複数マスクを返す仕組み
  • SAM 2 で動画にも対応:より包括的な時空間セグメンテーションを目指す
  • 使い方:PyTorchでpip install後、Checkpointをダウンロード → SamPredictor or SamAutomaticMaskGeneratorで推論

§参考文献

以下より、写真や図、文章を引用

Note: SA-1Bのデータセットはライセンスやプライバシーに配慮されつつも非常に巨大です。ダウンロードや利用時は公式のライセンス文書をご確認ください。


おわりに

以上、Segment Anythingの論文&GitHubリポジトリをベースに、技術的背景から導入方法までを駆け足でご紹介しました。
個人的には「これだけ簡単にセグメンテーションできる時代が来たか!」と驚きを禁じ得ません。

特に実装は思いのほかシンプルで、モデル本体をロード→画像embedding計算→Point/Box/Text/etc.プロンプトを入力→マスク出力という流れが分かりやすくまとまっています。
近年のLLMのように、プロンプト次第で多彩なタスクを実現できる点は非常に面白いですよね。

みなさんも、ぜひ一度Segment Anythingをインストールして試してみてください。
意外なものもパッとマスクとして切り出してくれて、作業効率がグッと上がるかもしれません。

長文になりましたが、最後までお読みいただき、ありがとうございました!


ライセンス表記:
本記事におけるスクリーンショットはMetaの公開論文からの引用です。
Courtesy of Meta AI (CC-BY-4.0)

【画像解析】MS-COCO徹底解説:シーン理解に革新をもたらす「Common Objects in Context」【コード例付き】

こんにちは、ゆずかきです。
今回は、Microsoft COCO (Common Objects in Context)」 と呼ばれる、大規模かつ高精度なアノテーションが特徴の物体認識データセットについて、論文原文(「Microsoft COCO: Common Objects in Context」 by Tsung-Yi Lin et al., 2015)を参照しながら、中級者から上級者向けにガッツリ技術解説を行ってみたいと思います。

ご存じの方も多いかもしれませんが、近年のコンピュータビジョン(Computer Visionの発展は目覚ましく、特にDeep Learning(深層学習)のブレークスルーをきっかけに、様々な応用が飛躍的に進化しています。
その中心にあるのが、イメージ分類や物体検出・セグメンテーションといったタスク。
そこで今回ご紹介するMS-COCOは、そうした物体認識タスクをさらに高度化するために「非アイコニックな(多彩なアングル・混雑する)日常画像」を大量に集め、かつ「個別物体のピクセル単位(セグメンテーション)」でアノテーションしたという、極めてインパクトのあるデータセットなのです。

それでは、論文の背景やMS-COCOの設計思想を紐解きながら、その真髄に迫っていきましょう。


§本記事の構成

  1. はじめに:MS-COCOとは?
  2. 既存データセットとの比較:PASCAL VOC、ImageNet、SUNとの違い
  3. 論文で示されるMS-COCOの特長
  4. 画像収集とアノテーション手法:大量データをどう作った?
  5. アノテーションの質を確保する秘訣
  6. 実験・評価:Deformable Parts Model(DPM)でのベースライン検証
  7. MS-COCOがもたらすシーン理解の可能性
  8. まとめと今後の展望

それでは順を追って、細かく見ていきます。


§はじめに:MS-COCOとは?

「MS-COCO: Common Objects in Context」は、カリフォルニア工科大学Caltech)、Cornell、Brown、Microsoft Researchなど複数研究機関の共著で発表され、2014年arXivで公開された研究プロジェクトです。(論文ID:arXiv:1405.0312v3)

MS-COCOが目指すゴールは、物体認識を「シーン理解(Scene Understanding)」の文脈に位置づけるという大きなチャレンジにあります。
従来から、画像内に存在する物体(Object)のカテゴリや位置情報(Bounding Box)を手掛かりに解析するアプローチはありましたが、本当に「現実の雑多なシーン(Context)」に出会ったとき、既存手法では歯が立たないケースが多かったわけです。

つまり、アイコニック(代表的アングル、背景すっきり)な画像だけではなく、

  • 背景が煩雑で多数の物体が同時に存在する
  • 部分的に物体が隠れている
  • 非常に小さいオブジェクトがある
  • 複数のオブジェクト同士が相互作用している

こうした難易度の高い日常シーンでこそ、ロバストに物体を見つけ出し、ピクセル単位のセマンティックセグメンテーションを正しく行う必要があります。

そこに着目したのが、MS-COCOというわけです。


§既存データセットとの比較:PASCAL VOC、ImageNet、SUNとの違い

MS-COCOがリリースされる以前も、オブジェクト認識・シーン理解のために著名な大規模データセットは多数ありました。
例えば:

  • PASCAL VOC
    20種類のオブジェクトカテゴリに対してBounding Boxやセグメンテーションラベルを付与。合計約11,000枚の画像で、27,000個ほどのオブジェクトが含まれる。年度ごとにベンチマークが更新されており、物体検出やセマンティックセグメンテーションの初期研究をけん引。

  • ImageNet
    14,000,000枚以上という膨大な画像が揃った、イメージ分類(1,000カテゴリ)を中心に成長してきたデータセット。後にImageNet Detection(200クラス・数十万のバウンディングボックス)やImageNet Localizationなどの拡張を行い、Deep Learningのブレークスルー(AlexNet)を支えた歴史的貢献が有名。

  • SUN
    908種類以上のシーンタイプ(例:公園、教室、リビングルームなど)をラベル付けし、内部のオブジェクトもセマンティックにタグづけした包括的データセットシーン理解を重視しているため、背景要素や「stuff(壁・空・床など)」が多く、オブジェクトの総数も多いが、特定のカテゴリごとのアノテーション数は少なめという特徴。

これらと比較すると、MS-COCOには以下の大きな特徴があると論文で整理されています:

  1. 「より多様な非アイコニック画像」を豊富に含む

    • フリッカーFlickr)の写真を中心に集めて、物体を単体検索したわけではなく、2カテゴリの組み合わせやシーンカテゴリ×オブジェクトカテゴリのキーワード検索などを駆使。
    • 結果として、アイコニック(対象が大きく、背景がスッキリ)ではなく、背後に雑多なオブジェクトがたくさん映り込んでいる画像が多い。
  2. 全てのオブジェクトに「インスタンスセグメンテーション」ラベルを付与

    • 1つの画像に複数(場合によっては10個以上)オブジェクトが混在していても、個別ピクセルごとの輪郭を丁寧に描画。
    • セマンティックセグメンテーション(どのピクセルが何のクラスか)に加え、インスタンスを分離できるため、1画像内のどのピクセルがどの個体に属するかを識別できるのが強み。
  3. 約91種の一般的なオブジェクトカテゴリ(最終的には80カテゴリのセグメンテーションが中心)

    • 「犬」「猫」「車」「人」「椅子」など、4歳児が認識できる程度のエントリーレベルカテゴリを厳選。
    • 2014~15年時点で合計328,000枚超の画像、そして2,500,000件のオブジェクトインスタンスアノテーション
  4. 一枚あたりに含まれるオブジェクト数が多い

つまり、1枚の写真に多くの物体がゴチャッと写り込んでいるので、コンテクスト推論の要素(小さい物体を周辺状況で見つけるなど)も要求される設計になっています。


§論文で示されるMS-COCOの特長

上記に加えて、論文では特に以下の3点を強調しています:

  1. Non-iconic Views(非アイコニック視点)の重要性

    • アイコニック画像(正面や横からドカンと大きく写っているような画像)だと、比較的検出は簡単。
    • しかし、実世界では(車や人など)雑多なアングル&重複オブジェクトが当たり前なので、そちらに特化したデータセットを作る必要がある。
  2. Contextual Reasoning(物体間のコンテクスト推論)

    • 画像内でオブジェクトが配置される状況(上下関係、隣接、サイズ比)によって、判別がつきやすい場合がある。
    • たとえば、「非常に小さい物体」や「これ何だろう…?」と人間でも迷うような状況でも、周囲の物体に注目することで認識精度を高められるのでは?というモチベーション。
  3. Precise 2D Localization(高精度な位置情報)

    • 単なるBounding Boxだけでなく、インスタンスセグメンテーションを用意したのは、物体の正確な形状や領域を評価しようという狙い。
    • 従来の境界ボックスだけだと、たとえば人間の腕部分が欠けているかどうかは分からないが、MS-COCOは実際にピクセルを塗りつぶしているため、より高品質の位置検出を研究できる。

§画像収集とアノテーション手法:大量データをどう作った?

70,000時間以上のアノテーション作業が投じられたという壮大なこのプロジェクト。その要となる「どうやって画像を集めて厳密にラベルを付けたか」を論文は非常に詳しく紹介しています。主なポイントをまとめると:

  1. Flickrから日常写真を収集

    • 単なる「dog」検索ではなく、「dog + car」など、複数オブジェクトの組み合わせをキーワードにすることで、複数物体が混在する画像を効率的に入手。
    • アイコニック画像を除外するフィルタリングを機械+クラウドワーカーの協力で行い、自然な背景の写真を選定。
  2. カテゴリラベリング(Category Labeling)

    • 画像に対して、91の候補カテゴリのうち「どれが写っているか」を効率よく判別させるため、階層化したユーザーインターフェースを設計。
    • 8人のワーカーに複数回チェックさせることで高いリコールを達成。
  3. インスタンススポッティング(Instance Spotting)

    • 具体的に何個のオブジェクトがあるか、全て×印をつけていく作業。
    • やはり8人のワーカーによる多重チェックを通すことで、取りこぼしを防ぐ。
  4. セグメンテーション(Instance Segmentation)

    • 各オブジェクトをピクセル単位で塗りつぶし、領域を割り当てる。
    • 膨大な作業量を分散処理するため、クラウドワーカーに対し事前トレーニング(簡単な課題)を課してフィルタリング。
    • アノテーションの品質検証ステージを導入し、低品質のマスクは破棄して再アノテーションするプロセスを徹底。
  5. クラウドワーカーの「精度とリコール」の管理

    • カテゴリの見落としが起こらないよう、複数回の投票を行い、「もしカテゴリが写っていれば誰かは気づくだろう」方式で網羅。
    • カテゴリの誤検出が含まれていても、次段階で除去できる(False Positive除去)。
  6. Crowd(大量インスタンス)の扱い

    • 例:人混みやフルーツが山積みなど、一画像に数十個以上の同カテゴリオブジェクトが映り込むケース。
    • その場合、個別にセグメンテーションを描画すると現実的でないため、10~15個以上は「crowd」領域として一括りにするオプションも。

こうしたフローを踏むことで、大規模かつ高精度アノテーションが完成。画像1枚あたり、平均7.7個のアノテーションが密に配置され、かつピクセルレベルでの輪郭がしっかり確定している点こそ、MS-COCO最大の強みといえます。


§アノテーションの質を確保する秘訣

論文で興味深いのが、「クラウドワーカー間の投票」と「学習+テスト」の仕組みです。
単に「手書きで輪郭描いてもらう」だけでは、雑な作業をする人が混ざって全体の精度が下がる恐れがある。

  1. カテゴリ検出では8人以上に投票させ、どれか1人でも見つければOK

    • この多数決手法により、高いリコール(見逃しの少なさ)を実現。
  2. セグメンテーション担当ワーカーには、事前テストタスクで一定精度を求める

    • 簡単な例画像を1つ与え、そのマスクが一定の品質閾値(IoUなど)を超えていないと、本番タスクには参加できない。
  3. 各セグメンテーションは複数人で検証し、もし1人でも「不十分」と判断すれば再アノテーション

    • こうしてダメなワーカーを排除し、最終的にクリーンなラベルを得る仕組み。

結果、アノテーション品質の管理に徹底した時間が割かれましたが、そのおかげでオブジェクトの輪郭が比較的正確だと論文中で報告されています。


§実験・評価:Deformable Parts Model(DPM)でのベースライン検証

論文の後半では、MS-COCOを使った代表的なオブジェクト検出タスクのベースライン実験として、当時のクラシカルな「Deformable Parts Model (DPM)」を利用し、Pascal VOCデータ(DPMv5-P)とCOCOデータ(DPMv5-C)で学習したモデルを比較しています。

  1. MS-COCOはPASCAL VOCより難易度が高い

    • PASCAL VOCで訓練+評価したときの平均AP(Average Precision)と、COCOで同様に訓練+評価した結果を比較すると、COCOの方がAPが大きく下がる。
    • つまり、非アイコニック・雑多シーンを含むCOCOは、識別が難しい
  2. MS-COCOで学習したモデルはPASCAL VOCを一般化する傾向

    • COCOの大量データで学習すると、PASCAL VOCの一部カテゴリにおいてパフォーマンスが改善するケースも。
    • 多様な背景・アングルでの学習データを得た分、汎化能力が高まっているという見方が示唆。

また、セグメンテーション評価では、DPMが出力するパーツを合成して得られる粗い物体形状マスクを、実際のMS-COCOのピクセル領域とどれほど重なるか(Intersection over Union)を測定。
結果はかなり低いIoUでしたが、「そもそもDPMはセグメンテーションに特化していないため、最初のベースラインとしては当然」という論調。
それでも、細かいオブジェクト形状を追いたい場合にCOCOの詳細なアノテーションが役立つことが分かる例になっています。


§MS-COCOがもたらすシーン理解の可能性

MS-COCOは、物体検出・セグメンテーションだけでなく、「シーン理解(Scene Understanding)のさらなる要素」を今後追加していく意義を論文中でも示唆しています。
例えば:

  • Stuff(壁や床など境界の曖昧な領域)も含めたラベリング
  • オクルージョン(物体同士の重なり)の度合い・部位認識(アームや足などパーツ)
  • 属性(Attributes)関係性(Relations)に関するメタデータ付与
  • 3D情報(深度推定や3Dボクセル)との連携

実際、MS-COCOはその後キャプション生成(画像文章化)を研究する際の主要ベンチマークにもなりました。
さらに、マルチモーダル(テキスト×画像)研究でもCOCO Captionsが多用され、強力なTransformer系モデルの訓練にも頻繁に使われるようになっています。


§まとめと今後の展望

以上が、MS-COCO論文の要点です。
最大のポイントは「日常シーンに溢れる雑多なオブジェクトを、可能な限り正確なインスタンスセグメンテーション付きで集めた」という部分でしょう。

  • 大量データ(328k画像 / 2.5Mインスタンスに加え
  • 多様なカテゴリ(91種 / 80種はセグメンテーション付き)
  • 非アイコニック画像が多い
  • 精密なアノテーション工程

このおかげで、従来のデータセットでは難しかった「小物や重なり合う物体」の識別・位置特定が、より厳密に研究可能になっています。

一方で、難易度が高いデータセットでもあるため、古典的なDPMや従来手法は苦戦を強いられる結果となっています。
近年のディープラーニング(Faster R-CNN、Mask R-CNN、YOLO、Detectron2など)ではCOCOが当たり前に使われ、大幅な精度向上が報告されているとはいえ、まだまだ未解決の課題は多い。
たとえば、セマンティックセグメンテーションだけでなく、インスタンスセグメンテーションパノプティックセグメンテーション、さらには3Dポーズ推定画像キャプション生成との統合など、COCOを軸に研究がどんどん発展している状況です。

今後は人物の姿勢(キーポイント)物体間の関係推論シーングラフ(Scene Graph)構築といった応用がますます活発化するでしょうし、クラス数を増やした拡張版COCOマルチモーダル拡張などが広がっていくかもしれません。
いずれにせよ、「MS-COCOを制する者は、物体認識タスクを制する」と言っても過言ではないくらい、多岐にわたるタスクのベンチマークとして活躍中なので、ぜひ一度実際に触れてみることをお勧めします。


§おわりに

今回は、Microsoft COCO (Common Objects in Context) 論文をベースに、その概要や特徴、アノテーション手法、評価実験までを一気に解説しました。
物体検出や画像セグメンテーションの世界では、COCOが今日の事実上の標準ベンチマークとして機能しており、新手法の性能を評価するうえで欠かせない存在です。

もし、まだCOCOを使ったことがないという方は、公式サイト(http://mscoco.org/) からデータを入手できますので、ぜひいろいろ試してみるといいでしょう!
リッチなアノテーションと膨大なデータ量で、きっと学習データや検証タスクの幅が広がるはずです。

最後までお読みいただき、ありがとうございました!


🔑この記事のポイントまとめ

  • MS-COCO(Common Objects in Context):アイコニックでない自然な日常シーンを集め、高精度なピクセル単位セグメンテーションを付与した大規模データセット
  • PASCAL VOC、ImageNet、SUN など既存データセットと比較しても、1枚あたりのオブジェクト数が多く、難易度が高い分だけ汎化性能の向上が期待できる
  • 膨大なクラウドワーカーによる 多重アノテーションセグメンテーション検証ステージアノテーション品質を担保
  • Deformable Parts Model (DPM) など従来手法ではAPが大きく落ちるほどチャレンジングなベンチマーク
  • 人間の視点に近いシーン理解 へ近づくため、MS-COCOが今も研究コミュニティを大きく支えている

§参考文献(論文リンクなど)


§マスキング画像からCOCO形式対応のJsonファイルを作成するコード例

以下、マスキング画像からCOCO形式のJsonファイルを自動作成するコード例を記載いたします。 このコードを使えば、マスキング画像を用意すれば、COCO形式でカスタムデータセットを作成することが可能になると思います。

import os
import json
import cv2
import shutil
import numpy as np
from pycocotools import mask as maskUtils
from sklearn.model_selection import train_test_split

BASE_DIR = "path/to/your_dir"

def get_segmentation_points(mask):
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    segmentation = []
    for contour in contours:
        contour = contour.flatten().tolist()
        if len(contour) > 4:  # 有効な輪郭は最低でも4つの点が必要
            segmentation.append(contour)
    return segmentation

def create_coco_annotation(images_dir, mask_dir, output_file):
    annotations = []
    images = []
    categories = []
    category_id = 1

    for class_name in os.listdir(mask_dir):
        class_dir = os.path.join(mask_dir, class_name)
        if os.path.isdir(class_dir) and class_name != "coco" and class_name != "images":  # "coco"と"images"フォルダを無視
            category = {
                "supercategory": "none",
                "id": int(category_id),
                "name": class_name
            }
            categories.append(category)

            for image_id, image_file in enumerate(os.listdir(images_dir)):
                if image_file.endswith('.jpg'):
                    image_file_name = os.path.splitext(image_file)[0]
                    mask_file = os.path.join(class_dir, image_file_name + '.png')
                    if os.path.exists(mask_file):
                        image_path = os.path.join(images_dir, image_file)
                        image = cv2.imread(mask_file, cv2.IMREAD_GRAYSCALE)
                        height, width = image.shape

                        # COCO 画像情報
                        image_info = {
                            "license": 1,
                            "file_name": image_file,
                            "coco_url":"dummy/COCO/images/URL",
                            "height": int(height),
                            "width": int(width),
                            "date_captured":"2024/06/26",
                            "flickr_url":"dummy/flickr/images/URL",
                            "id": int(image_id)
                        }
                        images.append(image_info)

                        # セグメンテーション情報
                        segmentation = get_segmentation_points(image)
                        if segmentation:
                            # バウンディングボックス情報
                            num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(image)
                            for label in range(1, num_labels):  # 0は背景なので無視
                                x, y, w, h, area = stats[label]
                                bbox = [int(x), int(y), int(w), int(h)]

                                annotation = {
                                    "segmentation": segmentation,
                                    "area": int(area),
                                    "iscrowd": 0,
                                    "image_id": int(image_id),
                                    "bbox": bbox,
                                    "category_id": int(category_id),
                                    "id": int(len(annotations) + 1)
                                }
                                annotations.append(annotation)

            category_id += 1

    coco_format = {
        "info": {
            "description": "COCO 2017 Dataset",
            "url": "http://cocodataset.org",
            "version": "1.0",
            "year": 2017,
            "contributor": "COCO Consortium",
            "date_created": "2017/09/01"
        },
        "licenses": [
            {
                "url": "http://creativecommons.org/licenses/by-nc-sa/2.0/",
                "id": 1,
                "name": "Attribution-NonCommercial-ShareAlike License"
            },
            {
                "url": "http://creativecommons.org/licenses/by-nc/2.0/",
                "id": 2,
                "name": "Attribution-NonCommercial License"
            },
            {
                "url": "http://creativecommons.org/licenses/by-nc-nd/2.0/",
                "id": 3,
                "name": "Attribution-NonCommercial-NoDerivs License"
            },
            {
                "url": "http://creativecommons.org/licenses/by/2.0/",
                "id": 4,
                "name": "Attribution License"
            },
            {
                "url": "http://creativecommons.org/licenses/by-sa/2.0/",
                "id": 5,
                "name": "Attribution-ShareAlike License"
            },
            {
                "url": "http://creativecommons.org/licenses/by-nd/2.0/",
                "id": 6,
                "name": "Attribution-NoDerivs License"
            },
            {
                "url": "http://flickr.com/commons/usage/",
                "id": 7,
                "name": "No known copyright restrictions"
            },
            {
                "url": "http://www.usa.gov/copyright.shtml",
                "id": 8,
                "name": "United States Government Work"
            }
        ],
        "images": images,
        "annotations": annotations,
        "categories": categories
    }

    with open(output_file, 'w') as f:
        json.dump(coco_format, f, indent=4)

def split_dataset(images_dir, train_dir, val_dir, split_ratio=0.7):
    images = [f for f in os.listdir(images_dir) if f.endswith('.jpg')]
    train_images, val_images = train_test_split(images, train_size=split_ratio, random_state=42)
    
    os.makedirs(train_dir, exist_ok=True)
    os.makedirs(val_dir, exist_ok=True)
    
    for image in train_images:
        shutil.copy(os.path.join(images_dir, image), os.path.join(train_dir, image))
    
    for image in val_images:
        shutil.copy(os.path.join(images_dir, image), os.path.join(val_dir, image))

if __name__ == "__main__":
    base_dir = BASE_DIR
    images_dir = os.path.join(base_dir, "images")
    coco_dir = os.path.join(base_dir, "coco")
    train_dir = os.path.join(coco_dir, "train2017")
    val_dir = os.path.join(coco_dir, "val2017")
    annotations_dir = os.path.join(coco_dir, "annotations")
    
    # 画像を7:3に分割してコピー
    split_dataset(images_dir, train_dir, val_dir, split_ratio=0.7)
    
    # アノテーションを生成
    os.makedirs(annotations_dir, exist_ok=True)
    create_coco_annotation(train_dir, base_dir, os.path.join(annotations_dir, "instances_train2017.json"))
    create_coco_annotation(val_dir, base_dir, os.path.join(annotations_dir, "instances_val2017.json"))

    print(f"COCO形式のアノテーションJsonファイルが{annotations_dir}に保存されました。")