nassimb0u commited on
Commit
ecc2e61
·
verified ·
1 Parent(s): 9b26899

add app files

Browse files
Files changed (8) hide show
  1. .gitignore +201 -0
  2. main.py +17 -0
  3. out/bar002_labeled.json +162 -0
  4. out/json_labeled.json +162 -0
  5. requirements.txt +179 -0
  6. src/app.py +40 -0
  7. src/inference.py +61 -0
  8. src/utils.py +149 -0
.gitignore ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+ #poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ #pdm.lock
114
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
115
+ # in version control.
116
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
117
+ .pdm.toml
118
+ .pdm-python
119
+ .pdm-build/
120
+
121
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
122
+ __pypackages__/
123
+
124
+ # Celery stuff
125
+ celerybeat-schedule
126
+ celerybeat.pid
127
+
128
+ # SageMath parsed files
129
+ *.sage.py
130
+
131
+ # Environments
132
+ .env
133
+ .envrc
134
+ .venv
135
+ env/
136
+ venv/
137
+ ENV/
138
+ env.bak/
139
+ venv.bak/
140
+
141
+ # Spyder project settings
142
+ .spyderproject
143
+ .spyproject
144
+
145
+ # Rope project settings
146
+ .ropeproject
147
+
148
+ # mkdocs documentation
149
+ /site
150
+
151
+ # mypy
152
+ .mypy_cache/
153
+ .dmypy.json
154
+ dmypy.json
155
+
156
+ # Pyre type checker
157
+ .pyre/
158
+
159
+ # pytype static type analyzer
160
+ .pytype/
161
+
162
+ # Cython debug symbols
163
+ cython_debug/
164
+
165
+ # PyCharm
166
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
167
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
168
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
169
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
170
+ #.idea/
171
+
172
+ # Abstra
173
+ # Abstra is an AI-powered process automation framework.
174
+ # Ignore directories containing user credentials, local state, and settings.
175
+ # Learn more at https://abstra.io/docs
176
+ .abstra/
177
+
178
+ # Visual Studio Code
179
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
180
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
181
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
182
+ # you could uncomment the following to ignore the entire vscode folder
183
+ # .vscode/
184
+
185
+ # Ruff stuff:
186
+ .ruff_cache/
187
+
188
+ # PyPI configuration file
189
+ .pypirc
190
+
191
+ # Cursor
192
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
193
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
194
+ # refer to https://docs.cursor.com/context/ignore-files
195
+ .cursorignore
196
+ .cursorindexingignore
197
+
198
+ # Marimo
199
+ marimo/_static/
200
+ marimo/_lsp/
201
+ __marimo__/
main.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+
4
+ def greet(name: str, intensity: int) -> str:
5
+ return "Hello, " + name + "!" * int(intensity)
6
+
7
+
8
+ demo = gr.Interface(
9
+ fn=greet,
10
+ inputs=[
11
+ "text",
12
+ "slider",
13
+ ], # the inputs are a text box and a slider ("text" and "slider" are components in Gradio)
14
+ outputs=["text"], # the output is a text box
15
+ )
16
+
17
+ demo.launch()
out/bar002_labeled.json ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "text":"n",
4
+ "bbox":[
5
+ 54,
6
+ 26,
7
+ 73,
8
+ 46
9
+ ],
10
+ "labels":"AXIS_TITLE"
11
+ },
12
+ {
13
+ "text":"40",
14
+ "bbox":[
15
+ 54,
16
+ 54,
17
+ 94,
18
+ 84
19
+ ],
20
+ "labels":"TICK_LABEL"
21
+ },
22
+ {
23
+ "text":"35",
24
+ "bbox":[
25
+ 54,
26
+ 111,
27
+ 91,
28
+ 141
29
+ ],
30
+ "labels":"TICK_LABEL"
31
+ },
32
+ {
33
+ "text":"30",
34
+ "bbox":[
35
+ 53,
36
+ 168,
37
+ 92,
38
+ 198
39
+ ],
40
+ "labels":"TICK_LABEL"
41
+ },
42
+ {
43
+ "text":"25",
44
+ "bbox":[
45
+ 52,
46
+ 223,
47
+ 90,
48
+ 253
49
+ ],
50
+ "labels":"TICK_LABEL"
51
+ },
52
+ {
53
+ "text":"20",
54
+ "bbox":[
55
+ 51,
56
+ 280,
57
+ 90,
58
+ 310
59
+ ],
60
+ "labels":"TICK_LABEL"
61
+ },
62
+ {
63
+ "text":"15",
64
+ "bbox":[
65
+ 56,
66
+ 338,
67
+ 88,
68
+ 367
69
+ ],
70
+ "labels":"TICK_LABEL"
71
+ },
72
+ {
73
+ "text":"10",
74
+ "bbox":[
75
+ 53,
76
+ 394,
77
+ 88,
78
+ 424
79
+ ],
80
+ "labels":"TICK_LABEL"
81
+ },
82
+ {
83
+ "text":"5",
84
+ "bbox":[
85
+ 69,
86
+ 449,
87
+ 86,
88
+ 478
89
+ ],
90
+ "labels":"TICK_LABEL"
91
+ },
92
+ {
93
+ "text":"0",
94
+ "bbox":[
95
+ 68,
96
+ 506,
97
+ 88,
98
+ 535
99
+ ],
100
+ "labels":"TICK_LABEL"
101
+ },
102
+ {
103
+ "text":"17",
104
+ "bbox":[
105
+ 177,
106
+ 535,
107
+ 212,
108
+ 564
109
+ ],
110
+ "labels":"TICK_LABEL"
111
+ },
112
+ {
113
+ "text":"18",
114
+ "bbox":[
115
+ 366,
116
+ 539,
117
+ 401,
118
+ 569
119
+ ],
120
+ "labels":"TICK_LABEL"
121
+ },
122
+ {
123
+ "text":"19",
124
+ "bbox":[
125
+ 562,
126
+ 542,
127
+ 595,
128
+ 571
129
+ ],
130
+ "labels":"TICK_LABEL"
131
+ },
132
+ {
133
+ "text":"20",
134
+ "bbox":[
135
+ 753,
136
+ 545,
137
+ 793,
138
+ 576
139
+ ],
140
+ "labels":"TICK_LABEL"
141
+ },
142
+ {
143
+ "text":"(a)",
144
+ "bbox":[
145
+ 777,
146
+ 195,
147
+ 822,
148
+ 230
149
+ ],
150
+ "labels":"OTHER"
151
+ },
152
+ {
153
+ "text":"Number of rays in A",
154
+ "bbox":[
155
+ 314,
156
+ 586,
157
+ 652,
158
+ 627
159
+ ],
160
+ "labels":"AXIS_TITLE"
161
+ }
162
+ ]
out/json_labeled.json ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "text":"n",
4
+ "bbox":[
5
+ 54,
6
+ 26,
7
+ 73,
8
+ 46
9
+ ],
10
+ "labels":"AXIS_TITLE"
11
+ },
12
+ {
13
+ "text":"40",
14
+ "bbox":[
15
+ 54,
16
+ 54,
17
+ 94,
18
+ 84
19
+ ],
20
+ "labels":"TICK_LABEL"
21
+ },
22
+ {
23
+ "text":"35",
24
+ "bbox":[
25
+ 54,
26
+ 111,
27
+ 91,
28
+ 141
29
+ ],
30
+ "labels":"TICK_LABEL"
31
+ },
32
+ {
33
+ "text":"30",
34
+ "bbox":[
35
+ 53,
36
+ 168,
37
+ 92,
38
+ 198
39
+ ],
40
+ "labels":"TICK_LABEL"
41
+ },
42
+ {
43
+ "text":"25",
44
+ "bbox":[
45
+ 52,
46
+ 223,
47
+ 90,
48
+ 253
49
+ ],
50
+ "labels":"TICK_LABEL"
51
+ },
52
+ {
53
+ "text":"20",
54
+ "bbox":[
55
+ 51,
56
+ 280,
57
+ 90,
58
+ 310
59
+ ],
60
+ "labels":"TICK_LABEL"
61
+ },
62
+ {
63
+ "text":"15",
64
+ "bbox":[
65
+ 56,
66
+ 338,
67
+ 88,
68
+ 367
69
+ ],
70
+ "labels":"TICK_LABEL"
71
+ },
72
+ {
73
+ "text":"10",
74
+ "bbox":[
75
+ 53,
76
+ 394,
77
+ 88,
78
+ 424
79
+ ],
80
+ "labels":"TICK_LABEL"
81
+ },
82
+ {
83
+ "text":"5",
84
+ "bbox":[
85
+ 69,
86
+ 449,
87
+ 86,
88
+ 478
89
+ ],
90
+ "labels":"TICK_LABEL"
91
+ },
92
+ {
93
+ "text":"0",
94
+ "bbox":[
95
+ 68,
96
+ 506,
97
+ 88,
98
+ 535
99
+ ],
100
+ "labels":"TICK_LABEL"
101
+ },
102
+ {
103
+ "text":"17",
104
+ "bbox":[
105
+ 177,
106
+ 535,
107
+ 212,
108
+ 564
109
+ ],
110
+ "labels":"TICK_LABEL"
111
+ },
112
+ {
113
+ "text":"18",
114
+ "bbox":[
115
+ 366,
116
+ 539,
117
+ 401,
118
+ 569
119
+ ],
120
+ "labels":"TICK_LABEL"
121
+ },
122
+ {
123
+ "text":"19",
124
+ "bbox":[
125
+ 562,
126
+ 542,
127
+ 595,
128
+ 571
129
+ ],
130
+ "labels":"TICK_LABEL"
131
+ },
132
+ {
133
+ "text":"20",
134
+ "bbox":[
135
+ 753,
136
+ 545,
137
+ 793,
138
+ 576
139
+ ],
140
+ "labels":"TICK_LABEL"
141
+ },
142
+ {
143
+ "text":"(a)",
144
+ "bbox":[
145
+ 777,
146
+ 195,
147
+ 822,
148
+ 230
149
+ ],
150
+ "labels":"OTHER"
151
+ },
152
+ {
153
+ "text":"Number of rays in A",
154
+ "bbox":[
155
+ 314,
156
+ 586,
157
+ 652,
158
+ 627
159
+ ],
160
+ "labels":"AXIS_TITLE"
161
+ }
162
+ ]
requirements.txt ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ accelerate==1.7.0
2
+ aiofiles==24.1.0
3
+ aiohappyeyeballs==2.6.1
4
+ aiohttp==3.12.12
5
+ aiosignal==1.3.2
6
+ annotated-types==0.7.0
7
+ anyio==4.9.0
8
+ argon2-cffi==25.1.0
9
+ argon2-cffi-bindings==21.2.0
10
+ arrow==1.3.0
11
+ asttokens==3.0.0
12
+ async-lru==2.0.5
13
+ attrs==25.3.0
14
+ babel==2.17.0
15
+ beautifulsoup4==4.13.4
16
+ black==25.1.0
17
+ bleach==6.2.0
18
+ certifi==2025.4.26
19
+ cffi==1.17.1
20
+ charset-normalizer==3.4.2
21
+ click==8.2.1
22
+ comm==0.2.2
23
+ datasets==3.6.0
24
+ debugpy==1.8.14
25
+ decorator==5.2.1
26
+ defusedxml==0.7.1
27
+ dill==0.3.8
28
+ evaluate==0.4.3
29
+ executing==2.2.0
30
+ fastapi==0.115.12
31
+ fastjsonschema==2.21.1
32
+ ffmpy==0.6.0
33
+ filelock==3.18.0
34
+ fqdn==1.5.1
35
+ frozenlist==1.7.0
36
+ fsspec==2025.3.0
37
+ gradio==5.34.0
38
+ gradio_client==1.10.3
39
+ groovy==0.1.2
40
+ h11==0.16.0
41
+ hf-xet==1.1.3
42
+ httpcore==1.0.9
43
+ httpx==0.28.1
44
+ huggingface-hub==0.33.0
45
+ idna==3.10
46
+ ipykernel==6.29.5
47
+ ipython==9.3.0
48
+ ipython_pygments_lexers==1.1.1
49
+ ipywidgets==8.1.7
50
+ isoduration==20.11.0
51
+ isort==6.0.1
52
+ jedi==0.19.2
53
+ Jinja2==3.1.6
54
+ json5==0.12.0
55
+ jsonpointer==3.0.0
56
+ jsonschema==4.24.0
57
+ jsonschema-specifications==2025.4.1
58
+ jupyter==1.1.1
59
+ jupyter-console==6.6.3
60
+ jupyter-events==0.12.0
61
+ jupyter-lsp==2.2.5
62
+ jupyter_client==8.6.3
63
+ jupyter_core==5.8.1
64
+ jupyter_server==2.16.0
65
+ jupyter_server_terminals==0.5.3
66
+ jupyterlab==4.4.3
67
+ jupyterlab_code_formatter==3.0.2
68
+ jupyterlab_pygments==0.3.0
69
+ jupyterlab_server==2.27.3
70
+ jupyterlab_widgets==3.0.15
71
+ markdown-it-py==3.0.0
72
+ MarkupSafe==3.0.2
73
+ matplotlib-inline==0.1.7
74
+ mdurl==0.1.2
75
+ mistune==3.1.3
76
+ mpmath==1.3.0
77
+ multidict==6.4.4
78
+ multiprocess==0.70.16
79
+ mypy_extensions==1.1.0
80
+ nbclient==0.10.2
81
+ nbconvert==7.16.6
82
+ nbformat==5.10.4
83
+ nest-asyncio==1.6.0
84
+ networkx==3.5
85
+ notebook==7.4.3
86
+ notebook_shim==0.2.4
87
+ numpy==2.3.0
88
+ nvidia-cublas-cu12==12.6.4.1
89
+ nvidia-cuda-cupti-cu12==12.6.80
90
+ nvidia-cuda-nvrtc-cu12==12.6.77
91
+ nvidia-cuda-runtime-cu12==12.6.77
92
+ nvidia-cudnn-cu12==9.5.1.17
93
+ nvidia-cufft-cu12==11.3.0.4
94
+ nvidia-cufile-cu12==1.11.1.6
95
+ nvidia-curand-cu12==10.3.7.77
96
+ nvidia-cusolver-cu12==11.7.1.2
97
+ nvidia-cusparse-cu12==12.5.4.2
98
+ nvidia-cusparselt-cu12==0.6.3
99
+ nvidia-nccl-cu12==2.26.2
100
+ nvidia-nvjitlink-cu12==12.6.85
101
+ nvidia-nvtx-cu12==12.6.77
102
+ opencv-python==4.11.0.86
103
+ orjson==3.10.18
104
+ overrides==7.7.0
105
+ packaging==25.0
106
+ pandas==2.3.0
107
+ pandocfilters==1.5.1
108
+ parso==0.8.4
109
+ pathspec==0.12.1
110
+ pexpect==4.9.0
111
+ pillow==11.2.1
112
+ platformdirs==4.3.8
113
+ prometheus_client==0.22.1
114
+ prompt_toolkit==3.0.51
115
+ propcache==0.3.2
116
+ psutil==7.0.0
117
+ ptyprocess==0.7.0
118
+ pure_eval==0.2.3
119
+ pyarrow==20.0.0
120
+ pycparser==2.22
121
+ pydantic==2.11.7
122
+ pydantic_core==2.33.2
123
+ pydub==0.25.1
124
+ Pygments==2.19.1
125
+ pytesseract==0.3.13
126
+ python-dateutil==2.9.0.post0
127
+ python-json-logger==3.3.0
128
+ python-multipart==0.0.20
129
+ pytz==2025.2
130
+ PyYAML==6.0.2
131
+ pyzmq==27.0.0
132
+ referencing==0.36.2
133
+ regex==2024.11.6
134
+ requests==2.32.4
135
+ rfc3339-validator==0.1.4
136
+ rfc3986-validator==0.1.1
137
+ rich==14.0.0
138
+ rpds-py==0.25.1
139
+ ruff==0.11.13
140
+ safehttpx==0.1.6
141
+ safetensors==0.5.3
142
+ semantic-version==2.10.0
143
+ Send2Trash==1.8.3
144
+ setuptools==80.9.0
145
+ shellingham==1.5.4
146
+ six==1.17.0
147
+ sniffio==1.3.1
148
+ soupsieve==2.7
149
+ stack-data==0.6.3
150
+ starlette==0.46.2
151
+ sympy==1.14.0
152
+ terminado==0.18.1
153
+ timm==1.0.15
154
+ tinycss2==1.4.0
155
+ tokenizers==0.21.1
156
+ tomlkit==0.13.3
157
+ torch==2.7.1
158
+ torchvision==0.22.1
159
+ tornado==6.5.1
160
+ tqdm==4.67.1
161
+ traitlets==5.14.3
162
+ transformers==4.52.4
163
+ triton==3.3.1
164
+ typer==0.16.0
165
+ types-python-dateutil==2.9.0.20250516
166
+ typing-inspection==0.4.1
167
+ typing_extensions==4.14.0
168
+ tzdata==2025.2
169
+ uri-template==1.3.0
170
+ urllib3==2.4.0
171
+ uvicorn==0.34.3
172
+ wcwidth==0.2.13
173
+ webcolors==24.11.1
174
+ webencodings==0.5.1
175
+ websocket-client==1.8.0
176
+ websockets==15.0.1
177
+ widgetsnbextension==4.0.14
178
+ xxhash==3.5.0
179
+ yarl==1.20.1
src/app.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import inference
3
+
4
+
5
+ def classify_tokens(image, annots_file, annots_format):
6
+ if image is None:
7
+ raise gr.Error("Please upload an image.")
8
+ if annots_file is None:
9
+ raise gr.Error("Please upload an annotation file.")
10
+ if annots_format is None:
11
+ raise gr.Error("Please choose annotation foramt.")
12
+
13
+ annot_image, labeled_annot_file = inference.perform_inference(
14
+ image, annots_file, annots_format
15
+ )
16
+
17
+ return annot_image, labeled_annot_file
18
+
19
+
20
+ iface = gr.Interface(
21
+ fn=classify_tokens,
22
+ inputs=[
23
+ gr.Image(type="pil", label="Input image"),
24
+ gr.File(label="Annotation file"),
25
+ gr.Dropdown(
26
+ choices=["STD", "ICPR22", "EconBiz & CHIMIE-R"],
27
+ label="Annotation file format",
28
+ value="STD",
29
+ ),
30
+ ],
31
+ outputs=[
32
+ gr.Image(type="pil", label="Annotated image"),
33
+ gr.File(label="Labeled annotation file"),
34
+ ],
35
+ title="Chart Text Role Classification Demo",
36
+ description="Upload an image and an annotation file to classify token roles. The application will return the annotated image and the labeled annotation file.",
37
+ )
38
+
39
+ if __name__ == "__main__":
40
+ iface.launch()
src/inference.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from utils import load_annots, normalize_bbox, annotate_image
2
+ from transformers import AutoModelForTokenClassification, AutoProcessor
3
+ import os
4
+ import pandas as pd
5
+
6
+
7
+ model = AutoModelForTokenClassification.from_pretrained(
8
+ "nassimb0u/chart-text-role-classification-model"
9
+ )
10
+ processor = AutoProcessor.from_pretrained(
11
+ "nassimb0u/chart-text-role-classification-model"
12
+ )
13
+
14
+
15
+ def process_image_and_annot(image, annots_data):
16
+ prepro_annots_data = {"text": [], "bbox": []}
17
+
18
+ for b, t in zip(annots_data["bbox"], annots_data["text"]):
19
+ prepro_annots_data["bbox"].append(normalize_bbox(b, image.size, type="polygon"))
20
+ prepro_annots_data["text"].append(t)
21
+ return image.convert("RGB"), annots_data
22
+
23
+
24
+ def perform_inference(image, annots_file, annots_format):
25
+ annots_data = load_annots(annots_file, annots_format)
26
+ _, prepro_annots_data = process_image_and_annot(
27
+ image,
28
+ annots_data,
29
+ )
30
+
31
+ encoding = processor(
32
+ image,
33
+ prepro_annots_data["text"],
34
+ boxes=prepro_annots_data["bbox"],
35
+ return_tensors="pt",
36
+ )
37
+ outputs = model(**encoding)
38
+ predictions = outputs.logits.argmax(-1)
39
+
40
+ labels = [model.config.id2label[idx.item()] for idx in predictions[0]]
41
+ mask = []
42
+ for i in range(encoding["bbox"].shape[1]):
43
+ zero = True
44
+ equal_to_pred = True
45
+ for j in range(encoding["bbox"].shape[2]):
46
+ if encoding["bbox"][0][i][j] != 0:
47
+ zero = False
48
+ if i > 0 and encoding["bbox"][0][i - 1][j] != encoding["bbox"][0][i][j]:
49
+ equal_to_pred = False
50
+
51
+ mask.append(not (zero or equal_to_pred))
52
+
53
+ annots_data["labels"] = [label for (m, label) in zip(mask, labels) if m]
54
+
55
+ image = annotate_image(image, annots_data)
56
+
57
+ out_file_name = f"out/{os.path.basename(annots_file).split(".")[0]}_labeled.json"
58
+ df = pd.DataFrame(annots_data)
59
+ df.to_json(out_file_name, orient="records", lines=False, indent=2)
60
+
61
+ return image, out_file_name
src/utils.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import cv2
3
+ from PIL import ImageDraw
4
+
5
+
6
+ def load_annots(annots_file, annot_format):
7
+ with open(annots_file, "r") as f:
8
+ data = json.load(f)
9
+
10
+ annots = {"text": [], "bbox": []}
11
+ if annot_format == "STD":
12
+ for text_block in data:
13
+ annots["text"].append(text_block["text"])
14
+ annots["bbox"].append(text_block["bbox"])
15
+
16
+ elif annot_format == "ICPR22":
17
+ for text_block in data["task2"]["output"]["text_blocks"]:
18
+ annots["text"].append(text_block["text"])
19
+ annots["bbox"].append(quad_to_box(text_block["polygon"]))
20
+
21
+ elif annot_format == "EconBiz & CHIMIE-R":
22
+ for text_block in data["textelements"]:
23
+ annots["text"].append(text_block["content"])
24
+ annots["bbox"].append(
25
+ quad_to_box(
26
+ get_quad(text_block["boundingbox"], data["width"], data["height"])
27
+ )
28
+ )
29
+ else:
30
+ raise ValueError(f"Unknown annotation format: {annot_format}")
31
+
32
+ return annots
33
+
34
+
35
+ def annotate_image(image, labeled_annots_data):
36
+ draw = ImageDraw.Draw(image)
37
+ width, height = image.size
38
+
39
+ for b, label in zip(labeled_annots_data["bbox"], labeled_annots_data["labels"]):
40
+ x0, y0, x1, y1 = b
41
+ # Skip zero bboxes if needed
42
+ if (x0, y0, x1, y1) == (0, 0, 0, 0):
43
+ continue
44
+ draw.rectangle([x0, y0, x1, y1], outline="red", width=2)
45
+ draw.text((x0, y0 - 10), label, fill="red")
46
+
47
+ return image
48
+
49
+
50
+ def normalize_bbox(bbox, size, type=None):
51
+ if type == "box":
52
+ height = int(bbox["height"])
53
+ width = int(bbox["width"])
54
+ left = max(0, bbox["x0"])
55
+ top = max(0, bbox["y0"])
56
+ right = left + width
57
+ bottom = top + height
58
+ if type == "polygon":
59
+ left = bbox[0]
60
+ top = bbox[1]
61
+ right = bbox[2]
62
+ bottom = bbox[3]
63
+ return [
64
+ int(1000 * left / size[0]),
65
+ int(1000 * top / size[1]),
66
+ int(1000 * right / size[0]),
67
+ int(1000 * bottom / size[1]),
68
+ ]
69
+
70
+
71
+ def quad_to_box(quad):
72
+ box = (max(0, quad["x0"]), max(0, quad["y0"]), quad["x2"], quad["y2"])
73
+ if box[3] < box[1]:
74
+ bbox = list(box)
75
+ tmp = bbox[3]
76
+ bbox[3] = bbox[1]
77
+ bbox[1] = tmp
78
+ box = tuple(bbox)
79
+ if box[2] < box[0]:
80
+ bbox = list(box)
81
+ tmp = bbox[2]
82
+ bbox[2] = bbox[0]
83
+ bbox[0] = tmp
84
+ box = tuple(bbox)
85
+ return box
86
+
87
+
88
+ def get_quad(bbox, width, height):
89
+ x0 = int(bbox["center_x"] - bbox["width"] / 2)
90
+ x1 = int(bbox["center_x"] + bbox["width"] / 2)
91
+ x2 = int(bbox["center_x"] + bbox["width"] / 2)
92
+ x3 = int(bbox["center_x"] - bbox["width"] / 2)
93
+ y0 = int(bbox["center_y"] - bbox["height"] / 2)
94
+ y1 = int(bbox["center_y"] - bbox["height"] / 2)
95
+ y2 = int(bbox["center_y"] + bbox["height"] / 2)
96
+ y3 = int(bbox["center_y"] + bbox["height"] / 2)
97
+
98
+ if bbox["orientation"] == 0:
99
+ return {
100
+ "x0": x0,
101
+ "x1": x1,
102
+ "x2": x2,
103
+ "x3": x3,
104
+ "y0": y0,
105
+ "y1": y1,
106
+ "y2": y2,
107
+ "y3": y3,
108
+ }
109
+
110
+ # rotate coordinates if orientation is not 0
111
+
112
+ cx, cy = (int(width / 2), int(height / 2))
113
+
114
+ bbox_tuple = [
115
+ (x0, y0),
116
+ (x1, y1),
117
+ (x2, y2),
118
+ (x3, y3),
119
+ ]
120
+
121
+ rotated_bbox = []
122
+
123
+ for i, coord in enumerate(bbox_tuple):
124
+ M = cv2.getRotationMatrix2D((cx, cy), bbox["orientation"], 1.0)
125
+ v = [coord[0], coord[1], 1]
126
+ adjusted_coord = np.matmul(M, v)
127
+ rotated_bbox.insert(i, (adjusted_coord[0], adjusted_coord[1]))
128
+
129
+ result = [int(x) for t in rotated_bbox for x in t]
130
+
131
+ # make sure resulting bbox coordinates are within the range of the image
132
+ for i, n in enumerate(result):
133
+ if i % 2 == 0 and n > width:
134
+ result[i] = width
135
+ elif i % 2 == 1 and n > height:
136
+ result[i] = height
137
+ elif n < 0:
138
+ result[i] = 0
139
+
140
+ return {
141
+ "x0": result[0],
142
+ "x1": result[2],
143
+ "x2": result[4],
144
+ "x3": result[6],
145
+ "y0": result[1],
146
+ "y1": result[3],
147
+ "y2": result[5],
148
+ "y3": result[7],
149
+ }