Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ICDAR2013 dataset integration #662

Merged
merged 37 commits into from
Dec 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
e7122a5
start synth
felixdittrich92 Nov 13, 2021
4fa0aff
cleanup
felixdittrich92 Nov 15, 2021
3ffdcc5
Merge branch 'mindee:main' into main
felixdittrich92 Nov 15, 2021
b74f06a
start synth
felixdittrich92 Nov 15, 2021
1a661e0
add synthtext
felixdittrich92 Nov 15, 2021
6270c93
add docu and tests
felixdittrich92 Nov 15, 2021
d74f148
apply code factor suggestions
felixdittrich92 Nov 15, 2021
9099e95
apply changes
felixdittrich92 Nov 15, 2021
23eca0d
Merge branch 'mindee:main' into main
felixdittrich92 Nov 15, 2021
7ba31e1
clean
felixdittrich92 Nov 15, 2021
02a8104
Merge branch 'mindee:main' into main
felixdittrich92 Nov 15, 2021
6955110
Merge branch 'mindee:main' into main
felixdittrich92 Nov 16, 2021
8fbeb30
Merge branch 'mindee:main' into main
felixdittrich92 Nov 16, 2021
7408935
Merge branch 'mindee:main' into main
felixdittrich92 Nov 17, 2021
a2b0fbc
Merge branch 'mindee:main' into main
felixdittrich92 Nov 19, 2021
a9cbd14
Merge branch 'mindee:main' into main
felixdittrich92 Nov 20, 2021
b245443
Merge branch 'mindee:main' into main
felixdittrich92 Nov 23, 2021
0cb2f7b
Merge branch 'mindee:main' into main
felixdittrich92 Nov 23, 2021
743c54a
Merge branch 'mindee:main' into main
felixdittrich92 Nov 25, 2021
1c1cbcb
Merge branch 'mindee:main' into main
felixdittrich92 Nov 25, 2021
cfbd898
Merge branch 'mindee:main' into main
felixdittrich92 Nov 30, 2021
8787f74
Merge branch 'mindee:main' into main
felixdittrich92 Nov 30, 2021
47ed381
Merge branch 'mindee:main' into main
felixdittrich92 Nov 30, 2021
eb7f59c
Merge branch 'mindee:main' into main
felixdittrich92 Dec 2, 2021
efaa2c0
Merge branch 'mindee:main' into main
felixdittrich92 Dec 3, 2021
f7288ca
Merge branch 'mindee:main' into main
felixdittrich92 Dec 3, 2021
044f523
Merge branch 'mindee:main' into main
felixdittrich92 Dec 7, 2021
9dc3983
Merge branch 'mindee:main' into main
felixdittrich92 Dec 7, 2021
fc4c2d9
Merge branch 'mindee:main' into main
felixdittrich92 Dec 7, 2021
78b8d27
Merge branch 'mindee:main' into main
felixdittrich92 Dec 8, 2021
d26341b
Merge branch 'mindee:main' into main
felixdittrich92 Dec 9, 2021
6338cac
Merge branch 'mindee:main' into main
felixdittrich92 Dec 10, 2021
351e008
opening ic13
felixdittrich92 Nov 30, 2021
f046b63
opening ic13
felixdittrich92 Nov 30, 2021
ebb7a82
update tests
felixdittrich92 Nov 30, 2021
dc4a033
change tests
felixdittrich92 Nov 30, 2021
e2d15f2
apply changes
felixdittrich92 Dec 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/datasets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Here are all datasets that are available through docTR:
.. autoclass:: SVT
.. autoclass:: SynthText
.. autoclass:: IC03
.. autoclass:: IC13


Data Loading
Expand Down
1 change: 1 addition & 0 deletions doctr/datasets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .doc_artefacts import *
from .funsd import *
from .ic03 import *
from .ic13 import *
from .iiit5k import *
from .ocr import *
from .recognition import *
Expand Down
91 changes: 91 additions & 0 deletions doctr/datasets/ic13.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Copyright (C) 2021, Mindee.

# This program is licensed under the Apache License version 2.
# See LICENSE or go to <https://www.apache.org/licenses/LICENSE-2.0.txt> for full license details.

import csv
import os
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Tuple

import numpy as np

from .datasets import AbstractDataset

__all__ = ["IC13"]


class IC13(AbstractDataset):
"""IC13 dataset from `"ICDAR 2013 Robust Reading Competition" <https://rrc.cvc.uab.es/>`_.
Example::
>>> # NOTE: You need to download both image and label parts from Focused Scene Text challenge Task2.1 2013-2015.
>>> from doctr.datasets import IC13
>>> train_set = IC13(img_folder="/path/to/Challenge2_Training_Task12_Images",
>>> label_folder="/path/to/Challenge2_Training_Task1_GT")
>>> img, target = train_set[0]
>>> test_set = IC13(img_folder="/path/to/Challenge2_Test_Task12_Images",
>>> label_folder="/path/to/Challenge2_Test_Task1_GT")
>>> img, target = test_set[0]
Args:
img_folder: folder with all the images of the dataset
label_folder: folder with all annotation files for the images
sample_transforms: composable transformations that will be applied to each image
rotated_bbox: whether polygons should be considered as rotated bounding box (instead of straight ones)
"""

def __init__(
self,
img_folder: str,
label_folder: str,
sample_transforms: Optional[Callable[[Any], Any]] = None,
rotated_bbox: bool = False,
) -> None:
super().__init__(img_folder)
self.sample_transforms = sample_transforms

# File existence check
if not os.path.exists(label_folder) or not os.path.exists(img_folder):
raise FileNotFoundError(
f"unable to locate {label_folder if not os.path.exists(label_folder) else img_folder}")

