formhtr 0.1.0__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 (47) hide show
  1. formhtr-0.1.0/LICENSE +21 -0
  2. formhtr-0.1.0/MANIFEST.in +2 -0
  3. formhtr-0.1.0/PKG-INFO +183 -0
  4. formhtr-0.1.0/README.md +151 -0
  5. formhtr-0.1.0/pyproject.toml +52 -0
  6. formhtr-0.1.0/setup.cfg +4 -0
  7. formhtr-0.1.0/src/formhtr/__init__.py +8 -0
  8. formhtr-0.1.0/src/formhtr/cli.py +163 -0
  9. formhtr-0.1.0/src/formhtr/data/fonts/Arial.ttf +0 -0
  10. formhtr-0.1.0/src/formhtr/deps.py +42 -0
  11. formhtr-0.1.0/src/formhtr/libs/__init__.py +2 -0
  12. formhtr-0.1.0/src/formhtr/libs/annotate_ROI/__init__.py +0 -0
  13. formhtr-0.1.0/src/formhtr/libs/annotate_ROI/annotate_ROIs_widget.py +71 -0
  14. formhtr-0.1.0/src/formhtr/libs/annotate_ROI/cli_inputs.py +20 -0
  15. formhtr-0.1.0/src/formhtr/libs/annotate_ROI/utils.py +24 -0
  16. formhtr-0.1.0/src/formhtr/libs/extract_ROI/__init__.py +0 -0
  17. formhtr-0.1.0/src/formhtr/libs/extract_ROI/autodect.py +106 -0
  18. formhtr-0.1.0/src/formhtr/libs/extract_ROI/cli_inputs.py +13 -0
  19. formhtr-0.1.0/src/formhtr/libs/extract_ROI/select_ROIs_widget.py +74 -0
  20. formhtr-0.1.0/src/formhtr/libs/logsheet_config.py +115 -0
  21. formhtr-0.1.0/src/formhtr/libs/pdf_to_image.py +48 -0
  22. formhtr-0.1.0/src/formhtr/libs/processing/__init__.py +0 -0
  23. formhtr-0.1.0/src/formhtr/libs/processing/align_images.py +99 -0
  24. formhtr-0.1.0/src/formhtr/libs/processing/barcode.py +55 -0
  25. formhtr-0.1.0/src/formhtr/libs/processing/checkbox.py +39 -0
  26. formhtr-0.1.0/src/formhtr/libs/processing/process_area.py +319 -0
  27. formhtr-0.1.0/src/formhtr/libs/processing/read_content.py +47 -0
  28. formhtr-0.1.0/src/formhtr/libs/processing/rtree.py +69 -0
  29. formhtr-0.1.0/src/formhtr/libs/processing/store_results.py +120 -0
  30. formhtr-0.1.0/src/formhtr/libs/region.py +81 -0
  31. formhtr-0.1.0/src/formhtr/libs/services/__init__.py +0 -0
  32. formhtr-0.1.0/src/formhtr/libs/services/amazon_vision.py +44 -0
  33. formhtr-0.1.0/src/formhtr/libs/services/azure_vision.py +47 -0
  34. formhtr-0.1.0/src/formhtr/libs/services/call_services.py +31 -0
  35. formhtr-0.1.0/src/formhtr/libs/services/google_vision.py +30 -0
  36. formhtr-0.1.0/src/formhtr/libs/services/utils.py +15 -0
  37. formhtr-0.1.0/src/formhtr/libs/statistics.py +16 -0
  38. formhtr-0.1.0/src/formhtr/libs/visualise_regions.py +40 -0
  39. formhtr-0.1.0/src/formhtr/logsheet.py +188 -0
  40. formhtr-0.1.0/src/formhtr/manual_align.py +109 -0
  41. formhtr-0.1.0/src/formhtr/roi_tools.py +73 -0
  42. formhtr-0.1.0/src/formhtr.egg-info/PKG-INFO +183 -0
  43. formhtr-0.1.0/src/formhtr.egg-info/SOURCES.txt +45 -0
  44. formhtr-0.1.0/src/formhtr.egg-info/dependency_links.txt +1 -0
  45. formhtr-0.1.0/src/formhtr.egg-info/entry_points.txt +2 -0
  46. formhtr-0.1.0/src/formhtr.egg-info/requires.txt +17 -0
  47. formhtr-0.1.0/src/formhtr.egg-info/top_level.txt +1 -0
