Ultralytics YOLO27: ウェイティングリストに登録してください。
はじめる

シーンテキストOBBデータセット

シーンテキストOBBデータセットは、COCO-TextとHierTextを統合し、方向付きバウンディングボックス(OBB)のトレーニング用に単一クラスの単語検出データセットを構成します。自然風景や文書の画像を含み、回転したテキスト、曲線状のテキスト、高密度に配置されたテキスト、手書きテキスト、印刷テキストを収録しています。

主な機能#

  • 変換後は65,325枚の画像と1,288,991件の判読可能な単語アノテーションを含みます。
  • Ultralytics YOLO OBBモデルと互換性のある正規化ポリゴンラベルを使用する、textの1クラス構成です。
  • COCO-Textは多様な自然風景を提供し、HierTextは自然風景や文書から高密度のテキストを提供します。
  • データセットYAMLを介して、ソースの自動ダウンロード、アノテーション変換、クロスプラットフォームでの画像リンク作成を行います。
  • 4点を超えるソースポリゴンは保持され、OBBデータパイプラインによって最小面積の矩形に変換されます。

データセットの構成#

変換後のデータセットは以下の分割構成です。件数には、輪郭面積が1ピクセル以上の判読可能な単語のみが含まれます。元画像ファイルがない画像は除外されます。

分割COCO-Textの画像数 / 単語数HierTextの画像数 / 単語数統合後の画像数 / 単語数
学習43,686 / 65,9198,281 / 862,57751,967 / 928,496
検証4,927 / 7,2111,724 / 178,3826,651 / 185,593
テスト5,073 / 7,7141,634 / 167,1886,707 / 174,902
合計53,686 / 80,84411,639 / 1,208,14765,325 / 1,288,991

COCO-Textでは公式のトレーニング分割を使用します。公式の検証画像は、画像IDを使って決定論的にほぼ均等な検証用サブセットとテスト用サブセットに分割されます。HierTextでは公式のトレーニング、検証、テストの各分割を維持し、validationをvalに名称変更します。

保持対象の単語がない画像も、空のラベルファイルを付けて背景画像としてデータセットに残します。変換後のディレクトリ構成は次のとおりです。

datasets/
└── scene-text-yolo/
    ├── coco-text/
    │   ├── train/
    │   │   ├── images/
    │   │   └── labels/
    │   ├── val/
    │   └── test/
    └── hiertext/
        ├── train/
        ├── val/
        └── test/

各ラベル行には、クラス0の後に正規化ポリゴン座標が続きます。ファイルシステムが対応している場合は画像をハードリンクし、対応していない場合はコピーします。

データセットYAML#

scene-text-obb.yaml設定ファイルは、変換済みの両データセットを参照します。初回使用時には、変換前にCOCO 2017の画像、COCO-Textのアノテーション、HierTextのアノテーション、HierTextの画像をダウンロードします。

scene-text-obb.yaml
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license

# Scene Text OBB dataset combining COCO-Text and HierText legible word annotations
# COCO-Text: https://bgshih.github.io/cocotext/ (annotations: CC BY 4.0; COCO image terms apply)
# HierText: https://github.com/google-research-datasets/hiertext (CC BY-SA 4.0; Open Images terms apply)
# Documentation: https://docs.ultralytics.com/datasets/obb/scene-text
# Example usage: yolo obb train data=scene-text-obb.yaml model=yolo26n-obb.pt imgsz=640
# parent
# ├── ultralytics
# └── datasets
#     ├── coco
#     ├── coco-text
#     ├── hiertext
#     └── scene-text-yolo

# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: scene-text-yolo # dataset root dir
train:
  - coco-text/train/images # 43686 images, 65919 words
  - hiertext/train/images # 8281 images, 862577 words
val:
  - coco-text/val/images # 4927 images, 7211 words
  - hiertext/val/images # 1724 images, 178382 words
test:
  - coco-text/test/images # 5073 images, 7714 words
  - hiertext/test/images # 1634 images, 167188 words

names:
  0: text

