ocr-harness 1.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. ocr_harness-1.0.3/LICENSE +21 -0
  2. ocr_harness-1.0.3/PKG-INFO +322 -0
  3. ocr_harness-1.0.3/README.md +291 -0
  4. ocr_harness-1.0.3/consts.py +5 -0
  5. ocr_harness-1.0.3/evaluation/__init__.py +13 -0
  6. ocr_harness-1.0.3/evaluation/evaluation_analysis.py +237 -0
  7. ocr_harness-1.0.3/evaluation/evaluation_pipeline.py +483 -0
  8. ocr_harness-1.0.3/evaluation/metrics.py +265 -0
  9. ocr_harness-1.0.3/evaluation/ocr_ground_truth.py +42 -0
  10. ocr_harness-1.0.3/ocr_backbone/__init__.py +16 -0
  11. ocr_harness-1.0.3/ocr_backbone/bounding_box.py +74 -0
  12. ocr_harness-1.0.3/ocr_backbone/image_preprocessing.py +181 -0
  13. ocr_harness-1.0.3/ocr_backbone/input_image.py +24 -0
  14. ocr_harness-1.0.3/ocr_backbone/ocr_abstract.py +212 -0
  15. ocr_harness-1.0.3/ocr_backbone/ocr_config.py +218 -0
  16. ocr_harness-1.0.3/ocr_backbone/ocr_result.py +44 -0
  17. ocr_harness-1.0.3/ocr_backbone/polygon.py +92 -0
  18. ocr_harness-1.0.3/ocr_harness.egg-info/PKG-INFO +322 -0
  19. ocr_harness-1.0.3/ocr_harness.egg-info/SOURCES.txt +44 -0
  20. ocr_harness-1.0.3/ocr_harness.egg-info/dependency_links.txt +1 -0
  21. ocr_harness-1.0.3/ocr_harness.egg-info/requires.txt +24 -0
  22. ocr_harness-1.0.3/ocr_harness.egg-info/top_level.txt +7 -0
  23. ocr_harness-1.0.3/ocr_modules/__init__.py +40 -0
  24. ocr_harness-1.0.3/ocr_modules/easyocr_module.py +64 -0
  25. ocr_harness-1.0.3/ocr_modules/paddleocr_module.py +137 -0
  26. ocr_harness-1.0.3/ocr_modules/pytesseract_module.py +75 -0
  27. ocr_harness-1.0.3/pyproject.toml +88 -0
  28. ocr_harness-1.0.3/scripts/__init__.py +1 -0
  29. ocr_harness-1.0.3/scripts/draw_bboxes.py +107 -0
  30. ocr_harness-1.0.3/scripts/run_evaluation.py +120 -0
  31. ocr_harness-1.0.3/setup.cfg +4 -0
  32. ocr_harness-1.0.3/tagging_tool/__init__.py +0 -0
  33. ocr_harness-1.0.3/tagging_tool/__main__.py +58 -0
  34. ocr_harness-1.0.3/tagging_tool/app.py +271 -0
  35. ocr_harness-1.0.3/tests/tests_runner.py +20 -0
  36. ocr_harness-1.0.3/utils/__init__.py +1 -0
  37. ocr_harness-1.0.3/utils/binarize.py +15 -0
  38. ocr_harness-1.0.3/utils/callable_descriptors.py +121 -0
  39. ocr_harness-1.0.3/utils/dataset_utils.py +271 -0
  40. ocr_harness-1.0.3/utils/image_utils.py +49 -0
  41. ocr_harness-1.0.3/utils/json_utils.py +30 -0
  42. ocr_harness-1.0.3/utils/lazy_import.py +36 -0
  43. ocr_harness-1.0.3/utils/logging_config.py +21 -0
  44. ocr_harness-1.0.3/utils/serialize_utils.py +211 -0
  45. ocr_harness-1.0.3/utils/statistics.py +93 -0
  46. ocr_harness-1.0.3/utils/text_utils.py +27 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Royee Yosibash
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,322 @@
1
+ Metadata-Version: 2.4
2
+ Name: ocr-harness
3
+ Version: 1.0.3
4
+ Summary: A unified toolkit for integrating, benchmarking, and comparing OCR engines with modular preprocessing and evaluation.
5
+ Author: Royee Yosibash
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.12
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: datasets==4.5.0
11
+ Requires-Dist: matplotlib>=3.10.0
12
+ Requires-Dist: numpy==2.4.2
13
+ Requires-Dist: opencv-python-headless==4.13.0.92
14
+ Requires-Dist: pillow==12.1.0
15
+ Requires-Dist: scipy>=1.17.0
16
+ Provides-Extra: ocrs
17
+ Requires-Dist: easyocr==1.7.2; extra == "ocrs"
18
+ Requires-Dist: paddleocr>=3.4.0; extra == "ocrs"
19
+ Requires-Dist: paddlepaddle==3.2.0; extra == "ocrs"
20
+ Requires-Dist: pytesseract>=0.3.10; extra == "ocrs"
21
+ Provides-Extra: dev
22
+ Requires-Dist: pre-commit; extra == "dev"
23
+ Requires-Dist: pytest; extra == "dev"
24
+ Requires-Dist: ruff>=0.15.0; extra == "dev"
25
+ Provides-Extra: dataset-tagging-app
26
+ Requires-Dist: flask>=3.0; extra == "dataset-tagging-app"
27
+ Requires-Dist: pytest-playwright>=0.7.0; extra == "dataset-tagging-app"
28
+ Provides-Extra: full
29
+ Requires-Dist: ocr-harness[dataset-tagging-app,dev,ocrs]; extra == "full"
30
+ Dynamic: license-file
31
+
32
+ # OCR-Toolkit
33
+
34
+ This toolkit aims to create a unified "one-stop-shop" framework for integrating, benchmarking, and comparing OCR
35
+ engines. It aims to provide a seamless and clean interface to integrate multiple OCR engines for common and uncommon
36
+ OCR tasks. The OCR-Toolkit cleanly decouples the OCR engine, preprocessing pipeline, and the evaluation metrics into interchangeable
37
+ components allowing easy tinkering and experimentation for OCr configuration and engine evaluation. Using the toolkit
38
+ Integrating a new OCR engine requires one subclass with one method and the framework handles plugging image
39
+ preprocessing, coordinate remapping, metric computation, and result persistence automatically.
40
+
41
+ ## Introduction
42
+
43
+ Selecting the optimal OCR engine and configuration for a given input type is
44
+ largely trial and error experiment. Different engines, preprocessing pipelines, and
45
+ parameter choices can yield vastly different results, yet there is no
46
+ standardized method for quantifying those differences across a dataset.
47
+ OCR-Toolkit replaces intuition with data: execute the pipeline, examine the
48
+ metrics, and let confidence intervals, per-tag breakdowns, and side-by-side
49
+ plots identify the optimal combination. It then allows copying the successful configuration
50
+ to production with no additional harness and a lightweight installation.
51
+
52
+ ## Use Cases
53
+
54
+ - Compare multiple OCR engines on the same dataset under identical conditions.
55
+ - Measure the impact of a preprocessing step (e.g. contour splitting vs. grid
56
+ splitting) on recognition accuracy.
57
+ - Integrate a new OCR engine with minimal boilerplate and immediately benchmark
58
+ it against existing solutions.
59
+ - Create and maintain ground-truth annotations using the included tagging tool.
60
+ - Make data-driven decisions about OCR configuration rather than relying on
61
+ manual inspection.
62
+
63
+ ## Example: Compare OCR Configurations in One Command
64
+
65
+ The fastest way to see what OCR-Toolkit can do: **two JSON files, one command,
66
+ and a data-driven answer.** The `examples/` directory ships a ready-to-run
67
+ comparison that asks a typical question -- *does splitting an image into a 2x2
68
+ grid improve PaddleOCR?* -- and answers it across 70 real-world images.
69
+
70
+ ### Compare configurations
71
+
72
+ Start with the baseline -- `examples/paddleocr_baseline.json` runs PaddleOCR with
73
+ its stock pipeline:
74
+
75
+ ```json
76
+ {
77
+ "alias": "Paddle Baseline",
78
+ "model_name": "PaddleOCRModule",
79
+ "model_params": {
80
+ "lang": "en",
81
+ "use_doc_orientation_classify": false,
82
+ "use_doc_unwarping": false,
83
+ "use_textline_orientation": false
84
+ },
85
+ "preprocess_methods": []
86
+ }
87
+ ```
88
+
89
+ Now compare it to a configuration that is *exactly the same, plus one key* --
90
+ `examples/paddleocr_split_image.json` adds a single `preprocess_methods` entry to
91
+ split every image into a 2x2 grid before recognition. The diff is just:
92
+
93
+ ```json
94
+ {
95
+ "alias": "Paddle 2x2 Grid Split",
96
+ ...
97
+ "preprocess_methods": [
98
+ {"name": "grid_split_image", "kwargs": {"grid": [2, 2]}}
99
+ ]
100
+ }
101
+ ```
102
+
103
+ The toolkit handles the rest -- running each cell through the engine and
104
+ remapping the detections back to the original image coordinates automatically.
105
+ Testing any other preprocessing idea (contour splitting, binarization, custom
106
+ steps) is the same one-line change -- no engine or pipeline code to touch.
107
+
108
+ ### Running the comparison
109
+
110
+ Run both configurations against the 70-image dataset in one shot:
111
+
112
+ ```bash
113
+ python -m scripts.run_evaluation \
114
+ --config examples \
115
+ --dataset dataset \
116
+ --output-dir scripts/results
117
+ ```
118
+
119
+ The pipeline computes character accuracy, word accuracy, word recall, word
120
+ precision, and word count ratio -- with per-image results, confidence
121
+ intervals, and comparison plots for every configuration in the directory.
122
+
123
+ ### Automatic Report Generation
124
+
125
+ In this example the baseline wins on average across all images: grid-splitting reduces accuracy and
126
+ precision while detecting more (spurious) words. On natural-scene text the
127
+ picture flips -- the grid-split recovers words the baseline misses, lifting
128
+ word recall roughly 9 points at essentially no precision cost:
129
+
130
+ <table>
131
+ <tr>
132
+ <td align="center">
133
+ <img src="examples/radar_pad_base_vs_2x2.png" alt="PaddleOCR baseline vs 2x2 grid split" width="420"/>
134
+ <br />
135
+ <em>All images: baseline is more accurate</em>
136
+ </td>
137
+ <td align="center">
138
+ <img src="examples/radar_pad_base_vs_2x2_natural_scene.png" alt="PaddleOCR baseline vs 2x2 grid split (natural scene)" width="420"/>
139
+ <br />
140
+ <em>Natural scenes: grid split recovers more words</em>
141
+ </td>
142
+ </tr>
143
+ </table>
144
+
145
+ One command, two configs, and the trade-off is quantified.
146
+
147
+ ## Features
148
+
149
+ - **Unified OCR interface** -- integrate any OCR engine by subclassing
150
+ `OCRAbstract`. Ships optionally with EasyOCR, PaddleOCR, and pytesseract
151
+ example implementations.
152
+ - **Modular preprocessing** -- composable preprocessing pipeline supporting
153
+ grid splitting, contour-based segmentation, binarization (adaptive and Otsu),
154
+ and user-supplied steps. Combine stages via configuration or code to build the
155
+ pipeline that fits your input data.
156
+ - **Evaluation pipeline** -- compute character accuracy, word accuracy, word
157
+ recall, word precision, and word count ratio against ground-truth
158
+ annotations, with per-image and aggregate results including confidence
159
+ intervals and supporting visualizations
160
+ - **Tagging tool** -- browser-based Flask UI for creating and editing
161
+ ground-truth bounding-box annotations.
162
+
163
+ ## Versioning
164
+
165
+ Current version: **1.0.3** (released 2026-09-22).
166
+
167
+ ## Installation
168
+
169
+ Requires Python 3.12+.
170
+
171
+ The project ships three independent optional dependency groups on top of the
172
+ always-installed base. Pick any subset, combine them, or use `full` for
173
+ everything.
174
+
175
+ **Base** (core abstractions, evaluation primitives, no engines, no app):
176
+
177
+ ```bash
178
+ pip install -e .
179
+ ```
180
+
181
+ **OCRs** (all OCR engines: EasyOCR, PaddleOCR, PaddlePaddle, pytesseract):
182
+
183
+ ```bash
184
+ pip install -e ".[ocrs]"
185
+ ```
186
+
187
+ > Note: `pytesseract` also requires the system `tesseract` binary, e.g.
188
+ > `sudo apt-get install -y tesseract-ocr` on Debian/Ubuntu.
189
+
190
+ **Dataset Tagging App** (Flask UI plus its end-to-end test deps):
191
+
192
+ ```bash
193
+ pip install -e ".[dataset-tagging-app]"
194
+ ```
195
+
196
+ **Developer** (ruff, pytest, pre-commit):
197
+
198
+ ```bash
199
+ pip install -e ".[dev]"
200
+ pre-commit install
201
+ ```
202
+
203
+ **Full** (all groups above):
204
+
205
+ ```bash
206
+ pip install -e ".[full]"
207
+ ```
208
+
209
+ Combinations are supported, e.g. `pip install -e ".[ocrs,dev]"`.
210
+
211
+ All code style is enforced by [Ruff](https://docs.astral.sh/ruff/) via
212
+ pre-commit hooks. After `pre-commit install`, every commit is automatically
213
+ checked (lint + format). The ruff configuration lives in `pyproject.toml`
214
+ under `[tool.ruff]`.
215
+
216
+ ## Project Structure
217
+
218
+ ```
219
+ ocr_backbone/ Core abstractions: OCRAbstract, OCRConfig, OCRResult, BoundingBox, InputImage
220
+ ocr_modules/ Concrete OCR engine implementations (EasyOCR, PaddleOCR, pytesseract)
221
+ evaluation/ Evaluation pipeline, metrics, ground truth, and plotting utilities
222
+ utils/ Shared utilities (JSON I/O, dataset loading, statistics, serialization)
223
+ scripts/ CLI tools: run_evaluation, draw_bboxes, tagging tool
224
+ dataset/ Local dataset with images/ and ground_truth/ subdirectories
225
+ tests/ Unit, regression, and application tests
226
+ ```
227
+
228
+ ## Usage
229
+
230
+ ### Run Evaluation
231
+
232
+ Execute all registered OCR modules against the dataset and generate metrics
233
+ and plots:
234
+
235
+ ```bash
236
+ python -m scripts.run_evaluation [--config config.json] [--dataset path/to/dataset] [--output-dir scripts/results]
237
+ ```
238
+
239
+ - `--config` accepts a single JSON config file or a directory of JSON configs.
240
+ Without it, all registered OCR modules are evaluated with default settings.
241
+ - `--dataset` defaults to the built-in `dataset/` directory. The directory must
242
+ contain `images/` and `ground_truth/` subdirectories with matching stems.
243
+ - `--output-dir` defaults to `scripts/results`.
244
+ - `--overwrite` re-runs OCR even when cached per-image results already exist.
245
+
246
+ Results are saved as JSON files and PNG plots (bar chart, radar, range plot).
247
+
248
+ ### Tagging Tool
249
+
250
+ Launch the browser-based annotation UI:
251
+
252
+ ```bash
253
+ python -m tagging_tool [--port 5000]
254
+ ```
255
+
256
+ Opens `http://localhost:5000` in the default browser.
257
+
258
+ ### Draw Bounding Boxes
259
+
260
+ Overlay persisted OCR results on an image:
261
+
262
+ ```bash
263
+ python -m scripts.draw_bboxes <image_path> <results_json>
264
+ ```
265
+
266
+ Produces `<image_stem>_with_bounding_box.<ext>` in the same directory.
267
+
268
+
269
+ ## Configuration
270
+
271
+ OCR runs are configured via `OCRConfig`, which can be loaded from a JSON file.
272
+ A minimal configuration requires only `model_name` and `model_params`:
273
+
274
+ ```json
275
+ {
276
+ "model_name": "EasyOCRModule",
277
+ "model_params": {}
278
+ }
279
+ ```
280
+
281
+ A full configuration can include preprocessing steps and a bounding-box
282
+ validator:
283
+
284
+ ```json
285
+ {
286
+ "model_name": "EasyOCRModule",
287
+ "model_params": {"languages": ["en"]},
288
+ "preprocess_methods": [
289
+ {"name": "contour_split_image"}
290
+ ]
291
+ }
292
+ ```
293
+
294
+ Preprocessing method names are resolved from `ocr_backbone.image_preprocessing`
295
+ by default. Dotted paths (e.g. `"my_package.module.func"`) are dynamically
296
+ imported. Optional `"kwargs"` are bound via `functools.partial`.
297
+
298
+ ## Adding a New OCR Module
299
+
300
+ Subclass `OCRAbstract` and implement `_run_single`:
301
+
302
+ ```python
303
+ from ocr_backbone.ocr_abstract import OCRAbstract
304
+ from ocr_backbone.ocr_result import OCRResult
305
+
306
+
307
+ class MyOCRModule(OCRAbstract):
308
+ def __init__(self, config):
309
+ super().__init__(config)
310
+ # initialize your engine
311
+
312
+ def _run_single(self, image, single_run_model_params):
313
+ # run inference, return OCRResult
314
+ ...
315
+ ```
316
+
317
+ The subclass is auto-registered by class name and becomes available to
318
+ `OCRAbstract.from_config` and the evaluation pipeline.
319
+
320
+ ## License
321
+
322
+ MIT -- see [LICENSE](LICENSE).
@@ -0,0 +1,291 @@
1
+ # OCR-Toolkit
2
+
3
+ This toolkit aims to create a unified "one-stop-shop" framework for integrating, benchmarking, and comparing OCR
4
+ engines. It aims to provide a seamless and clean interface to integrate multiple OCR engines for common and uncommon
5
+ OCR tasks. The OCR-Toolkit cleanly decouples the OCR engine, preprocessing pipeline, and the evaluation metrics into interchangeable
6
+ components allowing easy tinkering and experimentation for OCr configuration and engine evaluation. Using the toolkit
7
+ Integrating a new OCR engine requires one subclass with one method and the framework handles plugging image
8
+ preprocessing, coordinate remapping, metric computation, and result persistence automatically.
9
+
10
+ ## Introduction
11
+
12
+ Selecting the optimal OCR engine and configuration for a given input type is
13
+ largely trial and error experiment. Different engines, preprocessing pipelines, and
14
+ parameter choices can yield vastly different results, yet there is no
15
+ standardized method for quantifying those differences across a dataset.
16
+ OCR-Toolkit replaces intuition with data: execute the pipeline, examine the
17
+ metrics, and let confidence intervals, per-tag breakdowns, and side-by-side
18
+ plots identify the optimal combination. It then allows copying the successful configuration
19
+ to production with no additional harness and a lightweight installation.
20
+
21
+ ## Use Cases
22
+
23
+ - Compare multiple OCR engines on the same dataset under identical conditions.
24
+ - Measure the impact of a preprocessing step (e.g. contour splitting vs. grid
25
+ splitting) on recognition accuracy.
26
+ - Integrate a new OCR engine with minimal boilerplate and immediately benchmark
27
+ it against existing solutions.
28
+ - Create and maintain ground-truth annotations using the included tagging tool.
29
+ - Make data-driven decisions about OCR configuration rather than relying on
30
+ manual inspection.
31
+
32
+ ## Example: Compare OCR Configurations in One Command
33
+
34
+ The fastest way to see what OCR-Toolkit can do: **two JSON files, one command,
35
+ and a data-driven answer.** The `examples/` directory ships a ready-to-run
36
+ comparison that asks a typical question -- *does splitting an image into a 2x2
37
+ grid improve PaddleOCR?* -- and answers it across 70 real-world images.
38
+
39
+ ### Compare configurations
40
+
41
+ Start with the baseline -- `examples/paddleocr_baseline.json` runs PaddleOCR with
42
+ its stock pipeline:
43
+
44
+ ```json
45
+ {
46
+ "alias": "Paddle Baseline",
47
+ "model_name": "PaddleOCRModule",
48
+ "model_params": {
49
+ "lang": "en",
50
+ "use_doc_orientation_classify": false,
51
+ "use_doc_unwarping": false,
52
+ "use_textline_orientation": false
53
+ },
54
+ "preprocess_methods": []
55
+ }
56
+ ```
57
+
58
+ Now compare it to a configuration that is *exactly the same, plus one key* --
59
+ `examples/paddleocr_split_image.json` adds a single `preprocess_methods` entry to
60
+ split every image into a 2x2 grid before recognition. The diff is just:
61
+
62
+ ```json
63
+ {
64
+ "alias": "Paddle 2x2 Grid Split",
65
+ ...
66
+ "preprocess_methods": [
67
+ {"name": "grid_split_image", "kwargs": {"grid": [2, 2]}}
68
+ ]
69
+ }
70
+ ```
71
+
72
+ The toolkit handles the rest -- running each cell through the engine and
73
+ remapping the detections back to the original image coordinates automatically.
74
+ Testing any other preprocessing idea (contour splitting, binarization, custom
75
+ steps) is the same one-line change -- no engine or pipeline code to touch.
76
+
77
+ ### Running the comparison
78
+
79
+ Run both configurations against the 70-image dataset in one shot:
80
+
81
+ ```bash
82
+ python -m scripts.run_evaluation \
83
+ --config examples \
84
+ --dataset dataset \
85
+ --output-dir scripts/results
86
+ ```
87
+
88
+ The pipeline computes character accuracy, word accuracy, word recall, word
89
+ precision, and word count ratio -- with per-image results, confidence
90
+ intervals, and comparison plots for every configuration in the directory.
91
+
92
+ ### Automatic Report Generation
93
+
94
+ In this example the baseline wins on average across all images: grid-splitting reduces accuracy and
95
+ precision while detecting more (spurious) words. On natural-scene text the
96
+ picture flips -- the grid-split recovers words the baseline misses, lifting
97
+ word recall roughly 9 points at essentially no precision cost:
98
+
99
+ <table>
100
+ <tr>
101
+ <td align="center">
102
+ <img src="examples/radar_pad_base_vs_2x2.png" alt="PaddleOCR baseline vs 2x2 grid split" width="420"/>
103
+ <br />
104
+ <em>All images: baseline is more accurate</em>
105
+ </td>
106
+ <td align="center">
107
+ <img src="examples/radar_pad_base_vs_2x2_natural_scene.png" alt="PaddleOCR baseline vs 2x2 grid split (natural scene)" width="420"/>
108
+ <br />
109
+ <em>Natural scenes: grid split recovers more words</em>
110
+ </td>
111
+ </tr>
112
+ </table>
113
+
114
+ One command, two configs, and the trade-off is quantified.
115
+
116
+ ## Features
117
+
118
+ - **Unified OCR interface** -- integrate any OCR engine by subclassing
119
+ `OCRAbstract`. Ships optionally with EasyOCR, PaddleOCR, and pytesseract
120
+ example implementations.
121
+ - **Modular preprocessing** -- composable preprocessing pipeline supporting
122
+ grid splitting, contour-based segmentation, binarization (adaptive and Otsu),
123
+ and user-supplied steps. Combine stages via configuration or code to build the
124
+ pipeline that fits your input data.
125
+ - **Evaluation pipeline** -- compute character accuracy, word accuracy, word
126
+ recall, word precision, and word count ratio against ground-truth
127
+ annotations, with per-image and aggregate results including confidence
128
+ intervals and supporting visualizations
129
+ - **Tagging tool** -- browser-based Flask UI for creating and editing
130
+ ground-truth bounding-box annotations.
131
+
132
+ ## Versioning
133
+
134
+ Current version: **1.0.3** (released 2026-09-22).
135
+
136
+ ## Installation
137
+
138
+ Requires Python 3.12+.
139
+
140
+ The project ships three independent optional dependency groups on top of the
141
+ always-installed base. Pick any subset, combine them, or use `full` for
142
+ everything.
143
+
144
+ **Base** (core abstractions, evaluation primitives, no engines, no app):
145
+
146
+ ```bash
147
+ pip install -e .
148
+ ```
149
+
150
+ **OCRs** (all OCR engines: EasyOCR, PaddleOCR, PaddlePaddle, pytesseract):
151
+
152
+ ```bash
153
+ pip install -e ".[ocrs]"
154
+ ```
155
+
156
+ > Note: `pytesseract` also requires the system `tesseract` binary, e.g.
157
+ > `sudo apt-get install -y tesseract-ocr` on Debian/Ubuntu.
158
+
159
+ **Dataset Tagging App** (Flask UI plus its end-to-end test deps):
160
+
161
+ ```bash
162
+ pip install -e ".[dataset-tagging-app]"
163
+ ```
164
+
165
+ **Developer** (ruff, pytest, pre-commit):
166
+
167
+ ```bash
168
+ pip install -e ".[dev]"
169
+ pre-commit install
170
+ ```
171
+
172
+ **Full** (all groups above):
173
+
174
+ ```bash
175
+ pip install -e ".[full]"
176
+ ```
177
+
178
+ Combinations are supported, e.g. `pip install -e ".[ocrs,dev]"`.
179
+
180
+ All code style is enforced by [Ruff](https://docs.astral.sh/ruff/) via
181
+ pre-commit hooks. After `pre-commit install`, every commit is automatically
182
+ checked (lint + format). The ruff configuration lives in `pyproject.toml`
183
+ under `[tool.ruff]`.
184
+
185
+ ## Project Structure
186
+
187
+ ```
188
+ ocr_backbone/ Core abstractions: OCRAbstract, OCRConfig, OCRResult, BoundingBox, InputImage
189
+ ocr_modules/ Concrete OCR engine implementations (EasyOCR, PaddleOCR, pytesseract)
190
+ evaluation/ Evaluation pipeline, metrics, ground truth, and plotting utilities
191
+ utils/ Shared utilities (JSON I/O, dataset loading, statistics, serialization)
192
+ scripts/ CLI tools: run_evaluation, draw_bboxes, tagging tool
193
+ dataset/ Local dataset with images/ and ground_truth/ subdirectories
194
+ tests/ Unit, regression, and application tests
195
+ ```
196
+
197
+ ## Usage
198
+
199
+ ### Run Evaluation
200
+
201
+ Execute all registered OCR modules against the dataset and generate metrics
202
+ and plots:
203
+
204
+ ```bash
205
+ python -m scripts.run_evaluation [--config config.json] [--dataset path/to/dataset] [--output-dir scripts/results]
206
+ ```
207
+
208
+ - `--config` accepts a single JSON config file or a directory of JSON configs.
209
+ Without it, all registered OCR modules are evaluated with default settings.
210
+ - `--dataset` defaults to the built-in `dataset/` directory. The directory must
211
+ contain `images/` and `ground_truth/` subdirectories with matching stems.
212
+ - `--output-dir` defaults to `scripts/results`.
213
+ - `--overwrite` re-runs OCR even when cached per-image results already exist.
214
+
215
+ Results are saved as JSON files and PNG plots (bar chart, radar, range plot).
216
+
217
+ ### Tagging Tool
218
+
219
+ Launch the browser-based annotation UI:
220
+
221
+ ```bash
222
+ python -m tagging_tool [--port 5000]
223
+ ```
224
+
225
+ Opens `http://localhost:5000` in the default browser.
226
+
227
+ ### Draw Bounding Boxes
228
+
229
+ Overlay persisted OCR results on an image:
230
+
231
+ ```bash
232
+ python -m scripts.draw_bboxes <image_path> <results_json>
233
+ ```
234
+
235
+ Produces `<image_stem>_with_bounding_box.<ext>` in the same directory.
236
+
237
+
238
+ ## Configuration
239
+
240
+ OCR runs are configured via `OCRConfig`, which can be loaded from a JSON file.
241
+ A minimal configuration requires only `model_name` and `model_params`:
242
+
243
+ ```json
244
+ {
245
+ "model_name": "EasyOCRModule",
246
+ "model_params": {}
247
+ }
248
+ ```
249
+
250
+ A full configuration can include preprocessing steps and a bounding-box
251
+ validator:
252
+
253
+ ```json
254
+ {
255
+ "model_name": "EasyOCRModule",
256
+ "model_params": {"languages": ["en"]},
257
+ "preprocess_methods": [
258
+ {"name": "contour_split_image"}
259
+ ]
260
+ }
261
+ ```
262
+
263
+ Preprocessing method names are resolved from `ocr_backbone.image_preprocessing`
264
+ by default. Dotted paths (e.g. `"my_package.module.func"`) are dynamically
265
+ imported. Optional `"kwargs"` are bound via `functools.partial`.
266
+
267
+ ## Adding a New OCR Module
268
+
269
+ Subclass `OCRAbstract` and implement `_run_single`:
270
+
271
+ ```python
272
+ from ocr_backbone.ocr_abstract import OCRAbstract
273
+ from ocr_backbone.ocr_result import OCRResult
274
+
275
+
276
+ class MyOCRModule(OCRAbstract):
277
+ def __init__(self, config):
278
+ super().__init__(config)
279
+ # initialize your engine
280
+
281
+ def _run_single(self, image, single_run_model_params):
282
+ # run inference, return OCRResult
283
+ ...
284
+ ```
285
+
286
+ The subclass is auto-registered by class name and becomes available to
287
+ `OCRAbstract.from_config` and the evaluation pipeline.
288
+
289
+ ## License
290
+
291
+ MIT -- see [LICENSE](LICENSE).
@@ -0,0 +1,5 @@
1
+ from pathlib import Path
2
+
3
+ APP_ROOT = Path(__file__).parent
4
+
5
+ IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".bmp", ".tiff", ".tif", ".webp"}
@@ -0,0 +1,13 @@
1
+ """Evaluation pipeline, metrics, and ground-truth types.
2
+
3
+ The re-export below exposes the package's public ground-truth type
4
+ and, as a necessary side effect, ensures every ``SerializableClass``
5
+ subclass in this package registers itself in
6
+ ``SerializableClass._registry`` (via ``__init_subclass__``) the moment
7
+ any consumer touches the package. Add a line here when introducing a
8
+ new ``SerializableClass`` subclass under ``evaluation/``.
9
+ """
10
+
11
+ from evaluation.ocr_ground_truth import OCRGroundTruth
12
+
13
+ __all__ = ["OCRGroundTruth"]