Arviano commited on
Commit
02cd66d
·
1 Parent(s): 3198e88

Fix: Add model with LFS and include source code

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ models/multi_smoothing1.2_reducelr_epoch116.pth filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # Byte-compiled / optimized / DLL files
3
+ __pycache__/
4
+ *.py[codz]
5
+ *$py.class
6
+
7
+ # C extensions
8
+ *.so
9
+
10
+ # Distribution / packaging
11
+ .Python
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py.cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+ cover/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ .pybuilder/
77
+ target/
78
+
79
+ # Jupyter Notebook
80
+ .ipynb_checkpoints
81
+
82
+ # IPython
83
+ profile_default/
84
+ ipython_config.py
85
+
86
+ # pyenv
87
+ # For a library or package, you might want to ignore these files since the code is
88
+ # intended to run in multiple environments; otherwise, check them in:
89
+ # .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ #Pipfile.lock
97
+
98
+ # UV
99
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
100
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
101
+ # commonly ignored for libraries.
102
+ #uv.lock
103
+
104
+ # poetry
105
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
106
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
107
+ # commonly ignored for libraries.
108
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
109
+ #poetry.lock
110
+ #poetry.toml
111
+
112
+ # pdm
113
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
114
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
115
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
116
+ #pdm.lock
117
+ #pdm.toml
118
+ .pdm-python
119
+ .pdm-build/
120
+
121
+ # pixi
122
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
123
+ #pixi.lock
124
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
125
+ # in the .venv directory. It is recommended not to include this directory in version control.
126
+ .pixi
127
+
128
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
129
+ __pypackages__/
130
+
131
+ # Celery stuff
132
+ celerybeat-schedule
133
+ celerybeat.pid
134
+
135
+ # SageMath parsed files
136
+ *.sage.py
137
+
138
+ # Environments
139
+ .env
140
+ .envrc
141
+ .venv
142
+ env/
143
+ venv/
144
+ ENV/
145
+ env.bak/
146
+ venv.bak/
147
+
148
+ # Spyder project settings
149
+ .spyderproject
150
+ .spyproject
151
+
152
+ # Rope project settings
153
+ .ropeproject
154
+
155
+ # mkdocs documentation
156
+ /site
157
+
158
+ # mypy
159
+ .mypy_cache/
160
+ .dmypy.json
161
+ dmypy.json
162
+
163
+ # Pyre type checker
164
+ .pyre/
165
+
166
+ # pytype static type analyzer
167
+ .pytype/
168
+
169
+ # Cython debug symbols
170
+ cython_debug/
171
+
172
+ # PyCharm
173
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
174
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
175
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
176
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
177
+ #.idea/
178
+
179
+ # Abstra
180
+ # Abstra is an AI-powered process automation framework.
181
+ # Ignore directories containing user credentials, local state, and settings.
182
+ # Learn more at https://abstra.io/docs
183
+ .abstra/
184
+
185
+ # Visual Studio Code
186
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
187
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
188
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
189
+ # you could uncomment the following to ignore the entire vscode folder
190
+ # .vscode/
191
+
192
+ # Ruff stuff:
193
+ .ruff_cache/
194
+
195
+ # PyPI configuration file
196
+ .pypirc
197
+
198
+ # Cursor
199
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
200
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
201
+ # refer to https://docs.cursor.com/context/ignore-files
202
+ .cursorignore
203
+ .cursorindexingignore
204
+
205
+ # Marimo
206
+ marimo/_static/
207
+ marimo/_lsp/
208
+ __marimo__/
app.py CHANGED
@@ -1,7 +1,69 @@
1
  import gradio as gr
 
 
 
 
2
 
3
- def greet(name):
4
- return "Hello " + name + "!!"
 
 
 
 
5
 
6
- demo = gr.Interface(fn=greet, inputs="text", outputs="text")
7
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ import torch
3
+ import torch.nn.functional as F
4
+ from torchvision import transforms
5
+ from PIL import Image
6
 
7
+ # ---------------------------------------------------------
8
+ # 1. CONFIGURATION (Edit these!)
9
+ # ---------------------------------------------------------
10
+ MODEL_PATH = "models/multi_smoothing1.2_reducelr_epoch116.pth" # Your model file name
11
+ # IMPORTANT: These must match the order of folders/classes used during training!
12
+ LABELS = ["uc", "infeksi", "crohn", "tb"] # Example for your Colitis demo
13
 
