场景文本 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,919 | 8,281 / 862,577 | 51,967 / 928,496 |
| 验证 | 4,927 / 7,211 | 1,724 / 178,382 | 6,651 / 185,593 |
| 测试 | 5,073 / 7,714 | 1,634 / 167,188 | 6,707 / 174,902 |
| 总计 | 53,686 / 80,844 | 11,639 / 1,208,147 | 65,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 图像,然后再进行转换。
# 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。
评论
提出问题、分享结果,或帮助其他人。