MS-COCO徹底解説:深層学習時代の最強オブジェクト検出・セグメンテーションBenchmark!シーン理解に革新をもたらす「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など複数研究機関の共著で発表され、2015年に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カテゴリの組み合わせやシーンカテゴリ×オブジェクトカテゴリのキーワード検索などを駆使。
  • 結果として、アイコニック(対象が大きく、背景がスッキリ)ではなく、背後に雑多なオブジェクトがたくさん映り込んでいる画像が多い。
  1. 全てのオブジェクトに「インスタンスセグメンテーション」ラベルを付与
  • 1つの画像に複数(場合によっては10個以上)オブジェクトが混在していても、個別にピクセルごとの輪郭を丁寧に描画。
  • セマンティックセグメンテーション(どのピクセルが何のクラスか)に加え、インスタンスを分離できるため、1画像内のどのピクセルがどの個体に属するかを識別できるのが強み。
  1. 約91種の一般的なオブジェクトカテゴリ(最終的には80カテゴリのセグメンテーションが中心)
  • 「犬」「猫」「車」「人」「椅子」など、4歳児が認識できる程度のエントリーレベルカテゴリを厳選。
  • 2014~15年時点で合計328,000枚超の画像、そして2,500,000件のオブジェクトインスタンスをアノテーション。
  1. 一枚あたりに含まれるオブジェクト数が多い
  • MS-COCO:1枚あたり平均7.7インスタンス/3.5カテゴリ
  • PASCAL VOCやImageNetは、平均2~3インスタンス程度

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


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

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

  1. Non-iconic Views(非アイコニック視点)の重要性
  • アイコニック画像(正面や横からドカンと大きく写っているような画像)だと、比較的検出は簡単。
  • しかし、実世界では(車や人など)雑多なアングル&重複オブジェクトが当たり前なので、そちらに特化したデータセットを作る必要がある。
  1. Contextual Reasoning(物体間のコンテクスト推論)
  • 画像内でオブジェクトが配置される状況(上下関係、隣接、サイズ比)によって、判別がつきやすい場合がある。
  • たとえば、「非常に小さい物体」や「これ何だろう…?」と人間でも迷うような状況でも、周囲の物体に注目することで認識精度を高められるのでは?というモチベーション。
  1. Precise 2D Localization(高精度な位置情報)
  • 単なるBounding Boxだけでなく、インスタンスセグメンテーションを用意したのは、物体の正確な形状や領域を評価しようという狙い。
  • 従来の境界ボックスだけだと、たとえば人間の腕部分が欠けているかどうかは分からないが、MS-COCOは実際にピクセルを塗りつぶしているため、より高品質の位置検出を研究できる。

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

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

  1. Flickrから日常写真を収集
  • 単なる「dog」検索ではなく、「dog + car」など、複数オブジェクトの組み合わせをキーワードにすることで、複数物体が混在する画像を効率的に入手。
  • アイコニック画像を除外するフィルタリングを機械+クラウドワーカーの協力で行い、自然な背景の写真を選定。
  1. カテゴリラベリング(Category Labeling)
  • 画像に対して、91の候補カテゴリのうち「どれが写っているか」を効率よく判別させるため、階層化したユーザーインターフェースを設計。
  • 8人のワーカーに複数回チェックさせることで高いリコールを達成。
  1. インスタンススポッティング(Instance Spotting)
  • 具体的に何個のオブジェクトがあるか、全て×印をつけていく作業。
  • やはり8人のワーカーによる多重チェックを通すことで、取りこぼしを防ぐ。
  1. セグメンテーション(Instance Segmentation)
  • 各オブジェクトをピクセル単位で塗りつぶし、領域を割り当てる。
  • 膨大な作業量を分散処理するため、クラウドワーカーに対し事前トレーニング(簡単な課題)を課してフィルタリング。
  • アノテーションの品質検証ステージを導入し、低品質のマスクは破棄して再アノテーションするプロセスを徹底。
  1. クラウドワーカーの「精度とリコール」の管理
  • カテゴリの見落としが起こらないよう、複数回の投票を行い、「もしカテゴリが写っていれば誰かは気づくだろう」方式で網羅。
  • カテゴリの誤検出が含まれていても、次段階で除去できる(False Positive除去)。
  1. Crowd(大量インスタンス)の扱い
  • 例:人混みやフルーツが山積みなど、一画像に数十個以上の同カテゴリオブジェクトが映り込むケース。
  • その場合、個別にセグメンテーションを描画すると現実的でないため、10~15個以上は「crowd」領域として一括りにするオプションも。

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


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

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

  1. カテゴリ検出では8人以上に投票させ、どれか1人でも見つければOK
  • この多数決手法により、高いリコール(見逃しの少なさ)を実現。
  1. セグメンテーション担当ワーカーには、事前テストタスクで一定精度を求める
  • 簡単な例画像を1つ与え、そのマスクが一定の品質閾値(IoUなど)を超えていないと、本番タスクには参加できない。
  1. 各セグメンテーションは複数人で検証し、もし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は、識別が難しい
  1. 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を使ったことがないという方は、公式サイト(https://mscoco.org/) からデータを入手できますので、ぜひいろいろ試してみるといいでしょう!
リッチなアノテーションと膨大なデータ量で、きっと学習データや検証タスクの幅が広がるはずです。

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


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

  • MS-COCO(Common Objects in Context):アイコニックでない自然な日常シーンを集め、高精度なピクセル単位セグメンテーションを付与した大規模データセット
  • PASCAL VOC、ImageNet、SUN など既存データセットと比較しても、1枚あたりのオブジェクト数が多く、難易度が高い分だけ汎化性能の向上が期待できる
  • 膨大なクラウドワーカーによる 多重アノテーションセグメンテーション検証ステージ でアノテーション品質を担保
  • Deformable Parts Model (DPM) など従来手法ではAPが大きく落ちるほどチャレンジングなベンチマーク
  • 人間の視点に近いシーン理解 へ近づくため、MS-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": "https://cocodataset.org",
            "version": "1.0",
            "year": 2017,
            "contributor": "COCO Consortium",
            "date_created": "2017/09/01"
        },
        "licenses": [
            {
                "url": "https://creativecommons.org/licenses/by-nc-sa/2.0/",
                "id": 1,
                "name": "Attribution-NonCommercial-ShareAlike License"
            },
            {
                "url": "https://creativecommons.org/licenses/by-nc/2.0/",
                "id": 2,
                "name": "Attribution-NonCommercial License"
            },
            {
                "url": "https://creativecommons.org/licenses/by-nc-nd/2.0/",
                "id": 3,
                "name": "Attribution-NonCommercial-NoDerivs License"
            },
            {
                "url": "https://creativecommons.org/licenses/by/2.0/",
                "id": 4,
                "name": "Attribution License"
            },
            {
                "url": "https://creativecommons.org/licenses/by-sa/2.0/",
                "id": 5,
                "name": "Attribution-ShareAlike License"
            },
            {
                "url": "https://creativecommons.org/licenses/by-nd/2.0/",
                "id": 6,
                "name": "Attribution-NoDerivs License"
            },
            {
                "url": "https://flickr.com/commons/usage/",
                "id": 7,
                "name": "No known copyright restrictions"
            },
            {
                "url": "https://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}に保存されました。")

画像解析

Posted by yuzukaki-dialog