self.data: List[Tuple[Path, Dict[str, Any]]] = []
np_dtype = np.float32

img_names = os.listdir(img_folder)

for img_name in img_names:

img_path = Path(img_folder, img_name)
label_path = Path(label_folder, "gt_" + Path(img_name).stem + ".txt")
felixdittrich92 marked this conversation as resolved.
Show resolved Hide resolved

with open(label_path, newline='\n') as f:
_lines = [
[val[:-1] if val.endswith(",") else val for val in row]
for row in csv.reader(f, delimiter=' ', quotechar="'")
]
labels = [line[-1] for line in _lines]
# xmin, ymin, xmax, ymax
box_targets = np.array([list(map(int, line[:4])) for line in _lines], dtype=np_dtype)
if rotated_bbox:
# x_center, y_center, width, height, 0
box_targets = np.array([[coords[0] + (coords[2] - coords[0]) / 2,
coords[1] + (coords[3] - coords[1]) / 2,
(coords[2] - coords[0]),
(coords[3] - coords[1]), 0.0] for coords in box_targets], dtype=np_dtype)

self.data.append((img_path, dict(boxes=box_targets, labels=labels)))

def __getitem__(self, index: int) -> Tuple[np.ndarray, Dict[str, Any]]:
img, target = self._read_sample(index)
h, w = self._get_img_shape(img)
if self.sample_transforms is not None:
img = self.sample_transforms(img)

# Boxes
boxes = target['boxes'].copy()
boxes[..., [0, 2]] /= w
boxes[..., [1, 3]] /= h
boxes = boxes.clip(0, 1)
target['boxes'] = boxes

return img, target
20 changes: 20 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,23 @@ def mock_ocrdataset(tmpdir_factory, mock_image_stream):
f.write(file.getbuffer())

return str(image_folder), str(label_file)


@pytest.fixture(scope="session")
def mock_ic13(tmpdir_factory, mock_image_stream):
file = BytesIO(mock_image_stream)
image_folder = tmpdir_factory.mktemp("images")
label_folder = tmpdir_factory.mktemp("labels")
labels = ["1309, 2240, 1440, 2341, 'I'\n",
"800, 2240, 1440, 2341, 'am'\n",
"500, 2240, 1440, 2341, 'a'\n",
"900, 2240, 1440, 2341, 'jedi'\n",
"400, 2240, 1440, 2341, '!'"]
for i in range(5):
fn_l = label_folder.join(f"gt_mock_image_file_{i}.txt")
with open(fn_l, 'w') as f:
f.writelines(labels)
fn_i = image_folder.join(f"mock_image_file_{i}.jpg")
with open(fn_i, 'wb') as f:
f.write(file.getbuffer())
return str(image_folder), str(label_folder)
31 changes: 31 additions & 0 deletions tests/pytorch/test_datasets_pt.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,34 @@ def test_charactergenerator():
assert isinstance(images, torch.Tensor) and images.shape == (2, 3, *input_size)
assert isinstance(targets, torch.Tensor) and targets.shape == (2,)
assert targets.dtype == torch.int64


@pytest.mark.parametrize(
"size, rotate",
[
[5, True], # Actual set has 229 train and 233 test samples
[5, False]

],
)
def test_ic13_dataset(mock_ic13, size, rotate):
input_size = (512, 512)
ds = datasets.IC13(
*mock_ic13,
sample_transforms=Resize(input_size),
rotated_bbox=rotate,
)

assert len(ds) == size
img, target = ds[0]
assert isinstance(img, torch.Tensor)
assert img.shape[-2:] == input_size
assert img.dtype == torch.float32
assert isinstance(target, dict)
assert isinstance(target['boxes'], np.ndarray) and np.all((target['boxes'] <= 1) & (target['boxes'] >= 0))
assert isinstance(target['labels'], list) and all(isinstance(s, str) for s in target['labels'])

loader = DataLoader(ds, batch_size=2, collate_fn=ds.collate_fn)
images, targets = next(iter(loader))
assert isinstance(images, torch.Tensor) and images.shape == (2, 3, *input_size)
assert isinstance(targets, list) and all(isinstance(elt, dict) for elt in targets)
31 changes: 31 additions & 0 deletions tests/tensorflow/test_datasets_tf.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,34 @@ def test_charactergenerator():
assert isinstance(images, tf.Tensor) and images.shape == (2, *input_size, 3)
assert isinstance(targets, tf.Tensor) and targets.shape == (2,)
assert targets.dtype == tf.int32


@pytest.mark.parametrize(
"size, rotate",
[
[5, True], # Actual set has 229 train and 233 test samples
[5, False]

],
)
def test_ic13_dataset(mock_ic13, size, rotate):
input_size = (512, 512)
ds = datasets.IC13(
*mock_ic13,
sample_transforms=Resize(input_size),
rotated_bbox=rotate,
)

assert len(ds) == size
img, target = ds[0]
assert isinstance(img, tf.Tensor)
assert img.shape[:2] == input_size
assert img.dtype == tf.float32
assert isinstance(target, dict)
assert isinstance(target['boxes'], np.ndarray) and np.all((target['boxes'] <= 1) & (target['boxes'] >= 0))
assert isinstance(target['labels'], list) and all(isinstance(s, str) for s in target['labels'])

loader = DataLoader(ds, batch_size=2)
images, targets = next(iter(loader))
assert isinstance(images, tf.Tensor) and images.shape == (2, *input_size, 3)
assert isinstance(targets, list) and all(isinstance(elt, dict) for elt in targets)