Ultralytics YOLO27: 대기자 명단에 등록해 주십시오.

장면 텍스트 OBB 데이터셋

장면 텍스트 OBB 데이터셋은 COCO-Text와 HierText를 하나의 클래스로 구성된 단어 탐지 데이터셋으로 결합하여 방향 바운딩 박스(OBB) 학습에 사용합니다. 회전되거나 곡선 형태인 텍스트, 조밀하게 배치된 텍스트, 손글씨 및 인쇄 텍스트가 포함된 자연 장면과 문서 이미지를 포함합니다.

주요 기능#

  • 이미지 65,325개 및 변환 후 판독 가능한 단어 어노테이션 1,288,991개.
  • 정규화된 폴리곤 레이블을 사용하는 단일 text 클래스로, Ultralytics YOLO OBB 모델과 호환됩니다.
  • 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/ 레이아웃에 따라 Ultralytics datasets_dir 아래에 파일을 배치하고, images/train2017, images/val2017 및 COCO 분할 텍스트 파일을 포함하세요. 다운로드 스크립트는 COCO를 다시 다운로드하는 대신 기존 파일을 재사용합니다.

기여자

댓글

질문을 하거나 결과를 공유하거나 다른 사람을 도와주세요.