14
+ # ---------------------------------------------------------
15
+ # 2. LOAD MODEL
16
+ # ---------------------------------------------------------
17
+ # We load to CPU since Hugging Face basic tier is CPU-only
18
+ from src.densenet import DenseNet121
19
+ model = DenseNet121(num_classes=len(LABELS))
20
+
21
+ try:
22
+ model.load_state_dict(torch.load(MODEL_PATH, map_location=torch.device('cpu')))
23
+ model.eval() # Set to evaluation mode
24
+ except Exception as e:
25
+ print(f"Error loading model: {e}")
26
+ print("Make sure your model file is uploaded and matches MODEL_PATH.")
27
+ # Create a dummy model just so the app doesn't crash immediately for testing
28
+
29
+ # ---------------------------------------------------------
30
+ # 3. PREPROCESSING
31
+ # ---------------------------------------------------------
32
+ # Standard preprocessing. Adjust "Resize" if your model expects 299x299 (Inception) or others.
33
+ from src.preprocessing import preprocess
34
+
35
+ target_input_size = tuple([3, 299, 299])
36
+
37
+ val_transform = preprocess(target_input_size=target_input_size)
38
+
39
+ # ---------------------------------------------------------
40
+ # 4. PREDICTION FUNCTION
41
+ # ---------------------------------------------------------
42
+ def predict(image):
43
+ if model is None:
44
+ return {"Error": 1.0}
45
+
46
+ input_tensor = val_transform(image).unsqueeze(0)
47
+
48
+ with torch.no_grad():
49
+ output = model(input_tensor)
50
+
51
+ probabilities = F.softmax(output[0], dim=0)
52
+
53
+ # 4. Map to labels
54
+ # Returns a dictionary: {"Class A": 0.9, "Class B": 0.1}
55
+ return {LABELS[i]: float(probabilities[i]) for i in range(len(LABELS))}
56
+
57
+ # ---------------------------------------------------------
58
+ # 5. GRADIO INTERFACE
59
+ # ---------------------------------------------------------
60
+ demo = gr.Interface(
61
+ fn=predict,
62
+ inputs=gr.Image(type="pil"),
63
+ outputs=gr.Label(num_top_classes=3),
64
+ title="Medical Image Classification Demo",
65
+ description="Upload a scan to classify it."
66
+ )
67
+
68
+ if __name__ == "__main__":
69
+ demo.launch()
models/multi_smoothing1.2_reducelr_epoch116.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5ccc18364e541f77e359530c5fd889c732a3a133efef53ce548b42ca1e258c31
3
+ size 32555962
requirements.txt ADDED
@@ -0,0 +1,1188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file was autogenerated by uv via the following command:
2
+ # uv export --format requirements-txt
3
+ aiofiles==24.1.0 \
4
+ --hash=sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c \
5
+ --hash=sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5
6
+ # via gradio
7
+ annotated-doc==0.0.4 \
8
+ --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 \
9
+ --hash=sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4
10
+ # via fastapi
11
+ annotated-types==0.7.0 \
12
+ --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \
13
+ --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89
14
+ # via pydantic
15
+ anyio==4.12.1 \
16
+ --hash=sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703 \
17
+ --hash=sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c
18
+ # via
19
+ # gradio
20
+ # httpx
21
+ # starlette
22
+ audioop-lts==0.2.2 ; python_full_version >= '3.13' \
23
+ --hash=sha256:0337d658f9b81f4cd0fdb1f47635070cc084871a3d4646d9de74fdf4e7c3d24a \
24
+ --hash=sha256:03f061a1915538fd96272bac9551841859dbb2e3bf73ebe4a23ef043766f5449 \
25
+ --hash=sha256:068aa17a38b4e0e7de771c62c60bbca2455924b67a8814f3b0dee92b5820c0b3 \
26
+ --hash=sha256:088327f00488cdeed296edd9215ca159f3a5a5034741465789cad403fcf4bec0 \
27
+ --hash=sha256:0d9385e96f9f6da847f4d571ce3cb15b5091140edf3db97276872647ce37efd7 \
28
+ --hash=sha256:106753a83a25ee4d6f473f2be6b0966fc1c9af7e0017192f5531a3e7463dce58 \
29
+ --hash=sha256:143fad0311e8209ece30a8dbddab3b65ab419cbe8c0dde6e8828da25999be911 \
30
+ --hash=sha256:15ab25dd3e620790f40e9ead897f91e79c0d3ce65fe193c8ed6c26cffdd24be7 \
31
+ --hash=sha256:167d3b62586faef8b6b2275c3218796b12621a60e43f7e9d5845d627b9c9b80e \
32
+ --hash=sha256:2b267b70747d82125f1a021506565bdc5609a2b24bcb4773c16d79d2bb260bbd \
33
+ --hash=sha256:3bcddaaf6cc5935a300a8387c99f7a7fbbe212a11568ec6cf6e4bc458c048636 \
34
+ --hash=sha256:3fc38008969796f0f689f1453722a0f463da1b8a6fbee11987830bfbb664f623 \
35
+ --hash=sha256:47eba38322370347b1c47024defbd36374a211e8dd5b0dcbce7b34fdb6f8847b \
36
+ --hash=sha256:48159d96962674eccdca9a3df280e864e8ac75e40a577cc97c5c42667ffabfc5 \
37
+ --hash=sha256:49ee1a41738a23e98d98b937a0638357a2477bc99e61b0f768a8f654f45d9b7a \
38
+ --hash=sha256:4a53aa7c16a60a6857e6b0b165261436396ef7293f8b5c9c828a3a203147ed4a \
39
+ --hash=sha256:4b4cd51a57b698b2d06cb9993b7ac8dfe89a3b2878e96bc7948e9f19ff51dba6 \
40
+ --hash=sha256:51c916108c56aa6e426ce611946f901badac950ee2ddaf302b7ed35d9958970d \
41
+ --hash=sha256:550c114a8df0aafe9a05442a1162dfc8fec37e9af1d625ae6060fed6e756f303 \
42
+ --hash=sha256:58cf54380c3884fb49fdd37dfb7a772632b6701d28edd3e2904743c5e1773602 \
43
+ --hash=sha256:5b00be98ccd0fc123dcfad31d50030d25fcf31488cde9e61692029cd7394733b \
44
+ --hash=sha256:5f93a5db13927a37d2d09637ccca4b2b6b48c19cd9eda7b17a2e9f77edee6a6f \
45
+ --hash=sha256:64d0c62d88e67b98a1a5e71987b7aa7b5bcffc7dcee65b635823dbdd0a8dbbd0 \
46
+ --hash=sha256:73f80bf4cd5d2ca7814da30a120de1f9408ee0619cc75da87d0641273d202a09 \
47
+ --hash=sha256:752d76472d9804ac60f0078c79cdae8b956f293177acd2316cd1e15149aee132 \
48
+ --hash=sha256:83c381767e2cc10e93e40281a04852facc4cd9334550e0f392f72d1c0a9c5753 \
49
+ --hash=sha256:8fefe5868cd082db1186f2837d64cfbfa78b548ea0d0543e9b28935ccce81ce9 \
50
+ --hash=sha256:9191d68659eda01e448188f60364c7763a7ca6653ed3f87ebb165822153a8547 \
51
+ --hash=sha256:96f19de485a2925314f5020e85911fb447ff5fbef56e8c7c6927851b95533a1c \
52
+ --hash=sha256:9a13dc409f2564de15dd68be65b462ba0dde01b19663720c68c1140c782d1d75 \
53
+ --hash=sha256:a2c2a947fae7d1062ef08c4e369e0ba2086049a5e598fda41122535557012e9e \
54
+ --hash=sha256:a2d4f1513d63c795e82948e1305f31a6d530626e5f9f2605408b300ae6095093 \
55
+ --hash=sha256:a5bf613e96f49712073de86f20dbdd4014ca18efd4d34ed18c75bd808337851b \
56
+ --hash=sha256:a6d2e0f9f7a69403e388894d4ca5ada5c47230716a03f2847cfc7bd1ecb589d6 \
57
+ --hash=sha256:b492c3b040153e68b9fdaff5913305aaaba5bb433d8a7f73d5cf6a64ed3cc1dd \
58
+ --hash=sha256:ba7c3a7e5f23e215cb271516197030c32aef2e754252c4c70a50aaff7031a2c8 \
59
+ --hash=sha256:c0022283e9556e0f3643b7c3c03f05063ca72b3063291834cca43234f20c60bb \
60
+ --hash=sha256:c174e322bb5783c099aaf87faeb240c8d210686b04bd61dfd05a8e5a83d88969 \
61
+ --hash=sha256:c9c8e68d8b4a56fda8c025e538e639f8c5953f5073886b596c93ec9b620055e7 \
62
+ --hash=sha256:cfcac6aa6f42397471e4943e0feb2244549db5c5d01efcd02725b96af417f3fe \
63
+ --hash=sha256:d5e73fa573e273e4f2e5ff96f9043858a5e9311e94ffefd88a3186a910c70917 \
64
+ --hash=sha256:def246fe9e180626731b26e89816e79aae2276f825420a07b4a647abaa84becc \
65
+ --hash=sha256:dfbbc74ec68a0fd08cfec1f4b5e8cca3d3cd7de5501b01c4b5d209995033cde9 \
66
+ --hash=sha256:e160bf9df356d841bb6c180eeeea1834085464626dc1b68fa4e1d59070affdc3 \
67
+ --hash=sha256:e541c3ef484852ef36545f66209444c48b28661e864ccadb29daddb6a4b8e5f5 \
68
+ --hash=sha256:f9b0b8a03ef474f56d1a842af1a2e01398b8f7654009823c6d9e0ecff4d5cfbf \
69
+ --hash=sha256:f9ee9b52f5f857fbaf9d605a360884f034c92c1c23021fb90b2e39b8e64bede6 \
70
+ --hash=sha256:fbdd522624141e40948ab3e8cdae6e04c748d78710e9f0f8d4dae2750831de19 \
71
+ --hash=sha256:fd3d4602dc64914d462924a08c1a9816435a2155d74f325853c1f1ac3b2d9800
72
+ # via gradio
73
+ brotli==1.2.0 \
74
+ --hash=sha256:022426c9e99fd65d9475dce5c195526f04bb8be8907607e27e747893f6ee3e24 \
75
+ --hash=sha256:072e7624b1fc4d601036ab3f4f27942ef772887e876beff0301d261210bca97f \
76
+ --hash=sha256:0bbd5b5ccd157ae7913750476d48099aaf507a79841c0d04a9db4415b14842de \
77
+ --hash=sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c \
78
+ --hash=sha256:15b33fe93cedc4caaff8a0bd1eb7e3dab1c61bb22a0bf5bdfdfd97cd7da79744 \
79
+ --hash=sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a \
80
+ --hash=sha256:1b557b29782a643420e08d75aea889462a4a8796e9a6cf5621ab05a3f7da8ef2 \
81
+ --hash=sha256:260d3692396e1895c5034f204f0db022c056f9e2ac841593a4cf9426e2a3faca \
82
+ --hash=sha256:26e8d3ecb0ee458a9804f47f21b74845cc823fd1bb19f02272be70774f56e2a6 \
83
+ --hash=sha256:2a7f1d03727130fc875448b65b127a9ec5d06d19d0148e7554384229706f9d1b \
84
+ --hash=sha256:2e1ad3fda65ae0d93fec742a128d72e145c9c7a99ee2fcd667785d99eb25a7fe \
85
+ --hash=sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac \
86
+ --hash=sha256:350c8348f0e76fff0a0fd6c26755d2653863279d086d3aa2c290a6a7251135dd \
87
+ --hash=sha256:35d382625778834a7f3061b15423919aa03e4f5da34ac8e02c074e4b75ab4f84 \
88
+ --hash=sha256:3b90b767916ac44e93a8e28ce6adf8d551e43affb512f2377c732d486ac6514e \
89
+ --hash=sha256:3e1b35d56856f3ed326b140d3c6d9db91740f22e14b06e840fe4bb1923439a18 \
90
+ --hash=sha256:3f3c908bcc404c90c77d5a073e55271a0a498f4e0756e48127c35d91cf155947 \
91
+ --hash=sha256:40d918bce2b427a0c4ba189df7a006ac0c7277c180aee4617d99e9ccaaf59e6a \
92
+ --hash=sha256:4ecdb3b6dc36e6d6e14d3a1bdc6c1057c8cbf80db04031d566eb6080ce283a48 \
93
+ --hash=sha256:54a50a9dad16b32136b2241ddea9e4df159b41247b2ce6aac0b3276a66a8f1e5 \
94
+ --hash=sha256:67a91c5187e1eec76a61625c77a6c8c785650f5b576ca732bd33ef58b0dff49c \
95
+ --hash=sha256:6be67c19e0b0c56365c6a76e393b932fb0e78b3b56b711d180dd7013cb1fd984 \
96
+ --hash=sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21 \
97
+ --hash=sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b \
98
+ --hash=sha256:7a47ce5c2288702e09dc22a44d0ee6152f2c7eda97b3c8482d826a1f3cfc7da7 \
99
+ --hash=sha256:7a61c06b334bd99bc5ae84f1eeb36bfe01400264b3c352f968c6e30a10f9d08b \
100
+ --hash=sha256:81da1b229b1889f25adadc929aeb9dbc4e922bd18561b65b08dd9343cfccca84 \
101
+ --hash=sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d \
102
+ --hash=sha256:844a8ceb8483fefafc412f85c14f2aae2fb69567bf2a0de53cdb88b73e7c43ae \
103
+ --hash=sha256:898be2be399c221d2671d29eed26b6b2713a02c2119168ed914e7d00ceadb56f \
104
+ --hash=sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7 \
105
+ --hash=sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e \
106
+ --hash=sha256:9c79f57faa25d97900bfb119480806d783fba83cd09ee0b33c17623935b05fa3 \
107
+ --hash=sha256:9e5825ba2c9998375530504578fd4d5d1059d09621a02065d1b6bfc41a8e05ab \
108
+ --hash=sha256:a1778532b978d2536e79c05dac2d8cd857f6c55cd0c95ace5b03740824e0e2f1 \
109
+ --hash=sha256:aa47441fa3026543513139cb8926a92a8e305ee9c71a6209ef7a97d91640ea03 \
110
+ --hash=sha256:acec55bb7c90f1dfc476126f9711a8e81c9af7fb617409a9ee2953115343f08d \
111
+ --hash=sha256:adedc4a67e15327dfdd04884873c6d5a01d3e3b6f61406f99b1ed4865a2f6d28 \
112
+ --hash=sha256:af43b8711a8264bb4e7d6d9a6d004c3a2019c04c01127a868709ec29962b6036 \
113
+ --hash=sha256:b232029d100d393ae3c603c8ffd7e3fe6f798c5e28ddca5feabb8e8fdb732997 \
114
+ --hash=sha256:b35c13ce241abdd44cb8ca70683f20c0c079728a36a996297adb5334adfc1c44 \
115
+ --hash=sha256:b63daa43d82f0cdabf98dee215b375b4058cce72871fd07934f179885aad16e8 \
116
+ --hash=sha256:c8565e3cdc1808b1a34714b553b262c5de5fbda202285782173ec137fd13709f \
117
+ --hash=sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63 \
118
+ --hash=sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888 \
119
+ --hash=sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a \
120
+ --hash=sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3 \
121
+ --hash=sha256:e99befa0b48f3cd293dafeacdd0d191804d105d279e0b387a32054c1180f3161 \
122
+ --hash=sha256:ef87b8ab2704da227e83a246356a2b179ef826f550f794b2c52cddb4efbd0196 \
123
+ --hash=sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361 \
124
+ --hash=sha256:ff09cd8c5eec3b9d02d2408db41be150d8891c5566addce57513bf546e3d6c6d
125
+ # via gradio
126
+ certifi==2026.1.4 \
127
+ --hash=sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c \
128
+ --hash=sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120
129
+ # via
130
+ # httpcore
131
+ # httpx
132
+ click==8.3.1 \
133
+ --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \
134
+ --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6
135
+ # via
136
+ # typer
137
+ # typer-slim
138
+ # uvicorn
139
+ colorama==0.4.6 ; sys_platform == 'win32' \
140
+ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
141
+ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
142
+ # via
143
+ # click
144
+ # tqdm
145
+ cuda-bindings==12.9.4 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
146
+ --hash=sha256:32bdc5a76906be4c61eb98f546a6786c5773a881f3b166486449b5d141e4a39f \
147
+ --hash=sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5 \
148
+ --hash=sha256:56e0043c457a99ac473ddc926fe0dc4046694d99caef633e92601ab52cbe17eb \
149
+ --hash=sha256:8b72ee72a9cc1b531db31eebaaee5c69a8ec3500e32c6933f2d3b15297b53686 \
150
+ --hash=sha256:c912a3d9e6b6651853eed8eed96d6800d69c08e94052c292fec3f282c5a817c9 \
151
+ --hash=sha256:d80bffc357df9988dca279734bc9674c3934a654cab10cadeed27ce17d8635ee \
152
+ --hash=sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8
153
+ # via torch
154
+ cuda-pathfinder==1.3.3 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
155
+ --hash=sha256:9984b664e404f7c134954a771be8775dfd6180ea1e1aef4a5a37d4be05d9bbb1
156
+ # via cuda-bindings
157
+ exceptiongroup==1.3.1 ; python_full_version < '3.11' \
158
+ --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \
159
+ --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598
160
+ # via anyio
161
+ fastapi==0.128.7 \
162
+ --hash=sha256:6bd9bd31cb7047465f2d3fa3ba3f33b0870b17d4eaf7cdb36d1576ab060ad662 \
163
+ --hash=sha256:783c273416995486c155ad2c0e2b45905dedfaf20b9ef8d9f6a9124670639a24
164
+ # via gradio
165
+ ffmpy==1.0.0 \
166
+ --hash=sha256:5640e5f0fd03fb6236d0e119b16ccf6522db1c826fdf35dcb87087b60fd7504f \
167
+ --hash=sha256:b12932e95435c8820f1cd041024402765f821971e4bae753b327fc02a6e12f8b
168
+ # via gradio
169
+ filelock==3.20.3 \
170
+ --hash=sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1 \
171
+ --hash=sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1
172
+ # via
173
+ # huggingface-hub
174
+ # torch
175
+ fsspec==2026.2.0 \
176
+ --hash=sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff \
177
+ --hash=sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437
178
+ # via
179
+ # gradio-client
180
+ # huggingface-hub
181
+ # torch
182
+ gradio==6.5.1 \
183
+ --hash=sha256:31223a1699f15072176dbf48a94f08457228a38263bb4c221a0ccea3a639a595 \
184
+ --hash=sha256:5d49ff9691413ca5411189a694de5cbf1b171e2d49bf9f113952ae8a93c7088d
185
+ # via xdl-colitis-demo
186
+ gradio-client==2.0.3 \
187
+ --hash=sha256:8f1cec02dccaf64ac0285ed60479a2b0db3778dfe74c85a36d7ec9a95daeccc4 \
188
+ --hash=sha256:bcc88da74e3a387bcd41535578abbafe2091bcf4715c9542111804741b9e50b0
189
+ # via gradio
190
+ groovy==0.1.2 \
191
+ --hash=sha256:25c1dc09b3f9d7e292458aa762c6beb96ea037071bf5e917fc81fb78d2231083 \
192
+ --hash=sha256:7f7975bab18c729a257a8b1ae9dcd70b7cafb1720481beae47719af57c35fa64
193
+ # via gradio
194
+ h11==0.16.0 \
195
+ --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \
196
+ --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86
197
+ # via
198
+ # httpcore
199
+ # uvicorn
200
+ hf-xet==1.2.0 ; platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64' \
201
+ --hash=sha256:10bfab528b968c70e062607f663e21e34e2bba349e8038db546646875495179e \
202
+ --hash=sha256:210d577732b519ac6ede149d2f2f34049d44e8622bf14eb3d63bbcd2d4b332dc \
203
+ --hash=sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4 \
204
+ --hash=sha256:293a7a3787e5c95d7be1857358a9130694a9c6021de3f27fa233f37267174382 \
205
+ --hash=sha256:29c8fc913a529ec0a91867ce3d119ac1aac966e098cf49501800c870328cc090 \
206
+ --hash=sha256:2a212e842647b02eb6a911187dc878e79c4aa0aa397e88dd3b26761676e8c1f8 \
207
+ --hash=sha256:30e06daccb3a7d4c065f34fc26c14c74f4653069bb2b194e7f18f17cbe9939c0 \
208
+ --hash=sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd \
209
+ --hash=sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848 \
210
+ --hash=sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737 \
211
+ --hash=sha256:66e159cbfcfbb29f920db2c09ed8b660eb894640d284f102ada929b6e3dc410a \
212
+ --hash=sha256:6de1fc44f58f6dd937956c8d304d8c2dea264c80680bcfa61ca4a15e7b76780f \
213
+ --hash=sha256:7d40b18769bb9a8bc82a9ede575ce1a44c75eb80e7375a01d76259089529b5dc \
214
+ --hash=sha256:9c91d5ae931510107f148874e9e2de8a16052b6f1b3ca3c1b12f15ccb491390f \
215
+ --hash=sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865 \
216
+ --hash=sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f \
217
+ --hash=sha256:b70218dd548e9840224df5638fdc94bd033552963cfa97f9170829381179c813 \
218
+ --hash=sha256:cd3a6027d59cfb60177c12d6424e31f4b5ff13d8e3a1247b3a584bf8977e6df5 \
219
+ --hash=sha256:ceeefcd1b7aed4956ae8499e2199607765fbd1c60510752003b6cc0b8413b649 \
220
+ --hash=sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c \
221
+ --hash=sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69 \
222
+ --hash=sha256:f182f264ed2acd566c514e45da9f2119110e48a87a327ca271027904c70c5832
223
+ # via huggingface-hub
224
+ httpcore==1.0.9 \
225
+ --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \
226
+ --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8
227
+ # via httpx
228
+ httpx==0.28.1 \
229
+ --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \
230
+ --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad
231
+ # via
232
+ # gradio
233
+ # gradio-client
234
+ # huggingface-hub
235
+ # safehttpx
236
+ huggingface-hub==1.4.1 \
237
+ --hash=sha256:9931d075fb7a79af5abc487106414ec5fba2c0ae86104c0c62fd6cae38873d18 \
238
+ --hash=sha256:b41131ec35e631e7383ab26d6146b8d8972abc8b6309b963b306fbcca87f5ed5
239
+ # via
240
+ # gradio
241
+ # gradio-client
242
+ idna==3.11 \
243
+ --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \
244
+ --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902
245
+ # via
246
+ # anyio
247
+ # httpx
248
+ jinja2==3.1.6 \
249
+ --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \
250
+ --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67
251
+ # via
252
+ # gradio
253
+ # torch
254
+ markdown-it-py==4.0.0 \
255
+ --hash=sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147 \
256
+ --hash=sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3
257
+ # via rich
258
+ markupsafe==3.0.3 \
259
+ --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \
260
+ --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \
261
+ --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \
262
+ --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \
263
+ --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \
264
+ --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \
265
+ --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \
266
+ --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \
267
+ --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \
268
+ --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \
269
+ --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \
270
+ --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \
271
+ --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \
272
+ --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \
273
+ --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \
274
+ --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \
275
+ --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \
276
+ --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \
277
+ --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \
278
+ --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \
279
+ --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \
280
+ --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \
281
+ --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \
282
+ --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \
283
+ --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \
284
+ --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \
285
+ --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \
286
+ --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \
287
+ --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \
288
+ --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \
289
+ --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \
290
+ --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \
291
+ --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \
292
+ --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \
293
+ --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \
294
+ --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \
295
+ --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \
296
+ --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \
297
+ --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \
298
+ --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \
299
+ --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \
300
+ --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \
301
+ --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \
302
+ --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \
303
+ --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \
304
+ --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \
305
+ --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \
306
+ --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \
307
+ --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \
308
+ --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \
309
+ --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \
310
+ --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \
311
+ --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \
312
+ --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \
313
+ --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \
314
+ --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \
315
+ --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \
316
+ --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \
317
+ --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \
318
+ --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \
319
+ --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \
320
+ --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \
321
+ --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \
322
+ --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \
323
+ --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \
324
+ --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \
325
+ --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \
326
+ --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \
327
+ --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \
328
+ --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \
329
+ --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \
330
+ --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \
331
+ --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \
332
+ --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \
333
+ --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \
334
+ --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \
335
+ --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \
336
+ --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50
337
+ # via
338
+ # gradio
339
+ # jinja2
340
+ mdurl==0.1.2 \
341
+ --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
342
+ --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
343
+ # via markdown-it-py
344
+ mpmath==1.3.0 \
345
+ --hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \
346
+ --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c
347
+ # via sympy
348
+ networkx==3.4.2 ; python_full_version < '3.11' \
349
+ --hash=sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1 \
350
+ --hash=sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f
351
+ # via torch
352
+ networkx==3.6.1 ; python_full_version >= '3.11' \
353
+ --hash=sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509 \
354
+ --hash=sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762
355
+ # via torch
356
+ numpy==2.2.6 ; python_full_version < '3.11' \
357
+ --hash=sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff \
358
+ --hash=sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47 \
359
+ --hash=sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84 \
360
+ --hash=sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d \
361
+ --hash=sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6 \
362
+ --hash=sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f \
363
+ --hash=sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b \
364
+ --hash=sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49 \
365
+ --hash=sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163 \
366
+ --hash=sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571 \
367
+ --hash=sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42 \
368
+ --hash=sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff \
369
+ --hash=sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491 \
370
+ --hash=sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4 \
371
+ --hash=sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566 \
372
+ --hash=sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf \
373
+ --hash=sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40 \
374
+ --hash=sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd \
375
+ --hash=sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06 \
376
+ --hash=sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282 \
377
+ --hash=sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680 \
378
+ --hash=sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db \
379
+ --hash=sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3 \
380
+ --hash=sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90 \
381
+ --hash=sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1 \
382
+ --hash=sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289 \
383
+ --hash=sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab \
384
+ --hash=sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c \
385
+ --hash=sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d \
386
+ --hash=sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb \
387
+ --hash=sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d \
388
+ --hash=sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a \
389
+ --hash=sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf \
390
+ --hash=sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1 \
391
+ --hash=sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2 \
392
+ --hash=sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a \
393
+ --hash=sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543 \
394
+ --hash=sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00 \
395
+ --hash=sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c \
396
+ --hash=sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f \
397
+ --hash=sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd \
398
+ --hash=sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868 \
399
+ --hash=sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303 \
400
+ --hash=sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83 \
401
+ --hash=sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3 \
402
+ --hash=sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d \
403
+ --hash=sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87 \
404
+ --hash=sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa \
405
+ --hash=sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f \
406
+ --hash=sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae \
407
+ --hash=sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda \
408
+ --hash=sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915 \
409
+ --hash=sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249 \
410
+ --hash=sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de \
411
+ --hash=sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8
412
+ # via
413
+ # gradio
414
+ # pandas
415
+ # torchvision
416
+ # xdl-colitis-demo
417
+ numpy==2.4.2 ; python_full_version >= '3.11' \
418
+ --hash=sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82 \
419
+ --hash=sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75 \
420
+ --hash=sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257 \
421
+ --hash=sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71 \
422
+ --hash=sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a \
423
+ --hash=sha256:1ae241bbfc6ae276f94a170b14785e561cb5e7f626b6688cf076af4110887413 \
424
+ --hash=sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181 \
425
+ --hash=sha256:209fae046e62d0ce6435fcfe3b1a10537e858249b3d9b05829e2a05218296a85 \
426
+ --hash=sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef \
427
+ --hash=sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a \
428
+ --hash=sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c \
429
+ --hash=sha256:2653de5c24910e49c2b106499803124dde62a5a1fe0eedeaecf4309a5f639390 \
430
+ --hash=sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e \
431
+ --hash=sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f \
432
+ --hash=sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1 \
433
+ --hash=sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b \
434
+ --hash=sha256:47c5a6ed21d9452b10227e5e8a0e1c22979811cad7dcc19d8e3e2fb8fa03f1a3 \
435
+ --hash=sha256:4f069069931240b3fc703f1e23df63443dbd6390614c8c44a87d96cd0ec81eb1 \
436
+ --hash=sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657 \
437
+ --hash=sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262 \
438
+ --hash=sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a \
439
+ --hash=sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b \
440
+ --hash=sha256:602f65afdef699cda27ec0b9224ae5dc43e328f4c24c689deaf77133dbee74d0 \
441
+ --hash=sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae \
442
+ --hash=sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554 \
443
+ --hash=sha256:6d82351358ffbcdcd7b686b90742a9b86632d6c1c051016484fa0b326a0a1548 \
444
+ --hash=sha256:6e9f61981ace1360e42737e2bae58b27bf28a1b27e781721047d84bd754d32e7 \
445
+ --hash=sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05 \
446
+ --hash=sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1 \
447
+ --hash=sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622 \
448
+ --hash=sha256:7edc794af8b36ca37ef5fcb5e0d128c7e0595c7b96a2318d1badb6fcd8ee86b1 \
449
+ --hash=sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a \
450
+ --hash=sha256:805cc8de9fd6e7a22da5aed858e0ab16be5a4db6c873dde1d7451c541553aa27 \
451
+ --hash=sha256:8906e71fd8afcb76580404e2a950caef2685df3d2a57fe82a86ac8d33cc007ba \
452
+ --hash=sha256:89f7268c009bc492f506abd6f5265defa7cb3f7487dc21d357c3d290add45082 \
453
+ --hash=sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443 \
454
+ --hash=sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98 \
455
+ --hash=sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110 \
456
+ --hash=sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308 \
457
+ --hash=sha256:9e35d3e0144137d9fdae62912e869136164534d64a169f86438bc9561b6ad49f \
458
+ --hash=sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5 \
459
+ --hash=sha256:adb6ed2ad29b9e15321d167d152ee909ec73395901b70936f029c3bc6d7f4460 \
460
+ --hash=sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef \
461
+ --hash=sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab \
462
+ --hash=sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909 \
463
+ --hash=sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e \
464
+ --hash=sha256:b9c618d56a29c9cb1c4da979e9899be7578d2e0b3c24d52079c166324c9e8695 \
465
+ --hash=sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325 \
466
+ --hash=sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979 \
467
+ --hash=sha256:be71bf1edb48ebbbf7f6337b5bfd2f895d1902f6335a5830b20141fc126ffba0 \
468
+ --hash=sha256:c02ef4401a506fb60b411467ad501e1429a3487abca4664871d9ae0b46c8ba32 \
469
+ --hash=sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7 \
470
+ --hash=sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7 \
471
+ --hash=sha256:cb7bbb88aa74908950d979eeaa24dbdf1a865e3c7e45ff0121d8f70387b55f73 \
472
+ --hash=sha256:cd2bd2bbed13e213d6b55dc1d035a4f91748a7d3edc9480c13898b0353708920 \
473
+ --hash=sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74 \
474
+ --hash=sha256:cf28c0c1d4c4bf00f509fa7eb02c58d7caf221b50b467bcb0d9bbf1584d5c821 \
475
+ --hash=sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499 \
476
+ --hash=sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000 \
477
+ --hash=sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a \
478
+ --hash=sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913 \
479
+ --hash=sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8 \
480
+ --hash=sha256:df1b10187212b198dd45fa943d8985a3c8cf854aed4923796e0e019e113a1bda \
481
+ --hash=sha256:e04ae107ac591763a47398bb45b568fc38f02dbc4aa44c063f67a131f99346cb \
482
+ --hash=sha256:e6dee3bb76aa4009d5a912180bf5b2de012532998d094acee25d9cb8dee3e44a \
483
+ --hash=sha256:e7e88598032542bd49af7c4747541422884219056c268823ef6e5e89851c8825 \
484
+ --hash=sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d \
485
+ --hash=sha256:ec055f6dae239a6299cace477b479cca2fc125c5675482daf1dd886933a1076f \
486
+ --hash=sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb \
487
+ --hash=sha256:fbde1b0c6e81d56f5dccd95dd4a711d9b95df1ae4009a60887e56b27e8d903fa \
488
+ --hash=sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236 \
489
+ --hash=sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1
490
+ # via
491
+ # gradio
492
+ # pandas
493
+ # torchvision
494
+ # xdl-colitis-demo
495
+ nvidia-cublas-cu12==12.8.4.1 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
496
+ --hash=sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142
497
+ # via
498
+ # nvidia-cudnn-cu12
499
+ # nvidia-cusolver-cu12
500
+ # torch
501
+ nvidia-cuda-cupti-cu12==12.8.90 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
502
+ --hash=sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182
503
+ # via torch
504
+ nvidia-cuda-nvrtc-cu12==12.8.93 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
505
+ --hash=sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994
506
+ # via torch
507
+ nvidia-cuda-runtime-cu12==12.8.90 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
508
+ --hash=sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90
509
+ # via torch
510
+ nvidia-cudnn-cu12==9.10.2.21 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
511
+ --hash=sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8
512
+ # via torch
513
+ nvidia-cufft-cu12==11.3.3.83 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
514
+ --hash=sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74
515
+ # via torch
516
+ nvidia-cufile-cu12==1.13.1.3 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
517
+ --hash=sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc
518
+ # via torch
519
+ nvidia-curand-cu12==10.3.9.90 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
520
+ --hash=sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9
521
+ # via torch
522
+ nvidia-cusolver-cu12==11.7.3.90 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
523
+ --hash=sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450
524
+ # via torch
525
+ nvidia-cusparse-cu12==12.5.8.93 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
526
+ --hash=sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b
527
+ # via
528
+ # nvidia-cusolver-cu12
529
+ # torch
530
+ nvidia-cusparselt-cu12==0.7.1 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
531
+ --hash=sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623
532
+ # via torch
533
+ nvidia-nccl-cu12==2.27.5 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
534
+ --hash=sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457
535
+ # via torch
536
+ nvidia-nvjitlink-cu12==12.8.93 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
537
+ --hash=sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88
538
+ # via
539
+ # nvidia-cufft-cu12
540
+ # nvidia-cusolver-cu12
541
+ # nvidia-cusparse-cu12
542
+ # torch
543
+ nvidia-nvshmem-cu12==3.4.5 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
544
+ --hash=sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd
545
+ # via torch
546
+ nvidia-nvtx-cu12==12.8.90 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
547
+ --hash=sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f
548
+ # via torch
549
+ orjson==3.11.7 \
550
+ --hash=sha256:043d3006b7d32c7e233b8cfb1f01c651013ea079e08dcef7189a29abd8befe11 \
551
+ --hash=sha256:0527a4510c300e3b406591b0ba69b5dc50031895b0a93743526a3fc45f59d26e \
552
+ --hash=sha256:0724e265bc548af1dedebd9cb3d24b4e1c1e685a343be43e87ba922a5c5fff2f \
553
+ --hash=sha256:136dcd6a2e796dfd9ffca9fc027d778567b0b7c9968d092842d3c323cef88aa8 \
554
+ --hash=sha256:14f440c7268c8f8633d1b3d443a434bd70cb15686117ea6beff8fdc8f5917a1e \
555
+ --hash=sha256:1d98b30cc1313d52d4af17d9c3d307b08389752ec5f2e5febdfada70b0f8c733 \
556
+ --hash=sha256:1eb80451a9c351a71dfaf5b7ccc13ad065405217726b59fdbeadbcc544f9d223 \
557
+ --hash=sha256:1ee5cc7160a821dfe14f130bc8e63e7611051f964b463d9e2a3a573204446a4d \
558
+ --hash=sha256:23d6c20517a97a9daf1d48b580fcdc6f0516c6f4b5038823426033690b4d2650 \
559
+ --hash=sha256:26c3b9132f783b7d7903bf1efb095fed8d4a3a85ec0d334ee8beff3d7a4749d5 \
560
+ --hash=sha256:31c80ce534ac4ea3739c5ee751270646cbc46e45aea7576a38ffec040b4029a1 \
561
+ --hash=sha256:3726be79e36e526e3d9c1aceaadbfb4a04ee80a72ab47b3f3c17fefb9812e7b8 \
562
+ --hash=sha256:390a1dce0c055ddf8adb6aa94a73b45a4a7d7177b5c584b8d1c1947f2ba60fb3 \
563
+ --hash=sha256:3a2479753bbb95b0ebcf7969f562cdb9668e6d12416a35b0dda79febf89cdea2 \
564
+ --hash=sha256:3c4bc6c6ac52cdaa267552544c73e486fecbd710b7ac09bc024d5a78555a22f6 \
565
+ --hash=sha256:411ebaf34d735e25e358a6d9e7978954a9c9d58cfb47bc6683cdc3964cd2f910 \
566
+ --hash=sha256:4682d1db3bcebd2b64757e0ddf9e87ae5f00d29d16c5cdf3a62f561d08cc3dd2 \
567
+ --hash=sha256:4a2e9c5be347b937a2e0203866f12bba36082e89b402ddb9e927d5822e43088d \
568
+ --hash=sha256:57036b27ac8a25d81112eb0cc9835cd4833c5b16e1467816adc0015f59e870dc \
569
+ --hash=sha256:5ede977b5fe5ac91b1dffc0a517ca4542d2ec8a6a4ff7b2652d94f640796342a \
570
+ --hash=sha256:5fdfad2093bdd08245f2e204d977facd5f871c88c4a71230d5bcbd0e43bf6222 \
571
+ --hash=sha256:623ad1b9548ef63886319c16fa317848e465a21513b31a6ad7b57443c3e0dcf5 \
572
+ --hash=sha256:652c6c3af76716f4a9c290371ba2e390ede06f6603edb277b481daf37f6f464e \
573
+ --hash=sha256:6543001328aa857187f905308a028935864aefe9968af3848401b6fe80dbb471 \
574
+ --hash=sha256:6e776b998ac37c0396093d10290e60283f59cfe0fc3fccbd0ccc4bd04dd19892 \
575
+ --hash=sha256:71924496986275a737f38e3f22b4e0878882b3f7a310d2ff4dc96e812789120c \
576
+ --hash=sha256:733ae23ada68b804b222c44affed76b39e30806d38660bf1eb200520d259cc16 \
577
+ --hash=sha256:7477aa6a6ec6139c5cb1cc7b214643592169a5494d200397c7fc95d740d5fcf3 \
578
+ --hash=sha256:79cacb0b52f6004caf92405a7e1f11e6e2de8bdf9019e4f76b44ba045125cd6b \
579
+ --hash=sha256:7ba61079379b0ae29e117db13bda5f28d939766e410d321ec1624afc6a0b0504 \
580
+ --hash=sha256:800988273a014a0541483dc81021247d7eacb0c845a9d1a34a422bc718f41539 \
581
+ --hash=sha256:814be4b49b228cfc0b3c565acf642dd7d13538f966e3ccde61f4f55be3e20785 \
582
+ --hash=sha256:845c3e0d8ded9c9271cd79596b9b552448b885b97110f628fb687aee2eed11c1 \
583
+ --hash=sha256:849e38203e5be40b776ed2718e587faf204d184fc9a008ae441f9442320c0cab \
584
+ --hash=sha256:89e13dd3f89f1c38a9c9eba5fbf7cdc2d1feca82f5f290864b4b7a6aac704576 \
585
+ --hash=sha256:89e440ebc74ce8ab5c7bc4ce6757b4a6b1041becb127df818f6997b5c71aa60b \
586
+ --hash=sha256:8ff206156006da5b847c9304b6308a01e8cdbc8cce824e2779a5ba71c3def141 \
587
+ --hash=sha256:91c81ef070c8f3220054115e1ef468b1c9ce8497b4e526cb9f68ab4dc0a7ac62 \
588
+ --hash=sha256:9487abc2c2086e7c8eb9a211d2ce8855bae0e92586279d0d27b341d5ad76c85c \
589
+ --hash=sha256:962d046ee1765f74a1da723f4b33e3b228fe3a48bd307acce5021dfefe0e29b2 \
590
+ --hash=sha256:996b65230271f1a97026fd0e6a753f51fbc0c335d2ad0c6201f711b0da32693b \
591
+ --hash=sha256:9b1a67243945819ce55d24a30b59d6a168e86220452d2c96f4d1f093e71c0c49 \
592
+ --hash=sha256:9c0b51672e466fd7e56230ffbae7f1639e18d0ce023351fb75da21b71bc2c960 \
593
+ --hash=sha256:9e54f3808e2b6b945078c41aa8d9b5834b28c50843846e97807e5adb75fa9705 \
594
+ --hash=sha256:a02c833f38f36546ba65a452127633afce4cf0dd7296b753d3bb54e55e5c0174 \
595
+ --hash=sha256:a12b80df61aab7b98b490fe9e4879925ba666fccdfcd175252ce4d9035865ace \
596
+ --hash=sha256:a16bcd08ab0bcdfc7e8801d9c4a9cc17e58418e4d48ddc6ded4e9e4b1a94062b \
597
+ --hash=sha256:a56df3239294ea5964adf074c54bcc4f0ccd21636049a2cf3ca9cf03b5d03cf1 \
598
+ --hash=sha256:a709e881723c9b18acddcfb8ba357322491ad553e277cf467e1e7e20e2d90561 \
599
+ --hash=sha256:ab49d4b2a6a1d415ddb9f37a21e02e0d5dbfe10b7870b21bf779fc21e9156157 \
600
+ --hash=sha256:ae9e0b37a834cef7ce8f99de6498f8fad4a2c0bf6bfc3d02abd8ed56aa15b2de \
601
+ --hash=sha256:b4a9eefdc70bf8bf9857f0290f973dec534ac84c35cd6a7f4083be43e7170a8f \
602
+ --hash=sha256:b63c6e6738d7c3470ad01601e23376aa511e50e1f3931395b9f9c722406d1a67 \
603
+ --hash=sha256:b7b1dae39230a393df353827c855a5f176271c23434cfd2db74e0e424e693e10 \
604
+ --hash=sha256:b8d14b71c0b12963fe8a62aac87119f1afdf4cb88a400f61ca5ae581449efcb5 \
605
+ --hash=sha256:b9f95dcdea9d4f805daa9ddf02617a89e484c6985fa03055459f90e87d7a0757 \
606
+ --hash=sha256:b9fc4d0f81f394689e0814617aadc4f2ea0e8025f38c226cbf22d3b5ddbf025d \
607
+ --hash=sha256:bd03ea7606833655048dab1a00734a2875e3e86c276e1d772b2a02556f0d895f \
608
+ --hash=sha256:bd0d68edd7dfca1b2eca9361a44ac9f24b078de3481003159929a0573f21a6bf \
609
+ --hash=sha256:bda117c4148e81f746655d5a3239ae9bd00cb7bc3ca178b5fc5a5997e9744183 \
610
+ --hash=sha256:bf742e149121dc5648ba0a08ea0871e87b660467ef168a3a5e53bc1fbd64bb74 \
611
+ --hash=sha256:c2428d358d85e8da9d37cba18b8c4047c55222007a84f97156a5b22028dfbfc0 \
612
+ --hash=sha256:c2e85fe4698b6a56d5e2ebf7ae87544d668eb6bde1ad1226c13f44663f20ec9e \
613
+ --hash=sha256:c43b8b5bab288b6b90dac410cca7e986a4fa747a2e8f94615aea407da706980d \
614
+ --hash=sha256:cededd6738e1c153530793998e31c05086582b08315db48ab66649768f326baa \
615
+ --hash=sha256:d06e5c5fed5caedd2e540d62e5b1c25e8c82431b9e577c33537e5fa4aa909539 \
616
+ --hash=sha256:d772afdb22555f0c58cfc741bdae44180122b3616faa1ecadb595cd526e4c993 \
617
+ --hash=sha256:d897e81f8d0cbd2abb82226d1860ad2e1ab3ff16d7b08c96ca00df9d45409ef4 \
618
+ --hash=sha256:de0a37f21d0d364954ad5de1970491d7fbd0fb1ef7417d4d56a36dc01ba0c0a0 \
619
+ --hash=sha256:e7745312efa9e11c17fbd3cb3097262d079da26930ae9ae7ba28fb738367cbad \
620
+ --hash=sha256:ed46f17096e28fb28d2975834836a639af7278aa87c84f68ab08fbe5b8bd75fa \
621
+ --hash=sha256:f4f7c956b5215d949a1f65334cf9d7612dde38f20a95f2315deef167def91a6f \
622
+ --hash=sha256:f50979824bde13d32b4320eedd513431c921102796d86be3eee0b58e58a3ecd1 \
623
+ --hash=sha256:f904c24bdeabd4298f7a977ef14ca2a022ca921ed670b92ecd16ab6f3d01f867
624
+ # via gradio
625
+ packaging==26.0 \
626
+ --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \
627
+ --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529
628
+ # via
629
+ # gradio
630
+ # gradio-client
631
+ # huggingface-hub
632
+ pandas==2.3.3 ; python_full_version < '3.11' \
633
+ --hash=sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7 \
634
+ --hash=sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593 \
635
+ --hash=sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5 \
636
+ --hash=sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791 \
637
+ --hash=sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec \
638
+ --hash=sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4 \
639
+ --hash=sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5 \
640
+ --hash=sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac \
641
+ --hash=sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084 \
642
+ --hash=sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c \
643
+ --hash=sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87 \
644
+ --hash=sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35 \
645
+ --hash=sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250 \
646
+ --hash=sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c \
647
+ --hash=sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826 \
648
+ --hash=sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713 \
649
+ --hash=sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1 \
650
+ --hash=sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523 \
651
+ --hash=sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3 \
652
+ --hash=sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78 \
653
+ --hash=sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53 \
654
+ --hash=sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c \
655
+ --hash=sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21 \
656
+ --hash=sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5 \
657
+ --hash=sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45 \
658
+ --hash=sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110 \
659
+ --hash=sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493 \
660
+ --hash=sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b \
661
+ --hash=sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450 \
662
+ --hash=sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86 \
663
+ --hash=sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98 \
664
+ --hash=sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89 \
665
+ --hash=sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66 \
666
+ --hash=sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b \
667
+ --hash=sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8 \
668
+ --hash=sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6 \
669
+ --hash=sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc \
670
+ --hash=sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788 \
671
+ --hash=sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151 \
672
+ --hash=sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838 \
673
+ --hash=sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b \
674
+ --hash=sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a \
675
+ --hash=sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d \
676
+ --hash=sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908 \
677
+ --hash=sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0 \
678
+ --hash=sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b \
679
+ --hash=sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c \
680
+ --hash=sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee
681
+ # via
682
+ # gradio
683
+ # xdl-colitis-demo
684
+ pandas==3.0.0 ; python_full_version >= '3.11' \
685
+ --hash=sha256:0192fee1f1a8e743b464a6607858ee4b071deb0b118eb143d71c2a1d170996d5 \
686
+ --hash=sha256:083b11415b9970b6e7888800c43c82e81a06cd6b06755d84804444f0007d6bb7 \
687
+ --hash=sha256:0facf7e87d38f721f0af46fe70d97373a37701b1c09f7ed7aeeb292ade5c050f \
688
+ --hash=sha256:113b4cca2614ff7e5b9fee9b6f066618fe73c5a83e99d721ffc41217b2bf57dd \
689
+ --hash=sha256:125eb901e233f155b268bbef9abd9afb5819db74f0e677e89a61b246228c71ac \
690
+ --hash=sha256:14c2a4099cd38a1d18ff108168ea417909b2dea3bd1ebff2ccf28ddb6a74d740 \
691
+ --hash=sha256:15d59f885ee5011daf8335dff47dcb8a912a27b4ad7826dc6cbe809fd145d327 \
692
+ --hash=sha256:177d9df10b3f43b70307a149d7ec49a1229a653f907aa60a48f1877d0e6be3be \
693
+ --hash=sha256:1c39eab3ad38f2d7a249095f0a3d8f8c22cc0f847e98ccf5bbe732b272e2d9fa \
694
+ --hash=sha256:1fbbb5a7288719e36b76b4f18d46ede46e7f916b6c8d9915b756b0a6c3f792b3 \
695
+ --hash=sha256:24e6547fb64d2c92665dd2adbfa4e85fa4fd70a9c070e7cfb03b629a0bbab5eb \
696
+ --hash=sha256:2713810ad3806767b89ad3b7b69ba153e1c6ff6d9c20f9c2140379b2a98b6c98 \
697
+ --hash=sha256:33fd3e6baa72899746b820c31e4b9688c8e1b7864d7aec2de7ab5035c285277a \
698
+ --hash=sha256:3c9a1a149aed3b6c9bf246033ff91e1b02d529546c5d6fb6b74a28fea0cf4c70 \
699
+ --hash=sha256:412d1a89aab46889f3033a386912efcdfa0f1131c5705ff5b668dda88305e986 \
700
+ --hash=sha256:447b2d68ac5edcbf94655fe909113a6dba6ef09ad7f9f60c80477825b6c489fe \
701
+ --hash=sha256:48ee04b90e2505c693d3f8e8f524dab8cb8aaf7ddcab52c92afa535e717c4812 \
702
+ --hash=sha256:4a4a400ca18230976724a5066f20878af785f36c6756e498e94c2a5e5d57779c \
703
+ --hash=sha256:4a66384f017240f3858a4c8a7cf21b0591c3ac885cddb7758a589f0f71e87ebb \
704
+ --hash=sha256:597c08fb9fef0edf1e4fa2f9828dd27f3d78f9b8c9b4a748d435ffc55732310b \
705
+ --hash=sha256:5db1e62cb99e739fa78a28047e861b256d17f88463c76b8dafc7c1338086dca8 \
706
+ --hash=sha256:613e13426069793aa1ec53bdcc3b86e8d32071daea138bbcf4fa959c9cdaa2e2 \
707
+ --hash=sha256:66f72fb172959af42a459e27a8d8d2c7e311ff4c1f7db6deb3b643dbc382ae08 \
708
+ --hash=sha256:69780c98f286076dcafca38d8b8eee1676adf220199c0a39f0ecbf976b68151a \
709
+ --hash=sha256:697b8f7d346c68274b1b93a170a70974cdc7d7354429894d5927c1effdcccd73 \
710
+ --hash=sha256:707a9a877a876c326ae2cb640fbdc4ef63b0a7b9e2ef55c6df9942dcee8e2af9 \
711
+ --hash=sha256:783ac35c4d0fe0effdb0d67161859078618b1b6587a1af15928137525217a721 \
712
+ --hash=sha256:8cb3120f0d9467ed95e77f67a75e030b67545bcfa08964e349252d674171def2 \
713
+ --hash=sha256:8e8b9808590fa364416b49b2a35c1f4cf2785a6c156935879e57f826df22038e \
714
+ --hash=sha256:940eebffe55528074341a5a36515f3e4c5e25e958ebbc764c9502cfc35ba3faa \
715
+ --hash=sha256:95683af6175d884ee89471842acfca29172a85031fccdabc35e50c0984470a0e \
716
+ --hash=sha256:9803b31f5039b3c3b10cc858c5e40054adb4b29b4d81cb2fd789f4121c8efbcd \
717
+ --hash=sha256:98212a38a709feb90ae658cb6227ea3657c22ba8157d4b8f913cd4c950de5e7e \
718
+ --hash=sha256:a453aad8c4f4e9f166436994a33884442ea62aa8b27d007311e87521b97246e1 \
719
+ --hash=sha256:a8942e333dc67ceda1095227ad0febb05a3b36535e520154085db632c40ad084 \
720
+ --hash=sha256:afd0aa3d0b5cda6e0b8ffc10dbcca3b09ef3cbcd3fe2b27364f85fdc04e1989d \
721
+ --hash=sha256:b78d646249b9a2bc191040988c7bb524c92fa8534fb0898a0741d7e6f2ffafa6 \
722
+ --hash=sha256:b86d113b6c109df3ce0ad5abbc259fe86a1bd4adfd4a31a89da42f84f65509bb \
723
+ --hash=sha256:bc9cba7b355cb4162442a88ce495e01cb605f17ac1e27d6596ac963504e0305f \
724
+ --hash=sha256:be8c515c9bc33989d97b89db66ea0cececb0f6e3c2a87fcc8b69443a6923e95f \
725
+ --hash=sha256:c14837eba8e99a8da1527c0280bba29b0eb842f64aa94982c5e21227966e164b \
726
+ --hash=sha256:d257699b9a9960e6125686098d5714ac59d05222bef7a5e6af7a7fd87c650801 \
727
+ --hash=sha256:d64ce01eb9cdca96a15266aa679ae50212ec52757c79204dbc7701a222401850 \
728
+ --hash=sha256:da768007b5a33057f6d9053563d6b74dd6d029c337d93c6d0d22a763a5c2ecc0 \
729
+ --hash=sha256:debb95c77ff3ed3ba0d9aa20c3a2f19165cc7956362f9873fce1ba0a53819d70 \
730
+ --hash=sha256:e979d22316f9350c516479dd3a92252be2937a9531ed3a26ec324198a99cdd49 \
731
+ --hash=sha256:f0b853319dec8d5e0c8b875374c078ef17f2269986a78168d9bd57e49bf650ae \
732
+ --hash=sha256:fedabf175e7cd82b69b74c30adbaa616de301291a5231138d7242596fc296a8d
733
+ # via
734
+ # gradio
735
+ # xdl-colitis-demo
736
+ pillow==12.1.0 \
737
+ --hash=sha256:00162e9ca6d22b7c3ee8e61faa3c3253cd19b6a37f126cad04f2f88b306f557d \
738
+ --hash=sha256:079af2fb0c599c2ec144ba2c02766d1b55498e373b3ac64687e43849fbbef5bc \
739
+ --hash=sha256:0b022eaaf709541b391ee069f0022ee5b36c709df71986e3f7be312e46f42c84 \
740
+ --hash=sha256:0c27407a2d1b96774cbc4a7594129cc027339fd800cd081e44497722ea1179de \
741
+ --hash=sha256:0ddedfaa8b5f0b4ffbc2fa87b556dc59f6bb4ecb14a53b33f9189713ae8053c0 \
742
+ --hash=sha256:0deedf2ea233722476b3a81e8cdfbad786f7adbed5d848469fa59fe52396e4ef \
743
+ --hash=sha256:0ed07dca4a8464bada6139ab38f5382f83e5f111698caf3191cb8dbf27d908b4 \
744
+ --hash=sha256:0fde7ec5538ab5095cc02df38ee99b0443ff0e1c847a045554cf5f9af1f4aa82 \
745
+ --hash=sha256:15c794d74303828eaa957ff8070846d0efe8c630901a1c753fdc63850e19ecd9 \
746
+ --hash=sha256:1a949604f73eb07a8adab38c4fe50791f9919344398bdc8ac6b307f755fc7030 \
747
+ --hash=sha256:1f345e7bc9d7f368887c712aa5054558bad44d2a301ddf9248599f4161abc7c0 \
748
+ --hash=sha256:1fcc52d86ce7a34fd17cb04e87cfdb164648a3662a6f20565910a99653d66c18 \
749
+ --hash=sha256:21e686a21078b0f9cb8c8a961d99e6a4ddb88e0fc5ea6e130172ddddc2e5221a \
750
+ --hash=sha256:2415373395a831f53933c23ce051021e79c8cd7979822d8cc478547a3f4da8ef \
751
+ --hash=sha256:277518bf4fe74aa91489e1b20577473b19ee70fb97c374aa50830b279f25841b \
752
+ --hash=sha256:27b9baecb428899db6c0de572d6d305cfaf38ca1596b5c0542a5182e3e74e8c6 \
753
+ --hash=sha256:29a4cef9cb672363926f0470afc516dbf7305a14d8c54f7abbb5c199cd8f8179 \
754
+ --hash=sha256:3413c2ae377550f5487991d444428f1a8ae92784aac79caa8b1e3b89b175f77e \
755
+ --hash=sha256:351889afef0f485b84078ea40fe33727a0492b9af3904661b0abbafee0355b72 \
756
+ --hash=sha256:3ffaa2f0659e2f740473bcf03c702c39a8d4b2b7ffc629052028764324842c64 \
757
+ --hash=sha256:40a8e3b9e8773876d6e30daed22f016509e3987bab61b3b7fe309d7019a87451 \
758
+ --hash=sha256:414b9a78e14ffeb98128863314e62c3f24b8a86081066625700b7985b3f529bd \
759
+ --hash=sha256:43aca0a55ce1eefc0aefa6253661cb54571857b1a7b2964bd8a1e3ef4b729924 \
760
+ --hash=sha256:43b4899cfd091a9693a1278c4982f3e50f7fb7cff5153b05174b4afc9593b616 \
761
+ --hash=sha256:461f9dfdafa394c59cd6d818bdfdbab4028b83b02caadaff0ffd433faf4c9a7a \
762
+ --hash=sha256:4f9f6a650743f0ddee5593ac9e954ba1bdbc5e150bc066586d4f26127853ab94 \
763
+ --hash=sha256:53d8b764726d3af1a138dd353116f774e3862ec7e3794e0c8781e30db0f35dfc \
764
+ --hash=sha256:565c986f4b45c020f5421a4cea13ef294dde9509a8577f29b2fc5edc7587fff8 \
765
+ --hash=sha256:5c5ae0a06e9ea030ab786b0251b32c7e4ce10e58d983c0d5c56029455180b5b9 \
766
+ --hash=sha256:5cb7bc1966d031aec37ddb9dcf15c2da5b2e9f7cc3ca7c54473a20a927e1eb91 \
767
+ --hash=sha256:5da841d81b1a05ef940a8567da92decaa15bc4d7dedb540a8c219ad83d91808a \
768
+ --hash=sha256:5fee4c04aad8932da9f8f710af2c1a15a83582cfb884152a9caa79d4efcdbf9c \
769
+ --hash=sha256:609e89d9f90b581c8d16358c9087df76024cf058fa693dd3e1e1620823f39670 \
770
+ --hash=sha256:6258f3260986990ba2fa8a874f8b6e808cf5abb51a94015ca3dc3c68aa4f30ea \
771
+ --hash=sha256:64efdf00c09e31efd754448a383ea241f55a994fd079866b92d2bbff598aad91 \
772
+ --hash=sha256:65b80c1ee7e14a87d6a068dd3b0aea268ffcabfe0498d38661b00c5b4b22e74c \
773
+ --hash=sha256:6741e6f3074a35e47c77b23a4e4f2d90db3ed905cb1c5e6e0d49bff2045632bc \
774
+ --hash=sha256:681088909d7e8fa9e31b9799aaa59ba5234c58e5e4f1951b4c4d1082a2e980e0 \
775
+ --hash=sha256:6b7a9d1db5dad90e2991645874f708e87d9a3c370c243c2d7684d28f7e133e6b \
776
+ --hash=sha256:7315f9137087c4e0ee73a761b163fc9aa3b19f5f606a7fc08d83fd3e4379af65 \
777
+ --hash=sha256:742aea052cf5ab5034a53c3846165bc3ce88d7c38e954120db0ab867ca242661 \
778
+ --hash=sha256:75af0b4c229ac519b155028fa1be632d812a519abba9b46b20e50c6caa184f19 \
779
+ --hash=sha256:7b5dd7cbae20285cdb597b10eb5a2c13aa9de6cde9bb64a3c1317427b1db1ae1 \
780
+ --hash=sha256:7d6daa89a00b58c37cb1747ec9fb7ac3bc5ffd5949f5888657dfddde6d1312e0 \
781
+ --hash=sha256:800429ac32c9b72909c671aaf17ecd13110f823ddb7db4dfef412a5587c2c24e \
782
+ --hash=sha256:806f3987ffe10e867bab0ddad45df1148a2b98221798457fa097ad85d6e8bc75 \
783
+ --hash=sha256:808b99604f7873c800c4840f55ff389936ef1948e4e87645eaf3fccbc8477ac4 \
784
+ --hash=sha256:80941e6d573197a0c28f394753de529bb436b1ca990ed6e765cf42426abc39f8 \
785
+ --hash=sha256:84cabc7095dd535ca934d57e9ce2a72ffd216e435a84acb06b2277b1de2689bd \
786
+ --hash=sha256:8637e29d13f478bc4f153d8daa9ffb16455f0a6cb287da1b432fdad2bfbd66c7 \
787
+ --hash=sha256:896866d2d436563fa2a43a9d72f417874f16b5545955c54a64941e87c1376c61 \
788
+ --hash=sha256:8e178e3e99d3c0ea8fc64b88447f7cac8ccf058af422a6cedc690d0eadd98c51 \
789
+ --hash=sha256:907bfa8a9cb790748a9aa4513e37c88c59660da3bcfffbd24a7d9e6abf224551 \
790
+ --hash=sha256:9212d6b86917a2300669511ed094a9406888362e085f2431a7da985a6b124f45 \
791
+ --hash=sha256:92a7fe4225365c5e3a8e598982269c6d6698d3e783b3b1ae979e7819f9cd55c1 \
792
+ --hash=sha256:935b9d1aed48fcfb3f838caac506f38e29621b44ccc4f8a64d575cb1b2a88644 \
793
+ --hash=sha256:97e9993d5ed946aba26baf9c1e8cf18adbab584b99f452ee72f7ee8acb882796 \
794
+ --hash=sha256:983976c2ab753166dc66d36af6e8ec15bb511e4a25856e2227e5f7e00a160587 \
795
+ --hash=sha256:9f5fefaca968e700ad1a4a9de98bf0869a94e397fe3524c4c9450c1445252304 \
796
+ --hash=sha256:a332ac4ccb84b6dde65dbace8431f3af08874bf9770719d32a635c4ef411b18b \
797
+ --hash=sha256:a40905599d8079e09f25027423aed94f2823adaf2868940de991e53a449e14a8 \
798
+ --hash=sha256:a6dfc2af5b082b635af6e08e0d1f9f1c4e04d17d4e2ca0ef96131e85eda6eb17 \
799
+ --hash=sha256:a786bf667724d84aa29b5db1c61b7bfdde380202aaca12c3461afd6b71743171 \
800
+ --hash=sha256:a83e0850cb8f5ac975291ebfc4170ba481f41a28065277f7f735c202cd8e0af3 \
801
+ --hash=sha256:aa0c9cc0b82b14766a99fbe6084409972266e82f459821cd26997a488a7261a7 \
802
+ --hash=sha256:b17fbdbe01c196e7e159aacb889e091f28e61020a8abeac07b68079b6e626988 \
803
+ --hash=sha256:b63e13dd27da389ed9475b3d28510f0f954bca0041e8e551b2a4eb1eab56a39a \
804
+ --hash=sha256:b6e53e82ec2db0717eabb276aa56cf4e500c9a7cec2c2e189b55c24f65a3e8c0 \
805
+ --hash=sha256:bb0984b30e973f7e2884362b7d23d0a348c7143ee559f38ef3eaab640144204c \
806
+ --hash=sha256:bc11908616c8a283cf7d664f77411a5ed2a02009b0097ff8abbba5e79128ccf2 \
807
+ --hash=sha256:bdec5e43377761c5dbca620efb69a77f6855c5a379e32ac5b158f54c84212b14 \
808
+ --hash=sha256:bef9768cab184e7ae6e559c032e95ba8d07b3023c289f79a2bd36e8bf85605a5 \
809
+ --hash=sha256:c990547452ee2800d8506c4150280757f88532f3de2a58e3022e9b179107862a \
810
+ --hash=sha256:ca94b6aac0d7af2a10ba08c0f888b3d5114439b6b3ef39968378723622fed377 \
811
+ --hash=sha256:cad302dc10fac357d3467a74a9561c90609768a6f73a1923b0fd851b6486f8b0 \
812
+ --hash=sha256:d0a7735df32ccbcc98b98a1ac785cc4b19b580be1bdf0aeb5c03223220ea09d5 \
813
+ --hash=sha256:d70347c8a5b7ccd803ec0c85c8709f036e6348f1e6a5bf048ecd9c64d3550b8b \
814
+ --hash=sha256:d70534cea9e7966169ad29a903b99fc507e932069a881d0965a1a84bb57f6c6d \
815
+ --hash=sha256:db44d5c160a90df2d24a24760bbd37607d53da0b34fb546c4c232af7192298ac \
816
+ --hash=sha256:e115c15e3bc727b1ca3e641a909f77f8ca72a64fff150f666fcc85e57701c26c \
817
+ --hash=sha256:e2479c7f02f9d505682dc47df8c0ea1fc5e264c4d1629a5d63fe3e2334b89554 \
818
+ --hash=sha256:e5dcbe95016e88437ecf33544ba5db21ef1b8dd6e1b434a2cb2a3d605299e643 \
819
+ --hash=sha256:e6bdb408f7c9dd2a5ff2b14a3b0bb6d4deb29fb9961e6eb3ae2031ae9a5cec13 \
820
+ --hash=sha256:e75d3dba8fc1ddfec0cd752108f93b83b4f8d6ab40e524a95d35f016b9683b09 \
821
+ --hash=sha256:efdc140e7b63b8f739d09a99033aa430accce485ff78e6d311973a67b6bf3208 \
822
+ --hash=sha256:f10c98f49227ed8383d28174ee95155a675c4ed7f85e2e573b04414f7e371bda \
823
+ --hash=sha256:f188028b5af6b8fb2e9a76ac0f841a575bd1bd396e46ef0840d9b88a48fdbcea \
824
+ --hash=sha256:f188d580bd870cda1e15183790d1cc2fa78f666e76077d103edf048eed9c356e \
825
+ --hash=sha256:f45bd71d1fa5e5749587613037b172e0b3b23159d1c00ef2fc920da6f470e6f0 \
826
+ --hash=sha256:f61333d817698bdcdd0f9d7793e365ac3d2a21c1f1eb02b32ad6aefb8d8ea831 \
827
+ --hash=sha256:fb125d860738a09d363a88daa0f59c4533529a90e564785e20fe875b200b6dbd
828
+ # via
829
+ # gradio
830
+ # torchvision
831
+ pydantic==2.12.5 \
832
+ --hash=sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49 \
833
+ --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d
834
+ # via
835
+ # fastapi
836
+ # gradio
837
+ pydantic-core==2.41.5 \
838
+ --hash=sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90 \
839
+ --hash=sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740 \
840
+ --hash=sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84 \
841
+ --hash=sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33 \
842
+ --hash=sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c \
843
+ --hash=sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0 \
844
+ --hash=sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e \
845
+ --hash=sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 \
846
+ --hash=sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a \
847
+ --hash=sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 \
848
+ --hash=sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2 \
849
+ --hash=sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3 \
850
+ --hash=sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815 \
851
+ --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 \
852
+ --hash=sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba \
853
+ --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 \
854
+ --hash=sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf \
855
+ --hash=sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963 \
856
+ --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 \
857
+ --hash=sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808 \
858
+ --hash=sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553 \
859
+ --hash=sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1 \
860
+ --hash=sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2 \
861
+ --hash=sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470 \
862
+ --hash=sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2 \
863
+ --hash=sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b \
864
+ --hash=sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660 \
865
+ --hash=sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c \
866
+ --hash=sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093 \
867
+ --hash=sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594 \
868
+ --hash=sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008 \
869
+ --hash=sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a \
870
+ --hash=sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a \
871
+ --hash=sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd \
872
+ --hash=sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284 \
873
+ --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 \
874
+ --hash=sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869 \
875
+ --hash=sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294 \
876
+ --hash=sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f \
877
+ --hash=sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66 \
878
+ --hash=sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51 \
879
+ --hash=sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc \
880
+ --hash=sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97 \
881
+ --hash=sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a \
882
+ --hash=sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d \
883
+ --hash=sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9 \
884
+ --hash=sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c \
885
+ --hash=sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07 \
886
+ --hash=sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36 \
887
+ --hash=sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e \
888
+ --hash=sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05 \
889
+ --hash=sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e \
890
+ --hash=sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941 \
891
+ --hash=sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612 \
892
+ --hash=sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b \
893
+ --hash=sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe \
894
+ --hash=sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146 \
895
+ --hash=sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11 \
896
+ --hash=sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd \
897
+ --hash=sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b \
898
+ --hash=sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c \
899
+ --hash=sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a \
900
+ --hash=sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1 \
901
+ --hash=sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf \
902
+ --hash=sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858 \
903
+ --hash=sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2 \
904
+ --hash=sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9 \
905
+ --hash=sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2 \
906
+ --hash=sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3 \
907
+ --hash=sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6 \
908
+ --hash=sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770 \
909
+ --hash=sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc \
910
+ --hash=sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23 \
911
+ --hash=sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26 \
912
+ --hash=sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa \
913
+ --hash=sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8 \
914
+ --hash=sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d \
915
+ --hash=sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3 \
916
+ --hash=sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d \
917
+ --hash=sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034 \
918
+ --hash=sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9 \
919
+ --hash=sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1 \
920
+ --hash=sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56 \
921
+ --hash=sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b \
922
+ --hash=sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c \
923
+ --hash=sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a \
924
+ --hash=sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e \
925
+ --hash=sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9 \
926
+ --hash=sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5 \
927
+ --hash=sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a \
928
+ --hash=sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556 \
929
+ --hash=sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e \
930
+ --hash=sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49 \
931
+ --hash=sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2 \
932
+ --hash=sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9 \
933
+ --hash=sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc \
934
+ --hash=sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb \
935
+ --hash=sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0 \
936
+ --hash=sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8 \
937
+ --hash=sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69 \
938
+ --hash=sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b \
939
+ --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c \
940
+ --hash=sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75 \
941
+ --hash=sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f \
942
+ --hash=sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad \
943
+ --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b \
944
+ --hash=sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7 \
945
+ --hash=sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52
946
+ # via pydantic
947
+ pydub==0.25.1 \
948
+ --hash=sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6 \
949
+ --hash=sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f
950
+ # via gradio
951
+ pygments==2.19.2 \
952
+ --hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \
953
+ --hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b
954
+ # via rich
955
+ python-dateutil==2.9.0.post0 \
956
+ --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
957
+ --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
958
+ # via pandas
959
+ python-multipart==0.0.22 \
960
+ --hash=sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155 \
961
+ --hash=sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58
962
+ # via gradio
963
+ pytz==2025.2 \
964
+ --hash=sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3 \
965
+ --hash=sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00
966
+ # via
967
+ # gradio
968
+ # pandas
969
+ pyyaml==6.0.3 \
970
+ --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \
971
+ --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \
972
+ --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \
973
+ --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \
974
+ --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \
975
+ --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \
976
+ --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \
977
+ --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \
978
+ --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \
979
+ --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \
980
+ --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \
981
+ --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \
982
+ --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \
983
+ --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \
984
+ --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \
985
+ --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \
986
+ --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \
987
+ --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \
988
+ --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \
989
+ --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \
990
+ --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \
991
+ --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \
992
+ --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \
993
+ --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \
994
+ --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \
995
+ --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \
996
+ --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \
997
+ --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \
998
+ --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \
999
+ --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \
1000
+ --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \
1001
+ --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \
1002
+ --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \
1003
+ --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \
1004
+ --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \
1005
+ --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \
1006
+ --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \
1007
+ --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \
1008
+ --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \
1009
+ --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \
1010
+ --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \
1011
+ --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \
1012
+ --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \
1013
+ --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \
1014
+ --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \
1015
+ --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \
1016
+ --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \
1017
+ --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \
1018
+ --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \
1019
+ --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \
1020
+ --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \
1021
+ --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \
1022
+ --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \
1023
+ --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \
1024
+ --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \
1025
+ --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \
1026
+ --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0
1027
+ # via
1028
+ # gradio
1029
+ # huggingface-hub
1030
+ rich==14.3.2 \
1031
+ --hash=sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69 \
1032
+ --hash=sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8
1033
+ # via typer
1034
+ safehttpx==0.1.7 \
1035
+ --hash=sha256:c4f4a162db6993464d7ca3d7cc4af0ffc6515a606dfd220b9f82c6945d869cde \
1036
+ --hash=sha256:db201c0978c41eddb8bb480f3eee59dd67304fdd91646035e9d9a720049a9d23
1037
+ # via gradio
1038
+ semantic-version==2.10.0 \
1039
+ --hash=sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c \
1040
+ --hash=sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177
1041
+ # via gradio
1042
+ setuptools==82.0.0 ; python_full_version >= '3.12' \
1043
+ --hash=sha256:22e0a2d69474c6ae4feb01951cb69d515ed23728cf96d05513d36e42b62b37cb \
1044
+ --hash=sha256:70b18734b607bd1da571d097d236cfcfacaf01de45717d59e6e04b96877532e0
1045
+ # via torch
1046
+ shellingham==1.5.4 \
1047
+ --hash=sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686 \
1048
+ --hash=sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de
1049
+ # via
1050
+ # huggingface-hub
1051
+ # typer
1052
+ six==1.17.0 \
1053
+ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
1054
+ --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
1055
+ # via python-dateutil
1056
+ starlette==0.52.1 \
1057
+ --hash=sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74 \
1058
+ --hash=sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933
1059
+ # via
1060
+ # fastapi
1061
+ # gradio
1062
+ sympy==1.14.0 \
1063
+ --hash=sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517 \
1064
+ --hash=sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5
1065
+ # via torch
1066
+ tomlkit==0.13.3 \
1067
+ --hash=sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1 \
1068
+ --hash=sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0
1069
+ # via gradio
1070
+ torch==2.10.0 \
1071
+ --hash=sha256:233aed0659a2503b831d8a67e9da66a62c996204c0bba4f4c442ccc0c68a3f60 \
1072
+ --hash=sha256:29b7009dba4b7a1c960260fc8ac85022c784250af43af9fb0ebafc9883782ebd \
1073
+ --hash=sha256:2c66c61f44c5f903046cc696d088e21062644cbe541c7f1c4eaae88b2ad23547 \
1074
+ --hash=sha256:3202429f58309b9fa96a614885eace4b7995729f44beb54d3e4a47773649d382 \
1075
+ --hash=sha256:3282d9febd1e4e476630a099692b44fdc214ee9bf8ee5377732d9d9dfe5712e4 \
1076
+ --hash=sha256:35e407430795c8d3edb07a1d711c41cc1f9eaddc8b2f1cc0a165a6767a8fb73d \
1077
+ --hash=sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d \
1078
+ --hash=sha256:5c4d217b14741e40776dd7074d9006fd28b8a97ef5654db959d8635b2fe5f29b \
1079
+ --hash=sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf \
1080
+ --hash=sha256:6021db85958db2f07ec94e1bc77212721ba4920c12a18dc552d2ae36a3eb163f \
1081
+ --hash=sha256:6528f13d2a8593a1a412ea07a99812495bec07e9224c28b2a25c0a30c7da025c \
1082
+ --hash=sha256:682497e16bdfa6efeec8cde66531bc8d1fbbbb4d8788ec6173c089ed3cc2bfe5 \
1083
+ --hash=sha256:6abb224c2b6e9e27b592a1c0015c33a504b00a0e0938f1499f7f514e9b7bfb5c \
1084
+ --hash=sha256:6b71486353fce0f9714ca0c9ef1c850a2ae766b409808acd58e9678a3edb7738 \
1085
+ --hash=sha256:6d3707a61863d1c4d6ebba7be4ca320f42b869ee657e9b2c21c736bf17000294 \
1086
+ --hash=sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185 \
1087
+ --hash=sha256:716b01a176c2a5659c98f6b01bf868244abdd896526f1c692712ab36dbaf9b63 \
1088
+ --hash=sha256:7350f6652dfd761f11f9ecb590bfe95b573e2961f7a242eccb3c8e78348d26fe \
1089
+ --hash=sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb \
1090
+ --hash=sha256:a2f9edd8dbc99f62bc4dfb78af7bf89499bca3d753423ac1b4e06592e467b763 \
1091
+ --hash=sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb \
1092
+ --hash=sha256:aae1b29cd68e50a9397f5ee897b9c24742e9e306f88a807a27d617f07adb3bd8 \
1093
+ --hash=sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444 \
1094
+ --hash=sha256:b7bd80f3477b830dd166c707c5b0b82a898e7b16f59a7d9d42778dd058272e8b \
1095
+ --hash=sha256:bf0d9ff448b0218e0433aeb198805192346c4fd659c852370d5cc245f602a06a \
1096
+ --hash=sha256:c2ee399c644dc92ef7bc0d4f7e74b5360c37cdbe7c5ba11318dda49ffac2bc57 \
1097
+ --hash=sha256:c37fc46eedd9175f9c81814cc47308f1b42cfe4987e532d4b423d23852f2bf63 \
1098
+ --hash=sha256:cdf2a523d699b70d613243211ecaac14fe9c5df8a0b0a9c02add60fb2a413e0f \
1099
+ --hash=sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6 \
1100
+ --hash=sha256:f5ab4ba32383061be0fb74bda772d470140a12c1c3b58a0cfbf3dae94d164c28 \
1101
+ --hash=sha256:f699f31a236a677b3118bc0a3ef3d89c0c29b5ec0b20f4c4bf0b110378487464 \
1102
+ --hash=sha256:ff43db38af76fda183156153983c9a096fc4c78d0cd1e07b14a2314c7f01c2c8
1103
+ # via
1104
+ # torchvision
1105
+ # xdl-colitis-demo
1106
+ torchvision==0.25.0 \
1107
+ --hash=sha256:0b5e7f50002a8145a98c5694a018e738c50e2972608310c7e88e1bd4c058f6ce \
1108
+ --hash=sha256:0d9a3f925a081dd2ebb0b791249b687c2ef2c2717d027946654607494b9b64b6 \
1109
+ --hash=sha256:146d02c9876858420adf41f3189fe90e3d6a409cbfa65454c09f25fb33bf7266 \
1110
+ --hash=sha256:153c0d2cbc34b7cf2da19d73450f24ba36d2b75ec9211b9962b5022fb9e4ecee \
1111
+ --hash=sha256:24e11199e4d84ba9c5ee7825ebdf1cd37ce8deec225117f10243cae984ced3ec \
1112
+ --hash=sha256:40a122c3cf4d14b651f095e0f672b688dde78632783fc5cd3d4d5e4f6a828563 \
1113
+ --hash=sha256:5e6b449e9fa7d642142c0e27c41e5a43b508d57ed8e79b7c0a0c28652da8678c \
1114
+ --hash=sha256:5f271136d2d2c0b7a24c5671795c6e4fd8da4e0ea98aeb1041f62bc04c4370ef \
1115
+ --hash=sha256:620a236288d594dcec7634c754484542dc0a5c1b0e0b83a34bda5e91e9b7c3a1 \
1116
+ --hash=sha256:632db02300e83793812eee4f61ae6a2686dab10b4cfd628b620dc47747aa9d03 \
1117
+ --hash=sha256:846890161b825b38aa85fc37fb3ba5eea74e7091ff28bab378287111483b6443 \
1118
+ --hash=sha256:855c0dc6d37f462482da7531c6788518baedca1e0847f3df42a911713acdfe52 \
1119
+ --hash=sha256:a8f8061284395ce31bcd460f2169013382ccf411148ceb2ee38e718e9860f5a7 \
1120
+ --hash=sha256:a95c47abb817d4e90ea1a8e57bd0d728e3e6b533b3495ae77d84d883c4d11f56 \
1121
+ --hash=sha256:acc339aba4a858192998c2b91f635827e40d9c469d9cf1455bafdda6e4c28ea4 \
1122
+ --hash=sha256:ad9a8a5877782944d99186e4502a614770fe906626d76e9cd32446a0ac3075f2 \
1123
+ --hash=sha256:b57430fbe9e9b697418a395041bb615124d9c007710a2712fda6e35fb310f264 \
1124
+ --hash=sha256:b75deafa2dfea3e2c2a525559b04783515e3463f6e830cb71de0fb7ea36fe233 \
1125
+ --hash=sha256:c2abe430c90b1d5e552680037d68da4eb80a5852ebb1c811b2b89d299b10573b \
1126
+ --hash=sha256:c4d395cb2c4a2712f6eb93a34476cdf7aae74bb6ea2ea1917f858e96344b00aa \
1127
+ --hash=sha256:cef0196be31be421f6f462d1e9da1101be7332d91984caa6f8022e6c78a5877f \
1128
+ --hash=sha256:d1abd5ed030c708f5dbf4812ad5f6fbe9384b63c40d6bd79f8df41a4a759a917 \
1129
+ --hash=sha256:db74a551946b75d19f9996c419a799ffdf6a223ecf17c656f90da011f1d75b20 \
1130
+ --hash=sha256:ea580ffd6094cc01914ad32f8c8118174f18974629af905cea08cb6d5d48c7b7 \
1131
+ --hash=sha256:f07f01d27375ad89d72aa2b3f2180f07da95dd9d2e4c758e015c0acb2da72977 \
1132
+ --hash=sha256:f25aa9e380865b11ea6e9d99d84df86b9cc959f1a007cd966fc6f1ab2ed0e248 \
1133
+ --hash=sha256:f49964f96644dbac2506dffe1a0a7ec0f2bf8cf7a588c3319fed26e6329ffdf3 \
1134
+ --hash=sha256:f9c55ae8d673ab493325d1267cbd285bb94d56f99626c00ac4644de32a59ede3
1135
+ # via xdl-colitis-demo
1136
+ tqdm==4.67.3 \
1137
+ --hash=sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb \
1138
+ --hash=sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf
1139
+ # via huggingface-hub
1140
+ triton==3.6.0 ; platform_machine == 'x86_64' and sys_platform == 'linux' \
1141
+ --hash=sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9 \
1142
+ --hash=sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7 \
1143
+ --hash=sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca \
1144
+ --hash=sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803 \
1145
+ --hash=sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea \
1146
+ --hash=sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f \
1147
+ --hash=sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3
1148
+ # via torch
1149
+ typer==0.21.1 \
1150
+ --hash=sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01 \
1151
+ --hash=sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d
1152
+ # via gradio
1153
+ typer-slim==0.21.1 \
1154
+ --hash=sha256:6e6c31047f171ac93cc5a973c9e617dbc5ab2bddc4d0a3135dc161b4e2020e0d \
1155
+ --hash=sha256:73495dd08c2d0940d611c5a8c04e91c2a0a98600cbd4ee19192255a233b6dbfd
1156
+ # via huggingface-hub
1157
+ typing-extensions==4.15.0 \
1158
+ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \
1159
+ --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
1160
+ # via
1161
+ # anyio
1162
+ # exceptiongroup
1163
+ # fastapi
1164
+ # gradio
1165
+ # gradio-client
1166
+ # huggingface-hub
1167
+ # pydantic
1168
+ # pydantic-core
1169
+ # starlette
1170
+ # torch
1171
+ # typer
1172
+ # typer-slim
1173
+ # typing-inspection
1174
+ # uvicorn
1175
+ typing-inspection==0.4.2 \
1176
+ --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 \
1177
+ --hash=sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464
1178
+ # via
1179
+ # fastapi
1180
+ # pydantic
1181
+ tzdata==2025.3 ; python_full_version < '3.11' or sys_platform == 'emscripten' or sys_platform == 'win32' \
1182
+ --hash=sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 \
1183
+ --hash=sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7
1184
+ # via pandas
1185
+ uvicorn==0.40.0 \
1186
+ --hash=sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea \
1187
+ --hash=sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee
1188
+ # via gradio
src/dataset.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import pandas as pd
3
+ import torch
4
+ from torchvision.io import decode_image
5
+ from PIL import Image
6
+
7
+ class Dataset(torch.utils.data.Dataset):
8
+ def __init__(self,
9
+ dataframe: pd.DataFrame,
10
+ categories: list[str],
11
+ transform=None,
12
+ seed: int = 42,
13
+ shuffle: bool = True,
14
+ is_sampling: bool = False,
15
+ sampling_number: int = 50):
16
+ self.transform = transform
17
+ self.seed = seed
18
+ self.is_sampling = is_sampling
19
+ self.sampling_number = sampling_number
20
+ self.categories = categories
21
+ self.num_classes = len(self.categories)
22
+ if is_sampling:
23
+ self.dataframe = dataframe.reset_index(drop=True)[:sampling_number]
24
+ elif shuffle:
25
+ self.dataframe = dataframe.reset_index(drop=True).sample(frac=1, random_state=self.seed).reset_index(drop=True)
26
+ else:
27
+ self.dataframe = dataframe.reset_index(drop=True)
28
+
29
+ def __len__(self):
30
+ return len(self.dataframe)
31
+
32
+ def __getitem__(self, index):
33
+ row = self.dataframe.iloc[index]
34
+ img = Image.open(row["image_path"]).convert("RGB")
35
+ if self.transform:
36
+ img = self.transform(img)
37
+ label = torch.zeros(self.num_classes)
38
+ class_idx = self.categories.index(row["class"])
39
+ label[class_idx] = 1
40
+ assert torch.any(label), "Label tensor is empty"
41
+ return img, label
42
+
43
+ class IterableDataset(torch.utils.data.IterableDataset):
44
+ def __init__(self, dataframe: pd.DataFrame, transform=None, is_sampling: bool = False, sampling_number: int = 50):
45
+ if is_sampling:
46
+ self.dataframe = dataframe.reset_index(drop=True)[:sampling_number]
47
+ else:
48
+ self.dataframe = dataframe.reset_index(drop=True)
49
+ self.transform = transform
50
+
51
+ def __iter__(self):
52
+ worker_info = torch.utils.data.get_worker_info()
53
+ if worker_info is None:
54
+ start, end = 0, len(self.dataframe)
55
+ else:
56
+ per_worker = int(math.ceil(len(self.dataframe) / float(worker_info.num_workers)))
57
+ worker_id = worker_info.id
58
+ start = worker_id * per_worker
59
+ end = min(start + per_worker, len(self.dataframe))
60
+
61
+ for idx in range(start, end):
62
+ row = self.dataframe.iloc[idx]
63
+ img = decode_image(row["image_path"], mode="RGB")
64
+ if self.transform:
65
+ img = self.transform(img)
66
+ label = row["class"]
67
+ yield img, label
68
+
69
+ def __len__(self):
70
+ return len(self.dataframe)
71
+
72
+ def __getitem__(self, index):
73
+ return self.dataframe.iloc[index]
src/densenet.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from torch.nn import functional as F
4
+
5
+ import torchvision.models as models
6
+
7
+ class DenseNet121(nn.Module):
8
+ def __init__(self, num_classes, dropout_rate=0.25):
9
+ """
10
+ Initialize DenseNet121 model with custom number of classes
11
+
12
+ Args:
13
+ num_classes (int): Number of output classes for classification
14
+ """
15
+ super(DenseNet121, self).__init__()
16
+
17
+ self.densenet_model = models.densenet121(weights='IMAGENET1K_V1')
18
+ self.pool = True
19
+ self.dropout = nn.Dropout(p=dropout_rate)
20
+
21
+ if self.pool:
22
+ self.classifier = nn.Linear(1024, num_classes)
23
+ else:
24
+ self.classifier = nn.Linear(82944, num_classes)
25
+
26
+ def forward(self, x):
27
+ """
28
+ Forward pass of the model
29
+
30
+ Args:
31
+ x (torch.Tensor): Input tensor of shape (batch_size, channels, height, width)
32
+ or (channels, height, width) for single image
33
+
34
+ Returns:
35
+ torch.Tensor: Output tensor of shape (batch_size, num_classes)
36
+ """
37
+ # Add batch dimension if input is a single image
38
+ if x.dim() == 3:
39
+ x = x.unsqueeze(0)
40
+
41
+ features = self.densenet_model.features(x)
42
+ out = F.gelu(features)
43
+ # out = F.relu(features, inplace=True)
44
+
45
+ if self.pool:
46
+ out = F.adaptive_avg_pool2d(out, (1, 1)) # Global avg pool
47
+
48
+ if self.training:
49
+ # Apply dropout only during training
50
+ out = self.dropout(out)
51
+
52
+ # Flatten the output
53
+ out = torch.flatten(out, 1)
54
+
55
+ # Pass through the classifier
56
+ out = self.classifier(out)
57
+
58
+ return out
src/metrics.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torcheval.metrics.metric import Metric
3
+ from typing import Iterable, TYPE_CHECKING, TypeVar, cast, Union, Optional
4
+
5
+ if TYPE_CHECKING:
6
+ from typing_extensions import Self # Or from typing import Self for Python 3.11+
7
+
8
+ _T = TypeVar("_T", bound="Metric") # TypeVar for merge_state
9
+ class MulticlassMCC(Metric):
10
+ def __init__(self, num_classes, device: Optional[Union[torch.device, str]] = None):
11
+ resolved_device = device if isinstance(device, torch.device) else torch.device(device or 'cpu')
12
+ super().__init__(device=resolved_device)
13
+ self.num_classes = num_classes
14
+ self._add_state("confusion_matrix", torch.zeros(num_classes, num_classes, device=resolved_device))
15
+
16
+ def update(self, y_pred: torch.Tensor, y_true: torch.Tensor) -> "Self":
17
+ y_true_labels = y_true
18
+ y_pred_labels = y_pred
19
+
20
+ if self.confusion_matrix.device != y_true.device:
21
+ self.confusion_matrix = self.confusion_matrix.to(y_true.device)
22
+
23
+ for t, p in zip(y_true_labels, y_pred_labels):
24
+ self.confusion_matrix[t.long(), p.long()] += 1
25
+
26
+ return self
27
+
28
+ def compute(self) -> torch.Tensor:
29
+ cm = self.confusion_matrix
30
+ t = torch.sum(cm, dim=1)
31
+ p = torch.sum(cm, dim=0)
32
+ c = torch.sum(torch.diag(cm))
33
+ s = torch.sum(cm)
34
+ numerator = c * s - torch.dot(t, p)
35
+ denominator = torch.sqrt((s**2 - torch.dot(p, p)) * (s**2 - torch.dot(t, t)))
36
+ return numerator / (denominator + torch.finfo(torch.float32).eps)
37
+
38
+ def reset(self) -> "Self":
39
+ self.confusion_matrix.zero_()
40
+ return self
41
+
42
+ def merge_state(self: _T, metrics: Iterable[_T]) -> _T:
43
+ for metric_obj in metrics:
44
+ if not isinstance(metric_obj, MulticlassMCC):
45
+ raise TypeError(f"Cannot merge_state for MulticlassMCC with metric of type {type(metric_obj).__name__}")
46
+ typed_metric = cast(MulticlassMCC, metric_obj)
47
+
48
+ other_cm_state = typed_metric.confusion_matrix
49
+ if self.confusion_matrix.device != other_cm_state.device:
50
+ other_cm_state = other_cm_state.to(self.confusion_matrix.device)
51
+ self.confusion_matrix += other_cm_state
52
+ return self
53
+
54
+ class MulticlassSpecificity(Metric):
55
+ def __init__(self, num_classes, device: Optional[Union[torch.device, str]] = None, average="macro"):
56
+ resolved_device = device if isinstance(device, torch.device) else torch.device(device or 'cpu')
57
+ super().__init__(device=resolved_device)
58
+ self.num_classes = num_classes
59
+ self.average = average
60
+ self._add_state("confusion_matrix", torch.zeros(num_classes, num_classes, device=resolved_device))
61
+
62
+ def update(self, y_pred: torch.Tensor, y_true: torch.Tensor) -> "Self":
63
+ y_true_labels = y_true
64
+ y_pred_labels = y_pred
65
+
66
+ if self.confusion_matrix.device != y_true.device:
67
+ self.confusion_matrix = self.confusion_matrix.to(y_true.device)
68
+
69
+ for t, p in zip(y_true_labels, y_pred_labels):
70
+ self.confusion_matrix[t.long(), p.long()] += 1
71
+
72
+ return self
73
+
74
+ def compute(self) -> torch.Tensor:
75
+ cm = self.confusion_matrix
76
+ tp = torch.diag(cm)
77
+ fp = torch.sum(cm, dim=0) - tp
78
+ fn = torch.sum(cm, dim=1) - tp
79
+ tn = torch.sum(cm) - (tp + fp + fn)
80
+
81
+ specificity_per_class = tn / (tn + fp + torch.finfo(torch.float32).eps)
82
+
83
+ if self.average == "macro":
84
+ return torch.mean(specificity_per_class)
85
+ elif self.average == "micro":
86
+ total_tn = torch.sum(tn)
87
+ total_fp = torch.sum(fp)
88
+ return total_tn / (total_tn + total_fp + torch.finfo(torch.float32).eps)
89
+ elif self.average == "weighted":
90
+ support = torch.sum(cm, dim=0) - torch.diag(cm) + (torch.sum(cm) - torch.sum(cm, dim=1))
91
+ weights = support / (torch.sum(support) + torch.finfo(torch.float32).eps)
92
+ return torch.sum(specificity_per_class * weights)
93
+ elif self.average == "none":
94
+ return specificity_per_class
95
+ else:
96
+ raise ValueError(f"Unknown average type: {self.average}")
97
+
98
+ def reset(self) -> "Self":
99
+ self.confusion_matrix.zero_()
100
+ return self
101
+
102
+ def merge_state(self: _T, metrics: Iterable[_T]) -> _T:
103
+ for metric_obj in metrics:
104
+ if not isinstance(metric_obj, MulticlassSpecificity):
105
+ raise TypeError(f"Cannot merge_state for MulticlassSpecificity with metric of type {type(metric_obj).__name__}")
106
+ typed_metric = cast(MulticlassSpecificity, metric_obj)
107
+
108
+ other_cm_state = typed_metric.confusion_matrix
109
+ if self.confusion_matrix.device != other_cm_state.device:
110
+ other_cm_state = other_cm_state.to(self.confusion_matrix.device)
111
+ self.confusion_matrix += other_cm_state
112
+ return self
src/preprocessing.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torchvision.transforms as transforms
2
+
3
+ def preprocess(target_input_size, # Expected as (C, H, W)
4
+ rotation_range=None,
5
+ width_shift_range=None,
6
+ height_shift_range=None,
7
+ shear_range=None,
8
+ zoom_range=None,
9
+ horizontal_flip=None,
10
+ vertical_flip=None,
11
+ brightness_range=None,
12
+ channel_shift_range=None,
13
+ fill_mode='nearest'):
14
+
15
+ if not (isinstance(target_input_size, (list, tuple)) and len(target_input_size) == 3):
16
+ raise ValueError("target_input_size must be a tuple or list of (C, H, W)")
17
+
18
+ _ , H, W = target_input_size # C is not used for Resize directly
19
+ resize_dims = (H, W)
20
+
21
+ # Create transforms that match TensorFlow's ImageDataGenerator functionality
22
+ transform_list = [
23
+ transforms.Resize(resize_dims), # Added Resize
24
+ transforms.ToTensor(),
25
+ # transforms.Lambda(lambda x: x / 255.0) # User's current normalization
26
+ ]
27
+
28
+ if rotation_range is not None:
29
+ transform_list.append(transforms.RandomRotation(rotation_range))
30
+
31
+ if width_shift_range is not None or height_shift_range is not None:
32
+ transform_list.append(
33
+ transforms.RandomAffine(0, translate=(width_shift_range, height_shift_range))
34
+ )
35
+
36
+ if shear_range is not None:
37
+ transform_list.append(transforms.RandomAffine(0, shear=shear_range))
38
+
39
+ if zoom_range is not None:
40
+ transform_list.append(
41
+ transforms.RandomAffine(0, scale=(1-zoom_range, 1+zoom_range))
42
+ )
43
+
44
+ if horizontal_flip is not None:
45
+ transform_list.append(transforms.RandomHorizontalFlip())
46
+
47
+ if vertical_flip is not None:
48
+ transform_list.append(transforms.RandomVerticalFlip())
49
+
50
+ if brightness_range is not None:
51
+ transform_list.append(
52
+ transforms.ColorJitter(brightness=brightness_range[1]-brightness_range[0])
53
+ )
54
+
55
+ if channel_shift_range is not None:
56
+ # Clamp the channel_shift_range to ensure it falls within the acceptable range
57
+ hue_value = max(min(channel_shift_range/255.0, 0.5), -0.5)
58
+ transform_list.append(
59
+ transforms.ColorJitter(hue=hue_value)
60
+ )
61
+
62
+ return transforms.Compose(transform_list)
src/statistics.py ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ import seaborn as sns
4
+ import matplotlib.pyplot as plt
5
+ from sklearn.calibration import calibration_curve
6
+ import os
7
+
8
+ from sklearn.metrics import accuracy_score, confusion_matrix, f1_score, precision_score, recall_score, roc_auc_score, average_precision_score, matthews_corrcoef
9
+ from imblearn.metrics import specificity_score
10
+ from torch.nn.functional import softmax
11
+
12
+ class Statistics:
13
+ def __init__(self, df: pd.DataFrame, categories: list = None):
14
+ self.data = df
15
+ self.categories = categories
16
+ self.data['is_correct'] = self.data['true_label'] == self.data['predicted_label']
17
+ self.total_samples = len(self.data)
18
+ self.total_correct = self.data['is_correct'].sum()
19
+ self.total_incorrect = self.total_samples - self.total_correct
20
+
21
+ def plot_confidence_distribution(self, save_path: str = None):
22
+ plt.figure(figsize=(10, 6))
23
+ sns.histplot(
24
+ data=self.data,
25
+ x='confidence',
26
+ hue='is_correct',
27
+ # element='step',
28
+ # stat='density',
29
+ common_norm=False,
30
+ palette={True: 'green', False: 'red'},
31
+ alpha=0.3
32
+ )
33
+ plt.title('Confidence Distribution')
34
+ plt.xlabel('Confidence')
35
+ plt.ylabel('Frequency')
36
+ self._save_plot(save_path)
37
+ plt.show()
38
+
39
+ def plot_class_confidence_boxplot(self, by_column='true_label', save_path: str = None):
40
+ """Plots boxplots of confidence scores for each class."""
41
+ plt.figure(figsize=(14, 8))
42
+
43
+ sns.boxplot(
44
+ data=self.data,
45
+ x=by_column,
46
+ y='confidence',
47
+ hue='is_correct',
48
+ palette={True: 'green', False: 'red'},
49
+ showfliers=True,
50
+ linewidth=1.5
51
+ )
52
+
53
+ plt.title(f'Confidence Spread per Class (Grouped by {by_column})', fontsize=20)
54
+ plt.xlabel('Class Label')
55
+ plt.ylabel('Confidence Score')
56
+ plt.xticks(rotation=45, ha='right')
57
+ plt.legend(title='Prediction Status', loc='lower right', fontsize=13)
58
+ plt.tight_layout()
59
+ self._save_plot(save_path)
60
+ plt.show()
61
+
62
+ def recommend_threshold(self, save_path: str = None, target_accuracy=None, minimum_data_coverage = None, min_coverage=0.0):
63
+ """
64
+ Scans thresholds from 0.5 to 0.99 to find the optimal cutoff.
65
+ Returns a DataFrame of the trade-offs.
66
+ If target_accuracy is provided, recommends the lowest threshold that meets it.
67
+ If minimum_data_coverage is provided, recommends the highest threshold that meets it.
68
+ """
69
+ thresholds = np.linspace(0.5, 0.99, 50)
70
+ results = []
71
+
72
+ for t in thresholds:
73
+ passed = self.data[self.data['confidence'] >= t]
74
+
75
+ if len(passed) == 0:
76
+ continue
77
+
78
+ coverage = len(passed) / self.total_samples
79
+
80
+ results.append({
81
+ 'threshold': t,
82
+ 'filtered_accuracy': self._calc_accuracy(passed),
83
+ 'filtered_f1': self._calc_f1(passed),
84
+ 'filtered_precision': self._calc_precision(passed),
85
+ 'filtered_recall': self._calc_recall(passed),
86
+ 'filtered_mcc': self._calc_mcc(passed),
87
+ 'filtered_specificity': self._calc_specificity(passed),
88
+ 'coverage': coverage
89
+ })
90
+
91
+ results_df = pd.DataFrame(results)
92
+ baseline_accuracy = self.total_correct / self.total_samples
93
+ plt.figure(figsize=(10, 6))
94
+ sns.lineplot(data=results_df, x='threshold', y='filtered_accuracy', label='Accuracy (on retained)', color='blue')
95
+ sns.lineplot(data=results_df, x='threshold', y='coverage', label='Coverage (% data kept)', color='orange', linestyle='--')
96
+ plt.axhline(y=baseline_accuracy, color='red', linestyle=':', label=f'Baseline Accuracy: {baseline_accuracy:.2%}')
97
+
98
+ if target_accuracy is not None:
99
+ candidates = results_df[results_df['filtered_accuracy'] >= target_accuracy]
100
+ if not candidates.empty:
101
+ best = candidates.iloc[0]
102
+ plt.axvline(x=best['threshold'], color='green', linestyle='-.', label=f'Rec. Threshold: {best["threshold"]:.2f} \nat Coverage: {best["coverage"]:.2%}')
103
+ print(f"Recommended Threshold: {best['threshold']:.3f}")
104
+ print(f"Expected Accuracy: {best['filtered_accuracy']:.2%} | Data Retained: {best['coverage']:.2%}")
105
+ else:
106
+ print(f"Cannot achieve {target_accuracy} accuracy even at 99% threshold.")
107
+
108
+ plt.title(f'Accuracy-Coverage Analysis: Accuracy vs Data Retention\nRecommendation based on Target Accuracy ({target_accuracy:.2%})')
109
+
110
+ if minimum_data_coverage is not None:
111
+ candidates = results_df[results_df['coverage'] >= minimum_data_coverage]
112
+ if not candidates.empty:
113
+ # find the highest accuracy among candidates
114
+ best = candidates.loc[candidates['filtered_accuracy'].idxmax()]
115
+ plt.axvline(x=best['threshold'], color='purple', linestyle='-.', label=f'Rec. Threshold: {best["threshold"]:.2f} \nat Accuracy: {best["filtered_accuracy"]:.2%}')
116
+ print(f"Recommended Threshold for min coverage {minimum_data_coverage}: {best['threshold']:.3f}")
117
+ print(f"Expected Accuracy: {best['filtered_accuracy']:.2%} | Data Retained: {best['coverage']:.2%}")
118
+ else:
119
+ print(f"Cannot retain at least {minimum_data_coverage} of data even at 50% threshold.")
120
+ plt.title(f'Accuracy-Coverage Analysis: Accuracy vs Data Retention\nRecommendation based on Minimum Data Coverage ({minimum_data_coverage:.2%})')
121
+
122
+ plt.xlabel('Confidence Threshold')
123
+ plt.ylabel('Accuracy Score')
124
+ plt.legend(fontsize=13)
125
+ plt.grid(True, alpha=0.3)
126
+ self._save_plot(save_path)
127
+ plt.show()
128
+
129
+ self._best_threshold = best['threshold']
130
+ self._threshold_df = results_df
131
+
132
+ return self._threshold_df, self._best_threshold
133
+
134
+ def plot_filtered_threshold_cm(self, threshold=None, save_path=None):
135
+ # Plot confusion matrix using the best threshold
136
+ if not hasattr(self, '_best_threshold'):
137
+ raise ValueError("Best threshold not found. Please run recommend_threshold() first.")
138
+ elif threshold is None:
139
+ threshold = self._best_threshold
140
+
141
+ filtered_data = self.data[self.data['confidence'] >= threshold]
142
+ cm = confusion_matrix(filtered_data['true_label'], filtered_data['predicted_label'])
143
+ plt.figure(figsize=(8, 6))
144
+ sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
145
+ xticklabels=self.categories,
146
+ yticklabels=self.categories)
147
+ plt.title(f'Confusion Matrix at Threshold: {threshold:.2f}')
148
+ plt.xlabel('Predicted Label')
149
+ plt.ylabel('True Label')
150
+ self._save_plot(save_path)
151
+ plt.show()
152
+
153
+ def plot_filtered_class_condidence_boxplot(self, threshold=None, save_path=None):
154
+ # Plot boxplot of confidence scores for each class using the best threshold
155
+ if not hasattr(self, '_best_threshold'):
156
+ raise ValueError("Best threshold not found. Please run recommend_threshold() first.")
157
+ elif threshold is None:
158
+ threshold = self._best_threshold
159
+
160
+ filtered_data = self.data[self.data['confidence'] >= threshold]
161
+ plt.figure(figsize=(14, 8))
162
+
163
+ sns.boxplot(
164
+ data=filtered_data,
165
+ x='true_label',
166
+ y='confidence',
167
+ hue='is_correct',
168
+ palette={True: 'green', False: 'red'},
169
+ showfliers=True,
170
+ linewidth=1.5
171
+ )
172
+
173
+ plt.title(f'Confidence Spread per Class at Best Threshold: {threshold:.2f}', fontsize=20)
174
+ plt.xlabel('Class Label')
175
+ plt.ylabel('Confidence Score')
176
+ plt.xticks(rotation=45, ha='right')
177
+ plt.legend(title='Prediction Status', loc='lower right', fontsize=13)
178
+ plt.tight_layout()
179
+ self._save_plot(save_path)
180
+ plt.show()
181
+
182
+ def plot_calibration_curve(self, n_bins=10):
183
+ """
184
+ Checks if the model is overconfident.
185
+ Perfect calibration means the curve lies on the diagonal (y=x).
186
+ """
187
+ prob_true, prob_pred = calibration_curve(self.data['is_correct'], self.data['confidence'], n_bins=n_bins)
188
+
189
+ plt.figure(figsize=(8, 8))
190
+ plt.plot(prob_pred, prob_true, marker='o', label='Model Calibration')
191
+ plt.plot([0, 1], [0, 1], linestyle='--', color='gray', label='Perfectly Calibrated')
192
+
193
+ plt.title('Reliability Diagram (Calibration Curve)')
194
+ plt.xlabel('Mean Predicted Confidence')
195
+ plt.ylabel('Fraction of Positives (Actual Accuracy)')
196
+ plt.legend()
197
+ plt.grid(True)
198
+ plt.show()
199
+
200
+ @staticmethod
201
+ def _calc_accuracy(df):
202
+ return accuracy_score(df['true_label'], df['predicted_label'])
203
+
204
+ @staticmethod
205
+ def _calc_precision(df):
206
+ return precision_score(df['true_label'], df['predicted_label'], average='macro')
207
+
208
+ @staticmethod
209
+ def _calc_recall(df):
210
+ return recall_score(df['true_label'], df['predicted_label'], average='macro')
211
+
212
+ @staticmethod
213
+ def _calc_f1(df):
214
+ return f1_score(df['true_label'], df['predicted_label'], average='macro')
215
+
216
+ @staticmethod
217
+ def _calc_specificity(df):
218
+ return specificity_score(df['true_label'], df['predicted_label'], average='macro')
219
+
220
+ @staticmethod
221
+ def _calc_mcc(df):
222
+ return matthews_corrcoef(df['true_label'], df['predicted_label'])
223
+
224
+
225
+ def _save_plot(self, save_path):
226
+ if save_path:
227
+ directory = os.path.dirname(save_path)
228
+ if directory and not os.path.exists(directory):
229
+ os.makedirs(directory)
230
+ plt.savefig(save_path)
231
+ print(f"Plot saved to {save_path}")
232
+
233
+ def __len__(self):
234
+ return self.total_samples
src/test.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import torch
3
+ from tqdm import tqdm
4
+
5
+ def test(model, test_loader, device, verbose=False, print_every=10, metrics=None):
6
+ model.eval()
7
+ raw_predictions = []
8
+ target_indices = []
9
+ predicted_indices = []
10
+ loop = tqdm(test_loader, desc="Testing")
11
+ with torch.no_grad():
12
+ for batch_idx, (data, targets) in enumerate(loop):
13
+ data = data.to(device)
14
+ targets = targets.to(device)
15
+
16
+ model_outputs_raw = model(data)
17
+ raw_predictions.append(model_outputs_raw.detach().cpu())
18
+ # print(model_outputs_raw)
19
+ predicted_indices.append(torch.argmax(model_outputs_raw, dim=1).detach().cpu())
20
+ target_indices.append(torch.argmax(targets, dim=1).detach().cpu())
21
+
22
+ if (batch_idx + 1) % print_every == 0 and verbose:
23
+ print(f"Batch {batch_idx + 1}/{len(test_loader)}")
24
+
25
+ if torch.cuda.is_available():
26
+ torch.cuda.empty_cache()
27
+
28
+ # Concatenate tensors instead of converting to numpy
29
+ raw_predictions_flattened = torch.cat(raw_predictions)
30
+ predicted_indices_flattened = torch.cat(predicted_indices)
31
+ target_indices_flattened = torch.cat(target_indices)
32
+
33
+ if metrics:
34
+ test_metrics = {}
35
+ for metric_name, metric_obj in metrics.items():
36
+ if metric_name in ["auroc", "auprc"]:
37
+ # For binary classification metrics, use the positive class probabilities
38
+ # positive_class_probs = raw_predictions_flattened[:, 1] # Get probabilities for class 1
39
+ metric_obj.update(raw_predictions_flattened, target_indices_flattened)
40
+ # metric_obj.update(positive_class_probs, target_indices_flattened)
41
+ else:
42
+ metric_obj.update(predicted_indices_flattened, target_indices_flattened)
43
+ val = metric_obj.compute().clone().detach().cpu()
44
+ metric_val_to_store = val.item() if val.numel() == 1 else val.numpy()
45
+ test_metrics[metric_name] = metric_val_to_store
46
+ else:
47
+ test_metrics = None
48
+
49
+ # Convert to numpy only at the end for return values
50
+ predicted_indices_flattened = predicted_indices_flattened.numpy()
51
+ target_indices_flattened = target_indices_flattened.numpy()
52
+
53
+ return raw_predictions, predicted_indices_flattened, target_indices_flattened, test_metrics
src/utils.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import torch
3
+ import pandas as pd
4
+ import psutil
5
+ import gc
6
+ import numpy as np
7
+ import random
8
+ import yaml
9
+ from .densenet import DenseNet121
10
+
11
+ def set_seed(seed: int):
12
+ torch.manual_seed(seed)
13
+ torch.cuda.manual_seed(seed)
14
+ torch.cuda.manual_seed_all(seed)
15
+ torch.backends.cudnn.benchmark = False
16
+ torch.backends.cudnn.deterministic = True
17
+ np.random.seed(seed)
18
+ random.seed(seed)
19
+ os.environ['PYTHONHASHSEED'] = str(seed)\
20
+
21
+ def get_filenames(dataset_path):
22
+ # Get the list of file names
23
+ filenames = os.listdir(dataset_path)
24
+ filenames.sort()
25
+
26
+ return filenames
27
+
28
+ def generate_filenames_df(dataset_dir,
29
+ categories,
30
+ shuffle=False,
31
+ seed=42,
32
+ is_sample = False,
33
+ sample_size = 50):
34
+ full_filenames = {"image_path": [], "class": []}
35
+
36
+ for category in categories:
37
+ full_filenames_temp = get_filenames(os.path.join(dataset_dir, category))
38
+
39
+ for i in range(len(full_filenames_temp)):
40
+ full_filenames_temp[i] = os.path.join(dataset_dir, category, full_filenames_temp[i])
41
+
42
+ full_filenames["image_path"].extend(full_filenames_temp)
43
+ full_filenames["class"].extend([category] * len(full_filenames_temp))
44
+
45
+ full_filenames_df = pd.DataFrame.from_dict(full_filenames)
46
+ if shuffle:
47
+ full_filenames_df = full_filenames_df.sample(frac=1, random_state=seed).reset_index(drop=True)
48
+
49
+ if is_sample:
50
+ full_filenames_df = full_filenames_df.sample(n=sample_size, random_state=seed).reset_index(drop=True)
51
+
52
+ return full_filenames_df
53
+
54
+ def get_device():
55
+ if torch.cuda.is_available():
56
+ return torch.device("cuda")
57
+ else:
58
+ return torch.device("cpu")
59
+
60
+ def get_memory_usage():
61
+ """Get current memory usage"""
62
+ process = psutil.Process(os.getpid())
63
+ cpu_memory = process.memory_info().rss / 1024 / 1024 # in MB
64
+ if torch.cuda.is_available():
65
+ gpu_memory = torch.cuda.memory_allocated() / 1024 / 1024 # in MB
66
+ gpu_memory_reserved = torch.cuda.memory_reserved() / 1024 / 1024 # in MB
67
+ return {
68
+ 'cpu_memory': f"{cpu_memory:.2f}MB",
69
+ 'gpu_memory_allocated': f"{gpu_memory:.2f}MB",
70
+ 'gpu_memory_reserved': f"{gpu_memory_reserved:.2f}MB"
71
+ }
72
+ return {'cpu_memory': f"{cpu_memory:.2f}MB"}
73
+
74
+ def clear_memory():
75
+ """Clear unused memory"""
76
+ gc.collect()
77
+ if torch.cuda.is_available():
78
+ torch.cuda.empty_cache()
79
+
80
+
81
+ def is_best_model(current_metric, best_metric, mode="max", monitor_metric="loss"):
82
+ if mode == "max":
83
+ return current_metric > best_metric
84
+ else:
85
+ return current_metric < best_metric
86
+
87
+ def is_scheduler_per_batch(scheduler):
88
+ if scheduler is None:
89
+ return False
90
+ if (isinstance(scheduler, torch.optim.lr_scheduler.CyclicLR)
91
+ or isinstance(scheduler, torch.optim.lr_scheduler.CosineAnnealingWarmRestarts)
92
+ or isinstance(scheduler, torch.optim.lr_scheduler.OneCycleLR)):
93
+ return True
94
+ else:
95
+ return False
96
+
97
+ def load_model(model_path, num_classes=None, dropout_rate=0.25):
98
+ """
99
+ Load a DenseNet121 model from a saved state dict or create a new one
100
+
101
+ Args:
102
+ model_path (str): Path to the saved model state dict
103
+ num_classes (int, optional): Number of output classes. Required if creating new model
104
+ dropout_rate (float, optional): Dropout rate for the model. Defaults to 0.25
105
+
106
+ Returns:
107
+ DenseNet121: Loaded or newly created model
108
+ """
109
+ try:
110
+ # Try to load the model state dict
111
+ state_dict = torch.load(model_path)
112
+ model = DenseNet121(num_classes=num_classes, dropout_rate=dropout_rate)
113
+ model.load_state_dict(state_dict)
114
+ return model
115
+ except:
116
+ # If loading fails, create a new model
117
+ if num_classes is None:
118
+ raise ValueError("num_classes must be specified when creating a new model")
119
+ return DenseNet121(num_classes=num_classes, dropout_rate=dropout_rate)
src/xdl.py ADDED
@@ -0,0 +1,689 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import List, Tuple, Optional, Any, Union
3
+
4
+ import torch
5
+ import torch.nn as nn
6
+ from torch.utils.data import DataLoader
7
+ from pytorch_grad_cam import GradCAM
8
+ from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget
9
+ from pytorch_grad_cam.utils.image import show_cam_on_image
10
+
11
+ import cv2
12
+ import numpy as np
13
+ import matplotlib.pyplot as plt
14
+ import matplotlib.colors as colors
15
+
16
+ from src.densenet_multilayer import DenseNet121Multilayer
17
+ from src.densenet import DenseNet121
18
+ from src.resnet import ResNet50
19
+
20
+ def _preprocess_image(data_tensor: torch.Tensor) -> Tuple[np.ndarray, np.ndarray]:
21
+ """
22
+ Convert tensor image to numpy and normalize for visualization.
23
+
24
+ Args:
25
+ data_tensor: input image tensor of shape (C, H, W)
26
+
27
+ Returns:
28
+ Tuple of (normalized_float_image, uint8_image)
29
+ """
30
+ img = data_tensor.cpu().numpy()
31
+ img = np.transpose(img, (1, 2, 0))
32
+ img = (img - img.min()) / (img.max() - img.min())
33
+ img_uint8 = (img * 255).astype(np.uint8)
34
+ return img, img_uint8
35
+
36
+ def _process_smoothgrad_map(
37
+ smoothgrad_map: np.ndarray,
38
+ img_shape: Tuple[int, int],
39
+ percentile: float,
40
+ colormap: str,
41
+ ) -> Tuple[np.ndarray, np.ndarray]:
42
+ """
43
+ Process raw SmoothGrad map into visualization ready formats.
44
+
45
+ Args:
46
+ smoothgrad_map: Raw SmoothGrad attribution map
47
+ img_shape: Target shape (H, W)
48
+ percentile: Percentile for clipping high values
49
+ colormap: Colormap name ('hot' or 'viridis')
50
+
51
+ Returns:
52
+ Tuple of (normalized_map, heatmap_uint8)
53
+ """
54
+ smoothgrad_map = np.transpose(smoothgrad_map, (1, 2, 0))
55
+ smoothgrad_map = np.mean(smoothgrad_map, axis=2) # Convert to grayscale
56
+ smoothgrad_map = cv2.resize(smoothgrad_map, (img_shape[1], img_shape[0]))
57
+
58
+ # Increase contrast by clipping at specified percentile and re-normalizing
59
+ p_clip = np.percentile(smoothgrad_map, percentile)
60
+ if p_clip > 0:
61
+ smoothgrad_map = np.clip(smoothgrad_map, 0, p_clip)
62
+ smoothgrad_map = (smoothgrad_map - smoothgrad_map.min()) / (p_clip - smoothgrad_map.min() + 1e-8)
63
+
64
+ # Improve visualization
65
+ smoothgrad_heatmap = (smoothgrad_map * 255).astype(np.uint8)
66
+ heatmap = cv2.applyColorMap(
67
+ smoothgrad_heatmap,
68
+ cv2.COLORMAP_HOT if colormap == 'hot' else cv2.COLORMAP_VIRIDIS
69
+ )
70
+
71
+ return smoothgrad_map, heatmap
72
+
73
+ def _calculate_abnormality_score(heatmap: np.ndarray, threshold: float = 0.5) -> dict:
74
+ """
75
+ Calculate abnormality score based on heatmap coverage and intensity.
76
+
77
+ Args:
78
+ heatmap: Normalized GradCAM heatmap (0-1).
79
+ threshold: Threshold for considering a pixel as active.
80
+
81
+ Returns:
82
+ Dictionary containing coverage, avg_intensity, and calculate score.
83
+ """
84
+ active_pixels = heatmap > threshold
85
+ coverage = np.mean(active_pixels)
86
+
87
+ if coverage > 0:
88
+ avg_intensity = np.mean(heatmap[active_pixels])
89
+ else:
90
+ avg_intensity = 0.0
91
+
92
+ # Score definition: weighted combination or simple product
93
+ # Here we define it as Coverage * Average Intensity of active region
94
+ score = coverage * avg_intensity
95
+
96
+ return {
97
+ "coverage": coverage,
98
+ "avg_intensity": avg_intensity,
99
+ "score": score
100
+ }
101
+
102
+ def _get_target_layer(model: nn.Module) -> Any:
103
+ """Identify the target layer for GradCAM based on model architecture."""
104
+ if isinstance(model, DenseNet121):
105
+ return model.densenet_model.features[-1]
106
+ elif isinstance(model, ResNet50):
107
+ return model.resnet_model.layer4[-1]
108
+ elif isinstance(model, DenseNet121Multilayer):
109
+ return model.model_layer_2.densenet_model.features[-1]
110
+ # Fallback or error handling could go here
111
+ return None
112
+
113
+ def smoothgrad(
114
+ model: nn.Module,
115
+ input_tensor: torch.Tensor,
116
+ target_class: int,
117
+ n_samples: int = 100,
118
+ noise_level: float = 0.05
119
+ ) -> np.ndarray:
120
+ """
121
+ Compute SmoothGrad saliency map.
122
+
123
+ Args:
124
+ model: PyTorch model
125
+ input_tensor: Input tensor of shape (1, C, H, W)
126
+ target_class: Target class index
127
+ n_samples: Number of noisy samples to average (default: 100)
128
+ noise_level: Standard deviation of noise to add as fraction of range (default: 0.05)
129
+
130
+ Returns:
131
+ SmoothGrad saliency map as numpy array
132
+ """
133
+ model.eval()
134
+ accumulated_gradients = torch.zeros_like(input_tensor)
135
+ input_range = torch.max(input_tensor) - torch.min(input_tensor)
136
+ scaled_noise_level = noise_level * input_range
137
+
138
+ for _ in range(n_samples):
139
+ # Clone and detach input for each sample
140
+ noise = torch.randn_like(input_tensor) * scaled_noise_level
141
+ noisy_input = (input_tensor + noise).clone().detach().requires_grad_(True)
142
+
143
+ output = model(noisy_input)
144
+ if len(output.shape) == 1:
145
+ output = output.unsqueeze(0)
146
+
147
+ model.zero_grad()
148
+ loss = output[0, target_class]
149
+ loss.backward()
150
+
151
+ if noisy_input.grad is not None:
152
+ accumulated_gradients += noisy_input.grad.data
153
+
154
+ smoothgrad_map = accumulated_gradients / n_samples
155
+ smoothgrad_map = torch.abs(smoothgrad_map)
156
+
157
+ # Normalize map
158
+ min_val, max_val = smoothgrad_map.min(), smoothgrad_map.max()
159
+ smoothgrad_map = (smoothgrad_map - min_val) / (max_val - min_val + 1e-8)
160
+
161
+ return smoothgrad_map[0].cpu().numpy()
162
+
163
+ def _get_model_predictions(
164
+ model: nn.Module,
165
+ data: torch.Tensor
166
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
167
+ """
168
+ Get raw outputs, predicted indices and probabilities.
169
+
170
+ Returns:
171
+ Tuple of (model_outputs_raw, predicted_indices, probabilities)
172
+ """
173
+ with torch.no_grad():
174
+ model_outputs_raw = model(data)
175
+ predicted_indices = torch.argmax(model_outputs_raw, dim=1)
176
+ probabilities = torch.nn.functional.softmax(model_outputs_raw, dim=1)
177
+ return model_outputs_raw, predicted_indices, probabilities
178
+
179
+ def plot_XDL_Visualizations(
180
+ model: nn.Module,
181
+ test_loader: DataLoader,
182
+ device: torch.device,
183
+ num_samples: int = 5,
184
+ print_img: bool = False,
185
+ print_every: int = 10,
186
+ save_path: Optional[str] = None,
187
+ smoothgrad_percentile: float = 95,
188
+ smoothgrad_colormap: str = 'hot',
189
+ smoothgrad_overlay_alpha: float = 0.5
190
+ ) -> None:
191
+ """
192
+ Plot visualizations with GradCAM and SmoothGrad.
193
+
194
+ Args:
195
+ model: PyTorch model
196
+ test_loader: DataLoader for test data
197
+ device: Device to run model on
198
+ num_samples: Number of samples to visualize
199
+ print_img: Whether to display images
200
+ print_every: Print progress every N batches
201
+ save_path: Path to save visualizations
202
+ smoothgrad_percentile: Percentile for clipping SmoothGrad values (default: 95)
203
+ smoothgrad_colormap: Colormap for SmoothGrad visualization (default: 'hot')
204
+ smoothgrad_overlay_alpha: Alpha value for SmoothGrad overlay (default: 0.5)
205
+ """
206
+ model.eval()
207
+ print(f"Using model: {model.__class__.__name__}")
208
+
209
+ target_layer = _get_target_layer(model)
210
+ if target_layer is None:
211
+ print("Warning: Could not determine target layer for GradCAM.")
212
+
213
+ categories = test_loader.dataset.categories
214
+
215
+ # Initialize GradCAM
216
+ cam = GradCAM(
217
+ model=model,
218
+ target_layers=[target_layer] if target_layer else []
219
+ )
220
+
221
+ samples_processed = 0
222
+ samples_processed_per_class = {i: 0 for i in range(len(categories))}
223
+ max_samples_per_class = num_samples // len(categories) if num_samples > len(categories) else num_samples
224
+
225
+ for batch_idx, (data, targets) in enumerate(test_loader):
226
+ if samples_processed >= num_samples:
227
+ break
228
+
229
+ data = data.to(device)
230
+ targets = targets.to(device)
231
+
232
+ model_outputs_raw, predicted_indices, _ = _get_model_predictions(model, data)
233
+ target_indices = torch.argmax(targets, dim=1)
234
+
235
+ if batch_idx % print_every == 0:
236
+ print(f'Processing batch {batch_idx + 1}, samples processed: {samples_processed}')
237
+
238
+ for i in range(min(len(data), num_samples - samples_processed)):
239
+
240
+ if samples_processed >= num_samples:
241
+ break
242
+
243
+ img, img_uint8 = _preprocess_image(data[i])
244
+ pred_idx = predicted_indices[i].item()
245
+ true_idx = target_indices[i].item()
246
+
247
+ print(f'Processing sample {samples_processed + 1}, raw prediction: {model_outputs_raw[i].cpu().numpy()}, predicted index: {pred_idx}, true index: {true_idx}')
248
+
249
+ # Get GradCAM visualization
250
+ grayscale_cam = cam(
251
+ input_tensor=data[i:i+1],
252
+ targets=[ClassifierOutputTarget(pred_idx)]
253
+ )[0, :]
254
+ cam_image = show_cam_on_image(img, grayscale_cam, use_rgb=True)
255
+
256
+ # Get SmoothGrad visualization
257
+ smoothgrad_map_raw = smoothgrad(model, data[i:i+1], pred_idx)
258
+ smoothgrad_map, heatmap = _process_smoothgrad_map(
259
+ smoothgrad_map_raw,
260
+ img_shape=(img.shape[0], img.shape[1]),
261
+ percentile=smoothgrad_percentile,
262
+ colormap=smoothgrad_colormap
263
+ )
264
+
265
+ # Adjust overlay weights for better visibility
266
+ overlay = cv2.addWeighted(img_uint8, 1 - smoothgrad_overlay_alpha, heatmap, smoothgrad_overlay_alpha, 0)
267
+
268
+ # Create visualization
269
+ fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(18, 10))
270
+
271
+ # Firs row, First column: original image with label information
272
+ axes[0, 0].imshow(img)
273
+ axes[0, 0].set_title(f'True: {categories[true_idx]}, Pred: {categories[pred_idx]} \nConfidence: {torch.nn.functional.softmax(model_outputs_raw[i], dim=0)[pred_idx]:.4f}')
274
+ axes[0, 0].axis('off')
275
+
276
+ # First row, Second column: GradCAM visualization
277
+ axes[0, 1].imshow(cam_image)
278
+ axes[0, 1].set_title('GradCAM Visualization')
279
+ axes[0, 1].axis('off')
280
+
281
+ # Add colorbar for GradCAM
282
+ norm_cam = colors.Normalize(vmin=grayscale_cam.min(), vmax=grayscale_cam.max())
283
+ sm_cam = plt.cm.ScalarMappable(cmap='jet', norm=norm_cam)
284
+ sm_cam.set_array([])
285
+ fig.colorbar(sm_cam, ax=axes[0, 1], shrink=0.8)
286
+
287
+ # First row, Third column: SmoothGrad visualization
288
+ axes[0, 2].imshow(overlay)
289
+ axes[0, 2].set_title('SmoothGrad Visualization')
290
+ axes[0, 2].axis('off')
291
+
292
+ # Add colorbar for SmoothGrad
293
+ norm_smooth = colors.Normalize(vmin=smoothgrad_map.min(), vmax=smoothgrad_map.max())
294
+ sm_smooth = plt.cm.ScalarMappable(cmap='viridis', norm=norm_smooth)
295
+ sm_smooth.set_array([])
296
+ fig.colorbar(sm_smooth, ax=axes[0, 2], shrink=0.8)
297
+
298
+ # Second row, First column: Filtered Image (SmoothGrad)
299
+ threshold = 0.5
300
+
301
+ # Add channel dimension if needed for masking
302
+ smoothgrad_map_copy = smoothgrad_map.copy()
303
+ if len(smoothgrad_map_copy.shape) == 2:
304
+ smoothgrad_map_copy = np.expand_dims(smoothgrad_map_copy, axis=-1)
305
+ smoothgrad_map_copy = np.repeat(smoothgrad_map_copy, repeats=3, axis=-1)
306
+
307
+ filtered_img = np.where(smoothgrad_map_copy > threshold, img, 1)
308
+ axes[1, 0].imshow(filtered_img)
309
+ axes[1, 0].set_title(f'Filtered Image (SmoothGrad > {threshold})')
310
+ axes[1, 0].axis('off')
311
+
312
+ # Second row, Imshow SmoothGrad heatmap
313
+ axes[1, 2].imshow(smoothgrad_map, cmap='viridis')
314
+ axes[1, 2].set_title('SmoothGrad Heatmap')
315
+ axes[1, 2].axis('off')
316
+
317
+ # Add colorbar for SmoothGrad heatmap
318
+ sm_smooth_heatmap = plt.cm.ScalarMappable(cmap='viridis', norm=colors.Normalize(vmin=smoothgrad_map.min(), vmax=smoothgrad_map.max()))
319
+ sm_smooth_heatmap.set_array([])
320
+ fig.colorbar(sm_smooth_heatmap, ax=axes[1, 2], shrink=0.8)
321
+
322
+ # Second row, Second column: Filtered Image (GradCAM)
323
+ cam_threshold = 0.5
324
+
325
+ # Add channel dimension if needed
326
+ cam_image_copy = grayscale_cam.copy()
327
+ if len(cam_image_copy.shape) == 2:
328
+ cam_image_copy = np.expand_dims(cam_image_copy, axis=-1)
329
+ cam_image_copy = np.repeat(cam_image_copy, repeats=3, axis=-1)
330
+ filtered_cam_img = np.where(cam_image_copy > cam_threshold, img, 1)
331
+ axes[1, 1].imshow(filtered_cam_img)
332
+ axes[1, 1].set_title(f'Filtered Image (GradCAM > {cam_threshold})')
333
+ axes[1, 1].axis('off')
334
+
335
+ # Add invisible colorbar for alignment
336
+ norm_cam_invis = colors.Normalize(vmin=0, vmax=0)
337
+ sm_cam_invis = plt.cm.ScalarMappable(cmap='Greys', norm=norm_cam_invis)
338
+ sm_cam_invis.set_array([])
339
+ fig.colorbar(sm_cam_invis, ax=axes[1, 1], shrink=0.8)
340
+
341
+ plt.tight_layout()
342
+
343
+ if save_path:
344
+ true_label = categories[true_idx]
345
+ pred_label = categories[pred_idx]
346
+ os.makedirs(save_path, exist_ok=True)
347
+
348
+ if pred_label == true_label:
349
+ plt.savefig(os.path.join(save_path, f'{true_label}_{samples_processed}.png'))
350
+ else:
351
+ plt.savefig(os.path.join(save_path, f'wrong_{true_label}_{samples_processed}.png'))
352
+
353
+ if print_img:
354
+ plt.show()
355
+
356
+ plt.close(fig)
357
+ samples_processed += 1
358
+
359
+ if batch_idx % print_every == 0:
360
+ print(f'Processed {samples_processed} samples')
361
+
362
+ def plot_XDL_GradCAM(
363
+ model: nn.Module,
364
+ test_loader: DataLoader,
365
+ device: torch.device,
366
+ fontsize: int = 13,
367
+ num_samples: int = 5,
368
+ print_img: bool = False,
369
+ print_every: int = 10,
370
+ save_path: Optional[str] = None,
371
+ show_abnormality_score: bool = True
372
+ ) -> None:
373
+ """
374
+ Plot simple GradCAM visualizations.
375
+
376
+ Args:
377
+ model: PyTorch model
378
+ test_loader: DataLoader for test data
379
+ device: Device to run model on
380
+ fontsize: Font size for titles
381
+ num_samples: Number of samples to visualize
382
+ print_img: Whether to display images
383
+ print_every: Print progress every N batches
384
+ save_path: Path to save visualizations
385
+ show_abnormality_score: Whether to calculate and show abnormality score (default: True)
386
+ """
387
+ model.eval()
388
+
389
+ target_layer = _get_target_layer(model)
390
+ if target_layer is None:
391
+ print("Warning: Could not determine target layer for GradCAM.")
392
+
393
+ categories = test_loader.dataset.categories
394
+
395
+ cam = GradCAM(
396
+ model=model,
397
+ target_layers=[target_layer] if target_layer else []
398
+ )
399
+
400
+ samples_processed = 0
401
+ for batch_idx, (data, targets) in enumerate(test_loader):
402
+ if samples_processed >= num_samples:
403
+ break
404
+
405
+ data = data.to(device)
406
+ targets = targets.to(device)
407
+
408
+ _, predicted_indices, raw_probabilities = _get_model_predictions(model, data)
409
+ raw_predictions = raw_probabilities.cpu().numpy()
410
+ target_indices = torch.argmax(targets, dim=1)
411
+
412
+ for i in range(min(len(data), num_samples - samples_processed)):
413
+ if save_path:
414
+ os.makedirs(save_path, exist_ok=True)
415
+ img_path = os.path.join(save_path, f'{samples_processed}.png')
416
+ if os.path.exists(img_path):
417
+ samples_processed += 1
418
+ continue
419
+
420
+ img, _ = _preprocess_image(data[i])
421
+ pred_idx = predicted_indices[i].item()
422
+ true_idx = target_indices[i].item()
423
+
424
+ grayscale_cam = cam(
425
+ input_tensor=data[i:i+1],
426
+ targets=[ClassifierOutputTarget(pred_idx)]
427
+ )[0, :]
428
+
429
+ # Calculate Abnormality Score if requested
430
+ if show_abnormality_score:
431
+ abnormality_stats = _calculate_abnormality_score(grayscale_cam, threshold=0.5)
432
+ score = abnormality_stats["score"]
433
+ coverage = abnormality_stats["coverage"]
434
+ intensity = abnormality_stats["avg_intensity"]
435
+
436
+ print(f"Sample {samples_processed + 1} - Score: {score:.4f}, Coverage: {coverage:.4f}, Intensity: {intensity:.4f}")
437
+ title = f'GradCAM:\nAbnormality Score: {score:.3f}' # (Cov: {coverage:.2f}, Int: {intensity:.2f})'
438
+ else:
439
+ title = 'GradCAM Visualization'
440
+
441
+ cam_image = show_cam_on_image(img, grayscale_cam, use_rgb=True)
442
+
443
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
444
+ ax1.imshow(img)
445
+ ax1.set_title(f'True: {categories[true_idx]} \nPred: {categories[pred_idx]} (Confidence: {raw_predictions[i][pred_idx]:.2f})', fontsize=fontsize)
446
+ ax1.axis('off')
447
+
448
+ ax2.imshow(cam_image)
449
+ ax2.set_title(title, fontsize=fontsize)
450
+ ax2.axis('off')
451
+
452
+ # Add colorbar for GradCAM
453
+ norm_cam = colors.Normalize(vmin=grayscale_cam.min(), vmax=grayscale_cam.max())
454
+ sm_cam = plt.cm.ScalarMappable(cmap='jet', norm=norm_cam)
455
+ sm_cam.set_array([])
456
+ fig.colorbar(sm_cam, ax=ax2, shrink=0.8)
457
+
458
+ plt.tight_layout()
459
+ if save_path:
460
+ plt.savefig(img_path)
461
+
462
+ if print_img:
463
+ plt.show()
464
+
465
+ plt.close(fig)
466
+ samples_processed += 1
467
+
468
+ if batch_idx % print_every == 0:
469
+ print(f'Processed {samples_processed} samples')
470
+
471
+ def plot_XDL_SmoothGrad(
472
+ model: nn.Module,
473
+ test_loader: DataLoader,
474
+ device: torch.device,
475
+ num_samples: int = 5,
476
+ print_img: bool = True,
477
+ print_every: int = 10,
478
+ save_path: Optional[str] = None,
479
+ n_samples_smoothgrad: int = 100,
480
+ noise_level: float = 0.05,
481
+ smoothgrad_percentile: float = 99, # Slightly higher default for sharper maps
482
+ smoothgrad_colormap: str = 'hot',
483
+ smoothgrad_overlay_alpha: float = 0.5,
484
+ fontsize: int = 13,
485
+ show_abnormality_score: bool = True # NEW PARAMETER
486
+ ) -> None:
487
+ """
488
+ Plot SmoothGrad visualizations with Abnormality Scores.
489
+ """
490
+ model.eval()
491
+ categories = test_loader.dataset.categories
492
+ samples_processed = 0
493
+
494
+ # Define threshold for SmoothGrad (SmoothGrad is sharper/sparser than GradCAM,
495
+ # so sometimes a lower threshold captures extent better, but 0.5 is standard)
496
+ score_threshold = 0.3
497
+
498
+ for batch_idx, (data, targets) in enumerate(test_loader):
499
+ if samples_processed >= num_samples:
500
+ break
501
+
502
+ data = data.to(device)
503
+ targets = targets.to(device)
504
+
505
+ _, predicted_indices, raw_probabilities = _get_model_predictions(model, data)
506
+ raw_predictions = raw_probabilities.cpu().numpy()
507
+ target_indices = torch.argmax(targets, dim=1)
508
+
509
+ for i in range(min(len(data), num_samples - samples_processed)):
510
+ img, img_uint8 = _preprocess_image(data[i])
511
+ pred_idx = predicted_indices[i].item()
512
+ true_idx = target_indices[i].item()
513
+
514
+ # 1. Get SmoothGrad Raw Map
515
+ smoothgrad_map_raw = smoothgrad(model, data[i:i+1], pred_idx, n_samples=n_samples_smoothgrad, noise_level=noise_level)
516
+
517
+ # 2. Process to Grayscale (0-1) and Heatmap (Color)
518
+ # smoothgrad_map here is the Normalized Grayscale Map we need for scoring
519
+ smoothgrad_map, heatmap = _process_smoothgrad_map(
520
+ smoothgrad_map_raw,
521
+ img_shape=(img.shape[0], img.shape[1]),
522
+ percentile=smoothgrad_percentile,
523
+ colormap=smoothgrad_colormap
524
+ )
525
+
526
+ # 3. Calculate Abnormality Score (NEW STEP)
527
+ title_suffix = ""
528
+ if show_abnormality_score:
529
+ stats = _calculate_abnormality_score(smoothgrad_map, threshold=score_threshold)
530
+ score = stats["score"]
531
+ coverage = stats["coverage"]
532
+ intensity = stats["avg_intensity"]
533
+
534
+ print(f"[SmoothGrad] Sample {samples_processed + 1} - Abnormality Score: {score:.4f}") # (Cov: {coverage:.3f}, Int: {intensity:.3f})
535
+ title_suffix = f"\nAbnormality Score: {score:.3f}"
536
+
537
+ # Adjust overlay weights
538
+ overlay = cv2.addWeighted(img_uint8, 1 - smoothgrad_overlay_alpha, heatmap, smoothgrad_overlay_alpha, 0)
539
+
540
+ # Create visualization
541
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
542
+
543
+ # Plot Original
544
+ ax1.imshow(img)
545
+ ax1.set_title(f'True: {categories[true_idx]} \nPred: {categories[pred_idx]} (Conf: {raw_predictions[i][pred_idx]:.2f})', fontsize=fontsize)
546
+ ax1.axis('off')
547
+
548
+ # Plot SmoothGrad
549
+ ax2.imshow(overlay)
550
+ ax2.set_title(f'SmoothGrad Visualization{title_suffix}', fontsize=fontsize)
551
+ ax2.axis('off')
552
+
553
+ # Add colorbar
554
+ norm_smooth = colors.Normalize(vmin=smoothgrad_map.min(), vmax=smoothgrad_map.max())
555
+ sm_smooth = plt.cm.ScalarMappable(cmap='viridis', norm=norm_smooth)
556
+ sm_smooth.set_array([])
557
+ fig.colorbar(sm_smooth, ax=ax2, shrink=0.8)
558
+
559
+ plt.tight_layout()
560
+
561
+ if save_path:
562
+ os.makedirs(save_path, exist_ok=True)
563
+ plt.savefig(os.path.join(save_path, f'smoothgrad_{samples_processed}.png'))
564
+
565
+ if print_img:
566
+ plt.show()
567
+
568
+ plt.close(fig)
569
+
570
+ samples_processed += 1
571
+
572
+ if batch_idx % print_every == 0:
573
+ print(f'Processed {samples_processed} samples')
574
+
575
+ def plot_comparative_scoring(
576
+ model: nn.Module,
577
+ test_loader: DataLoader,
578
+ device: torch.device,
579
+ num_samples: int = 5,
580
+ save_path: Optional[str] = None,
581
+ print_img: bool = True,
582
+ smoothgrad_percentile: float = 98
583
+ ) -> None:
584
+ """
585
+ Generates a side-by-side comparison of Original, GradCAM, and SmoothGrad
586
+ specifically highlighting their calculated Abnormality Scores.
587
+ """
588
+ model.eval()
589
+ print(f"Generating comparative scores for {num_samples} samples...")
590
+
591
+ # 1. Setup GradCAM
592
+ target_layer = _get_target_layer(model)
593
+ cam = GradCAM(model=model, target_layers=[target_layer] if target_layer else [])
594
+
595
+ categories = test_loader.dataset.categories
596
+ samples_processed = 0
597
+
598
+ # 2. Loop through data
599
+ for batch_idx, (data, targets) in enumerate(test_loader):
600
+ if samples_processed >= num_samples:
601
+ break
602
+
603
+ data = data.to(device)
604
+ targets = targets.to(device)
605
+
606
+ # Get predictions
607
+ model_outputs_raw, predicted_indices, raw_probs = _get_model_predictions(model, data)
608
+ target_indices = torch.argmax(targets, dim=1)
609
+
610
+ for i in range(min(len(data), num_samples - samples_processed)):
611
+
612
+ # --- A. Preparation ---
613
+ img, img_uint8 = _preprocess_image(data[i])
614
+ pred_idx = predicted_indices[i].item()
615
+ true_idx = target_indices[i].item()
616
+ conf = raw_probs[i][pred_idx].item()
617
+
618
+ # --- B. Compute GradCAM & Score ---
619
+ # Generate Map
620
+ grayscale_cam = cam(input_tensor=data[i:i+1], targets=[ClassifierOutputTarget(pred_idx)])[0, :]
621
+
622
+ # Calculate Score (Threshold 0.5 is standard for GradCAM)
623
+ gcam_stats = _calculate_abnormality_score(grayscale_cam, threshold=0.5)
624
+ gcam_score = gcam_stats['score']
625
+
626
+ # Create Overlay
627
+ cam_overlay = show_cam_on_image(img, grayscale_cam, use_rgb=True)
628
+
629
+ # --- C. Compute SmoothGrad & Score ---
630
+ # Generate Map
631
+ smoothgrad_map_raw = smoothgrad(model, data[i:i+1], pred_idx)
632
+ smoothgrad_map, heatmap = _process_smoothgrad_map(
633
+ smoothgrad_map_raw,
634
+ img_shape=(img.shape[0], img.shape[1]),
635
+ percentile=smoothgrad_percentile, # High percentile for sharp contrast
636
+ colormap='hot'
637
+ )
638
+
639
+ # Calculate Score (Threshold 0.3 is often better for SmoothGrad as it is sparser)
640
+ sgrad_stats = _calculate_abnormality_score(smoothgrad_map, threshold=0.3)
641
+ sgrad_score = sgrad_stats['score']
642
+
643
+ # Create Overlay
644
+ smooth_overlay = cv2.addWeighted(img_uint8, 0.6, heatmap, 0.4, 0)
645
+
646
+ # --- D. Plotting ---
647
+ fig, axes = plt.subplots(1, 3, figsize=(18, 7))
648
+
649
+ # Panel 1: Original Image
650
+ axes[0].imshow(img)
651
+ axes[0].set_title(f"True: {categories[true_idx]}\nPred: {categories[pred_idx]} ({conf:.2f})", fontsize=14)
652
+ axes[0].axis('off')
653
+
654
+ # Panel 2: GradCAM
655
+ axes[1].imshow(cam_overlay)
656
+ axes[1].set_title(f"GradCAM\nAbnormality Score: {gcam_score:.4f}", fontsize=14, color='darkblue')
657
+ axes[1].axis('off')
658
+ # Colorbar 1
659
+ norm_g = colors.Normalize(vmin=0, vmax=1)
660
+ sm_g = plt.cm.ScalarMappable(cmap='jet', norm=norm_g)
661
+ sm_g.set_array([])
662
+ plt.colorbar(sm_g, ax=axes[1], fraction=0.046, pad=0.04)
663
+
664
+ # Panel 3: SmoothGrad
665
+ axes[2].imshow(smooth_overlay)
666
+ axes[2].set_title(f"SmoothGrad\nAbnormality Score: {sgrad_score:.4f}", fontsize=14, color='darkred')
667
+ axes[2].axis('off')
668
+ # Colorbar 2
669
+ norm_s = colors.Normalize(vmin=0, vmax=1)
670
+ sm_s = plt.cm.ScalarMappable(cmap='hot', norm=norm_s)
671
+ sm_s.set_array([])
672
+ plt.colorbar(sm_s, ax=axes[2], fraction=0.046, pad=0.04)
673
+
674
+ plt.tight_layout()
675
+
676
+ # Save/Print
677
+ if save_path:
678
+ os.makedirs(save_path, exist_ok=True)
679
+ fname = f"Score_Comparison_{samples_processed}_{categories[true_idx]}.png"
680
+ plt.savefig(os.path.join(save_path, fname))
681
+
682
+ if print_img:
683
+ plt.show()
684
+
685
+ plt.close(fig)
686
+ samples_processed += 1
687
+
688
+ # Print console summary
689
+ print(f"Sample {samples_processed}: GradCAM Score={gcam_score:.4f} | SmoothGrad Score={sgrad_score:.4f}")