formhtr-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Matej Troják
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,2 @@
1
+ recursive-include src/formhtr/data *
2
+
formhtr-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,183 @@
1
+ Metadata-Version: 2.4
2
+ Name: formhtr
3
+ Version: 0.1.0
4
+ Summary: Handprint text recognition in form documents.
5
+ Author-email: Matej Trojak <matej.trojak@embl.de>
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/grp-bork/formHTR
8
+ Keywords: ocr,forms,computer-vision
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: numpy
15
+ Requires-Dist: opencv-python
16
+ Requires-Dist: pillow==9.5.0
17
+ Requires-Dist: pdf2image
18
+ Requires-Dist: pandas
19
+ Requires-Dist: xlsxwriter
20
+ Requires-Dist: imutils
21
+ Requires-Dist: zxing-cpp
22
+ Requires-Dist: google-cloud-vision
23
+ Requires-Dist: rtree
24
+ Requires-Dist: boto3
25
+ Requires-Dist: azure-cognitiveservices-vision-computervision
26
+ Requires-Dist: pyzbar
27
+ Requires-Dist: biopython
28
+ Requires-Dist: scikit-image
29
+ Requires-Dist: img2pdf
30
+ Requires-Dist: PyPDF2
31
+ Dynamic: license-file
32
+
33
+ # formHTR
34
+ Handprint text recognition in form documents.
35
+
36
+ [![PyPI version](https://img.shields.io/pypi/v/formhtr.svg)](https://pypi.org/project/formhtr/)
37
+
38
+ ![Trec](https://github.com/grp-bork/formHTR/assets/15349569/c0789616-80d0-43c8-8693-d3d9f070511c)
39
+
40
+ ## Installation
41
+
42
+ ### pip
43
+
44
+ ```bash
45
+ pip install formhtr
46
+ ```
47
+
48
+ The tool also requires the `zbar` shared library installed (used by `pyzbar`).
49
+ For PDF-related tooling, `qpdf` is also required.
50
+
51
+ System dependencies:
52
+
53
+ - macOS (Homebrew): `brew install zbar qpdf`
54
+ - Debian/Ubuntu: `sudo apt-get install libzbar0 qpdf`
55
+ - Fedora: `sudo dnf install zbar qpdf`
56
+
57
+ You can verify runtime requirements with:
58
+
59
+ ```bash
60
+ formhtr doctor
61
+ ```
62
+
63
+ ### conda (dev)
64
+
65
+ ```
66
+ conda env create -f conda_env.yaml
67
+ ```
68
+
69
+ ## Usage
70
+
71
+ Run `formhtr --help` for full CLI help.
72
+
73
+ ### Quickstart
74
+
75
+ ```bash
76
+ # 1) Verify system dependencies
77
+ formhtr doctor
78
+
79
+ # 2) Create ROI config for a template
80
+ formhtr select-rois --pdf-file template.pdf --output-file config.json
81
+
82
+ # 3) Optionally annotate ROI types and variable names
83
+ formhtr annotate-rois --pdf-file template.pdf --config-file config.json --output-file config_annotated.json
84
+
85
+ # 4) Process a scanned logsheet into XLSX
86
+ formhtr process-logsheet \
87
+ --pdf-logsheet scan.pdf \
88
+ --pdf-template template.pdf \
89
+ --config-file config_annotated.json \
90
+ --output-file output.xlsx \
91
+ --google google_credentials.json \
92
+ --amazon amazon_credentials.json \
93
+ --azure azure_credentials.json
94
+ ```
95
+
96
+ ### Create ROIs
97
+
98
+ This functionality is split (for now) into two separate scripts.
99
+
100
+ #### select ROIs
101
+
102
+ Find and define locations of regions of interest (ROIs) in the given PDF.
103
+
104
+ Generally, it is possible to draw ROIs (rectangles) manually but also to detect them automatically.
105
+ The coordinates of ROIs are stored in a JSON file.
106
+
107
+ The tool is supposed to be run from the command line, as the control commands are entered there.
108
+
109
+ *Control commands*
110
+
111
+ * Press `q` or `Esc` to exit editing and save the config file.
112
+ * Press `r` to remove the last rectangle.
113
+
114
+ Run `formhtr select-rois -h` for details.
115
+
116
+ #### annotate ROIs
117
+
118
+ Specify the type of content for each rectangle.
119
+
120
+ The workflow is designed in a way that you can navigate over specified ROIs and assign them the expected type of their content.
121
+ This is done by pressing appropriate control commands.
122
+
123
+ *Control commands*
124
+
125
+ * Press `q` or `Esc` to exit editing and save the config file.
126
+ * Press `h` to add "Handwritten" type to the current ROI.
127
+ * Press `c` to add "Checkbox" type to the current ROI.
128
+ * Press `b` to add "Barcode" type to the current ROI.
129
+ * Press `r` or `d` to delete the type from the current ROI.
130
+ * Press `v` to enter the variable name.
131
+ * Press an arrow to navigate through ROIs (only left and right for now).
132
+
133
+ Run `formhtr annotate-rois -h` for details.
134
+
135
+ ### process logsheet
136
+
137
+ Extract values from specified ROIs.
138
+
139
+ This is the crucial step that applies various techniques to extract the information as precisely as possible.
140
+ It can process one logsheet at a time, given the template and config files.
141
+
142
+ Run `formhtr process-logsheet -h` for details.
143
+
144
+ #### Credentials
145
+
146
+ The processing of logsheets is using external services requiring credentials to use them. Here we specify structure that is expected for credentials, always in JSON format.
147
+
148
+ __Google__
149
+
150
+ ```
151
+ {
152
+ "type": "service_account",
153
+ "project_id": "theid",,
154
+ "private_key_id": "thekey",
155
+ "private_key": "-----BEGIN PRIVATE KEY-----anotherkey-----END PRIVATE KEY-----\n"
156
+ "client_email": "emailaddress",
157
+ "client_id": "id",
158
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
159
+ "token_uri": "https://oauth2.googleapis.com/token",
160
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
161
+ "client_x509_cert_url": "someurl",
162
+ "universe_domain": "googleapis.com"
163
+ }
164
+ ```
165
+
166
+ __Amazon__
167
+
168
+ ```
169
+ {
170
+ "ACCESS_KEY": "YOUR_KEY_ID_HERE",
171
+ "SECRET_KEY": "YOUR_ACCESS_KEY_HERE",
172
+ "REGION": "YOUR_REGION_NAME_HERE"
173
+ }
174
+ ```
175
+
176
+ __Microsoft__
177
+
178
+ ```
179
+ {
180
+ "SUBSCRIPTION_KEY": "YOURKEYHERE",
181
+ "ENDPOINT": "https://ENDPOINT"
182
+ }
183
+ ```
@@ -0,0 +1,151 @@
1
+ # formHTR
2
+ Handprint text recognition in form documents.
3
+
4
+ [![PyPI version](https://img.shields.io/pypi/v/formhtr.svg)](https://pypi.org/project/formhtr/)
5
+
6
+ ![Trec](https://github.com/grp-bork/formHTR/assets/15349569/c0789616-80d0-43c8-8693-d3d9f070511c)
7
+
8
+ ## Installation
9
+
10
+ ### pip
11
+
12
+ ```bash
13
+ pip install formhtr
14
+ ```
15
+
16
+ The tool also requires the `zbar` shared library installed (used by `pyzbar`).
17
+ For PDF-related tooling, `qpdf` is also required.
18
+
19
+ System dependencies:
20
+
21
+ - macOS (Homebrew): `brew install zbar qpdf`
22
+ - Debian/Ubuntu: `sudo apt-get install libzbar0 qpdf`
23
+ - Fedora: `sudo dnf install zbar qpdf`
24
+
25
+ You can verify runtime requirements with:
26
+
27
+ ```bash
28
+ formhtr doctor
29
+ ```
30
+
31
+ ### conda (dev)
32
+
33
+ ```
34
+ conda env create -f conda_env.yaml
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ Run `formhtr --help` for full CLI help.
40
+
41
+ ### Quickstart
42
+
43
+ ```bash
44
+ # 1) Verify system dependencies
45
+ formhtr doctor
46
+
47
+ # 2) Create ROI config for a template
48
+ formhtr select-rois --pdf-file template.pdf --output-file config.json
49
+
50
+ # 3) Optionally annotate ROI types and variable names
51
+ formhtr annotate-rois --pdf-file template.pdf --config-file config.json --output-file config_annotated.json
52
+
53
+ # 4) Process a scanned logsheet into XLSX
54
+ formhtr process-logsheet \
55
+ --pdf-logsheet scan.pdf \
56
+ --pdf-template template.pdf \
57
+ --config-file config_annotated.json \
58
+ --output-file output.xlsx \
59
+ --google google_credentials.json \
60
+ --amazon amazon_credentials.json \
61
+ --azure azure_credentials.json
62
+ ```
63
+
64
+ ### Create ROIs
65
+
66
+ This functionality is split (for now) into two separate scripts.
67
+
68
+ #### select ROIs
69
+
70
+ Find and define locations of regions of interest (ROIs) in the given PDF.
71
+
72
+ Generally, it is possible to draw ROIs (rectangles) manually but also to detect them automatically.
73
+ The coordinates of ROIs are stored in a JSON file.
74
+
75
+ The tool is supposed to be run from the command line, as the control commands are entered there.
76
+
77
+ *Control commands*
78
+
79
+ * Press `q` or `Esc` to exit editing and save the config file.
80
+ * Press `r` to remove the last rectangle.
81
+
82
+ Run `formhtr select-rois -h` for details.
83
+
84
+ #### annotate ROIs
85
+
86
+ Specify the type of content for each rectangle.
87
+
88
+ The workflow is designed in a way that you can navigate over specified ROIs and assign them the expected type of their content.
89
+ This is done by pressing appropriate control commands.
90
+
91
+ *Control commands*
92
+
93
+ * Press `q` or `Esc` to exit editing and save the config file.
94
+ * Press `h` to add "Handwritten" type to the current ROI.
95
+ * Press `c` to add "Checkbox" type to the current ROI.
96
+ * Press `b` to add "Barcode" type to the current ROI.
97
+ * Press `r` or `d` to delete the type from the current ROI.
98
+ * Press `v` to enter the variable name.
99
+ * Press an arrow to navigate through ROIs (only left and right for now).
100
+
101
+ Run `formhtr annotate-rois -h` for details.
102
+
103
+ ### process logsheet
104
+
105
+ Extract values from specified ROIs.
106
+
107
+ This is the crucial step that applies various techniques to extract the information as precisely as possible.
108
+ It can process one logsheet at a time, given the template and config files.
109
+
110
+ Run `formhtr process-logsheet -h` for details.
111
+
112
+ #### Credentials
113
+
114
+ The processing of logsheets is using external services requiring credentials to use them. Here we specify structure that is expected for credentials, always in JSON format.
115
+
116
+ __Google__
117
+
118
+ ```
119
+ {
120
+ "type": "service_account",
121
+ "project_id": "theid",,
122
+ "private_key_id": "thekey",
123
+ "private_key": "-----BEGIN PRIVATE KEY-----anotherkey-----END PRIVATE KEY-----\n"
124
+ "client_email": "emailaddress",
125
+ "client_id": "id",
126
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
127
+ "token_uri": "https://oauth2.googleapis.com/token",
128
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
129
+ "client_x509_cert_url": "someurl",
130
+ "universe_domain": "googleapis.com"
131
+ }
132
+ ```
133
+
134
+ __Amazon__
135
+
136
+ ```
137
+ {
138
+ "ACCESS_KEY": "YOUR_KEY_ID_HERE",
139
+ "SECRET_KEY": "YOUR_ACCESS_KEY_HERE",
140
+ "REGION": "YOUR_REGION_NAME_HERE"
141
+ }
142
+ ```
143
+
144
+ __Microsoft__
145
+
146
+ ```
147
+ {
148
+ "SUBSCRIPTION_KEY": "YOURKEYHERE",
149
+ "ENDPOINT": "https://ENDPOINT"
150
+ }
151
+ ```
@@ -0,0 +1,52 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "formhtr"
7
+ version = "0.1.0"
8
+ description = "Handprint text recognition in form documents."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "Matej Trojak", email = "matej.trojak@embl.de" }
14
+ ]
15
+ keywords = ["ocr", "forms", "computer-vision"]
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "Operating System :: OS Independent",
19
+ ]
20
+ dependencies = [
21
+ "numpy",
22
+ "opencv-python",
23
+ "pillow==9.5.0",
24
+ "pdf2image",
25
+ "pandas",
26
+ "xlsxwriter",
27
+ "imutils",
28
+ "zxing-cpp",
29
+ "google-cloud-vision",
30
+ "rtree",
31
+ "boto3",
32
+ "azure-cognitiveservices-vision-computervision",
33
+ "pyzbar",
34
+ "biopython",
35
+ "scikit-image",
36
+ "img2pdf",
37
+ "PyPDF2",
38
+ ]
39
+
40
+ [project.urls]
41
+ Repository = "https://github.com/grp-bork/formHTR"
42
+
43
+ [project.scripts]
44
+ formhtr = "formhtr.cli:main"
45
+
46
+ [tool.setuptools]
47
+ package-dir = {"" = "src"}
48
+ include-package-data = true
49
+
50
+ [tool.setuptools.packages.find]
51
+ where = ["src"]
52
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,8 @@
1
+ """formHTR: handprint text recognition in form documents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ __all__ = ["__version__"]
6
+
7
+ __version__ = "0.1.0"
8
+
@@ -0,0 +1,163 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import sys
5
+
6
+ from . import __version__
7
+ from .deps import check_system_dependencies, ensure_system_dependencies
8
+ from .logsheet import load_credentials, process_logsheet_to_xlsx
9
+ from .manual_align import manual_align_pdf
10
+ from .roi_tools import annotate_rois, select_rois
11
+
12
+
13
+ def _build_parser() -> argparse.ArgumentParser:
14
+ parser = argparse.ArgumentParser(prog="formhtr", description="formHTR CLI")
15
+ parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
16
+
17
+ sub = parser.add_subparsers(dest="command", required=True)
18
+
19
+ p_process = sub.add_parser("process-logsheet", help="Extract values from a scanned logsheet to XLSX")
20
+ p_process.add_argument("--pdf-logsheet", required=True, help="Scanned logsheet PDF")
21
+ p_process.add_argument("--pdf-template", required=True, help="Template PDF")
22
+ p_process.add_argument("--config-file", required=True, help="Config JSON")
23
+ p_process.add_argument("--output-file", required=True, help="Output XLSX file")
24
+ p_process.add_argument("--google", required=True, help="Path to Google Vision credentials JSON")
25
+ p_process.add_argument("--amazon", required=True, help="Path to Amazon credentials JSON")
26
+ p_process.add_argument("--azure", required=True, help="Path to Azure credentials JSON")
27
+ p_process.add_argument("--debug", action=argparse.BooleanOptionalAction, default=False, help="Output annotated PDFs")
28
+ p_process.add_argument("--backside", action=argparse.BooleanOptionalAction, default=False, help="Backside page present")
29
+ p_process.add_argument("--backside-template", help="Backside template PDF")
30
+ p_process.add_argument("--backside-config", help="Backside config JSON")
31
+ p_process.add_argument(
32
+ "--ugly-checkboxes",
33
+ action=argparse.BooleanOptionalAction,
34
+ default=False,
35
+ help="Checkboxes have irregular shape / thick edges",
36
+ )
37
+ p_process.add_argument(
38
+ "--aligned",
39
+ action=argparse.BooleanOptionalAction,
40
+ default=False,
41
+ help="Scanned image already aligned with template (skip alignment)",
42
+ )
43
+ p_process.add_argument(
44
+ "--filter-grayscale",
45
+ action=argparse.BooleanOptionalAction,
46
+ default=False,
47
+ help="During alignment keep only darkest grayscale pixels",
48
+ )
49
+
50
+ p_align = sub.add_parser("manual-align", help="Interactively align a scanned PDF to a template")
51
+ p_align.add_argument("--pdf-template", required=True, help="Template PDF")
52
+ p_align.add_argument("--pdf-logsheet", required=True, help="Scanned logsheet PDF")
53
+ p_align.add_argument("--output", required=True, help="Output aligned PDF")
54
+ p_align.add_argument("--backside-template", help="Backside template PDF")
55
+
56
+ p_select = sub.add_parser("select-rois", help="Interactively define ROIs in a template PDF")
57
+ p_select.add_argument("--pdf-file", required=True, help="Template PDF")
58
+ p_select.add_argument("--output-file", required=True, help="Output config JSON")
59
+ p_select.add_argument("--autodetect", action=argparse.BooleanOptionalAction, default=False)
60
+ p_select.add_argument("--autodetect-filter", type=float, default=3)
61
+ p_select.add_argument("--config-file", default=None, help="Existing config JSON to continue editing")
62
+ p_select.add_argument("--detect-residuals", action=argparse.BooleanOptionalAction, default=False)
63
+ p_select.add_argument("--credentials", default=None, help="Google credentials JSON (for residual detection)")
64
+ p_select.add_argument("--display-residuals", action=argparse.BooleanOptionalAction, default=False)
65
+
66
+ p_annot = sub.add_parser("annotate-rois", help="Interactively label ROI types/variables")
67
+ p_annot.add_argument("--pdf-file", required=True, help="Template PDF")
68
+ p_annot.add_argument("--config-file", required=True, help="Input config JSON")
69
+ p_annot.add_argument("--output-file", required=True, help="Output config JSON")
70
+ p_annot.add_argument("--remove-unannotated", action=argparse.BooleanOptionalAction, default=False)
71
+ p_annot.add_argument("--display-residuals", action=argparse.BooleanOptionalAction, default=False)
72
+
73
+ sub.add_parser("doctor", help="Check required system dependencies")
74
+
75
+ return parser
76
+
77
+
78
+ def main(argv: list[str] | None = None) -> int:
79
+ parser = _build_parser()
80
+ args = parser.parse_args(argv)
81
+
82
+ if args.command == "process-logsheet":
83
+ ensure_system_dependencies({"zbar"})
84
+ if args.backside and (not args.backside_template or not args.backside_config):
85
+ parser.error("--backside requires --backside-template and --backside-config.")
86
+
87
+ credentials = load_credentials(
88
+ google_credentials_path=args.google,
89
+ amazon_credentials_path=args.amazon,
90
+ azure_credentials_path=args.azure,
91
+ )
92
+
93
+ ratio = process_logsheet_to_xlsx(
94
+ scanned_logsheet_pdf=args.pdf_logsheet,
95
+ template_pdf=args.pdf_template,
96
+ config_json=args.config_file,
97
+ output_xlsx=args.output_file,
98
+ credentials=credentials,
99
+ debug=args.debug,
100
+ backside=args.backside,
101
+ backside_template_pdf=args.backside_template,
102
+ backside_config_json=args.backside_config,
103
+ ugly_checkboxes=args.ugly_checkboxes,
104
+ already_aligned=args.aligned,
105
+ filter_grayscale=args.filter_grayscale,
106
+ )
107
+ if ratio is not None:
108
+ print(f"Success ratio: {ratio['ratio']:.3f}")
109
+ return 0
110
+
111
+ if args.command == "manual-align":
112
+ ensure_system_dependencies({"qpdf"})
113
+ manual_align_pdf(
114
+ template_pdf=args.pdf_template,
115
+ scanned_logsheet_pdf=args.pdf_logsheet,
116
+ output_pdf=args.output,
117
+ backside_template_pdf=args.backside_template,
118
+ )
119
+ return 0
120
+
121
+ if args.command == "select-rois":
122
+ if args.detect_residuals and not args.credentials:
123
+ parser.error("--detect-residuals requires --credentials.")
124
+ select_rois(
125
+ template_pdf=args.pdf_file,
126
+ output_config_json=args.output_file,
127
+ autodetect=args.autodetect,
128
+ autodetect_filter=args.autodetect_filter,
129
+ existing_config_json=args.config_file,
130
+ detect_residuals=args.detect_residuals,
131
+ google_credentials_path=args.credentials,
132
+ display_residuals=args.display_residuals,
133
+ )
134
+ return 0
135
+
136
+ if args.command == "annotate-rois":
137
+ annotate_rois(
138
+ template_pdf=args.pdf_file,
139
+ config_json=args.config_file,
140
+ output_config_json=args.output_file,
141
+ remove_unannotated=args.remove_unannotated,
142
+ display_residuals=args.display_residuals,
143
+ )
144
+ return 0
145
+
146
+ if args.command == "doctor":
147
+ missing = check_system_dependencies()
148
+ if not missing:
149
+ print("All required system dependencies are available: qpdf, zbar.")
150
+ return 0
151
+
152
+ print("Missing system dependencies:")
153
+ for name, hint in missing:
154
+ print(f"- {name}: {hint}")
155
+ return 1
156
+
157
+ parser.error(f"Unknown command: {args.command}")
158
+ return 2
159
+
160
+
161
+ if __name__ == "__main__":
162
+ raise SystemExit(main(sys.argv[1:]))
163
+
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ import ctypes.util
4
+ import platform
5
+ import shutil
6
+
7
+
8
+ def _install_hint(dep_name: str) -> str:
9
+ system = platform.system().lower()
10
+ if system == "darwin":
11
+ return f"brew install {dep_name}"
12
+ if system == "linux":
13
+ if dep_name == "zbar":
14
+ return "apt: sudo apt-get install libzbar0 | dnf: sudo dnf install zbar"
15
+ return f"apt: sudo apt-get install {dep_name} | dnf: sudo dnf install {dep_name}"
16
+ return f"Install {dep_name} using your system package manager."
17
+
18
+
19
+ def check_system_dependencies() -> list[tuple[str, str]]:
20
+ missing: list[tuple[str, str]] = []
21
+
22
+ if shutil.which("qpdf") is None:
23
+ missing.append(("qpdf", _install_hint("qpdf")))
24
+
25
+ if ctypes.util.find_library("zbar") is None:
26
+ missing.append(("zbar", _install_hint("zbar")))
27
+
28
+ return missing
29
+
30
+
31
+ def ensure_system_dependencies(required: set[str]) -> None:
32
+ missing = check_system_dependencies()
33
+ missing_required = [(name, hint) for name, hint in missing if name in required]
34
+ if not missing_required:
35
+ return
36
+
37
+ lines = ["Missing required system dependencies:"]
38
+ for name, hint in missing_required:
39
+ lines.append(f"- {name}: {hint}")
40
+ lines.append("Run `formhtr doctor` for a full dependency report.")
41
+ raise RuntimeError("\n".join(lines))
42
+
@@ -0,0 +1,2 @@
1
+ from __future__ import annotations
2
+