# Download COCO 2017 images and both annotation sources, then convert legible words to YOLO OBB format (~24 GB)
download: |
  import gzip
  import hashlib
  import json
  import os
  import shutil
  from pathlib import Path
  from tempfile import TemporaryDirectory

  import cv2
  import numpy as np

  from ultralytics.data.utils import check_det_dataset
  from ultralytics.utils import TQDM
  from ultralytics.utils.downloads import download

  def polygon_label(vertices, width, height):
      polygon = np.asarray(vertices, dtype=np.float32).reshape(-1, 2)
      if len(polygon) < 3 or cv2.contourArea(polygon) < 1:
          return ""
      polygon[:, 0] = polygon[:, 0].clip(0, width - 1)
      polygon[:, 1] = polygon[:, 1].clip(0, height - 1)
      polygon /= np.array([width, height], dtype=np.float32)
      return "0 " + " ".join(f"{x:.7f} {y:.7f}" for x, y in polygon)

  def link_image(source, destination):
      destination.parent.mkdir(parents=True, exist_ok=True)
      try:
          os.link(source, destination)
      except OSError:
          shutil.copy2(source, destination)

  def write_labels(label_dir, stem, labels):
      label_dir.mkdir(parents=True, exist_ok=True)
      (label_dir / f"{stem}.txt").write_text("\n".join(labels) + ("\n" if labels else ""), encoding="utf-8")

  def convert_coco_text(parent, destination):
      source_dir = parent / "coco-text"
      annotations = source_dir / "cocotext.v2.json"
      if not annotations.is_file():
          download(
              "https://github.com/bgshih/cocotext/releases/download/dl/cocotext.v2.zip",
              dir=source_dir,
              delete=True,
          )
      coco = check_det_dataset("coco.yaml")
      images = Path(coco["path"]) / "images"
      data = json.loads(annotations.read_text(encoding="utf-8"))
      for image_id, image in TQDM(data["imgs"].items(), desc="Converting COCO-Text"):
          split = (
              "train"
              if image["set"] == "train"
              else ("val" if hashlib.sha256(str(image["id"]).encode()).digest()[0] < 128 else "test")
          )
          filename = image["file_name"].replace("COCO_train2014_", "", 1)
          source = images / "train2017" / filename
          if not source.exists():
              source = images / "val2017" / filename
          if not source.exists():
              continue
          image_dir = destination / split / "images"
          link_image(source, image_dir / filename)
          im = cv2.imread(str(source))
          if im is None:
              raise ValueError(f"Unreadable image: {source}")
          height, width = im.shape[:2]
          labels = []
          for ann_id in data["imgToAnns"].get(image_id, []):
              ann = data["anns"][str(ann_id)]
              if ann["legibility"] == "legible" and (label := polygon_label(ann["mask"], width, height)):
                  labels.append(label)
          write_labels(destination / split / "labels", Path(filename).stem, labels)

  def convert_hiertext(parent, destination):
      source = parent / "hiertext" / "source"
      splits = {"train": "train", "validation": "val", "test": "test"}
      base = "https://raw.githubusercontent.com/google-research-datasets/hiertext/main"
      if not all((source / "gt" / f"{split}.jsonl.gz").is_file() for split in splits):
          download([f"{base}/gt/{split}.jsonl.gz" for split in splits], dir=source / "gt", unzip=False, threads=3)
      if not all((source / split).is_dir() for split in splits):
          download(
              [f"https://open-images-dataset.s3.amazonaws.com/ocr/{split}.tgz" for split in splits],
              dir=source,
              delete=True,
              threads=3,
          )
      for source_split, split in splits.items():
          with gzip.open(source / "gt" / f"{source_split}.jsonl.gz", "rt", encoding="utf-8") as f:
              rows = json.load(f)["annotations"]
          for row in TQDM(rows, desc=f"Converting HierText {source_split}"):
              image_id = row["image_id"]
              source_image = source / source_split / f"{image_id}.jpg"
              if not source_image.exists():
                  continue
              image = destination / split / "images" / f"{image_id}.jpg"
              link_image(source_image, image)
              im = cv2.imread(str(image))
              if im is None:
                  raise ValueError(f"Unreadable image: {source_image}")
              height, width = im.shape[:2]
              labels = []
              for paragraph in row["paragraphs"]:
                  for line in paragraph["lines"]:
                      for word in line["words"]:
                          if word["legible"] and (label := polygon_label(word["vertices"], width, height)):
                              labels.append(label)
              write_labels(destination / split / "labels", image_id, labels)

  final = Path(yaml["path"])
  parent = final.parent
  parent.mkdir(parents=True, exist_ok=True)
  with TemporaryDirectory(dir=parent, prefix=".scene-text-") as tmp:
      destination = Path(tmp)
      convert_coco_text(parent, destination / "coco-text")
      convert_hiertext(parent, destination / "hiertext")
      destination.rename(final)
