Ultralytics YOLO27: 加入候补名单。
开始使用

场景文本 OBB 数据集

场景文本 OBB 数据集将 COCO-Text 和 HierText 合并为一个单类别词语检测数据集,用于有向边界框 (OBB)训练。该数据集包含自然场景和文档图像,涵盖旋转、弯曲、密集排列的文本,以及手写和印刷文本。

主要功能#

  • 65,325 张图像和1,288,991 个清晰可辨的词语标注(转换后)。
  • 一个 text 类别,使用归一化多边形标签,兼容 Ultralytics YOLO OBB 模型。
  • COCO-Text 提供多样的自然场景;HierText 提供来自自然场景和文档的密集文本。
  • 通过数据集 YAML 自动下载源文件、转换标注,并跨平台链接图像。
  • 源多边形中超过四个点的部分会被保留,并由 OBB 数据流水线转换为最小面积矩形。

数据集结构#

转换后的数据集包含以下划分。计数仅包含轮廓面积至少为一个像素的清晰可辨词语;缺少源文件的图像不计入。

划分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。初次下载和解压时,请至少预留 50 GB 可用存储空间,其中包括临时 COCO 压缩包所需空间。如果无法使用硬链接,复制图像还需要额外存储空间。使用或重新分发转换后的数据前,请查看许可条款。

用法#

使用以下命令,以 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 保留其官方划分。

  • 可以。将文件放在 Ultralytics datasets_dir 目录下,并采用标准的 coco/ 布局,其中包括 images/train2017、images/val2017 和 COCO 划分文本文件。下载脚本会重复使用这些文件,而不会再次下载 COCO。

贡献者

评论

提出问题、分享结果,或帮助其他人。