ダウンロードサイズとライセンス

ソースのダウンロードには約24 GBが必要です。COCOの一時アーカイブを含め、初回のダウンロードと展開中に少なくとも50 GBの空き容量を確保してください。ハードリンクを利用できない場合、画像のコピーに追加のストレージが必要です。変換済みデータを使用または再配布する前に、ライセンス条項を確認してください。

使用方法#

以下のコマンドを使用して、画像サイズ640ピクセルでYOLO26 OBBモデルをトレーニングします。Ultralyticsのdatasets_dir内にある既存のCOCOおよびHierTextのソースファイルは再利用されます。

トレーニング例
from ultralytics import YOLO

model = YOLO("yolo26n-obb.pt")
results = model.train(data="scene-text-obb.yaml", epochs=100, imgsz=640)

用途#

このデータセットを使用して、文書処理、レシート解析、標識翻訳、産業検査、OCRパイプライン向けの単語レベルテキスト検出器をトレーニングできます。検出はOCRの一段階にすぎません。文字起こしが必要な場合は、検出器とテキスト認識器を組み合わせてください。

ライセンス#

統合データセットには、単一の無条件ライセンスではなく、ソースごとに異なる条項が適用されます。

  • COCO-Textのアノテーション:CC BY 4.0。基盤となるCOCO画像の利用条件も引き続き適用されます。
  • HierTextのアノテーション:CC BY-SA 4.0。改変物を再配布する場合は、出典の表示、変更内容の明記、同一または互換性のあるライセンスでの提供が必要です。
  • HierTextの画像:Open Imagesから取得されています。Open Imagesの利用条件では、画像はCC BY 2.0として記載されており、各画像のライセンスを個別に確認するよう求めています。

これらのアノテーションライセンスでは、条件に従うことを前提に商用利用が認められていますが、データセットはパブリックドメインではありません。データを再配布する場合は、ソースの帰属表示を維持し、該当する画像の利用条件を確認してください。

引用と謝辞#

このデータセットを使用する場合は、両方のソースデータセットを引用してください。

引用
@article{veit2016cocotext,
  title={COCO-Text: Dataset and Benchmark for Text Detection and Recognition in Natural Images},
  author={Veit, Andreas and Matera, Tomas and Neumann, Lukas and Matas, Jiri and Belongie, Serge},
  journal={arXiv preprint arXiv:1601.07140},
  year={2016}
}

@inproceedings{long2022towards,
  title={Towards End-to-End Unified Scene Text Detection and Layout Analysis},
  author={Long, Shangbang and Qin, Siyang and Panteleev, Dmitry and Bissacco, Alessandro and Fujii, Yasuhisa and Raptis, Michalis},
  booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition},
  year={2022}
}

これらのデータセットを作成、維持しているCOCO-TextチームとHierTextチームに感謝します。

よくある質問#

  • いいえ。この変換では、OBBトレーニング用に単一クラスの単語検出ポリゴンが生成されます。パイプラインで書き起こしも必要な場合は、元のCOCO-TextおよびHierTextのアノテーションを使用してください。

  • COCO-Textには、トレーニング用と検証用のアノテーションが用意されています。この設定では公式のトレーニング分割を維持し、公式の検証画像を決定論的に検証用とテスト用のサブセットに分割します。HierTextでは公式の分割を維持します。

  • はい。標準のcoco/レイアウトで、images/train2017、images/val2017、COCOの分割テキストファイルを含め、Ultralyticsのdatasets_dir内に配置してください。ダウンロードスクリプトはCOCOを再ダウンロードせず、既存のファイルを再利用します。

コントリビューター

コメント

質問したり、結果を共有したり、ほかの人をサポートしたりできます。