Spaces:
Sleeping
Sleeping
AutoDeploy
commited on
Commit
·
8f59aab
0
Parent(s):
Fix: Python 3.8 compatibility (use Tuple from typing) + Gradio 4.48.1 security update
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +35 -0
- .gitignore +150 -0
- FILE_INDEX.md +364 -0
- IMPLEMENTATION_SUMMARY.md +300 -0
- LICENSE +21 -0
- QUICK_START.py +208 -0
- README.md +378 -0
- TRAINING_GUIDE.md +306 -0
- app.py +202 -0
- demo.ipynb +430 -0
- diagnose.py +185 -0
- download_dataset.py +166 -0
- prepare_dataset.py +173 -0
- requirements.txt +29 -0
- samples/case101_day26_slice_0096_266_266_1.50_1.50.png +0 -0
- samples/case107_day0_slice_0089_266_266_1.50_1.50.png +0 -0
- samples/case107_day21_slice_0069_266_266_1.50_1.50.png +0 -0
- samples/case113_day12_slice_0108_360_310_1.50_1.50.png +0 -0
- samples/case119_day20_slice_0063_266_266_1.50_1.50.png +0 -0
- samples/case119_day25_slice_0075_266_266_1.50_1.50.png +0 -0
- samples/case119_day25_slice_0095_266_266_1.50_1.50.png +0 -0
- samples/case121_day14_slice_0057_266_266_1.50_1.50.png +0 -0
- samples/case122_day25_slice_0087_266_266_1.50_1.50.png +0 -0
- samples/case124_day19_slice_0110_266_266_1.50_1.50.png +0 -0
- samples/case124_day20_slice_0110_266_266_1.50_1.50.png +0 -0
- samples/case130_day0_slice_0106_266_266_1.50_1.50.png +0 -0
- samples/case134_day21_slice_0085_360_310_1.50_1.50.png +0 -0
- samples/case139_day0_slice_0062_234_234_1.50_1.50.png +0 -0
- samples/case139_day18_slice_0094_266_266_1.50_1.50.png +0 -0
- samples/case146_day25_slice_0053_276_276_1.63_1.63.png +0 -0
- samples/case147_day0_slice_0085_360_310_1.50_1.50.png +0 -0
- samples/case148_day0_slice_0113_360_310_1.50_1.50.png +0 -0
- samples/case149_day15_slice_0057_266_266_1.50_1.50.png +0 -0
- samples/case29_day0_slice_0065_266_266_1.50_1.50.png +0 -0
- samples/case2_day1_slice_0054_266_266_1.50_1.50.png +0 -0
- samples/case2_day1_slice_0077_266_266_1.50_1.50.png +0 -0
- samples/case32_day19_slice_0091_266_266_1.50_1.50.png +0 -0
- samples/case32_day19_slice_0100_266_266_1.50_1.50.png +0 -0
- samples/case33_day21_slice_0114_266_266_1.50_1.50.png +0 -0
- samples/case36_day16_slice_0064_266_266_1.50_1.50.png +0 -0
- samples/case40_day0_slice_0094_266_266_1.50_1.50.png +0 -0
- samples/case41_day25_slice_0049_266_266_1.50_1.50.png +0 -0
- samples/case63_day22_slice_0076_266_266_1.50_1.50.png +0 -0
- samples/case63_day26_slice_0093_266_266_1.50_1.50.png +0 -0
- samples/case65_day28_slice_0133_266_266_1.50_1.50.png +0 -0
- samples/case66_day36_slice_0101_266_266_1.50_1.50.png +0 -0
- samples/case67_day0_slice_0049_266_266_1.50_1.50.png +0 -0
- samples/case67_day0_slice_0086_266_266_1.50_1.50.png +0 -0
- samples/case74_day18_slice_0101_266_266_1.50_1.50.png +0 -0
- samples/case74_day19_slice_0084_266_266_1.50_1.50.png +0 -0
.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz 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
|
.gitignore
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# C extensions
|
| 7 |
+
*.so
|
| 8 |
+
|
| 9 |
+
# Distribution / packaging
|
| 10 |
+
.Python
|
| 11 |
+
build/
|
| 12 |
+
develop-eggs/
|
| 13 |
+
dist/
|
| 14 |
+
downloads/
|
| 15 |
+
eggs/
|
| 16 |
+
.eggs/
|
| 17 |
+
lib/
|
| 18 |
+
lib64/
|
| 19 |
+
parts/
|
| 20 |
+
sdist/
|
| 21 |
+
var/
|
| 22 |
+
wheels/
|
| 23 |
+
pip-wheel-metadata/
|
| 24 |
+
share/python-wheels/
|
| 25 |
+
*.egg-info/
|
| 26 |
+
.installed.cfg
|
| 27 |
+
*.egg
|
| 28 |
+
MANIFEST
|
| 29 |
+
|
| 30 |
+
# PyInstaller
|
| 31 |
+
*.manifest
|
| 32 |
+
*.spec
|
| 33 |
+
|
| 34 |
+
# Unit test / coverage reports
|
| 35 |
+
htmlcov/
|
| 36 |
+
.tox/
|
| 37 |
+
.nox/
|
| 38 |
+
.coverage
|
| 39 |
+
.coverage.*
|
| 40 |
+
.cache
|
| 41 |
+
nosetests.xml
|
| 42 |
+
coverage.xml
|
| 43 |
+
*.cover
|
| 44 |
+
*.py,cover
|
| 45 |
+
.hypothesis/
|
| 46 |
+
.pytest_cache/
|
| 47 |
+
|
| 48 |
+
# Translations
|
| 49 |
+
*.mo
|
| 50 |
+
*.pot
|
| 51 |
+
|
| 52 |
+
# Django stuff:
|
| 53 |
+
*.log
|
| 54 |
+
local_settings.py
|
| 55 |
+
db.sqlite3
|
| 56 |
+
db.sqlite3-journal
|
| 57 |
+
|
| 58 |
+
# Flask stuff:
|
| 59 |
+
instance/
|
| 60 |
+
.webassets-cache
|
| 61 |
+
|
| 62 |
+
# Scrapy stuff:
|
| 63 |
+
.scrapy
|
| 64 |
+
|
| 65 |
+
# Sphinx documentation
|
| 66 |
+
docs/_build/
|
| 67 |
+
|
| 68 |
+
# PyBuilder
|
| 69 |
+
target/
|
| 70 |
+
|
| 71 |
+
# Jupyter Notebook
|
| 72 |
+
.ipynb_checkpoints
|
| 73 |
+
|
| 74 |
+
# IPython
|
| 75 |
+
profile_default/
|
| 76 |
+
ipython_config.py
|
| 77 |
+
|
| 78 |
+
# pyenv
|
| 79 |
+
.python-version
|
| 80 |
+
|
| 81 |
+
# pipenv
|
| 82 |
+
Pipfile.lock
|
| 83 |
+
|
| 84 |
+
# PEP 582
|
| 85 |
+
__pypackages__/
|
| 86 |
+
|
| 87 |
+
# Celery stuff
|
| 88 |
+
celerybeat-schedule
|
| 89 |
+
celerybeat.pid
|
| 90 |
+
|
| 91 |
+
# SageMath parsed files
|
| 92 |
+
*.sage.py
|
| 93 |
+
|
| 94 |
+
# Environments
|
| 95 |
+
.env
|
| 96 |
+
.venv
|
| 97 |
+
env/
|
| 98 |
+
venv/
|
| 99 |
+
ENV/
|
| 100 |
+
env.bak/
|
| 101 |
+
venv.bak/
|
| 102 |
+
|
| 103 |
+
# Spyder project settings
|
| 104 |
+
.spyderproject
|
| 105 |
+
.spyproject
|
| 106 |
+
|
| 107 |
+
# Rope project settings
|
| 108 |
+
.ropeproject
|
| 109 |
+
|
| 110 |
+
# mkdocs documentation
|
| 111 |
+
/site
|
| 112 |
+
|
| 113 |
+
# mypy
|
| 114 |
+
.mypy_cache/
|
| 115 |
+
.dmypy.json
|
| 116 |
+
dmypy.json
|
| 117 |
+
|
| 118 |
+
# Pyre type checker
|
| 119 |
+
.pyre/
|
| 120 |
+
|
| 121 |
+
# IDE
|
| 122 |
+
.vscode/
|
| 123 |
+
.idea/
|
| 124 |
+
*.swp
|
| 125 |
+
*.swo
|
| 126 |
+
*~
|
| 127 |
+
.DS_Store
|
| 128 |
+
|
| 129 |
+
# Project-specific
|
| 130 |
+
artifacts/
|
| 131 |
+
wandb/
|
| 132 |
+
models/
|
| 133 |
+
trained_models/
|
| 134 |
+
data/
|
| 135 |
+
*.zip
|
| 136 |
+
*.tar.gz
|
| 137 |
+
test_results/
|
| 138 |
+
test_results_simple/
|
| 139 |
+
predictions.json
|
| 140 |
+
*.pkl
|
| 141 |
+
*.pth
|
| 142 |
+
wandb/
|
| 143 |
+
|
| 144 |
+
# Large files
|
| 145 |
+
*.bin
|
| 146 |
+
!segformer_trained_weights/pytorch_model.bin
|
| 147 |
+
|
| 148 |
+
# OS
|
| 149 |
+
.DS_Store
|
| 150 |
+
Thumbs.db
|
FILE_INDEX.md
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 📚 Medical Image Segmentation - File Index
|
| 2 |
+
|
| 3 |
+
## 🎯 Quick Navigation
|
| 4 |
+
|
| 5 |
+
### 🚀 Bắt Đầu Nhanh (3 lệnh)
|
| 6 |
+
```bash
|
| 7 |
+
# 1. Chạy ứng dụng web
|
| 8 |
+
python app.py
|
| 9 |
+
|
| 10 |
+
# 2. Hoặc chạy Jupyter demo
|
| 11 |
+
jupyter notebook demo.ipynb
|
| 12 |
+
|
| 13 |
+
# 3. Hoặc train model mới
|
| 14 |
+
python train.py --data ./prepared_data
|
| 15 |
+
```
|
| 16 |
+
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
## 📋 Danh Sách File & Công Dụng
|
| 20 |
+
|
| 21 |
+
### 🔴 **Core Scripts (Chính)**
|
| 22 |
+
|
| 23 |
+
| File | Mô Tả | Lệnh |
|
| 24 |
+
|------|-------|------|
|
| 25 |
+
| **app.py** | Web interface (Gradio) | `python app.py` |
|
| 26 |
+
| **demo.ipynb** | Interactive Jupyter demo | `jupyter notebook demo.ipynb` |
|
| 27 |
+
| **download_dataset.py** | Tải dataset từ Kaggle | `python download_dataset.py` |
|
| 28 |
+
| **prepare_dataset.py** | Chuẩn bị data (split, RLE decode) | `python prepare_dataset.py` |
|
| 29 |
+
| **train.py** | Train SegFormer model | `python train.py --data ./prepared_data` |
|
| 30 |
+
| **test.py** | Test & evaluation | `python test.py --model ./models/best_model` |
|
| 31 |
+
|
| 32 |
+
### 📚 **Documentation (Tài Liệu)**
|
| 33 |
+
|
| 34 |
+
| File | Nội Dung |
|
| 35 |
+
|------|---------|
|
| 36 |
+
| **TRAINING_GUIDE.md** | 📖 Hướng dẫn chi tiết từng bước |
|
| 37 |
+
| **IMPLEMENTATION_SUMMARY.md** | 🎉 Tóm tắt triển khai |
|
| 38 |
+
| **FILE_INDEX.md** | 📚 File này - danh sách file |
|
| 39 |
+
| **README.md** | ℹ️ Info gốc của dự án |
|
| 40 |
+
|
| 41 |
+
### 🗂️ **Directories (Thư Mục)**
|
| 42 |
+
|
| 43 |
+
| Thư Mục | Nội Dung |
|
| 44 |
+
|---------|---------|
|
| 45 |
+
| **segformer_trained_weights/** | Pre-trained model weights |
|
| 46 |
+
| **samples/** | Sample images để test |
|
| 47 |
+
| **data/** | Raw dataset (sau tải) |
|
| 48 |
+
| **prepared_data/** | Processed dataset (sau chuẩn bị) |
|
| 49 |
+
| **models/** | Trained models (sau training) |
|
| 50 |
+
| **test_results/** | Test predictions (sau test) |
|
| 51 |
+
|
| 52 |
+
---
|
| 53 |
+
|
| 54 |
+
## 🎯 Hướng Dẫn Sử Dụng Theo Mục Đích
|
| 55 |
+
|
| 56 |
+
### 📥 **Chỉ muốn test demo**
|
| 57 |
+
```bash
|
| 58 |
+
# 1. Chạy app web
|
| 59 |
+
python app.py
|
| 60 |
+
# Truy cập: http://127.0.0.1:7860
|
| 61 |
+
|
| 62 |
+
# 2. Hoặc dùng notebook
|
| 63 |
+
jupyter notebook demo.ipynb
|
| 64 |
+
```
|
| 65 |
+
**File:** `app.py`, `demo.ipynb`, `segformer_trained_weights/`
|
| 66 |
+
|
| 67 |
+
---
|
| 68 |
+
|
| 69 |
+
### 📖 **Muốn hiểu cách hoạt động**
|
| 70 |
+
**Đọc các file tài liệu theo thứ tự:**
|
| 71 |
+
1. Bắt đầu: `IMPLEMENTATION_SUMMARY.md`
|
| 72 |
+
2. Chi tiết: `TRAINING_GUIDE.md`
|
| 73 |
+
3. Code: `app.py` hoặc `demo.ipynb`
|
| 74 |
+
|
| 75 |
+
---
|
| 76 |
+
|
| 77 |
+
### 🏋️ **Muốn train model mới**
|
| 78 |
+
```bash
|
| 79 |
+
# Step 1: Tải dataset (yêu cầu Kaggle API)
|
| 80 |
+
python download_dataset.py
|
| 81 |
+
|
| 82 |
+
# Step 2: Chuẩn bị dữ liệu
|
| 83 |
+
python prepare_dataset.py
|
| 84 |
+
|
| 85 |
+
# Step 3: Train model
|
| 86 |
+
python train.py --epochs 20 --batch-size 8
|
| 87 |
+
|
| 88 |
+
# Step 4: Test model
|
| 89 |
+
python test.py --model ./models/best_model --visualize
|
| 90 |
+
```
|
| 91 |
+
**Files:** `download_dataset.py`, `prepare_dataset.py`, `train.py`, `test.py`
|
| 92 |
+
|
| 93 |
+
---
|
| 94 |
+
|
| 95 |
+
### 🧪 **Chỉ muốn test model hiện có**
|
| 96 |
+
```bash
|
| 97 |
+
python test.py \
|
| 98 |
+
--model ./segformer_trained_weights \
|
| 99 |
+
--test-images ./samples \
|
| 100 |
+
--visualize
|
| 101 |
+
```
|
| 102 |
+
**File:** `test.py`
|
| 103 |
+
|
| 104 |
+
---
|
| 105 |
+
|
| 106 |
+
## 🔧 Script Chi Tiết
|
| 107 |
+
|
| 108 |
+
### **download_dataset.py**
|
| 109 |
+
Tải UW-Madison GI Tract dataset từ Kaggle
|
| 110 |
+
- ✅ Kiểm tra Kaggle API
|
| 111 |
+
- ✅ Tải ~10GB data
|
| 112 |
+
- ✅ Giải nén
|
| 113 |
+
- ✅ Verify structure
|
| 114 |
+
|
| 115 |
+
**Yêu cầu:** Kaggle API key (https://www.kaggle.com/account)
|
| 116 |
+
|
| 117 |
+
```bash
|
| 118 |
+
python download_dataset.py
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
---
|
| 122 |
+
|
| 123 |
+
### **prepare_dataset.py**
|
| 124 |
+
Xử lý RLE encoding thành image masks
|
| 125 |
+
- ✅ Giải mã RLE
|
| 126 |
+
- ✅ Chia train/val/test (80/10/10)
|
| 127 |
+
- ✅ Tạo folder structure
|
| 128 |
+
- ✅ Thống kê data
|
| 129 |
+
|
| 130 |
+
```bash
|
| 131 |
+
python prepare_dataset.py
|
| 132 |
+
```
|
| 133 |
+
|
| 134 |
+
**Đầu vào:** `data/` (từ Kaggle)
|
| 135 |
+
**Đầu ra:** `prepared_data/`
|
| 136 |
+
|
| 137 |
+
---
|
| 138 |
+
|
| 139 |
+
### **train.py**
|
| 140 |
+
Train SegFormer model mới
|
| 141 |
+
- ✅ Load pre-trained SegFormer-b0
|
| 142 |
+
- ✅ Custom training loop
|
| 143 |
+
- ✅ Validation mỗi epoch
|
| 144 |
+
- ✅ Save best model
|
| 145 |
+
- ✅ Loss history
|
| 146 |
+
|
| 147 |
+
```bash
|
| 148 |
+
python train.py \
|
| 149 |
+
--data ./prepared_data \
|
| 150 |
+
--epochs 20 \
|
| 151 |
+
--batch-size 8 \
|
| 152 |
+
--learning-rate 1e-4
|
| 153 |
+
```
|
| 154 |
+
|
| 155 |
+
**Tham số:**
|
| 156 |
+
- `--data`: Path to prepared_data
|
| 157 |
+
- `--output-dir`: Model output (mặc định: `./models`)
|
| 158 |
+
- `--epochs`: Số epoch (mặc định: 10)
|
| 159 |
+
- `--batch-size`: Batch size (mặc định: 8)
|
| 160 |
+
- `--learning-rate`: Learning rate (mặc định: 1e-4)
|
| 161 |
+
- `--num-workers`: DataLoader workers (mặc định: 4)
|
| 162 |
+
|
| 163 |
+
**Đầu ra:** `models/best_model/`, `models/final_model/`
|
| 164 |
+
|
| 165 |
+
---
|
| 166 |
+
|
| 167 |
+
### **test.py**
|
| 168 |
+
Test model & tính metrics
|
| 169 |
+
- ✅ Evaluate trên test set
|
| 170 |
+
- ✅ Tính mIoU, Precision, Recall
|
| 171 |
+
- ✅ Per-class metrics
|
| 172 |
+
- ✅ Tạo visualizations
|
| 173 |
+
- ✅ Export JSON results
|
| 174 |
+
|
| 175 |
+
```bash
|
| 176 |
+
python test.py \
|
| 177 |
+
--model ./models/best_model \
|
| 178 |
+
--test-images ./prepared_data/test_images \
|
| 179 |
+
--test-masks ./prepared_data/test_masks \
|
| 180 |
+
--output-dir ./test_results \
|
| 181 |
+
--visualize \
|
| 182 |
+
--num-samples 10
|
| 183 |
+
```
|
| 184 |
+
|
| 185 |
+
**Tham số:**
|
| 186 |
+
- `--model`: Path to model (bắt buộc)
|
| 187 |
+
- `--test-images`: Test images folder (bắt buộc)
|
| 188 |
+
- `--test-masks`: Test masks folder (bắt buộc)
|
| 189 |
+
- `--output-dir`: Output folder (mặc định: `./test_results`)
|
| 190 |
+
- `--visualize`: Tạo visualizations (flag)
|
| 191 |
+
- `--num-samples`: Số samples visualize (mặc định: 5)
|
| 192 |
+
|
| 193 |
+
**Đầu ra:** `test_results/evaluation_results.json`, visualizations
|
| 194 |
+
|
| 195 |
+
---
|
| 196 |
+
|
| 197 |
+
### **app.py**
|
| 198 |
+
Web interface sử dụng Gradio
|
| 199 |
+
- ✅ Upload ảnh
|
| 200 |
+
- ✅ Real-time prediction
|
| 201 |
+
- ✅ Color-coded segmentation
|
| 202 |
+
- ✅ Confidence scores
|
| 203 |
+
- ✅ Sample images
|
| 204 |
+
|
| 205 |
+
```bash
|
| 206 |
+
python app.py
|
| 207 |
+
```
|
| 208 |
+
|
| 209 |
+
**Truy cập:** http://127.0.0.1:7860
|
| 210 |
+
|
| 211 |
+
**Cộn:** 🔵 Blue = Stomach, 🟢 Green = Small bowel, 🔴 Red = Large bowel
|
| 212 |
+
|
| 213 |
+
---
|
| 214 |
+
|
| 215 |
+
### **demo.ipynb**
|
| 216 |
+
Jupyter notebook interactive
|
| 217 |
+
- Section 1: Imports & Config
|
| 218 |
+
- Section 2: Load model
|
| 219 |
+
- Section 3: Preprocessing
|
| 220 |
+
- Section 4: Prediction function
|
| 221 |
+
- Section 5: Load samples
|
| 222 |
+
- Section 6: Visualize results
|
| 223 |
+
- Section 7: Create overlays
|
| 224 |
+
- Section 8: Batch evaluation
|
| 225 |
+
|
| 226 |
+
```bash
|
| 227 |
+
jupyter notebook demo.ipynb
|
| 228 |
+
```
|
| 229 |
+
|
| 230 |
+
---
|
| 231 |
+
|
| 232 |
+
## 📚 Tài Liệu
|
| 233 |
+
|
| 234 |
+
### **TRAINING_GUIDE.md**
|
| 235 |
+
Hướng dẫn hoàn chỉnh:
|
| 236 |
+
- 📖 Tổng quan dự án
|
| 237 |
+
- 🚀 Quick start
|
| 238 |
+
- 📚 Step-by-step guide
|
| 239 |
+
- 🧪 Testing & evaluation
|
| 240 |
+
- 💻 Custom model usage
|
| 241 |
+
- 📊 Dataset format
|
| 242 |
+
- 🔧 Troubleshooting
|
| 243 |
+
- 📈 Performance tips
|
| 244 |
+
|
| 245 |
+
### **IMPLEMENTATION_SUMMARY.md**
|
| 246 |
+
Tóm tắt triển khai:
|
| 247 |
+
- ✅ Những gì đã triển khai
|
| 248 |
+
- 🚀 Full workflow
|
| 249 |
+
- 📊 Feature table
|
| 250 |
+
- 💡 Quick examples
|
| 251 |
+
- ✨ Highlights
|
| 252 |
+
|
| 253 |
+
---
|
| 254 |
+
|
| 255 |
+
## 🎓 Learning Path
|
| 256 |
+
|
| 257 |
+
### **Beginner (Bắt đầu):**
|
| 258 |
+
1. Đọc: `IMPLEMENTATION_SUMMARY.md`
|
| 259 |
+
2. Chạy: `python app.py`
|
| 260 |
+
3. Thử: `jupyter notebook demo.ipynb`
|
| 261 |
+
|
| 262 |
+
### **Intermediate (Trung bình):**
|
| 263 |
+
1. Đọc: `TRAINING_GUIDE.md`
|
| 264 |
+
2. Chạy: `python download_dataset.py` → `prepare_dataset.py`
|
| 265 |
+
3. Understand: Code trong `train.py`
|
| 266 |
+
|
| 267 |
+
### **Advanced (Nâng cao):**
|
| 268 |
+
1. Sửa hyperparameters trong `train.py`
|
| 269 |
+
2. Thêm data augmentation
|
| 270 |
+
3. Thử architectures khác
|
| 271 |
+
4. Fine-tune cho use case của bạn
|
| 272 |
+
|
| 273 |
+
---
|
| 274 |
+
|
| 275 |
+
## 🎯 Cheat Sheet
|
| 276 |
+
|
| 277 |
+
| Mục đích | Lệnh |
|
| 278 |
+
|---------|------|
|
| 279 |
+
| Demo web | `python app.py` |
|
| 280 |
+
| Jupyter | `jupyter notebook demo.ipynb` |
|
| 281 |
+
| Tải data | `python download_dataset.py` |
|
| 282 |
+
| Prep data | `python prepare_dataset.py` |
|
| 283 |
+
| Train | `python train.py --epochs 20` |
|
| 284 |
+
| Test | `python test.py --model ./models/best_model --visualize` |
|
| 285 |
+
| Help | Xem `--help`: `python train.py --help` |
|
| 286 |
+
|
| 287 |
+
---
|
| 288 |
+
|
| 289 |
+
## 📞 Troubleshooting
|
| 290 |
+
|
| 291 |
+
**Nếu gặp lỗi:**
|
| 292 |
+
1. Xem error message chi tiết
|
| 293 |
+
2. Check `TRAINING_GUIDE.md` phần Troubleshooting
|
| 294 |
+
3. Verify folders & permissions
|
| 295 |
+
4. Kiểm tra Python version (3.8+)
|
| 296 |
+
|
| 297 |
+
**Common issues:**
|
| 298 |
+
- GPU not found? → Model sẽ auto switch sang CPU
|
| 299 |
+
- Kaggle error? → Xem hướng dẫn setup API key
|
| 300 |
+
- Out of memory? → Giảm `--batch-size`
|
| 301 |
+
- Slow? → Tăng `--num-workers`
|
| 302 |
+
|
| 303 |
+
---
|
| 304 |
+
|
| 305 |
+
## 📊 Model Info
|
| 306 |
+
|
| 307 |
+
**Kiến trúc:** SegFormer-B0 (HuggingFace)
|
| 308 |
+
**Classes:** 4 (Background + 3 organs)
|
| 309 |
+
**Input:** 288x288 RGB images
|
| 310 |
+
**Normalization:** ImageNet (mean, std)
|
| 311 |
+
**Framework:** PyTorch
|
| 312 |
+
**Inference:** ~100ms per image (CPU)
|
| 313 |
+
|
| 314 |
+
---
|
| 315 |
+
|
| 316 |
+
## 🎨 Color Legend
|
| 317 |
+
|
| 318 |
+
```
|
| 319 |
+
🔴 Red (#FF0000) = Large bowel
|
| 320 |
+
🟢 Green (#009A17) = Small bowel
|
| 321 |
+
🔵 Blue (#007fff) = Stomach
|
| 322 |
+
⚪ White (0) = Background
|
| 323 |
+
```
|
| 324 |
+
|
| 325 |
+
---
|
| 326 |
+
|
| 327 |
+
## 📈 Expected Results
|
| 328 |
+
|
| 329 |
+
- **mIoU:** 0.60-0.75 (depending on training)
|
| 330 |
+
- **Precision:** 0.70-0.85
|
| 331 |
+
- **Recall:** 0.60-0.80
|
| 332 |
+
- **Inference time:** 0.1-0.5s per image
|
| 333 |
+
|
| 334 |
+
---
|
| 335 |
+
|
| 336 |
+
## 🔗 Links
|
| 337 |
+
|
| 338 |
+
- 📚 HuggingFace SegFormer: https://huggingface.co/docs/transformers/model_doc/segformer
|
| 339 |
+
- 🔗 Gradio: https://www.gradio.app/
|
| 340 |
+
- 🔬 PyTorch: https://pytorch.org/
|
| 341 |
+
- 🏆 Kaggle Challenge: https://www.kaggle.com/competitions/uw-madison-gi-tract-image-segmentation
|
| 342 |
+
|
| 343 |
+
---
|
| 344 |
+
|
| 345 |
+
## ✨ Features Checklist
|
| 346 |
+
|
| 347 |
+
- ✅ Web interface (Gradio)
|
| 348 |
+
- ✅ Jupyter notebook
|
| 349 |
+
- ✅ Dataset download (Kaggle)
|
| 350 |
+
- ✅ Data preparation (RLE decode)
|
| 351 |
+
- ✅ Model training (SegFormer)
|
| 352 |
+
- ✅ Model testing & evaluation
|
| 353 |
+
- ✅ Confidence scores
|
| 354 |
+
- ✅ Visualizations
|
| 355 |
+
- ✅ Batch processing
|
| 356 |
+
- ✅ Complete documentation
|
| 357 |
+
- ✅ Error handling
|
| 358 |
+
- ✅ GPU/CPU support
|
| 359 |
+
|
| 360 |
+
---
|
| 361 |
+
|
| 362 |
+
**Ready to go! 🚀**
|
| 363 |
+
|
| 364 |
+
Chọn một script ở trên và bắt đầu!
|
IMPLEMENTATION_SUMMARY.md
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🎉 Triển Khai Hoàn Tất - Medical Image Segmentation Project
|
| 2 |
+
|
| 3 |
+
## ✅ Những gì đã được triển khai:
|
| 4 |
+
|
| 5 |
+
### 📥 1. **download_dataset.py** - Tải Dataset từ Kaggle
|
| 6 |
+
**Chức năng:**
|
| 7 |
+
- Kiểm tra cài đặt Kaggle API
|
| 8 |
+
- Tải dataset từ Kaggle competition
|
| 9 |
+
- Giải nén tự động
|
| 10 |
+
- Xác minh cấu trúc dataset
|
| 11 |
+
|
| 12 |
+
**Sử dụng:**
|
| 13 |
+
```bash
|
| 14 |
+
python download_dataset.py
|
| 15 |
+
```
|
| 16 |
+
|
| 17 |
+
**Kết quả:** Tải 45K+ ảnh CT/MRI từ UW-Madison GI Tract challenge
|
| 18 |
+
|
| 19 |
+
---
|
| 20 |
+
|
| 21 |
+
### 🔄 2. **prepare_dataset.py** - Chuẩn Bị Dữ Liệu
|
| 22 |
+
**Chức năng:**
|
| 23 |
+
- Giải mã RLE encoding thành mask images
|
| 24 |
+
- Chia train/val/test (80/10/10)
|
| 25 |
+
- Tạo cấu trúc folder chuẩn
|
| 26 |
+
- Thống kê dataset
|
| 27 |
+
|
| 28 |
+
**Sử dụng:**
|
| 29 |
+
```bash
|
| 30 |
+
python prepare_dataset.py
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
**Đầu ra:**
|
| 34 |
+
```
|
| 35 |
+
prepared_data/
|
| 36 |
+
├── train_images/ (80%)
|
| 37 |
+
├── train_masks/
|
| 38 |
+
├── val_images/ (10%)
|
| 39 |
+
├── val_masks/
|
| 40 |
+
├── test_images/ (10%)
|
| 41 |
+
├── test_masks/
|
| 42 |
+
└── split.json
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
---
|
| 46 |
+
|
| 47 |
+
### 🧠 3. **train.py** - Train Mô Hình
|
| 48 |
+
**Chức năng:**
|
| 49 |
+
- Load pre-trained SegFormer model
|
| 50 |
+
- Huấn luyện trên dataset mới
|
| 51 |
+
- Validation mỗi epoch
|
| 52 |
+
- Lưu best model
|
| 53 |
+
- Tracking training history
|
| 54 |
+
|
| 55 |
+
**Sử dụng:**
|
| 56 |
+
```bash
|
| 57 |
+
python train.py \
|
| 58 |
+
--data ./prepared_data \
|
| 59 |
+
--epochs 10 \
|
| 60 |
+
--batch-size 8 \
|
| 61 |
+
--learning-rate 1e-4
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
**Tham số:**
|
| 65 |
+
- `--epochs`: Số lần lặp (mặc định: 10)
|
| 66 |
+
- `--batch-size`: Kích thước batch (mặc định: 8)
|
| 67 |
+
- `--learning-rate`: Tốc độ học (mặc định: 1e-4)
|
| 68 |
+
- `--num-workers`: Workers DataLoader (mặc định: 4)
|
| 69 |
+
|
| 70 |
+
**Kết quả:**
|
| 71 |
+
```
|
| 72 |
+
models/
|
| 73 |
+
├── best_model/
|
| 74 |
+
├── final_model/
|
| 75 |
+
└── training_history.json
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
---
|
| 79 |
+
|
| 80 |
+
### 🧪 4. **test.py** - Test & Evaluation
|
| 81 |
+
**Chức năng:**
|
| 82 |
+
- Đánh giá model trên test set
|
| 83 |
+
- Tính metrics (mIoU, Precision, Recall)
|
| 84 |
+
- Tạo visualizations
|
| 85 |
+
- Export results JSON
|
| 86 |
+
|
| 87 |
+
**Sử dụng:**
|
| 88 |
+
```bash
|
| 89 |
+
python test.py \
|
| 90 |
+
--model ./models/best_model \
|
| 91 |
+
--test-images ./prepared_data/test_images \
|
| 92 |
+
--test-masks ./prepared_data/test_masks \
|
| 93 |
+
--output-dir ./test_results \
|
| 94 |
+
--visualize
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
**Metrics:**
|
| 98 |
+
- mIoU (Intersection over Union)
|
| 99 |
+
- Precision, Recall, F1-score
|
| 100 |
+
- Per-class metrics
|
| 101 |
+
|
| 102 |
+
---
|
| 103 |
+
|
| 104 |
+
### 🎨 5. **app.py (Cập Nhật)** - Ứng Dụng Web
|
| 105 |
+
**Cải tiến:**
|
| 106 |
+
- ✅ Hiển thị confidence scores
|
| 107 |
+
- ✅ Giao diện tốt hơn (HTML/CSS)
|
| 108 |
+
- ✅ Legend màu sắc
|
| 109 |
+
- ✅ Hỗ trợ batch inference
|
| 110 |
+
|
| 111 |
+
**Sử dụng:**
|
| 112 |
+
```bash
|
| 113 |
+
python app.py
|
| 114 |
+
```
|
| 115 |
+
|
| 116 |
+
Truy cập: **http://127.0.0.1:7860**
|
| 117 |
+
|
| 118 |
+
---
|
| 119 |
+
|
| 120 |
+
### 📚 6. **demo.ipynb** - Jupyter Notebook Demo
|
| 121 |
+
**Các phần:**
|
| 122 |
+
1. Cài đặt & Config
|
| 123 |
+
2. Load SegFormer model
|
| 124 |
+
3. Preprocessing pipeline
|
| 125 |
+
4. Prediction function
|
| 126 |
+
5. Load sample images
|
| 127 |
+
6. Visualize results
|
| 128 |
+
7. Color-coded overlays
|
| 129 |
+
8. Batch evaluation table
|
| 130 |
+
|
| 131 |
+
**Sử dụng:**
|
| 132 |
+
```bash
|
| 133 |
+
jupyter notebook demo.ipynb
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
---
|
| 137 |
+
|
| 138 |
+
### 📖 7. **TRAINING_GUIDE.md** - Hướng Dẫn Hoàn Chỉnh
|
| 139 |
+
**Nội dung:**
|
| 140 |
+
- Quick start
|
| 141 |
+
- Chi tiết từng bước
|
| 142 |
+
- Troubleshooting
|
| 143 |
+
- Performance tips
|
| 144 |
+
- Dataset format
|
| 145 |
+
- References
|
| 146 |
+
|
| 147 |
+
---
|
| 148 |
+
|
| 149 |
+
## 🚀 Workflow Đầy Đủ:
|
| 150 |
+
|
| 151 |
+
### **Lần Đầu (Training Từ Đầu):**
|
| 152 |
+
```bash
|
| 153 |
+
# 1. Tải dataset
|
| 154 |
+
python download_dataset.py
|
| 155 |
+
|
| 156 |
+
# 2. Chuẩn bị dữ liệu
|
| 157 |
+
python prepare_dataset.py
|
| 158 |
+
|
| 159 |
+
# 3. Train model
|
| 160 |
+
python train.py --data ./prepared_data --epochs 20
|
| 161 |
+
|
| 162 |
+
# 4. Test & evaluate
|
| 163 |
+
python test.py \
|
| 164 |
+
--model ./models/best_model \
|
| 165 |
+
--test-images ./prepared_data/test_images \
|
| 166 |
+
--test-masks ./prepared_data/test_masks \
|
| 167 |
+
--visualize
|
| 168 |
+
```
|
| 169 |
+
|
| 170 |
+
### **Chạy Demo (Không cần train):**
|
| 171 |
+
```bash
|
| 172 |
+
# Chạy ứng dụng web
|
| 173 |
+
python app.py
|
| 174 |
+
|
| 175 |
+
# Hoặc Jupyter notebook
|
| 176 |
+
jupyter notebook demo.ipynb
|
| 177 |
+
```
|
| 178 |
+
|
| 179 |
+
---
|
| 180 |
+
|
| 181 |
+
## 📊 Tính Năng Chính:
|
| 182 |
+
|
| 183 |
+
| Tính Năng | Script | Trạng Thái |
|
| 184 |
+
|-----------|--------|-----------|
|
| 185 |
+
| Tải Kaggle dataset | `download_dataset.py` | ✅ |
|
| 186 |
+
| Xử lý & chuẩn bị dữ liệu | `prepare_dataset.py` | ✅ |
|
| 187 |
+
| Train mô hình mới | `train.py` | ✅ |
|
| 188 |
+
| Test & evaluation | `test.py` | ✅ |
|
| 189 |
+
| Web interface | `app.py` | ✅ |
|
| 190 |
+
| Jupyter demo | `demo.ipynb` | ✅ |
|
| 191 |
+
| Hướng dẫn chi tiết | `TRAINING_GUIDE.md` | ✅ |
|
| 192 |
+
| Confidence scores | `app.py` + `test.py` | ✅ |
|
| 193 |
+
| Batch processing | `test.py` | ✅ |
|
| 194 |
+
| Visualization | `test.py` + `demo.ipynb` | ✅ |
|
| 195 |
+
|
| 196 |
+
---
|
| 197 |
+
|
| 198 |
+
## 📁 Cấu Trúc File Hoàn Chỉnh:
|
| 199 |
+
|
| 200 |
+
```
|
| 201 |
+
UWMGI_Medical_Image_Segmentation/
|
| 202 |
+
├── 🎯 Ứng dụng chính
|
| 203 |
+
│ ├── app.py (Web interface)
|
| 204 |
+
│ ├── demo.ipynb (Jupyter notebook)
|
| 205 |
+
│ └── segformer_trained_weights/ (Pre-trained model)
|
| 206 |
+
│
|
| 207 |
+
├── 🛠️ Scripts công cụ
|
| 208 |
+
│ ├── download_dataset.py (Tải Kaggle)
|
| 209 |
+
│ ├── prepare_dataset.py (Chuẩn bị dữ liệu)
|
| 210 |
+
│ ├── train.py (Training)
|
| 211 |
+
│ └── test.py (Testing)
|
| 212 |
+
│
|
| 213 |
+
├── 📚 Tài liệu
|
| 214 |
+
│ ├── TRAINING_GUIDE.md (Hướng dẫn đầy đủ)
|
| 215 |
+
│ ├���─ README.md (Info gốc)
|
| 216 |
+
│ └── IMPLEMENTATION_SUMMARY.md (File này)
|
| 217 |
+
│
|
| 218 |
+
├── 📦 Data (tạo sau khi chạy)
|
| 219 |
+
│ ├── data/ (Raw data từ Kaggle)
|
| 220 |
+
│ ├── prepared_data/ (Processed data)
|
| 221 |
+
│ ├── models/ (Trained models)
|
| 222 |
+
│ └── test_results/ (Evaluation results)
|
| 223 |
+
│
|
| 224 |
+
└── 📸 Resources
|
| 225 |
+
├── samples/ (Ảnh mẫu)
|
| 226 |
+
└── requirements.txt (Dependencies)
|
| 227 |
+
```
|
| 228 |
+
|
| 229 |
+
---
|
| 230 |
+
|
| 231 |
+
## 🎓 Hướng Dẫn Chi Tiết:
|
| 232 |
+
|
| 233 |
+
**Xem tại:** [TRAINING_GUIDE.md](./TRAINING_GUIDE.md)
|
| 234 |
+
|
| 235 |
+
Các phần chính:
|
| 236 |
+
- 📋 Tổng quan dự án
|
| 237 |
+
- 🚀 Quick start
|
| 238 |
+
- 📚 Training từng bước
|
| 239 |
+
- 🧪 Testing & evaluation
|
| 240 |
+
- 💻 Custom model usage
|
| 241 |
+
- 🔧 Troubleshooting
|
| 242 |
+
- 📊 Performance tips
|
| 243 |
+
|
| 244 |
+
---
|
| 245 |
+
|
| 246 |
+
## 💡 Ví Dụ Nhanh:
|
| 247 |
+
|
| 248 |
+
### 1. **Chạy Demo Ngay Bây Giờ:**
|
| 249 |
+
```bash
|
| 250 |
+
cd UWMGI_Medical_Image_Segmentation
|
| 251 |
+
python app.py
|
| 252 |
+
# Mở: http://127.0.0.1:7860
|
| 253 |
+
```
|
| 254 |
+
|
| 255 |
+
### 2. **Thử Jupyter Notebook:**
|
| 256 |
+
```bash
|
| 257 |
+
jupyter notebook demo.ipynb
|
| 258 |
+
```
|
| 259 |
+
|
| 260 |
+
### 3. **Train Model Mới:**
|
| 261 |
+
```bash
|
| 262 |
+
# Full workflow
|
| 263 |
+
python download_dataset.py
|
| 264 |
+
python prepare_dataset.py
|
| 265 |
+
python train.py --epochs 20
|
| 266 |
+
python test.py --model ./models/best_model --visualize
|
| 267 |
+
```
|
| 268 |
+
|
| 269 |
+
---
|
| 270 |
+
|
| 271 |
+
## ✨ Đặc Điểm Nổi Bật:
|
| 272 |
+
|
| 273 |
+
✅ **Đầy đủ** - Từ tải data đến train và test
|
| 274 |
+
✅ **Dễ sử dụng** - CLI arguments rõ ràng
|
| 275 |
+
✅ **Có tài liệu** - Hướng dẫn chi tiết cho mỗi script
|
| 276 |
+
✅ **Flexible** - Tùy chỉnh hyperparameters
|
| 277 |
+
✅ **Visual** - Visualizations & metrics
|
| 278 |
+
✅ **Production-ready** - Error handling & validation
|
| 279 |
+
✅ **Demo-ready** - Web interface & notebook
|
| 280 |
+
|
| 281 |
+
---
|
| 282 |
+
|
| 283 |
+
## 📞 Hỗ Trợ:
|
| 284 |
+
|
| 285 |
+
1. **Xem Troubleshooting** trong TRAINING_GUIDE.md
|
| 286 |
+
2. **Kiểm tra error message** - Script in lỗi rõ ràng
|
| 287 |
+
3. **Kaggle documentation** - Nếu vấn đề về API
|
| 288 |
+
4. **PyTorch documentation** - Nếu vấn đề về GPU
|
| 289 |
+
|
| 290 |
+
---
|
| 291 |
+
|
| 292 |
+
**🎉 Tất cả đã sẵn sàng để bắt đầu!**
|
| 293 |
+
|
| 294 |
+
Bắt đầu với `python app.py` hoặc theo hướng dẫn trong TRAINING_GUIDE.md
|
| 295 |
+
|
| 296 |
+
---
|
| 297 |
+
|
| 298 |
+
*Tạo: January 2026*
|
| 299 |
+
*Framework: PyTorch + HuggingFace Transformers + Gradio*
|
| 300 |
+
*License: MIT*
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Medical Image Segmentation Contributors
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
QUICK_START.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
"""
|
| 3 |
+
Quick Start Script - Medical Image Segmentation Project
|
| 4 |
+
Chạy script này để xem tất cả options khả dụng
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import os
|
| 8 |
+
import sys
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
|
| 11 |
+
def print_header(title):
|
| 12 |
+
"""In header"""
|
| 13 |
+
width = 80
|
| 14 |
+
print("\n" + "="*width)
|
| 15 |
+
print(f" {title}".center(width))
|
| 16 |
+
print("="*width)
|
| 17 |
+
|
| 18 |
+
def main():
|
| 19 |
+
print_header("🏥 Medical Image Segmentation - Quick Start")
|
| 20 |
+
|
| 21 |
+
print("""
|
| 22 |
+
┌────────────────────────────────────────────────────────────────────────────┐
|
| 23 |
+
│ 📋 AVAILABLE OPTIONS │
|
| 24 |
+
└────────────────────────────────────────────────────────────────────────────┘
|
| 25 |
+
|
| 26 |
+
1️⃣ WEB INTERFACE (Gradio) - Sử dụng ngay
|
| 27 |
+
└─ python app.py
|
| 28 |
+
→ Truy cập: http://127.0.0.1:7860
|
| 29 |
+
→ Upload ảnh, click "Generate Predictions"
|
| 30 |
+
→ Xem kết quả phân đoạn
|
| 31 |
+
|
| 32 |
+
2️⃣ JUPYTER NOTEBOOK - Interactive Demo
|
| 33 |
+
└─ jupyter notebook demo.ipynb
|
| 34 |
+
→ Step-by-step visualization
|
| 35 |
+
→ Model explanation
|
| 36 |
+
→ Batch processing examples
|
| 37 |
+
|
| 38 |
+
3️⃣ TRAINING WORKFLOW (Complete)
|
| 39 |
+
└─ Step 1: python download_dataset.py
|
| 40 |
+
→ Tải ~10GB data từ Kaggle (cần Kaggle API key)
|
| 41 |
+
|
| 42 |
+
└─ Step 2: python prepare_dataset.py
|
| 43 |
+
→ Xử lý data (RLE decode, split train/val/test)
|
| 44 |
+
|
| 45 |
+
└─ Step 3: python train.py --epochs 20
|
| 46 |
+
→ Train SegFormer model (có thể mất vài giờ)
|
| 47 |
+
→ Save best_model & final_model
|
| 48 |
+
|
| 49 |
+
└─ Step 4: python test.py --model ./models/best_model --visualize
|
| 50 |
+
→ Evaluate & visualization
|
| 51 |
+
|
| 52 |
+
4️⃣ QUICK REFERENCE - Danh sách lệnh
|
| 53 |
+
└─ python train.py --help
|
| 54 |
+
└─ python test.py --help
|
| 55 |
+
└─ python prepare_dataset.py --help
|
| 56 |
+
└─ python download_dataset.py --help
|
| 57 |
+
|
| 58 |
+
5️⃣ DOCUMENTATION - Tài liệu
|
| 59 |
+
└─ TRAINING_GUIDE.md (Hướng dẫn chi tiết)
|
| 60 |
+
└─ IMPLEMENTATION_SUMMARY.md (Tóm tắt triển khai)
|
| 61 |
+
└─ FILE_INDEX.md (Danh sách file)
|
| 62 |
+
└─ README.md (Info gốc)
|
| 63 |
+
|
| 64 |
+
┌────────────────────────────────────────────────────────────────────────────┐
|
| 65 |
+
│ 🚀 RECOMMENDED START │
|
| 66 |
+
└────────────────────────────────────────────────────────────────────────────┘
|
| 67 |
+
|
| 68 |
+
【OPTION A】Demo ngay bây giờ (2 phút)
|
| 69 |
+
python app.py
|
| 70 |
+
# Hoặc
|
| 71 |
+
jupyter notebook demo.ipynb
|
| 72 |
+
|
| 73 |
+
【OPTION B】Train model mới (1-2 giờ)
|
| 74 |
+
python download_dataset.py
|
| 75 |
+
python prepare_dataset.py
|
| 76 |
+
python train.py --epochs 20
|
| 77 |
+
python test.py --model ./models/best_model --visualize
|
| 78 |
+
|
| 79 |
+
【OPTION C】Chỉ test model hiện có
|
| 80 |
+
python test.py \\
|
| 81 |
+
--model ./segformer_trained_weights \\
|
| 82 |
+
--test-images ./samples \\
|
| 83 |
+
--visualize
|
| 84 |
+
|
| 85 |
+
┌────────────────────────────────────────────────────────────────────────────┐
|
| 86 |
+
│ 📊 PROJECT INFO │
|
| 87 |
+
└────────────────────────────────────────────────────────────────────────────┘
|
| 88 |
+
|
| 89 |
+
Dataset: UW-Madison GI Tract Image Segmentation
|
| 90 |
+
Model: SegFormer-B0 (HuggingFace Transformers)
|
| 91 |
+
Framework: PyTorch + Gradio + HuggingFace
|
| 92 |
+
Task: Medical Image Segmentation
|
| 93 |
+
Classes: 4 (Background + 3 organs)
|
| 94 |
+
- Large bowel (Ruột già) 🔴
|
| 95 |
+
- Small bowel (Ruột non) 🟢
|
| 96 |
+
- Stomach (Dạ dày) 🔵
|
| 97 |
+
|
| 98 |
+
┌────────────────────────────────────────────────────────────────────────────┐
|
| 99 |
+
│ 🔧 REQUIREMENTS │
|
| 100 |
+
└────────────────────────────────────────────────────────────────────────────┘
|
| 101 |
+
|
| 102 |
+
Python: 3.8+
|
| 103 |
+
GPU: Optional (Auto-switch to CPU if not available)
|
| 104 |
+
RAM: 4GB+ (8GB recommended)
|
| 105 |
+
Disk: 20GB+ (for dataset & models)
|
| 106 |
+
|
| 107 |
+
Main Libraries:
|
| 108 |
+
- torch >= 2.0.0
|
| 109 |
+
- transformers >= 4.30.0
|
| 110 |
+
- gradio >= 6.0.0
|
| 111 |
+
- torchvision >= 0.15.0
|
| 112 |
+
- PIL, numpy, pandas, scikit-learn
|
| 113 |
+
|
| 114 |
+
Install dependencies:
|
| 115 |
+
pip install -r requirements.txt
|
| 116 |
+
|
| 117 |
+
┌────────────────────────────────────────────────────────────────────────────┐
|
| 118 |
+
│ 💡 USEFUL TIPS │
|
| 119 |
+
└────────────────────────────────────────────────────────────────────────────┘
|
| 120 |
+
|
| 121 |
+
✓ Web Interface slow?
|
| 122 |
+
→ Giảm batch size hoặc dùng CPU mode
|
| 123 |
+
|
| 124 |
+
✓ Train quá lâu?
|
| 125 |
+
→ Giảm epochs: --epochs 5
|
| 126 |
+
→ Giảm batch size: --batch-size 4
|
| 127 |
+
|
| 128 |
+
✓ GPU out of memory?
|
| 129 |
+
→ Giảm batch size: --batch-size 4 hoặc 2
|
| 130 |
+
→ Dùng num_workers=0 trong train.py
|
| 131 |
+
|
| 132 |
+
✓ Kaggle dataset lỗi?
|
| 133 |
+
→ Xem hướng dẫn API key: TRAINING_GUIDE.md
|
| 134 |
+
|
| 135 |
+
✓ Muốn custom model?
|
| 136 |
+
→ Edit train.py, thay model architecture
|
| 137 |
+
→ Hoặc fine-tune trên dataset riêng
|
| 138 |
+
|
| 139 |
+
┌────────────────────────────────────────────────────────────────────────────┐
|
| 140 |
+
│ 🎯 NEXT STEPS │
|
| 141 |
+
└────────────────────────────────────────────────────────────────────────────┘
|
| 142 |
+
|
| 143 |
+
1. Chọn option ở trên (A, B, hoặc C)
|
| 144 |
+
2. Chạy command
|
| 145 |
+
3. Xem kết quả
|
| 146 |
+
4. Đọc tài liệu nếu cần chi tiết
|
| 147 |
+
|
| 148 |
+
Ví dụ đơn giản nhất:
|
| 149 |
+
python app.py
|
| 150 |
+
# Mở browser: http://127.0.0.1:7860
|
| 151 |
+
|
| 152 |
+
┌────────────────────────────────────────────────────────────────────────────┐
|
| 153 |
+
│ 📚 DOCUMENTATION STRUCTURE │
|
| 154 |
+
└────────────────────────────────────────────────────────────────────────────┘
|
| 155 |
+
|
| 156 |
+
Start Here:
|
| 157 |
+
1. FILE_INDEX.md (bạn đang ở đây) ← Tôi là file navigation
|
| 158 |
+
2. IMPLEMENTATION_SUMMARY.md ← Tóm tắt những gì đã triển khai
|
| 159 |
+
3. TRAINING_GUIDE.md ← Hướng dẫn chi tiết từng bước
|
| 160 |
+
|
| 161 |
+
Code Documentation:
|
| 162 |
+
• app.py - Web interface
|
| 163 |
+
• demo.ipynb - Interactive notebook
|
| 164 |
+
• train.py - Training script
|
| 165 |
+
• test.py - Testing script
|
| 166 |
+
• download_dataset.py - Kaggle download
|
| 167 |
+
• prepare_dataset.py - Data preparation
|
| 168 |
+
|
| 169 |
+
┌────────────────────────────────────────────────────────────────────────────┐
|
| 170 |
+
│ ⚠️ IMPORTANT │
|
| 171 |
+
└────────────────────────────────────────────────────────────────────────────┘
|
| 172 |
+
|
| 173 |
+
⚠️ Kaggle API Key:
|
| 174 |
+
Cần cho download_dataset.py
|
| 175 |
+
→ Tạo tại https://www.kaggle.com/account
|
| 176 |
+
→ Lưu vào ~/.kaggle/kaggle.json
|
| 177 |
+
|
| 178 |
+
⚠️ GPU/CUDA:
|
| 179 |
+
Script tự detect GPU
|
| 180 |
+
Nếu GPU not found → auto sử dụng CPU
|
| 181 |
+
|
| 182 |
+
⚠️ Dataset Size:
|
| 183 |
+
UW-Madison dataset ~ 10GB
|
| 184 |
+
Prepared data ~ 15GB
|
| 185 |
+
Total with models ~ 30GB
|
| 186 |
+
|
| 187 |
+
┌────────────────────────────────────────────────────────────────────────────┐
|
| 188 |
+
│ 🎉 YOU'RE ALL SET! │
|
| 189 |
+
└────────────────────────────────────────────────────────────────────────────┘
|
| 190 |
+
|
| 191 |
+
Tất cả tools và scripts đã được triển khai.
|
| 192 |
+
|
| 193 |
+
Hãy chạy command đầu tiên của bạn:
|
| 194 |
+
|
| 195 |
+
python app.py
|
| 196 |
+
|
| 197 |
+
Hoặc xem hướng dẫn chi tiết:
|
| 198 |
+
|
| 199 |
+
cat TRAINING_GUIDE.md
|
| 200 |
+
|
| 201 |
+
Chúc bạn thành công! 🚀
|
| 202 |
+
|
| 203 |
+
""")
|
| 204 |
+
|
| 205 |
+
print("\n" + "="*80 + "\n")
|
| 206 |
+
|
| 207 |
+
if __name__ == "__main__":
|
| 208 |
+
main()
|
README.md
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Medical Image Segmentation - GI Tract
|
| 3 |
+
emoji: 🏥
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: indigo
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: "4.48.1"
|
| 8 |
+
python_version: "3.9"
|
| 9 |
+
app_file: app.py
|
| 10 |
+
pinned: false
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
# 🏥 Medical Image Segmentation - UW-Madison GI Tract
|
| 14 |
+
|
| 15 |
+

|
| 16 |
+

|
| 17 |
+

|
| 18 |
+

|
| 19 |
+
|
| 20 |
+
> Automated semantic segmentation of gastrointestinal tract organs in medical CT/MRI images using SegFormer and Gradio web interface.
|
| 21 |
+
|
| 22 |
+
## 📋 Table of Contents
|
| 23 |
+
- [Overview](#overview)
|
| 24 |
+
- [Features](#features)
|
| 25 |
+
- [Installation](#installation)
|
| 26 |
+
- [Quick Start](#quick-start)
|
| 27 |
+
- [Usage](#usage)
|
| 28 |
+
- [Project Structure](#project-structure)
|
| 29 |
+
- [Model Details](#model-details)
|
| 30 |
+
- [Training](#training)
|
| 31 |
+
- [API Reference](#api-reference)
|
| 32 |
+
- [Contributing](#contributing)
|
| 33 |
+
- [License](#license)
|
| 34 |
+
|
| 35 |
+
## 📊 Overview
|
| 36 |
+
|
| 37 |
+
This project provides an end-to-end solution for segmenting GI tract organs in medical images:
|
| 38 |
+
- **Stomach**
|
| 39 |
+
- **Large Bowel**
|
| 40 |
+
- **Small Bowel**
|
| 41 |
+
|
| 42 |
+
Built with state-of-the-art SegFormer architecture and trained on the UW-Madison GI Tract Image Segmentation dataset (45K+ images).
|
| 43 |
+
|
| 44 |
+
### Key Achievements
|
| 45 |
+
- ✅ 64M parameter efficient model
|
| 46 |
+
- ✅ Interactive Gradio web interface
|
| 47 |
+
- ✅ Real-time inference on CPU/GPU
|
| 48 |
+
- ✅ 40+ pre-loaded sample images
|
| 49 |
+
- ✅ Complete training pipeline included
|
| 50 |
+
- ✅ Production-ready code
|
| 51 |
+
|
| 52 |
+
## ✨ Features
|
| 53 |
+
|
| 54 |
+
### Core Capabilities
|
| 55 |
+
- **Web Interface**: Upload images and get instant segmentation predictions
|
| 56 |
+
- **Batch Processing**: Test on multiple images simultaneously
|
| 57 |
+
- **Color-Coded Output**: Intuitive visual representation of organ locations
|
| 58 |
+
- **Confidence Scores**: Pixel-level confidence metrics for each organ
|
| 59 |
+
- **Interactive Notebook**: Educational Jupyter notebook with step-by-step examples
|
| 60 |
+
|
| 61 |
+
### Development Tools
|
| 62 |
+
- Data download automation (Kaggle integration)
|
| 63 |
+
- Dataset preparation and preprocessing
|
| 64 |
+
- Model training with validation
|
| 65 |
+
- Comprehensive evaluation metrics
|
| 66 |
+
- Diagnostic system checker
|
| 67 |
+
- Simple testing without ground truth
|
| 68 |
+
|
| 69 |
+
## 🚀 Installation
|
| 70 |
+
|
| 71 |
+
### Requirements
|
| 72 |
+
- Python 3.8 or higher
|
| 73 |
+
- CUDA 11.8+ (optional, for GPU acceleration)
|
| 74 |
+
- 4GB RAM minimum (8GB recommended)
|
| 75 |
+
- 2GB disk space
|
| 76 |
+
|
| 77 |
+
### Step 1: Clone Repository
|
| 78 |
+
```bash
|
| 79 |
+
git clone https://github.com/hung2903/medical-image-segmentation.git
|
| 80 |
+
cd UWMGI_Medical_Image_Segmentation
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
### Step 2: Create Virtual Environment
|
| 84 |
+
```bash
|
| 85 |
+
# Using venv
|
| 86 |
+
python -m venv venv
|
| 87 |
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
| 88 |
+
|
| 89 |
+
# Or using conda
|
| 90 |
+
conda create -n medseg python=3.10
|
| 91 |
+
conda activate medseg
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
### Step 3: Install Dependencies
|
| 95 |
+
```bash
|
| 96 |
+
pip install -r requirements.txt
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
### Step 4: Verify Installation
|
| 100 |
+
```bash
|
| 101 |
+
python diagnose.py
|
| 102 |
+
```
|
| 103 |
+
|
| 104 |
+
All checks should show ✅ PASSED.
|
| 105 |
+
|
| 106 |
+
## 🎯 Quick Start
|
| 107 |
+
|
| 108 |
+
### 1. Run Web Interface (Easiest)
|
| 109 |
+
```bash
|
| 110 |
+
python app.py
|
| 111 |
+
```
|
| 112 |
+
Then open http://127.0.0.1:7860 in your browser.
|
| 113 |
+
|
| 114 |
+
### 2. Test on Sample Images
|
| 115 |
+
```bash
|
| 116 |
+
python test_simple.py \
|
| 117 |
+
--model segformer_trained_weights \
|
| 118 |
+
--images samples \
|
| 119 |
+
--output-dir results
|
| 120 |
+
```
|
| 121 |
+
|
| 122 |
+
### 3. Interactive Jupyter Notebook
|
| 123 |
+
```bash
|
| 124 |
+
jupyter notebook demo.ipynb
|
| 125 |
+
```
|
| 126 |
+
|
| 127 |
+
## 📖 Usage
|
| 128 |
+
|
| 129 |
+
### Web Interface
|
| 130 |
+
1. Launch: `python app.py`
|
| 131 |
+
2. Upload medical image (PNG/JPG)
|
| 132 |
+
3. Click "Generate Predictions"
|
| 133 |
+
4. View color-coded segmentation with confidence scores
|
| 134 |
+
5. Download result image
|
| 135 |
+
|
| 136 |
+
**Supported Formats**: PNG, JPG, JPEG, GIF, BMP, WEBP
|
| 137 |
+
|
| 138 |
+
### Command Line
|
| 139 |
+
```python
|
| 140 |
+
from app import get_model, predict
|
| 141 |
+
import torch
|
| 142 |
+
from PIL import Image
|
| 143 |
+
|
| 144 |
+
# Load model
|
| 145 |
+
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
| 146 |
+
model = get_model(device)
|
| 147 |
+
|
| 148 |
+
# Load image
|
| 149 |
+
image = Image.open('sample.png')
|
| 150 |
+
|
| 151 |
+
# Get predictions
|
| 152 |
+
output_image, confidence_info = predict(image)
|
| 153 |
+
```
|
| 154 |
+
|
| 155 |
+
### Python API
|
| 156 |
+
```python
|
| 157 |
+
import torch
|
| 158 |
+
from transformers import SegformerForSemanticSegmentation, SegformerImageProcessor
|
| 159 |
+
|
| 160 |
+
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
| 161 |
+
model = SegformerForSemanticSegmentation.from_pretrained(
|
| 162 |
+
'segformer_trained_weights'
|
| 163 |
+
).to(device)
|
| 164 |
+
processor = SegformerImageProcessor()
|
| 165 |
+
|
| 166 |
+
# Process image
|
| 167 |
+
image_input = processor(image, return_tensors='pt').to(device)
|
| 168 |
+
outputs = model(**image_input)
|
| 169 |
+
logits = outputs.logits
|
| 170 |
+
```
|
| 171 |
+
|
| 172 |
+
## 📁 Project Structure
|
| 173 |
+
|
| 174 |
+
```
|
| 175 |
+
.
|
| 176 |
+
├── app.py # Gradio web interface
|
| 177 |
+
├── train.py # Model training script
|
| 178 |
+
├── test.py # Comprehensive evaluation
|
| 179 |
+
├── test_simple.py # Simple testing without ground truth
|
| 180 |
+
├── download_dataset.py # Kaggle dataset download
|
| 181 |
+
├── prepare_dataset.py # Data preprocessing
|
| 182 |
+
├── diagnose.py # System diagnostics
|
| 183 |
+
├── demo.ipynb # Interactive notebook
|
| 184 |
+
├── requirements.txt # Python dependencies
|
| 185 |
+
├── LICENSE # MIT License
|
| 186 |
+
├── README.md # This file
|
| 187 |
+
├── TRAINING_GUIDE.md # Detailed training instructions
|
| 188 |
+
├── IMPLEMENTATION_SUMMARY.md # Technical details
|
| 189 |
+
├── FILE_INDEX.md # File navigation guide
|
| 190 |
+
├── samples/ # 40 pre-loaded sample images
|
| 191 |
+
├── segformer_trained_weights/ # Pre-trained model
|
| 192 |
+
│ ├── config.json
|
| 193 |
+
│ └── pytorch_model.bin
|
| 194 |
+
└── test_results_simple/ # Test outputs
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
## 🧠 Model Details
|
| 198 |
+
|
| 199 |
+
### Architecture
|
| 200 |
+
- **Model**: SegFormer-B0
|
| 201 |
+
- **Framework**: HuggingFace Transformers
|
| 202 |
+
- **Pre-training**: Cityscapes dataset
|
| 203 |
+
- **Fine-tuning**: UW-Madison GI Tract Dataset
|
| 204 |
+
|
| 205 |
+
### Specifications
|
| 206 |
+
| Aspect | Value |
|
| 207 |
+
|--------|-------|
|
| 208 |
+
| Input Size | 288 × 288 pixels |
|
| 209 |
+
| Output Classes | 4 (background + 3 organs) |
|
| 210 |
+
| Parameters | 64M |
|
| 211 |
+
| Model Size | 256 MB |
|
| 212 |
+
| Inference Time | ~500ms (CPU), ~100ms (GPU) |
|
| 213 |
+
|
| 214 |
+
### Normalization
|
| 215 |
+
```
|
| 216 |
+
Mean: [0.485, 0.456, 0.406]
|
| 217 |
+
Std: [0.229, 0.224, 0.225]
|
| 218 |
+
```
|
| 219 |
+
(ImageNet standard)
|
| 220 |
+
|
| 221 |
+
## 🎓 Training
|
| 222 |
+
|
| 223 |
+
### Download Full Dataset
|
| 224 |
+
```bash
|
| 225 |
+
# Requires Kaggle API key setup
|
| 226 |
+
python download_dataset.py
|
| 227 |
+
```
|
| 228 |
+
|
| 229 |
+
### Prepare Data
|
| 230 |
+
```bash
|
| 231 |
+
python prepare_dataset.py \
|
| 232 |
+
--data-dir /path/to/downloaded/data \
|
| 233 |
+
--output-dir prepared_data
|
| 234 |
+
```
|
| 235 |
+
|
| 236 |
+
### Train Model
|
| 237 |
+
```bash
|
| 238 |
+
python train.py \
|
| 239 |
+
--epochs 20 \
|
| 240 |
+
--batch-size 16 \
|
| 241 |
+
--learning-rate 1e-4 \
|
| 242 |
+
--train-dir prepared_data/train_images \
|
| 243 |
+
--val-dir prepared_data/val_images
|
| 244 |
+
```
|
| 245 |
+
|
| 246 |
+
### Evaluate
|
| 247 |
+
```bash
|
| 248 |
+
python test.py \
|
| 249 |
+
--model models/best_model \
|
| 250 |
+
--test-images prepared_data/test_images \
|
| 251 |
+
--test-masks prepared_data/test_masks \
|
| 252 |
+
--visualize
|
| 253 |
+
```
|
| 254 |
+
|
| 255 |
+
See [TRAINING_GUIDE.md](TRAINING_GUIDE.md) for detailed instructions.
|
| 256 |
+
|
| 257 |
+
## 📡 API Reference
|
| 258 |
+
|
| 259 |
+
### app.py
|
| 260 |
+
```python
|
| 261 |
+
def predict(image: Image.Image) -> Tuple[Image.Image, str]:
|
| 262 |
+
"""Perform segmentation on input image."""
|
| 263 |
+
|
| 264 |
+
def get_model(device: torch.device) -> SegformerForSemanticSegmentation:
|
| 265 |
+
"""Load pre-trained model."""
|
| 266 |
+
```
|
| 267 |
+
|
| 268 |
+
### test_simple.py
|
| 269 |
+
```python
|
| 270 |
+
class SimpleSegmentationTester:
|
| 271 |
+
def test_batch(self, image_paths: List[str]) -> Dict:
|
| 272 |
+
"""Segment multiple images."""
|
| 273 |
+
```
|
| 274 |
+
|
| 275 |
+
### train.py
|
| 276 |
+
```python
|
| 277 |
+
class MedicalImageSegmentationTrainer:
|
| 278 |
+
def train(self, num_epochs: int) -> None:
|
| 279 |
+
"""Train model with validation."""
|
| 280 |
+
```
|
| 281 |
+
|
| 282 |
+
## 🔄 Preprocessing Pipeline
|
| 283 |
+
|
| 284 |
+
1. **Image Resize**: 288 × 288
|
| 285 |
+
2. **Normalization**: ImageNet standard (mean/std)
|
| 286 |
+
3. **Tensor Conversion**: Convert to PyTorch tensors
|
| 287 |
+
4. **Device Transfer**: Move to GPU/CPU
|
| 288 |
+
|
| 289 |
+
## 📊 Output Format
|
| 290 |
+
|
| 291 |
+
### Web Interface
|
| 292 |
+
- Colored overlay image (red/green/blue for organs)
|
| 293 |
+
- Confidence percentages per organ
|
| 294 |
+
- Downloadable result image
|
| 295 |
+
|
| 296 |
+
### JSON Output (test_simple.py)
|
| 297 |
+
```json
|
| 298 |
+
{
|
| 299 |
+
"case101_day26": {
|
| 300 |
+
"large_bowel_pixels": 244,
|
| 301 |
+
"small_bowel_pixels": 1901,
|
| 302 |
+
"stomach_pixels": 2979,
|
| 303 |
+
"total_segmented": 5124
|
| 304 |
+
}
|
| 305 |
+
}
|
| 306 |
+
```
|
| 307 |
+
|
| 308 |
+
## 🐛 Troubleshooting
|
| 309 |
+
|
| 310 |
+
### ModuleNotFoundError
|
| 311 |
+
```bash
|
| 312 |
+
pip install -r requirements.txt --default-timeout=1000
|
| 313 |
+
```
|
| 314 |
+
|
| 315 |
+
### CUDA Out of Memory
|
| 316 |
+
```python
|
| 317 |
+
# Use CPU instead
|
| 318 |
+
device = torch.device('cpu')
|
| 319 |
+
|
| 320 |
+
# Or reduce batch size
|
| 321 |
+
batch_size = 4
|
| 322 |
+
```
|
| 323 |
+
|
| 324 |
+
### Model Loading Issues
|
| 325 |
+
```bash
|
| 326 |
+
python diagnose.py # Check all requirements
|
| 327 |
+
```
|
| 328 |
+
|
| 329 |
+
## 📈 Performance Metrics
|
| 330 |
+
|
| 331 |
+
Evaluated on validation set:
|
| 332 |
+
- **mIoU**: Intersection over Union
|
| 333 |
+
- **Precision**: Per-class accuracy
|
| 334 |
+
- **Recall**: Organ detection rate
|
| 335 |
+
- **F1-Score**: Harmonic mean
|
| 336 |
+
|
| 337 |
+
See [IMPLEMENTATION_SUMMARY.md](IMPLEMENTATION_SUMMARY.md) for details.
|
| 338 |
+
|
| 339 |
+
## 🤝 Contributing
|
| 340 |
+
|
| 341 |
+
Contributions welcome! Areas for improvement:
|
| 342 |
+
- [ ] Add more organ classes
|
| 343 |
+
- [ ] Improve inference speed
|
| 344 |
+
- [ ] Add DICOM format support
|
| 345 |
+
- [ ] Deploy to Hugging Face Spaces
|
| 346 |
+
- [ ] Add multi-modal support (CT/MRI)
|
| 347 |
+
|
| 348 |
+
## 📚 References
|
| 349 |
+
|
| 350 |
+
- [UW-Madison GI Tract Dataset](https://www.kaggle.com/competitions/uw-madison-gi-tract-image-segmentation)
|
| 351 |
+
- [SegFormer Paper](https://arxiv.org/abs/2105.15203)
|
| 352 |
+
- [HuggingFace Transformers](https://huggingface.co/docs/transformers)
|
| 353 |
+
|
| 354 |
+
## 📝 License
|
| 355 |
+
|
| 356 |
+
This project is licensed under the MIT License - see [LICENSE](LICENSE) file for details.
|
| 357 |
+
|
| 358 |
+
## 👥 Citation
|
| 359 |
+
|
| 360 |
+
If you use this project, please cite:
|
| 361 |
+
```bibtex
|
| 362 |
+
@software{medical_image_seg_2026,
|
| 363 |
+
title={Medical Image Segmentation - UW-Madison GI Tract},
|
| 364 |
+
author={Hungkm},
|
| 365 |
+
year={2026},
|
| 366 |
+
url={https://github.com/hung2903/medical-image-segmentation}
|
| 367 |
+
}
|
| 368 |
+
```
|
| 369 |
+
|
| 370 |
+
## 📧 Contact
|
| 371 |
+
|
| 372 |
+
For questions or issues:
|
| 373 |
+
- Open a GitHub issue
|
| 374 |
+
- Email: kmh2903.dsh@gmail.com
|
| 375 |
+
|
| 376 |
+
---
|
| 377 |
+
|
| 378 |
+
**Made with ❤️ for medical imaging**
|
TRAINING_GUIDE.md
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🏥 Medical Image Segmentation - Complete Guide
|
| 2 |
+
|
| 3 |
+
## 📋 Tổng Quan Dự Án
|
| 4 |
+
|
| 5 |
+
Dự án này phân đoạn tự động các cơ quan trong ảnh Y tế của đường tiêu hóa sử dụng **SegFormer** model từ HuggingFace Transformers.
|
| 6 |
+
|
| 7 |
+
### 🎯 Các Cơ Quan Được Phân Đoạn
|
| 8 |
+
- **Dạ dày** (Stomach) - 🔵 Xanh dương
|
| 9 |
+
- **Ruột non** (Small bowel) - 🟢 Xanh lá
|
| 10 |
+
- **Ruột già** (Large bowel) - 🔴 Đỏ
|
| 11 |
+
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
## 🚀 Quick Start
|
| 15 |
+
|
| 16 |
+
### 1. Chạy Ứng Dụng Demo
|
| 17 |
+
|
| 18 |
+
```bash
|
| 19 |
+
# Vào thư mục dự án
|
| 20 |
+
cd UWMGI_Medical_Image_Segmentation
|
| 21 |
+
|
| 22 |
+
# Chạy ứng dụng web
|
| 23 |
+
python app.py
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
Mở trình duyệt: **http://127.0.0.1:7860**
|
| 27 |
+
|
| 28 |
+
### 2. Sử Dụng Ứng Dụng
|
| 29 |
+
1. Upload ảnh Y tế hoặc chọn ảnh mẫu
|
| 30 |
+
2. Click nút "Generate Predictions"
|
| 31 |
+
3. Xem kết quả phân đoạn với màu sắc
|
| 32 |
+
|
| 33 |
+
---
|
| 34 |
+
|
| 35 |
+
## 📚 Hướng Dẫn Training (Huấn Luyện Mô Hình Mới)
|
| 36 |
+
|
| 37 |
+
### Bước 1: Cài Đặt Dependencies
|
| 38 |
+
|
| 39 |
+
```bash
|
| 40 |
+
pip install -r requirements.txt
|
| 41 |
+
pip install kaggle pandas scikit-learn matplotlib
|
| 42 |
+
```
|
| 43 |
+
|
| 44 |
+
### Bước 2: Tải Dataset từ Kaggle
|
| 45 |
+
|
| 46 |
+
```bash
|
| 47 |
+
python download_dataset.py
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
**Yêu cầu**: Kaggle API key (từ https://www.kaggle.com/account)
|
| 51 |
+
|
| 52 |
+
Nếu không có API key:
|
| 53 |
+
```bash
|
| 54 |
+
# Tạo thư mục Kaggle
|
| 55 |
+
mkdir ~/.kaggle
|
| 56 |
+
|
| 57 |
+
# Tải kaggle.json từ https://www.kaggle.com/account
|
| 58 |
+
# Lưu vào ~/.kaggle/kaggle.json
|
| 59 |
+
|
| 60 |
+
# Set permissions (Linux/Mac)
|
| 61 |
+
chmod 600 ~/.kaggle/kaggle.json
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
### Bước 3: Chuẩn Bị Dataset
|
| 65 |
+
|
| 66 |
+
```bash
|
| 67 |
+
python prepare_dataset.py
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
Script này sẽ:
|
| 71 |
+
- ✅ Giải mã RLE encoding thành mask images
|
| 72 |
+
- ✅ Chia train/val/test sets (80/10/10)
|
| 73 |
+
- ✅ Tạo cấu trúc folder chuẩn
|
| 74 |
+
|
| 75 |
+
**Kết quả**:
|
| 76 |
+
```
|
| 77 |
+
prepared_data/
|
| 78 |
+
├── train_images/ (80% ảnh)
|
| 79 |
+
├── train_masks/ (corresponding masks)
|
| 80 |
+
├── val_images/ (10% ảnh)
|
| 81 |
+
├── val_masks/
|
| 82 |
+
├── test_images/ (10% ảnh)
|
| 83 |
+
├── test_masks/
|
| 84 |
+
└── split.json (file metadata)
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
### Bước 4: Train Mô Hình
|
| 88 |
+
|
| 89 |
+
```bash
|
| 90 |
+
python train.py \
|
| 91 |
+
--data ./prepared_data \
|
| 92 |
+
--output-dir ./models \
|
| 93 |
+
--epochs 10 \
|
| 94 |
+
--batch-size 8 \
|
| 95 |
+
--learning-rate 1e-4
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
**Các tham số**:
|
| 99 |
+
- `--epochs`: Số lần lặp (mặc định: 10)
|
| 100 |
+
- `--batch-size`: Kích thước batch (mặc định: 8)
|
| 101 |
+
- `--learning-rate`: Tốc độ học (mặc định: 1e-4)
|
| 102 |
+
- `--num-workers`: Workers cho DataLoader (mặc định: 4)
|
| 103 |
+
|
| 104 |
+
**Kết quả**:
|
| 105 |
+
```
|
| 106 |
+
models/
|
| 107 |
+
├── best_model/ (model tốt nhất trên validation)
|
| 108 |
+
├── final_model/ (model sau training)
|
| 109 |
+
└── training_history.json (loss history)
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
---
|
| 113 |
+
|
| 114 |
+
## 🧪 Testing & Evaluation
|
| 115 |
+
|
| 116 |
+
### 1. Đánh Giá Trên Test Set
|
| 117 |
+
|
| 118 |
+
```bash
|
| 119 |
+
python test.py \
|
| 120 |
+
--model ./models/best_model \
|
| 121 |
+
--test-images ./prepared_data/test_images \
|
| 122 |
+
--test-masks ./prepared_data/test_masks \
|
| 123 |
+
--output-dir ./test_results
|
| 124 |
+
```
|
| 125 |
+
|
| 126 |
+
**Kết quả Metrics**:
|
| 127 |
+
- **mIoU** (mean Intersection over Union): 0.0 - 1.0 (cao hơn tốt hơn)
|
| 128 |
+
- **Precision**: Độ chính xác
|
| 129 |
+
- **Recall**: Độ nhạy
|
| 130 |
+
- **Per-class IoU**: Metrics cho từng cơ quan
|
| 131 |
+
|
| 132 |
+
### 2. Tạo Visualizations
|
| 133 |
+
|
| 134 |
+
```bash
|
| 135 |
+
python test.py \
|
| 136 |
+
--model ./models/best_model \
|
| 137 |
+
--test-images ./prepared_data/test_images \
|
| 138 |
+
--test-masks ./prepared_data/test_masks \
|
| 139 |
+
--output-dir ./test_results \
|
| 140 |
+
--visualize \
|
| 141 |
+
--num-samples 10
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
Sẽ tạo ra visualizations:
|
| 145 |
+
- Original image
|
| 146 |
+
- Prediction mask
|
| 147 |
+
- Confidence map
|
| 148 |
+
|
| 149 |
+
---
|
| 150 |
+
|
| 151 |
+
## 💻 Sử Dụng Mô Hình Tùy Chỉnh
|
| 152 |
+
|
| 153 |
+
### Thay Thế Mô Hình Mặc Định
|
| 154 |
+
|
| 155 |
+
```python
|
| 156 |
+
# Chỉnh sửa app.py
|
| 157 |
+
# Thay đổi dòng này:
|
| 158 |
+
model_dir = "./models/best_model" # Thay vào chỗ Configs.MODEL_PATH hoặc W&B artifact
|
| 159 |
+
```
|
| 160 |
+
|
| 161 |
+
Hoặc tạo script custom:
|
| 162 |
+
|
| 163 |
+
```python
|
| 164 |
+
from transformers import SegformerForSemanticSegmentation
|
| 165 |
+
import torch
|
| 166 |
+
from PIL import Image
|
| 167 |
+
|
| 168 |
+
# Load model
|
| 169 |
+
model = SegformerForSemanticSegmentation.from_pretrained("./models/best_model")
|
| 170 |
+
model.eval()
|
| 171 |
+
|
| 172 |
+
# Load image
|
| 173 |
+
image = Image.open("test.png").convert("RGB")
|
| 174 |
+
|
| 175 |
+
# Predict (xem app.py's predict function để chi tiết)
|
| 176 |
+
```
|
| 177 |
+
|
| 178 |
+
---
|
| 179 |
+
|
| 180 |
+
## 📊 Cấu Trúc File
|
| 181 |
+
|
| 182 |
+
```
|
| 183 |
+
UWMGI_Medical_Image_Segmentation/
|
| 184 |
+
├── app.py # Ứng dụng Gradio chính
|
| 185 |
+
├── download_dataset.py # Script tải dataset từ Kaggle
|
| 186 |
+
├── prepare_dataset.py # Script chuẩn bị dataset
|
| 187 |
+
├── train.py # Script training
|
| 188 |
+
├── test.py # Script testing & evaluation
|
| 189 |
+
├── requirements.txt # Dependencies
|
| 190 |
+
├── segformer_trained_weights/ # Pre-trained weights
|
| 191 |
+
├── samples/ # Ảnh mẫu
|
| 192 |
+
│
|
| 193 |
+
├── data/ # Raw dataset từ Kaggle
|
| 194 |
+
│ ├── train_images/
|
| 195 |
+
│ ├── test_images/
|
| 196 |
+
│ └── train_masks.csv
|
| 197 |
+
│
|
| 198 |
+
├── prepared_data/ # Processed dataset
|
| 199 |
+
│ ├── train_images/
|
| 200 |
+
│ ├── train_masks/
|
| 201 |
+
│ ├── val_images/
|
| 202 |
+
│ ├── val_masks/
|
| 203 |
+
│ ├── test_images/
|
| 204 |
+
│ ├── test_masks/
|
| 205 |
+
│ └── split.json
|
| 206 |
+
│
|
| 207 |
+
├── models/ # Trained models
|
| 208 |
+
│ ├── best_model/
|
| 209 |
+
│ ├── final_model/
|
| 210 |
+
│ └── training_history.json
|
| 211 |
+
│
|
| 212 |
+
└── test_results/ # Evaluation results
|
| 213 |
+
├── predictions/ # Predicted masks
|
| 214 |
+
├── visualizations/ # Visualization images
|
| 215 |
+
└── evaluation_results.json
|
| 216 |
+
```
|
| 217 |
+
|
| 218 |
+
---
|
| 219 |
+
|
| 220 |
+
## 🔧 Troubleshooting
|
| 221 |
+
|
| 222 |
+
### Lỗi: "Kaggle API not installed"
|
| 223 |
+
```bash
|
| 224 |
+
pip install kaggle
|
| 225 |
+
```
|
| 226 |
+
|
| 227 |
+
### Lỗi: "Kaggle credentials not found"
|
| 228 |
+
Xem hướng dẫn trong phần "Bước 2: Tải Dataset"
|
| 229 |
+
|
| 230 |
+
### GPU Memory Error
|
| 231 |
+
- Giảm batch-size: `--batch-size 4`
|
| 232 |
+
- Sử dụng CPU: Model sẽ tự detect CPU nếu GPU không available
|
| 233 |
+
|
| 234 |
+
### Dataset Quá Lớn
|
| 235 |
+
- Giảm số epochs: `--epochs 5`
|
| 236 |
+
- Tăng learning-rate: `--learning-rate 5e-4` (cẩn thận)
|
| 237 |
+
|
| 238 |
+
---
|
| 239 |
+
|
| 240 |
+
## 📈 Performance Tips
|
| 241 |
+
|
| 242 |
+
1. **Tăng chất lượng**:
|
| 243 |
+
- Tăng epochs (20-30)
|
| 244 |
+
- Tăng batch size (nếu GPU cho phép)
|
| 245 |
+
- Dùng augmentation (thêm vào prepare_dataset.py)
|
| 246 |
+
|
| 247 |
+
2. **Tăng tốc độ**:
|
| 248 |
+
- Giảm epochs
|
| 249 |
+
- Dùng mixed precision training
|
| 250 |
+
- Tăng num_workers (4-8)
|
| 251 |
+
|
| 252 |
+
3. **Tinh chỉnh hyperparameters**:
|
| 253 |
+
- Learning rate: 1e-5 to 5e-4
|
| 254 |
+
- Batch size: 4-32
|
| 255 |
+
- Warmup epochs: 2-3
|
| 256 |
+
|
| 257 |
+
---
|
| 258 |
+
|
| 259 |
+
## 📚 Dataset Format
|
| 260 |
+
|
| 261 |
+
### Input
|
| 262 |
+
- **Định dạng**: PNG, JPEG
|
| 263 |
+
- **Kích thước**: Tự động resize về 288x288
|
| 264 |
+
- **Channels**: RGB (3 channels)
|
| 265 |
+
|
| 266 |
+
### Output (Mask)
|
| 267 |
+
- **Giá trị pixel**:
|
| 268 |
+
- 0 = Background
|
| 269 |
+
- 1 = Large bowel
|
| 270 |
+
- 2 = Small bowel
|
| 271 |
+
- 3 = Stomach
|
| 272 |
+
|
| 273 |
+
---
|
| 274 |
+
|
| 275 |
+
## 🤝 Contributions
|
| 276 |
+
|
| 277 |
+
Muốn cải thiện dự án? Bạn có thể:
|
| 278 |
+
- Thêm augmentation techniques
|
| 279 |
+
- Cải tiến model architecture
|
| 280 |
+
- Thêm support cho các cơ quan khác
|
| 281 |
+
- Tối ưu performance
|
| 282 |
+
|
| 283 |
+
---
|
| 284 |
+
|
| 285 |
+
## 📞 Support
|
| 286 |
+
|
| 287 |
+
Nếu gặp vấn đề:
|
| 288 |
+
1. Kiểm tra error message
|
| 289 |
+
2. Xem phần Troubleshooting
|
| 290 |
+
3. Kiểm tra Kaggle/PyTorch documentation
|
| 291 |
+
|
| 292 |
+
---
|
| 293 |
+
|
| 294 |
+
## 📝 References
|
| 295 |
+
|
| 296 |
+
- **SegFormer**: https://huggingface.co/docs/transformers/model_doc/segformer
|
| 297 |
+
- **HuggingFace Transformers**: https://huggingface.co/
|
| 298 |
+
- **UW-Madison Challenge**: https://www.kaggle.com/competitions/uw-madison-gi-tract-image-segmentation
|
| 299 |
+
- **PyTorch**: https://pytorch.org/
|
| 300 |
+
- **Gradio**: https://www.gradio.app/
|
| 301 |
+
|
| 302 |
+
---
|
| 303 |
+
|
| 304 |
+
**Created**: January 2026
|
| 305 |
+
**License**: MIT
|
| 306 |
+
**Framework**: PyTorch + HuggingFace Transformers
|
app.py
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from typing import Tuple
|
| 3 |
+
import numpy as np
|
| 4 |
+
import gradio as gr
|
| 5 |
+
from glob import glob
|
| 6 |
+
from functools import partial
|
| 7 |
+
from dataclasses import dataclass
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
import torchvision.transforms as TF
|
| 12 |
+
from transformers import SegformerForSemanticSegmentation
|
| 13 |
+
|
| 14 |
+
"""
|
| 15 |
+
Medical Image Segmentation Web Interface
|
| 16 |
+
|
| 17 |
+
This module provides a Gradio-based web interface for performing semantic
|
| 18 |
+
segmentation on medical images using a pre-trained SegFormer model.
|
| 19 |
+
|
| 20 |
+
Features:
|
| 21 |
+
- Real-time image segmentation
|
| 22 |
+
- Confidence score visualization
|
| 23 |
+
- Color-coded organ detection (stomach, large bowel, small bowel)
|
| 24 |
+
- Interactive web interface
|
| 25 |
+
- CPU/GPU automatic detection
|
| 26 |
+
|
| 27 |
+
Author: Medical Image Segmentation Project
|
| 28 |
+
License: MIT
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
@dataclass
|
| 33 |
+
class Configs:
|
| 34 |
+
NUM_CLASSES: int = 4 # including background.
|
| 35 |
+
CLASSES: Tuple[str, ...] = ("Large bowel", "Small bowel", "Stomach")
|
| 36 |
+
IMAGE_SIZE: Tuple[int, int] = (288, 288) # W, H
|
| 37 |
+
MEAN: Tuple[float, ...] = (0.485, 0.456, 0.406)
|
| 38 |
+
STD: Tuple[float, ...] = (0.229, 0.224, 0.225)
|
| 39 |
+
MODEL_PATH: str = os.path.join(os.getcwd(), "segformer_trained_weights")
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def get_model(*, model_path, num_classes):
|
| 43 |
+
"""
|
| 44 |
+
Load pre-trained SegFormer model.
|
| 45 |
+
|
| 46 |
+
Args:
|
| 47 |
+
model_path (str): Path to model directory containing config.json and pytorch_model.bin
|
| 48 |
+
num_classes (int): Number of segmentation classes
|
| 49 |
+
|
| 50 |
+
Returns:
|
| 51 |
+
SegformerForSemanticSegmentation: Loaded model
|
| 52 |
+
|
| 53 |
+
Raises:
|
| 54 |
+
FileNotFoundError: If model files not found
|
| 55 |
+
RuntimeError: If model loading fails
|
| 56 |
+
"""
|
| 57 |
+
model = SegformerForSemanticSegmentation.from_pretrained(
|
| 58 |
+
model_path,
|
| 59 |
+
num_labels=num_classes,
|
| 60 |
+
ignore_mismatched_sizes=True
|
| 61 |
+
)
|
| 62 |
+
return model
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@torch.inference_mode()
|
| 66 |
+
def predict(input_image, model=None, preprocess_fn=None, device="cpu"):
|
| 67 |
+
"""
|
| 68 |
+
Perform semantic segmentation on input medical image.
|
| 69 |
+
|
| 70 |
+
Args:
|
| 71 |
+
input_image (PIL.Image): Input medical image
|
| 72 |
+
model (SegformerForSemanticSegmentation): Trained segmentation model
|
| 73 |
+
preprocess_fn (callable): Image preprocessing function
|
| 74 |
+
device (str or torch.device): Device to run inference on ('cpu' or 'cuda')
|
| 75 |
+
|
| 76 |
+
Returns:
|
| 77 |
+
Tuple[PIL.Image, str]:
|
| 78 |
+
- Color-coded segmentation mask
|
| 79 |
+
- Text with confidence scores for each organ
|
| 80 |
+
|
| 81 |
+
Raises:
|
| 82 |
+
ValueError: If input image is invalid
|
| 83 |
+
RuntimeError: If model inference fails
|
| 84 |
+
|
| 85 |
+
Example:
|
| 86 |
+
>>> from PIL import Image
|
| 87 |
+
>>> img = Image.open('medical_scan.png')
|
| 88 |
+
>>> output, info = predict(img, model, preprocess_fn, device)
|
| 89 |
+
"""
|
| 90 |
+
shape_H_W = input_image.size[::-1]
|
| 91 |
+
input_tensor = preprocess_fn(input_image)
|
| 92 |
+
input_tensor = input_tensor.unsqueeze(0).to(device)
|
| 93 |
+
|
| 94 |
+
# Generate predictions
|
| 95 |
+
outputs = model(pixel_values=input_tensor.to(device), return_dict=True)
|
| 96 |
+
predictions = F.interpolate(outputs["logits"], size=shape_H_W, mode="bilinear", align_corners=False)
|
| 97 |
+
|
| 98 |
+
# Get predicted class and confidence
|
| 99 |
+
probs = torch.softmax(predictions, dim=1)
|
| 100 |
+
preds_argmax = predictions.argmax(dim=1).cpu().squeeze().numpy()
|
| 101 |
+
confidence_map = probs.max(dim=1)[0].cpu().squeeze().numpy()
|
| 102 |
+
|
| 103 |
+
# Create segmentation info with confidence
|
| 104 |
+
seg_info = [
|
| 105 |
+
(preds_argmax == idx, f"{class_name} (confidence: {confidence_map[preds_argmax == idx].mean():.2%})")
|
| 106 |
+
for idx, class_name in enumerate(Configs.CLASSES, 1)
|
| 107 |
+
]
|
| 108 |
+
|
| 109 |
+
return (input_image, seg_info)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
if __name__ == "__main__":
|
| 113 |
+
"""
|
| 114 |
+
Main application entry point.
|
| 115 |
+
|
| 116 |
+
Initializes:
|
| 117 |
+
- Device selection (GPU/CPU)
|
| 118 |
+
- Model loading and setup
|
| 119 |
+
- Image preprocessing pipeline
|
| 120 |
+
- Gradio web interface
|
| 121 |
+
|
| 122 |
+
The web interface allows users to:
|
| 123 |
+
- Upload medical images
|
| 124 |
+
- Generate segmentation predictions
|
| 125 |
+
- View color-coded organ detection
|
| 126 |
+
- See confidence scores
|
| 127 |
+
"""
|
| 128 |
+
class2hexcolor = {"Stomach": "#007fff", "Small bowel": "#009A17", "Large bowel": "#FF0000"}
|
| 129 |
+
|
| 130 |
+
DEVICE = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu")
|
| 131 |
+
|
| 132 |
+
# Load model locally
|
| 133 |
+
try:
|
| 134 |
+
model_dir = Configs.MODEL_PATH
|
| 135 |
+
if not os.path.exists(model_dir):
|
| 136 |
+
print(f"Model path not found: {model_dir}")
|
| 137 |
+
model_dir = "./segformer_trained_weights"
|
| 138 |
+
except Exception as e:
|
| 139 |
+
print(f"Error loading model: {e}")
|
| 140 |
+
model_dir = "./segformer_trained_weights"
|
| 141 |
+
|
| 142 |
+
# Sử dụng đường dẫn từ W&B artifact hoặc mô hình cục bộ
|
| 143 |
+
model = get_model(model_path=model_dir, num_classes=Configs.NUM_CLASSES)
|
| 144 |
+
model.to(DEVICE)
|
| 145 |
+
model.eval()
|
| 146 |
+
_ = model(torch.randn(1, 3, *Configs.IMAGE_SIZE[::-1], device=DEVICE))
|
| 147 |
+
|
| 148 |
+
preprocess = TF.Compose(
|
| 149 |
+
[
|
| 150 |
+
TF.Resize(size=Configs.IMAGE_SIZE[::-1]),
|
| 151 |
+
TF.ToTensor(),
|
| 152 |
+
TF.Normalize(Configs.MEAN, Configs.STD, inplace=True),
|
| 153 |
+
]
|
| 154 |
+
)
|
| 155 |
+
|
| 156 |
+
with gr.Blocks(title="Medical Image Segmentation") as demo:
|
| 157 |
+
gr.Markdown("""
|
| 158 |
+
<h1><center>🏥 Medical Image Segmentation with UW-Madison GI Tract Dataset</center></h1>
|
| 159 |
+
<p><center>Phân đoạn tự động các cơ quan trong ảnh Y tế (Dạ dày, Ruột non, Ruột già)</center></p>
|
| 160 |
+
""")
|
| 161 |
+
|
| 162 |
+
with gr.Row():
|
| 163 |
+
with gr.Column():
|
| 164 |
+
gr.Markdown("### 📥 Input Image")
|
| 165 |
+
img_input = gr.Image(type="pil", height=360, width=360, label="Input image")
|
| 166 |
+
|
| 167 |
+
with gr.Column():
|
| 168 |
+
gr.Markdown("### 📊 Predictions")
|
| 169 |
+
img_output = gr.AnnotatedImage(label="Predictions", height=360, width=360, color_map=class2hexcolor)
|
| 170 |
+
|
| 171 |
+
section_btn = gr.Button("🎯 Generate Predictions", size="lg")
|
| 172 |
+
section_btn.click(partial(predict, model=model, preprocess_fn=preprocess, device=DEVICE), img_input, img_output)
|
| 173 |
+
|
| 174 |
+
gr.Markdown("---")
|
| 175 |
+
gr.Markdown("### 📸 Sample Images")
|
| 176 |
+
|
| 177 |
+
images_dir = glob(os.path.join(os.getcwd(), "samples") + os.sep + "*.png")
|
| 178 |
+
examples = [i for i in np.random.choice(images_dir, size=min(10, len(images_dir)), replace=False)]
|
| 179 |
+
|
| 180 |
+
gr.Examples(
|
| 181 |
+
examples=examples,
|
| 182 |
+
inputs=img_input,
|
| 183 |
+
outputs=img_output,
|
| 184 |
+
fn=partial(predict, model=model, preprocess_fn=preprocess, device=DEVICE),
|
| 185 |
+
cache_examples=False,
|
| 186 |
+
label="Click to load example"
|
| 187 |
+
)
|
| 188 |
+
|
| 189 |
+
gr.Markdown("""
|
| 190 |
+
---
|
| 191 |
+
### 🎨 Color Legend
|
| 192 |
+
- 🔵 **Blue (#007fff)**: Dạ dày (Stomach)
|
| 193 |
+
- 🟢 **Green (#009A17)**: Ruột non (Small bowel)
|
| 194 |
+
- 🔴 **Red (#FF0000)**: Ruột già (Large bowel)
|
| 195 |
+
|
| 196 |
+
### ℹ️ Information
|
| 197 |
+
- Model: SegFormer (HuggingFace Transformers)
|
| 198 |
+
- Input size: 288 × 288 pixels
|
| 199 |
+
- Framework: PyTorch + Gradio
|
| 200 |
+
""")
|
| 201 |
+
|
| 202 |
+
demo.launch()
|
demo.ipynb
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"id": "a3e1c6bc",
|
| 6 |
+
"metadata": {},
|
| 7 |
+
"source": [
|
| 8 |
+
"# 🏥 Medical Image Segmentation Demo\n",
|
| 9 |
+
"## UW-Madison GI Tract Segmentation using SegFormer\n",
|
| 10 |
+
"\n",
|
| 11 |
+
"This notebook demonstrates how to use the pre-trained SegFormer model to segment medical images of the GI tract (stomach, small bowel, large bowel)."
|
| 12 |
+
]
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"cell_type": "code",
|
| 16 |
+
"execution_count": null,
|
| 17 |
+
"id": "d82b1011",
|
| 18 |
+
"metadata": {},
|
| 19 |
+
"outputs": [],
|
| 20 |
+
"source": [
|
| 21 |
+
"import os\n",
|
| 22 |
+
"import numpy as np\n",
|
| 23 |
+
"from pathlib import Path\n",
|
| 24 |
+
"from dataclasses import dataclass\n",
|
| 25 |
+
"\n",
|
| 26 |
+
"import torch\n",
|
| 27 |
+
"import torch.nn.functional as F\n",
|
| 28 |
+
"import torchvision.transforms as TF\n",
|
| 29 |
+
"from transformers import SegformerForSemanticSegmentation\n",
|
| 30 |
+
"from PIL import Image\n",
|
| 31 |
+
"import matplotlib.pyplot as plt\n",
|
| 32 |
+
"import matplotlib.patches as mpatches\n",
|
| 33 |
+
"from glob import glob\n",
|
| 34 |
+
"import warnings\n",
|
| 35 |
+
"warnings.filterwarnings('ignore')\n",
|
| 36 |
+
"\n",
|
| 37 |
+
"# Display settings\n",
|
| 38 |
+
"plt.style.use('seaborn-v0_8-darkgrid')\n",
|
| 39 |
+
"%matplotlib inline\n",
|
| 40 |
+
"\n",
|
| 41 |
+
"# Define configuration\n",
|
| 42 |
+
"@dataclass\n",
|
| 43 |
+
"class Configs:\n",
|
| 44 |
+
" NUM_CLASSES: int = 4 # including background\n",
|
| 45 |
+
" CLASSES: tuple = (\"Large bowel\", \"Small bowel\", \"Stomach\")\n",
|
| 46 |
+
" IMAGE_SIZE: tuple = (288, 288) # W, H\n",
|
| 47 |
+
" MEAN: tuple = (0.485, 0.456, 0.406)\n",
|
| 48 |
+
" STD: tuple = (0.229, 0.224, 0.225)\n",
|
| 49 |
+
" MODEL_PATH: str = os.path.join(os.getcwd(), \"segformer_trained_weights\")\n",
|
| 50 |
+
"\n",
|
| 51 |
+
"config = Configs()\n",
|
| 52 |
+
"print(f\"✓ Configuration loaded\")\n",
|
| 53 |
+
"print(f\" Classes: {config.CLASSES}\")\n",
|
| 54 |
+
"print(f\" Image size: {config.IMAGE_SIZE}\")\n"
|
| 55 |
+
]
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"cell_type": "markdown",
|
| 59 |
+
"id": "61b319d6",
|
| 60 |
+
"metadata": {},
|
| 61 |
+
"source": [
|
| 62 |
+
"## 1️⃣ Load Pre-trained SegFormer Model"
|
| 63 |
+
]
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"cell_type": "code",
|
| 67 |
+
"execution_count": null,
|
| 68 |
+
"id": "f36ff588",
|
| 69 |
+
"metadata": {},
|
| 70 |
+
"outputs": [],
|
| 71 |
+
"source": [
|
| 72 |
+
"# Set device\n",
|
| 73 |
+
"device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
|
| 74 |
+
"print(f\"🖥️ Device: {device}\")\n",
|
| 75 |
+
"\n",
|
| 76 |
+
"# Load model\n",
|
| 77 |
+
"model = SegformerForSemanticSegmentation.from_pretrained(\n",
|
| 78 |
+
" config.MODEL_PATH,\n",
|
| 79 |
+
" num_labels=config.NUM_CLASSES,\n",
|
| 80 |
+
" ignore_mismatched_sizes=True\n",
|
| 81 |
+
")\n",
|
| 82 |
+
"model.to(device)\n",
|
| 83 |
+
"model.eval()\n",
|
| 84 |
+
"\n",
|
| 85 |
+
"# Test forward pass\n",
|
| 86 |
+
"with torch.no_grad():\n",
|
| 87 |
+
" dummy_input = torch.randn(1, 3, *config.IMAGE_SIZE[::-1], device=device)\n",
|
| 88 |
+
" _ = model(pixel_values=dummy_input)\n",
|
| 89 |
+
"\n",
|
| 90 |
+
"print(f\"✓ SegFormer model loaded successfully\")\n",
|
| 91 |
+
"print(f\" Total parameters: {sum(p.numel() for p in model.parameters())/1e6:.1f}M\")\n"
|
| 92 |
+
]
|
| 93 |
+
},
|
| 94 |
+
{
|
| 95 |
+
"cell_type": "markdown",
|
| 96 |
+
"id": "55e6ec48",
|
| 97 |
+
"metadata": {},
|
| 98 |
+
"source": [
|
| 99 |
+
"## 2️⃣ Define Image Preprocessing Pipeline"
|
| 100 |
+
]
|
| 101 |
+
},
|
| 102 |
+
{
|
| 103 |
+
"cell_type": "code",
|
| 104 |
+
"execution_count": null,
|
| 105 |
+
"id": "5fdb5622",
|
| 106 |
+
"metadata": {},
|
| 107 |
+
"outputs": [],
|
| 108 |
+
"source": [
|
| 109 |
+
"# Create preprocessing pipeline\n",
|
| 110 |
+
"preprocess = TF.Compose([\n",
|
| 111 |
+
" TF.Resize(size=config.IMAGE_SIZE[::-1]),\n",
|
| 112 |
+
" TF.ToTensor(),\n",
|
| 113 |
+
" TF.Normalize(config.MEAN, config.STD, inplace=True),\n",
|
| 114 |
+
"])\n",
|
| 115 |
+
"\n",
|
| 116 |
+
"print(\"✓ Preprocessing pipeline created\")\n",
|
| 117 |
+
"print(f\" - Resize: {config.IMAGE_SIZE}\")\n",
|
| 118 |
+
"print(f\" - Normalize with ImageNet statistics\")\n"
|
| 119 |
+
]
|
| 120 |
+
},
|
| 121 |
+
{
|
| 122 |
+
"cell_type": "markdown",
|
| 123 |
+
"id": "848798e0",
|
| 124 |
+
"metadata": {},
|
| 125 |
+
"source": [
|
| 126 |
+
"## 3️⃣ Implement Prediction Function"
|
| 127 |
+
]
|
| 128 |
+
},
|
| 129 |
+
{
|
| 130 |
+
"cell_type": "code",
|
| 131 |
+
"execution_count": null,
|
| 132 |
+
"id": "2862b1ca",
|
| 133 |
+
"metadata": {},
|
| 134 |
+
"outputs": [],
|
| 135 |
+
"source": [
|
| 136 |
+
"@torch.inference_mode()\n",
|
| 137 |
+
"def predict_segmentation(image_path):\n",
|
| 138 |
+
" \"\"\"\n",
|
| 139 |
+
" Predict segmentation for a medical image\n",
|
| 140 |
+
" \n",
|
| 141 |
+
" Args:\n",
|
| 142 |
+
" image_path: Path to input image\n",
|
| 143 |
+
" \n",
|
| 144 |
+
" Returns:\n",
|
| 145 |
+
" Dictionary with predictions and confidence scores\n",
|
| 146 |
+
" \"\"\"\n",
|
| 147 |
+
" # Load image\n",
|
| 148 |
+
" image = Image.open(image_path).convert(\"RGB\")\n",
|
| 149 |
+
" original_size = image.size[::-1] # (H, W)\n",
|
| 150 |
+
" \n",
|
| 151 |
+
" # Preprocess\n",
|
| 152 |
+
" input_tensor = preprocess(image)\n",
|
| 153 |
+
" input_tensor = input_tensor.unsqueeze(0).to(device)\n",
|
| 154 |
+
" \n",
|
| 155 |
+
" # Model inference\n",
|
| 156 |
+
" with torch.no_grad():\n",
|
| 157 |
+
" outputs = model(pixel_values=input_tensor, return_dict=True)\n",
|
| 158 |
+
" logits = outputs.logits\n",
|
| 159 |
+
" \n",
|
| 160 |
+
" # Interpolate to original size\n",
|
| 161 |
+
" predictions = F.interpolate(\n",
|
| 162 |
+
" logits,\n",
|
| 163 |
+
" size=original_size,\n",
|
| 164 |
+
" mode=\"bilinear\",\n",
|
| 165 |
+
" align_corners=False\n",
|
| 166 |
+
" )\n",
|
| 167 |
+
" \n",
|
| 168 |
+
" # Get predictions and confidence\n",
|
| 169 |
+
" probs = torch.softmax(predictions, dim=1)\n",
|
| 170 |
+
" pred_mask = predictions.argmax(dim=1)[0].cpu().numpy()\n",
|
| 171 |
+
" confidence_map = probs.max(dim=1)[0][0].cpu().numpy()\n",
|
| 172 |
+
" \n",
|
| 173 |
+
" return {\n",
|
| 174 |
+
" 'image': image,\n",
|
| 175 |
+
" 'pred_mask': pred_mask,\n",
|
| 176 |
+
" 'confidence_map': confidence_map,\n",
|
| 177 |
+
" 'original_size': original_size\n",
|
| 178 |
+
" }\n",
|
| 179 |
+
"\n",
|
| 180 |
+
"print(\"✓ Prediction function defined\")\n"
|
| 181 |
+
]
|
| 182 |
+
},
|
| 183 |
+
{
|
| 184 |
+
"cell_type": "markdown",
|
| 185 |
+
"id": "361ce3b5",
|
| 186 |
+
"metadata": {},
|
| 187 |
+
"source": [
|
| 188 |
+
"## 4️⃣ Load and Display Sample Medical Images"
|
| 189 |
+
]
|
| 190 |
+
},
|
| 191 |
+
{
|
| 192 |
+
"cell_type": "code",
|
| 193 |
+
"execution_count": null,
|
| 194 |
+
"id": "cee6daca",
|
| 195 |
+
"metadata": {},
|
| 196 |
+
"outputs": [],
|
| 197 |
+
"source": [
|
| 198 |
+
"# Load sample images\n",
|
| 199 |
+
"sample_dir = \"./samples\"\n",
|
| 200 |
+
"sample_images = sorted(glob(os.path.join(sample_dir, \"*.png\")))[:6]\n",
|
| 201 |
+
"\n",
|
| 202 |
+
"print(f\"✓ Found {len(sample_images)} sample images\")\n",
|
| 203 |
+
"\n",
|
| 204 |
+
"# Display sample images\n",
|
| 205 |
+
"fig, axes = plt.subplots(2, 3, figsize=(15, 10))\n",
|
| 206 |
+
"fig.suptitle(\"Sample Medical Images\", fontsize=16, fontweight='bold')\n",
|
| 207 |
+
"\n",
|
| 208 |
+
"for idx, (ax, img_path) in enumerate(zip(axes.flat, sample_images)):\n",
|
| 209 |
+
" image = Image.open(img_path).convert(\"RGB\")\n",
|
| 210 |
+
" ax.imshow(image)\n",
|
| 211 |
+
" ax.set_title(Path(img_path).stem)\n",
|
| 212 |
+
" ax.axis('off')\n",
|
| 213 |
+
"\n",
|
| 214 |
+
"plt.tight_layout()\n",
|
| 215 |
+
"plt.show()\n",
|
| 216 |
+
"\n",
|
| 217 |
+
"print(f\"Loaded {len(sample_images)} sample images for testing\")\n"
|
| 218 |
+
]
|
| 219 |
+
},
|
| 220 |
+
{
|
| 221 |
+
"cell_type": "markdown",
|
| 222 |
+
"id": "aeb2d9b7",
|
| 223 |
+
"metadata": {},
|
| 224 |
+
"source": [
|
| 225 |
+
"## 5️⃣ Perform Segmentation and Visualize Results"
|
| 226 |
+
]
|
| 227 |
+
},
|
| 228 |
+
{
|
| 229 |
+
"cell_type": "code",
|
| 230 |
+
"execution_count": null,
|
| 231 |
+
"id": "21aed5f3",
|
| 232 |
+
"metadata": {},
|
| 233 |
+
"outputs": [],
|
| 234 |
+
"source": [
|
| 235 |
+
"# Color mapping for organs\n",
|
| 236 |
+
"class2hexcolor = {\n",
|
| 237 |
+
" \"Large bowel\": \"#FF0000\", # Red\n",
|
| 238 |
+
" \"Small bowel\": \"#009A17\", # Green\n",
|
| 239 |
+
" \"Stomach\": \"#007fff\" # Blue\n",
|
| 240 |
+
"}\n",
|
| 241 |
+
"\n",
|
| 242 |
+
"# Predict on first 3 samples\n",
|
| 243 |
+
"sample_predictions = [predict_segmentation(img_path) for img_path in sample_images[:3]]\n",
|
| 244 |
+
"\n",
|
| 245 |
+
"# Visualize predictions\n",
|
| 246 |
+
"fig, axes = plt.subplots(3, 3, figsize=(18, 12))\n",
|
| 247 |
+
"fig.suptitle(\"Medical Image Segmentation Results\", fontsize=16, fontweight='bold')\n",
|
| 248 |
+
"\n",
|
| 249 |
+
"for row, pred_result in enumerate(sample_predictions):\n",
|
| 250 |
+
" image = pred_result['image']\n",
|
| 251 |
+
" pred_mask = pred_result['pred_mask']\n",
|
| 252 |
+
" confidence = pred_result['confidence_map']\n",
|
| 253 |
+
" \n",
|
| 254 |
+
" # Original image\n",
|
| 255 |
+
" axes[row, 0].imshow(image)\n",
|
| 256 |
+
" axes[row, 0].set_title(f\"Original Image\", fontweight='bold')\n",
|
| 257 |
+
" axes[row, 0].axis('off')\n",
|
| 258 |
+
" \n",
|
| 259 |
+
" # Prediction mask (colored)\n",
|
| 260 |
+
" pred_colored = np.zeros((*pred_mask.shape, 3))\n",
|
| 261 |
+
" colors = [(1, 0, 0), (0, 0.6, 0.1), (0, 0.5, 1)] # RGB for each class\n",
|
| 262 |
+
" for class_id, color in enumerate(colors, 1):\n",
|
| 263 |
+
" mask = (pred_mask == class_id)\n",
|
| 264 |
+
" pred_colored[mask] = color\n",
|
| 265 |
+
" \n",
|
| 266 |
+
" axes[row, 1].imshow(pred_colored)\n",
|
| 267 |
+
" axes[row, 1].set_title(f\"Segmentation Mask\", fontweight='bold')\n",
|
| 268 |
+
" axes[row, 1].axis('off')\n",
|
| 269 |
+
" \n",
|
| 270 |
+
" # Confidence map\n",
|
| 271 |
+
" im = axes[row, 2].imshow(confidence, cmap='hot')\n",
|
| 272 |
+
" axes[row, 2].set_title(f\"Confidence Map\", fontweight='bold')\n",
|
| 273 |
+
" axes[row, 2].axis('off')\n",
|
| 274 |
+
" plt.colorbar(im, ax=axes[row, 2], label='Confidence')\n",
|
| 275 |
+
"\n",
|
| 276 |
+
"plt.tight_layout()\n",
|
| 277 |
+
"plt.show()\n",
|
| 278 |
+
"\n",
|
| 279 |
+
"print(\"✓ Segmentation predictions generated successfully\")\n"
|
| 280 |
+
]
|
| 281 |
+
},
|
| 282 |
+
{
|
| 283 |
+
"cell_type": "markdown",
|
| 284 |
+
"id": "3f923634",
|
| 285 |
+
"metadata": {},
|
| 286 |
+
"source": [
|
| 287 |
+
"## 6️⃣ Create Color-Coded Segmentation Overlays"
|
| 288 |
+
]
|
| 289 |
+
},
|
| 290 |
+
{
|
| 291 |
+
"cell_type": "code",
|
| 292 |
+
"execution_count": null,
|
| 293 |
+
"id": "414a8549",
|
| 294 |
+
"metadata": {},
|
| 295 |
+
"outputs": [],
|
| 296 |
+
"source": [
|
| 297 |
+
"def create_overlay(image, mask, alpha=0.5):\n",
|
| 298 |
+
" \"\"\"\n",
|
| 299 |
+
" Create color-coded segmentation overlay\n",
|
| 300 |
+
" \"\"\"\n",
|
| 301 |
+
" image_array = np.array(image).astype(float) / 255.0\n",
|
| 302 |
+
" \n",
|
| 303 |
+
" # Create colored mask\n",
|
| 304 |
+
" overlay = image_array.copy()\n",
|
| 305 |
+
" \n",
|
| 306 |
+
" # Colors for each class (RGB)\n",
|
| 307 |
+
" colors = {\n",
|
| 308 |
+
" 1: np.array([1.0, 0.0, 0.0]), # Large bowel - Red\n",
|
| 309 |
+
" 2: np.array([0.0, 0.6, 0.1]), # Small bowel - Green\n",
|
| 310 |
+
" 3: np.array([0.0, 0.5, 1.0]) # Stomach - Blue\n",
|
| 311 |
+
" }\n",
|
| 312 |
+
" \n",
|
| 313 |
+
" for class_id, color in colors.items():\n",
|
| 314 |
+
" mask_region = (mask == class_id)\n",
|
| 315 |
+
" overlay[mask_region] = (\n",
|
| 316 |
+
" image_array[mask_region] * (1 - alpha) + \n",
|
| 317 |
+
" np.array(color) * alpha\n",
|
| 318 |
+
" )\n",
|
| 319 |
+
" \n",
|
| 320 |
+
" return (overlay * 255).astype(np.uint8)\n",
|
| 321 |
+
"\n",
|
| 322 |
+
"# Create overlays for all predictions\n",
|
| 323 |
+
"fig, axes = plt.subplots(3, 2, figsize=(15, 15))\n",
|
| 324 |
+
"fig.suptitle(\"Original vs Segmentation Overlay\", fontsize=16, fontweight='bold')\n",
|
| 325 |
+
"\n",
|
| 326 |
+
"class_names = {1: \"Large bowel\", 2: \"Small bowel\", 3: \"Stomach\"}\n",
|
| 327 |
+
"\n",
|
| 328 |
+
"for row, pred_result in enumerate(sample_predictions):\n",
|
| 329 |
+
" image = pred_result['image']\n",
|
| 330 |
+
" pred_mask = pred_result['pred_mask']\n",
|
| 331 |
+
" \n",
|
| 332 |
+
" # Original image\n",
|
| 333 |
+
" axes[row, 0].imshow(image)\n",
|
| 334 |
+
" axes[row, 0].set_title(\"Original Image\", fontweight='bold')\n",
|
| 335 |
+
" axes[row, 0].axis('off')\n",
|
| 336 |
+
" \n",
|
| 337 |
+
" # Overlay\n",
|
| 338 |
+
" overlay = create_overlay(image, pred_mask, alpha=0.4)\n",
|
| 339 |
+
" axes[row, 1].imshow(overlay)\n",
|
| 340 |
+
" \n",
|
| 341 |
+
" # Add detected classes info\n",
|
| 342 |
+
" detected_classes = [class_names[i] for i in np.unique(pred_mask) if i > 0]\n",
|
| 343 |
+
" title = f\"Overlay - Detected: {', '.join(detected_classes) if detected_classes else 'None'}\"\n",
|
| 344 |
+
" axes[row, 1].set_title(title, fontweight='bold')\n",
|
| 345 |
+
" axes[row, 1].axis('off')\n",
|
| 346 |
+
"\n",
|
| 347 |
+
"plt.tight_layout()\n",
|
| 348 |
+
"plt.show()\n",
|
| 349 |
+
"\n",
|
| 350 |
+
"print(\"✓ Segmentation overlays created\")\n"
|
| 351 |
+
]
|
| 352 |
+
},
|
| 353 |
+
{
|
| 354 |
+
"cell_type": "markdown",
|
| 355 |
+
"id": "e8da1936",
|
| 356 |
+
"metadata": {},
|
| 357 |
+
"source": [
|
| 358 |
+
"## 7️⃣ Evaluate Model Predictions on Batch Images"
|
| 359 |
+
]
|
| 360 |
+
},
|
| 361 |
+
{
|
| 362 |
+
"cell_type": "code",
|
| 363 |
+
"execution_count": null,
|
| 364 |
+
"id": "35db0fbf",
|
| 365 |
+
"metadata": {},
|
| 366 |
+
"outputs": [],
|
| 367 |
+
"source": [
|
| 368 |
+
"import pandas as pd\n",
|
| 369 |
+
"from tqdm import tqdm\n",
|
| 370 |
+
"\n",
|
| 371 |
+
"# Process all sample images\n",
|
| 372 |
+
"print(\"Processing all sample images...\")\n",
|
| 373 |
+
"batch_results = []\n",
|
| 374 |
+
"\n",
|
| 375 |
+
"for img_path in tqdm(sample_images):\n",
|
| 376 |
+
" try:\n",
|
| 377 |
+
" pred_result = predict_segmentation(img_path)\n",
|
| 378 |
+
" mask = pred_result['pred_mask']\n",
|
| 379 |
+
" confidence = pred_result['confidence_map']\n",
|
| 380 |
+
" \n",
|
| 381 |
+
" # Get detected organs\n",
|
| 382 |
+
" detected_organs = []\n",
|
| 383 |
+
" organ_confidences = []\n",
|
| 384 |
+
" \n",
|
| 385 |
+
" for class_id, organ_name in [(1, \"Large bowel\"), (2, \"Small bowel\"), (3, \"Stomach\")]:\n",
|
| 386 |
+
" if (mask == class_id).any():\n",
|
| 387 |
+
" organ_mask = (mask == class_id)\n",
|
| 388 |
+
" organ_conf = confidence[organ_mask].mean()\n",
|
| 389 |
+
" detected_organs.append(organ_name)\n",
|
| 390 |
+
" organ_confidences.append(f\"{organ_conf:.1%}\")\n",
|
| 391 |
+
" \n",
|
| 392 |
+
" batch_results.append({\n",
|
| 393 |
+
" 'Image': Path(img_path).stem,\n",
|
| 394 |
+
" 'Detected Organs': ', '.join(detected_organs) if detected_organs else 'None',\n",
|
| 395 |
+
" 'Avg Confidence': f\"{confidence.mean():.1%}\",\n",
|
| 396 |
+
" 'Max Confidence': f\"{confidence.max():.1%}\",\n",
|
| 397 |
+
" 'Min Confidence': f\"{confidence.min():.1%}\"\n",
|
| 398 |
+
" })\n",
|
| 399 |
+
" except Exception as e:\n",
|
| 400 |
+
" print(f\" Error processing {img_path}: {e}\")\n",
|
| 401 |
+
"\n",
|
| 402 |
+
"# Create results table\n",
|
| 403 |
+
"results_df = pd.DataFrame(batch_results)\n",
|
| 404 |
+
"\n",
|
| 405 |
+
"print(\"\\n\" + \"=\"*80)\n",
|
| 406 |
+
"print(\"📊 Batch Prediction Results\")\n",
|
| 407 |
+
"print(\"=\"*80)\n",
|
| 408 |
+
"display(results_df)\n",
|
| 409 |
+
"\n",
|
| 410 |
+
"# Summary statistics\n",
|
| 411 |
+
"print(\"\\n📈 Summary Statistics:\")\n",
|
| 412 |
+
"print(f\" Total images processed: {len(results_df)}\")\n",
|
| 413 |
+
"print(f\" Average confidence: {results_df['Avg Confidence'].apply(lambda x: float(x.strip('%'))/100).mean():.1%}\")\n",
|
| 414 |
+
"\n",
|
| 415 |
+
"# Create legend\n",
|
| 416 |
+
"print(\"\\n🎨 Color Legend:\")\n",
|
| 417 |
+
"print(\" 🔴 Red (#FF0000) : Large bowel\")\n",
|
| 418 |
+
"print(\" 🟢 Green (#009A17) : Small bowel\")\n",
|
| 419 |
+
"print(\" 🔵 Blue (#007fff) : Stomach\")\n"
|
| 420 |
+
]
|
| 421 |
+
}
|
| 422 |
+
],
|
| 423 |
+
"metadata": {
|
| 424 |
+
"language_info": {
|
| 425 |
+
"name": "python"
|
| 426 |
+
}
|
| 427 |
+
},
|
| 428 |
+
"nbformat": 4,
|
| 429 |
+
"nbformat_minor": 5
|
| 430 |
+
}
|
diagnose.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
"""
|
| 3 |
+
Diagnostic script - Kiểm tra tất cả lỗi trước khi chạy app
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import sys
|
| 7 |
+
import os
|
| 8 |
+
|
| 9 |
+
print("\n" + "="*70)
|
| 10 |
+
print("🔍 DIAGNOSTIC CHECK - Medical Image Segmentation App")
|
| 11 |
+
print("="*70)
|
| 12 |
+
|
| 13 |
+
# 1. Check Python version
|
| 14 |
+
print("\n1️⃣ Python Version:")
|
| 15 |
+
print(f" Version: {sys.version}")
|
| 16 |
+
if sys.version_info >= (3, 8):
|
| 17 |
+
print(" ✅ OK (>= 3.8)")
|
| 18 |
+
else:
|
| 19 |
+
print(" ❌ FAIL (need >= 3.8)")
|
| 20 |
+
sys.exit(1)
|
| 21 |
+
|
| 22 |
+
# 2. Check required modules
|
| 23 |
+
print("\n2️⃣ Checking Required Modules:")
|
| 24 |
+
required_modules = [
|
| 25 |
+
'torch',
|
| 26 |
+
'torchvision',
|
| 27 |
+
'transformers',
|
| 28 |
+
'gradio',
|
| 29 |
+
'numpy',
|
| 30 |
+
'PIL'
|
| 31 |
+
]
|
| 32 |
+
|
| 33 |
+
missing_modules = []
|
| 34 |
+
for module in required_modules:
|
| 35 |
+
try:
|
| 36 |
+
__import__(module)
|
| 37 |
+
print(f" ✅ {module}")
|
| 38 |
+
except ImportError as e:
|
| 39 |
+
print(f" ❌ {module}: {e}")
|
| 40 |
+
missing_modules.append(module)
|
| 41 |
+
|
| 42 |
+
if missing_modules:
|
| 43 |
+
print(f"\n❌ Missing modules: {', '.join(missing_modules)}")
|
| 44 |
+
print("Install with: pip install " + " ".join(missing_modules))
|
| 45 |
+
sys.exit(1)
|
| 46 |
+
|
| 47 |
+
# 3. Check model files
|
| 48 |
+
print("\n3️⃣ Checking Model Files:")
|
| 49 |
+
model_path = os.path.join(os.getcwd(), "segformer_trained_weights")
|
| 50 |
+
if os.path.exists(model_path):
|
| 51 |
+
print(f" ✅ Model path exists: {model_path}")
|
| 52 |
+
|
| 53 |
+
files = os.listdir(model_path)
|
| 54 |
+
print(f" Files in model dir: {files}")
|
| 55 |
+
|
| 56 |
+
if "pytorch_model.bin" in files:
|
| 57 |
+
print(" ✅ pytorch_model.bin found")
|
| 58 |
+
else:
|
| 59 |
+
print(" ⚠️ pytorch_model.bin NOT found")
|
| 60 |
+
|
| 61 |
+
if "config.json" in files:
|
| 62 |
+
print(" ✅ config.json found")
|
| 63 |
+
else:
|
| 64 |
+
print(" ⚠️ config.json NOT found")
|
| 65 |
+
else:
|
| 66 |
+
print(f" ❌ Model path NOT found: {model_path}")
|
| 67 |
+
|
| 68 |
+
# 4. Check samples directory
|
| 69 |
+
print("\n4️⃣ Checking Sample Images:")
|
| 70 |
+
samples_path = os.path.join(os.getcwd(), "samples")
|
| 71 |
+
if os.path.exists(samples_path):
|
| 72 |
+
sample_files = os.listdir(samples_path)
|
| 73 |
+
sample_count = len([f for f in sample_files if f.endswith('.png')])
|
| 74 |
+
print(f" ✅ Samples directory exists")
|
| 75 |
+
print(f" Found {sample_count} PNG images")
|
| 76 |
+
else:
|
| 77 |
+
print(f" ⚠️ Samples directory NOT found")
|
| 78 |
+
|
| 79 |
+
# 5. Try importing app modules
|
| 80 |
+
print("\n5️⃣ Testing App Imports:")
|
| 81 |
+
try:
|
| 82 |
+
import torch
|
| 83 |
+
print(" ✅ torch")
|
| 84 |
+
except ImportError as e:
|
| 85 |
+
print(f" ❌ torch: {e}")
|
| 86 |
+
sys.exit(1)
|
| 87 |
+
|
| 88 |
+
try:
|
| 89 |
+
import torch.nn.functional as F
|
| 90 |
+
print(" ✅ torch.nn.functional")
|
| 91 |
+
except ImportError as e:
|
| 92 |
+
print(f" ❌ torch.nn.functional: {e}")
|
| 93 |
+
sys.exit(1)
|
| 94 |
+
|
| 95 |
+
try:
|
| 96 |
+
import torchvision.transforms as TF
|
| 97 |
+
print(" ✅ torchvision.transforms")
|
| 98 |
+
except ImportError as e:
|
| 99 |
+
print(f" ❌ torchvision.transforms: {e}")
|
| 100 |
+
sys.exit(1)
|
| 101 |
+
|
| 102 |
+
try:
|
| 103 |
+
from transformers import SegformerForSemanticSegmentation
|
| 104 |
+
print(" ✅ transformers.SegformerForSemanticSegmentation")
|
| 105 |
+
except ImportError as e:
|
| 106 |
+
print(f" ❌ transformers: {e}")
|
| 107 |
+
sys.exit(1)
|
| 108 |
+
|
| 109 |
+
try:
|
| 110 |
+
import gradio as gr
|
| 111 |
+
print(" ✅ gradio")
|
| 112 |
+
except ImportError as e:
|
| 113 |
+
print(f" ❌ gradio: {e}")
|
| 114 |
+
sys.exit(1)
|
| 115 |
+
|
| 116 |
+
try:
|
| 117 |
+
from PIL import Image
|
| 118 |
+
print(" ✅ PIL.Image")
|
| 119 |
+
except ImportError as e:
|
| 120 |
+
print(f" ❌ PIL: {e}")
|
| 121 |
+
sys.exit(1)
|
| 122 |
+
|
| 123 |
+
# 6. Try loading the model
|
| 124 |
+
print("\n6️⃣ Testing Model Loading:")
|
| 125 |
+
try:
|
| 126 |
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 127 |
+
print(f" Device: {device}")
|
| 128 |
+
|
| 129 |
+
model = SegformerForSemanticSegmentation.from_pretrained(
|
| 130 |
+
model_path,
|
| 131 |
+
num_labels=4,
|
| 132 |
+
ignore_mismatched_sizes=True
|
| 133 |
+
)
|
| 134 |
+
model.to(device)
|
| 135 |
+
model.eval()
|
| 136 |
+
print(" ✅ Model loaded successfully")
|
| 137 |
+
print(f" Model parameters: {sum(p.numel() for p in model.parameters())/1e6:.1f}M")
|
| 138 |
+
except Exception as e:
|
| 139 |
+
print(f" ❌ Model loading failed: {e}")
|
| 140 |
+
import traceback
|
| 141 |
+
traceback.print_exc()
|
| 142 |
+
sys.exit(1)
|
| 143 |
+
|
| 144 |
+
# 7. Test preprocessing
|
| 145 |
+
print("\n7️⃣ Testing Preprocessing:")
|
| 146 |
+
try:
|
| 147 |
+
preprocess = TF.Compose([
|
| 148 |
+
TF.Resize(size=(288, 288)),
|
| 149 |
+
TF.ToTensor(),
|
| 150 |
+
TF.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225), inplace=True),
|
| 151 |
+
])
|
| 152 |
+
print(" ✅ Preprocessing pipeline created")
|
| 153 |
+
except Exception as e:
|
| 154 |
+
print(f" ❌ Preprocessing failed: {e}")
|
| 155 |
+
sys.exit(1)
|
| 156 |
+
|
| 157 |
+
# 8. Test with dummy input
|
| 158 |
+
print("\n8️⃣ Testing Inference with Dummy Input:")
|
| 159 |
+
try:
|
| 160 |
+
with torch.no_grad():
|
| 161 |
+
dummy = torch.randn(1, 3, 288, 288).to(device)
|
| 162 |
+
output = model(pixel_values=dummy)
|
| 163 |
+
print(" ✅ Model forward pass successful")
|
| 164 |
+
print(f" Output shape: {output.logits.shape}")
|
| 165 |
+
except Exception as e:
|
| 166 |
+
print(f" ❌ Model inference failed: {e}")
|
| 167 |
+
import traceback
|
| 168 |
+
traceback.print_exc()
|
| 169 |
+
sys.exit(1)
|
| 170 |
+
|
| 171 |
+
# 9. Check app.py syntax
|
| 172 |
+
print("\n9️⃣ Checking app.py Syntax:")
|
| 173 |
+
try:
|
| 174 |
+
with open("app.py", "r", encoding="utf-8") as f:
|
| 175 |
+
code = f.read()
|
| 176 |
+
compile(code, "app.py", "exec")
|
| 177 |
+
print(" ✅ app.py syntax OK")
|
| 178 |
+
except SyntaxError as e:
|
| 179 |
+
print(f" ❌ Syntax error: {e}")
|
| 180 |
+
sys.exit(1)
|
| 181 |
+
|
| 182 |
+
print("\n" + "="*70)
|
| 183 |
+
print("✅ ALL CHECKS PASSED - App should run successfully!")
|
| 184 |
+
print("="*70)
|
| 185 |
+
print("\n🚀 You can now run: python app.py\n")
|
download_dataset.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Script để tải UW-Madison GI Tract Image Segmentation dataset từ Kaggle
|
| 3 |
+
Cài đặt Kaggle API trước:
|
| 4 |
+
pip install kaggle
|
| 5 |
+
Tạo API key từ https://www.kaggle.com/account và lưu vào ~/.kaggle/kaggle.json
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import os
|
| 9 |
+
import subprocess
|
| 10 |
+
import shutil
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
|
| 13 |
+
def setup_kaggle_api():
|
| 14 |
+
"""Kiểm tra Kaggle API được cài đặt"""
|
| 15 |
+
try:
|
| 16 |
+
import kaggle
|
| 17 |
+
print("✓ Kaggle API đã được cài đặt")
|
| 18 |
+
return True
|
| 19 |
+
except ImportError:
|
| 20 |
+
print("✗ Kaggle API chưa được cài đặt")
|
| 21 |
+
print("Cài đặt: pip install kaggle")
|
| 22 |
+
return False
|
| 23 |
+
|
| 24 |
+
def check_kaggle_credentials():
|
| 25 |
+
"""Kiểm tra Kaggle credentials"""
|
| 26 |
+
kaggle_dir = Path.home() / ".kaggle"
|
| 27 |
+
kaggle_json = kaggle_dir / "kaggle.json"
|
| 28 |
+
|
| 29 |
+
if not kaggle_json.exists():
|
| 30 |
+
print("\n⚠️ Kaggle credentials không được tìm thấy!")
|
| 31 |
+
print("Hướng dẫn:")
|
| 32 |
+
print("1. Truy cập: https://www.kaggle.com/account")
|
| 33 |
+
print("2. Scroll xuống, click 'Create New API Token'")
|
| 34 |
+
print("3. File kaggle.json sẽ tải xuống")
|
| 35 |
+
print(f"4. Di chuyển file vào: {kaggle_dir}")
|
| 36 |
+
print("5. Chạy: chmod 600 ~/.kaggle/kaggle.json (trên Linux/Mac)")
|
| 37 |
+
return False
|
| 38 |
+
|
| 39 |
+
# Đặt permissions (Linux/Mac)
|
| 40 |
+
if not os.name == 'nt': # Không phải Windows
|
| 41 |
+
os.chmod(str(kaggle_json), 0o600)
|
| 42 |
+
|
| 43 |
+
print("✓ Kaggle credentials được tìm thấy")
|
| 44 |
+
return True
|
| 45 |
+
|
| 46 |
+
def download_dataset(competition_name="uw-madison-gi-tract-image-segmentation",
|
| 47 |
+
output_dir="./data"):
|
| 48 |
+
"""Tải dataset từ Kaggle competition"""
|
| 49 |
+
output_path = Path(output_dir)
|
| 50 |
+
output_path.mkdir(parents=True, exist_ok=True)
|
| 51 |
+
|
| 52 |
+
print(f"\n📥 Đang tải dataset từ Kaggle competition: {competition_name}")
|
| 53 |
+
print(f"📁 Thư mục đích: {output_path.absolute()}")
|
| 54 |
+
|
| 55 |
+
try:
|
| 56 |
+
cmd = [
|
| 57 |
+
"kaggle", "competitions", "download",
|
| 58 |
+
"-c", competition_name,
|
| 59 |
+
"-p", str(output_path)
|
| 60 |
+
]
|
| 61 |
+
|
| 62 |
+
print(f"\n⏳ Đang tải... (Điều này có thể mất vài phút)")
|
| 63 |
+
result = subprocess.run(cmd, check=True)
|
| 64 |
+
|
| 65 |
+
if result.returncode == 0:
|
| 66 |
+
print("✓ Tải xuống thành công!")
|
| 67 |
+
return True
|
| 68 |
+
else:
|
| 69 |
+
print("✗ Lỗi khi tải xuống")
|
| 70 |
+
return False
|
| 71 |
+
except subprocess.CalledProcessError as e:
|
| 72 |
+
print(f"✗ Lỗi: {e}")
|
| 73 |
+
print("Kiểm tra Kaggle credentials hoặc kết nối internet")
|
| 74 |
+
return False
|
| 75 |
+
except Exception as e:
|
| 76 |
+
print(f"✗ Lỗi: {e}")
|
| 77 |
+
return False
|
| 78 |
+
|
| 79 |
+
def extract_dataset(data_dir="./data"):
|
| 80 |
+
"""Giải nén các file ZIP trong thư mục"""
|
| 81 |
+
data_path = Path(data_dir)
|
| 82 |
+
|
| 83 |
+
if not data_path.exists():
|
| 84 |
+
print(f"✗ Thư mục {data_dir} không tồn tại")
|
| 85 |
+
return False
|
| 86 |
+
|
| 87 |
+
zip_files = list(data_path.glob("*.zip"))
|
| 88 |
+
|
| 89 |
+
if not zip_files:
|
| 90 |
+
print("ℹ️ Không có file ZIP để giải nén")
|
| 91 |
+
return True
|
| 92 |
+
|
| 93 |
+
print(f"\n📦 Đang giải nén {len(zip_files)} file(s)...")
|
| 94 |
+
|
| 95 |
+
try:
|
| 96 |
+
for zip_file in zip_files:
|
| 97 |
+
print(f" → {zip_file.name}")
|
| 98 |
+
shutil.unpack_archive(zip_file, data_path)
|
| 99 |
+
zip_file.unlink() # Xóa file ZIP sau khi giải nén
|
| 100 |
+
|
| 101 |
+
print("✓ Giải nén thành công!")
|
| 102 |
+
return True
|
| 103 |
+
except Exception as e:
|
| 104 |
+
print(f"✗ Lỗi: {e}")
|
| 105 |
+
return False
|
| 106 |
+
|
| 107 |
+
def verify_dataset(data_dir="./data"):
|
| 108 |
+
"""Kiểm tra cấu trúc dataset"""
|
| 109 |
+
data_path = Path(data_dir)
|
| 110 |
+
|
| 111 |
+
required_dirs = ["train_images", "train_masks", "test_images"]
|
| 112 |
+
existing_dirs = []
|
| 113 |
+
|
| 114 |
+
print("\n🔍 Kiểm tra cấu trúc dataset:")
|
| 115 |
+
|
| 116 |
+
for dir_name in required_dirs:
|
| 117 |
+
dir_path = data_path / dir_name
|
| 118 |
+
if dir_path.exists():
|
| 119 |
+
files_count = len(list(dir_path.glob("*")))
|
| 120 |
+
print(f" ✓ {dir_name}: {files_count} files")
|
| 121 |
+
existing_dirs.append(dir_name)
|
| 122 |
+
else:
|
| 123 |
+
print(f" ✗ {dir_name}: không tìm thấy")
|
| 124 |
+
|
| 125 |
+
return len(existing_dirs) > 0
|
| 126 |
+
|
| 127 |
+
def main():
|
| 128 |
+
"""Main function"""
|
| 129 |
+
print("=" * 60)
|
| 130 |
+
print("🎯 UW-Madison GI Tract Dataset Downloader")
|
| 131 |
+
print("=" * 60)
|
| 132 |
+
|
| 133 |
+
# 1. Kiểm tra Kaggle API
|
| 134 |
+
if not setup_kaggle_api():
|
| 135 |
+
print("\n⚠️ Vui lòng cài đặt Kaggle API trước")
|
| 136 |
+
return False
|
| 137 |
+
|
| 138 |
+
# 2. Kiểm tra Kaggle credentials
|
| 139 |
+
if not check_kaggle_credentials():
|
| 140 |
+
print("\n⚠️ Vui lòng cấu hình Kaggle credentials")
|
| 141 |
+
return False
|
| 142 |
+
|
| 143 |
+
# 3. Tải dataset
|
| 144 |
+
if not download_dataset():
|
| 145 |
+
return False
|
| 146 |
+
|
| 147 |
+
# 4. Giải nén dataset
|
| 148 |
+
if not extract_dataset():
|
| 149 |
+
return False
|
| 150 |
+
|
| 151 |
+
# 5. Kiểm tra dataset
|
| 152 |
+
if not verify_dataset():
|
| 153 |
+
print("\n⚠️ Dataset có thể bị lỗi, vui lòng kiểm tra thủ công")
|
| 154 |
+
return False
|
| 155 |
+
|
| 156 |
+
print("\n" + "=" * 60)
|
| 157 |
+
print("✅ Dataset đã sẵn sàng! Tiếp theo:")
|
| 158 |
+
print(" 1. Chạy: python prepare_dataset.py")
|
| 159 |
+
print(" 2. Sau đó: python train.py")
|
| 160 |
+
print("=" * 60)
|
| 161 |
+
|
| 162 |
+
return True
|
| 163 |
+
|
| 164 |
+
if __name__ == "__main__":
|
| 165 |
+
success = main()
|
| 166 |
+
exit(0 if success else 1)
|
prepare_dataset.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Script chuẩn bị dataset: chia train/val/test, tạo masks từ RLE encoding
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import os
|
| 6 |
+
import json
|
| 7 |
+
import numpy as np
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
from PIL import Image
|
| 10 |
+
import pandas as pd
|
| 11 |
+
from sklearn.model_selection import train_test_split
|
| 12 |
+
import warnings
|
| 13 |
+
warnings.filterwarnings('ignore')
|
| 14 |
+
|
| 15 |
+
class DatasetPreparator:
|
| 16 |
+
def __init__(self, data_dir="./data", output_dir="./prepared_data"):
|
| 17 |
+
self.data_dir = Path(data_dir)
|
| 18 |
+
self.output_dir = Path(output_dir)
|
| 19 |
+
self.output_dir.mkdir(parents=True, exist_ok=True)
|
| 20 |
+
|
| 21 |
+
# Tạo subdirectories
|
| 22 |
+
self.train_images_dir = self.output_dir / "train_images"
|
| 23 |
+
self.train_masks_dir = self.output_dir / "train_masks"
|
| 24 |
+
self.val_images_dir = self.output_dir / "val_images"
|
| 25 |
+
self.val_masks_dir = self.output_dir / "val_masks"
|
| 26 |
+
self.test_images_dir = self.output_dir / "test_images"
|
| 27 |
+
self.test_masks_dir = self.output_dir / "test_masks"
|
| 28 |
+
|
| 29 |
+
for dir_path in [self.train_images_dir, self.train_masks_dir,
|
| 30 |
+
self.val_images_dir, self.val_masks_dir,
|
| 31 |
+
self.test_images_dir, self.test_masks_dir]:
|
| 32 |
+
dir_path.mkdir(parents=True, exist_ok=True)
|
| 33 |
+
|
| 34 |
+
@staticmethod
|
| 35 |
+
def rle_decode(mask_rle, shape=(137, 236)):
|
| 36 |
+
"""Giải mã RLE encoding thành mask"""
|
| 37 |
+
if pd.isna(mask_rle):
|
| 38 |
+
return np.zeros(shape[0] * shape[1], dtype=np.uint8)
|
| 39 |
+
|
| 40 |
+
s = mask_rle.split()
|
| 41 |
+
starts, lengths = [np.asarray(x, dtype=int) for (x, y) in
|
| 42 |
+
zip(s[0:None:2], s[1:None:2])]
|
| 43 |
+
starts -= 1
|
| 44 |
+
ends = starts + lengths
|
| 45 |
+
|
| 46 |
+
img = np.zeros(shape[0] * shape[1], dtype=np.uint8)
|
| 47 |
+
for lo, hi in zip(starts, ends):
|
| 48 |
+
img[lo:hi] = 1
|
| 49 |
+
return img.reshape(shape[::-1]).T
|
| 50 |
+
|
| 51 |
+
def create_segmentation_mask(self, image_id, df_masks):
|
| 52 |
+
"""Tạo mask phân đoạn từ dữ liệu RLE"""
|
| 53 |
+
height, width = 137, 236
|
| 54 |
+
mask = np.zeros((height, width), dtype=np.uint8)
|
| 55 |
+
|
| 56 |
+
# Các class: 1=large_bowel, 2=small_bowel, 3=stomach
|
| 57 |
+
class_mapping = {'large_bowel': 1, 'small_bowel': 2, 'stomach': 3}
|
| 58 |
+
|
| 59 |
+
for idx, row in df_masks[df_masks['id'] == image_id].iterrows():
|
| 60 |
+
organ_class = class_mapping.get(row['organ'], 0)
|
| 61 |
+
if organ_class > 0:
|
| 62 |
+
rle_mask = self.rle_decode(row['segmentation'], shape=(height, width))
|
| 63 |
+
mask[rle_mask == 1] = organ_class
|
| 64 |
+
|
| 65 |
+
return mask
|
| 66 |
+
|
| 67 |
+
def process_dataset(self, train_size=0.8, val_size=0.1):
|
| 68 |
+
"""Xử lý toàn bộ dataset"""
|
| 69 |
+
print("\n📊 Đang chuẩn bị dataset...")
|
| 70 |
+
|
| 71 |
+
# 1. Tìm các ảnh huấn luyện
|
| 72 |
+
if (self.data_dir / "train_images").exists():
|
| 73 |
+
train_images = sorted(list((self.data_dir / "train_images").glob("*.png")))
|
| 74 |
+
print(f"✓ Tìm thấy {len(train_images)} ảnh huấn luyện")
|
| 75 |
+
else:
|
| 76 |
+
print("✗ Không tìm thấy thư mục train_images")
|
| 77 |
+
return False
|
| 78 |
+
|
| 79 |
+
# 2. Load RLE masks nếu có
|
| 80 |
+
train_masks_csv = self.data_dir / "train_masks.csv"
|
| 81 |
+
if train_masks_csv.exists():
|
| 82 |
+
df_masks = pd.read_csv(train_masks_csv)
|
| 83 |
+
print(f"✓ Load {len(df_masks)} mask annotations")
|
| 84 |
+
has_masks = True
|
| 85 |
+
else:
|
| 86 |
+
print("⚠️ Không tìm thấy train_masks.csv, bỏ qua giải mã RLE")
|
| 87 |
+
has_masks = False
|
| 88 |
+
|
| 89 |
+
# 3. Chia train/val/test
|
| 90 |
+
image_ids = [img.stem for img in train_images]
|
| 91 |
+
train_ids, test_ids = train_test_split(
|
| 92 |
+
image_ids, test_size=(1-train_size), random_state=42
|
| 93 |
+
)
|
| 94 |
+
train_ids, val_ids = train_test_split(
|
| 95 |
+
train_ids, test_size=val_size/(train_size), random_state=42
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
print(f" Train: {len(train_ids)}, Val: {len(val_ids)}, Test: {len(test_ids)}")
|
| 99 |
+
|
| 100 |
+
# 4. Copy ảnh và tạo masks
|
| 101 |
+
dataset_splits = {
|
| 102 |
+
'train': (train_ids, self.train_images_dir, self.train_masks_dir),
|
| 103 |
+
'val': (val_ids, self.val_images_dir, self.val_masks_dir),
|
| 104 |
+
'test': (test_ids, self.test_images_dir, self.test_masks_dir)
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
for split_name, (ids, images_dir, masks_dir) in dataset_splits.items():
|
| 108 |
+
print(f"\n 📁 Xử lý {split_name} set ({len(ids)} ảnh)...")
|
| 109 |
+
|
| 110 |
+
for i, img_id in enumerate(ids):
|
| 111 |
+
# Copy ảnh
|
| 112 |
+
src_img = self.data_dir / "train_images" / f"{img_id}.png"
|
| 113 |
+
if src_img.exists():
|
| 114 |
+
dst_img = images_dir / f"{img_id}.png"
|
| 115 |
+
Image.open(src_img).save(dst_img)
|
| 116 |
+
|
| 117 |
+
# Tạo mask
|
| 118 |
+
if has_masks:
|
| 119 |
+
mask = self.create_segmentation_mask(img_id, df_masks)
|
| 120 |
+
mask_img = Image.fromarray(mask)
|
| 121 |
+
mask_img.save(masks_dir / f"{img_id}_mask.png")
|
| 122 |
+
|
| 123 |
+
if (i + 1) % max(1, len(ids) // 5) == 0 or i == 0:
|
| 124 |
+
print(f" → {i+1}/{len(ids)} hoàn thành")
|
| 125 |
+
|
| 126 |
+
# 5. Lưu split info
|
| 127 |
+
split_info = {
|
| 128 |
+
'train': train_ids,
|
| 129 |
+
'val': val_ids,
|
| 130 |
+
'test': test_ids
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
with open(self.output_dir / "split.json", 'w') as f:
|
| 134 |
+
json.dump(split_info, f, indent=2)
|
| 135 |
+
|
| 136 |
+
print(f"\n✓ Split info lưu tại: {self.output_dir / 'split.json'}")
|
| 137 |
+
|
| 138 |
+
return True
|
| 139 |
+
|
| 140 |
+
def get_dataset_statistics(self):
|
| 141 |
+
"""Thống kê dataset"""
|
| 142 |
+
print("\n📈 Thống kê dataset:")
|
| 143 |
+
|
| 144 |
+
for split_dir in [self.train_images_dir, self.val_images_dir, self.test_images_dir]:
|
| 145 |
+
split_name = split_dir.parent.name.replace('_images', '')
|
| 146 |
+
num_images = len(list(split_dir.glob("*.png")))
|
| 147 |
+
total_size_mb = sum(f.stat().st_size for f in split_dir.glob("*.png")) / (1024*1024)
|
| 148 |
+
print(f" {split_name:8} - {num_images:5} ảnh ({total_size_mb:8.2f} MB)")
|
| 149 |
+
|
| 150 |
+
def main():
|
| 151 |
+
print("=" * 60)
|
| 152 |
+
print("🎯 Dataset Preparation Tool")
|
| 153 |
+
print("=" * 60)
|
| 154 |
+
|
| 155 |
+
preparator = DatasetPreparator(
|
| 156 |
+
data_dir="./data",
|
| 157 |
+
output_dir="./prepared_data"
|
| 158 |
+
)
|
| 159 |
+
|
| 160 |
+
if preparator.process_dataset():
|
| 161 |
+
preparator.get_dataset_statistics()
|
| 162 |
+
|
| 163 |
+
print("\n" + "=" * 60)
|
| 164 |
+
print("✅ Dataset đã được chuẩn bị! Tiếp theo:")
|
| 165 |
+
print(" python train.py --data ./prepared_data")
|
| 166 |
+
print("=" * 60)
|
| 167 |
+
return True
|
| 168 |
+
|
| 169 |
+
return False
|
| 170 |
+
|
| 171 |
+
if __name__ == "__main__":
|
| 172 |
+
success = main()
|
| 173 |
+
exit(0 if success else 1)
|
requirements.txt
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# PyTorch (CPU version - change to GPU if needed)
|
| 2 |
+
--find-links https://download.pytorch.org/whl/torch_stable.html
|
| 3 |
+
torch==2.0.0+cpu
|
| 4 |
+
torchvision==0.15.0
|
| 5 |
+
|
| 6 |
+
# Core dependencies
|
| 7 |
+
transformers==4.30.2
|
| 8 |
+
gradio==4.48.1
|
| 9 |
+
numpy>=1.21.0
|
| 10 |
+
Pillow>=9.0.0
|
| 11 |
+
|
| 12 |
+
# Data processing
|
| 13 |
+
pandas>=1.3.0
|
| 14 |
+
scikit-learn>=1.0.0
|
| 15 |
+
|
| 16 |
+
# Visualization
|
| 17 |
+
matplotlib>=3.4.0
|
| 18 |
+
|
| 19 |
+
# Utilities
|
| 20 |
+
tqdm>=4.62.0
|
| 21 |
+
opencv-python>=4.5.0
|
| 22 |
+
|
| 23 |
+
# Optional: Weights & Biases (for experiment tracking)
|
| 24 |
+
# wandb>=0.13.0
|
| 25 |
+
|
| 26 |
+
# Optional: For GPU support, replace torch+cpu with:
|
| 27 |
+
# torch==2.0.0
|
| 28 |
+
# torchaudio==2.0.0
|
| 29 |
+
# torchvision==0.15.0
|
samples/case101_day26_slice_0096_266_266_1.50_1.50.png
ADDED
|
samples/case107_day0_slice_0089_266_266_1.50_1.50.png
ADDED
|
samples/case107_day21_slice_0069_266_266_1.50_1.50.png
ADDED
|
samples/case113_day12_slice_0108_360_310_1.50_1.50.png
ADDED
|
samples/case119_day20_slice_0063_266_266_1.50_1.50.png
ADDED
|
samples/case119_day25_slice_0075_266_266_1.50_1.50.png
ADDED
|
samples/case119_day25_slice_0095_266_266_1.50_1.50.png
ADDED
|
samples/case121_day14_slice_0057_266_266_1.50_1.50.png
ADDED
|
samples/case122_day25_slice_0087_266_266_1.50_1.50.png
ADDED
|
samples/case124_day19_slice_0110_266_266_1.50_1.50.png
ADDED
|
samples/case124_day20_slice_0110_266_266_1.50_1.50.png
ADDED
|
samples/case130_day0_slice_0106_266_266_1.50_1.50.png
ADDED
|
samples/case134_day21_slice_0085_360_310_1.50_1.50.png
ADDED
|
samples/case139_day0_slice_0062_234_234_1.50_1.50.png
ADDED
|
samples/case139_day18_slice_0094_266_266_1.50_1.50.png
ADDED
|
samples/case146_day25_slice_0053_276_276_1.63_1.63.png
ADDED
|
samples/case147_day0_slice_0085_360_310_1.50_1.50.png
ADDED
|
samples/case148_day0_slice_0113_360_310_1.50_1.50.png
ADDED
|
samples/case149_day15_slice_0057_266_266_1.50_1.50.png
ADDED
|
samples/case29_day0_slice_0065_266_266_1.50_1.50.png
ADDED
|
samples/case2_day1_slice_0054_266_266_1.50_1.50.png
ADDED
|
samples/case2_day1_slice_0077_266_266_1.50_1.50.png
ADDED
|
samples/case32_day19_slice_0091_266_266_1.50_1.50.png
ADDED
|
samples/case32_day19_slice_0100_266_266_1.50_1.50.png
ADDED
|
samples/case33_day21_slice_0114_266_266_1.50_1.50.png
ADDED
|
samples/case36_day16_slice_0064_266_266_1.50_1.50.png
ADDED
|
samples/case40_day0_slice_0094_266_266_1.50_1.50.png
ADDED
|
samples/case41_day25_slice_0049_266_266_1.50_1.50.png
ADDED
|
samples/case63_day22_slice_0076_266_266_1.50_1.50.png
ADDED
|
samples/case63_day26_slice_0093_266_266_1.50_1.50.png
ADDED
|
samples/case65_day28_slice_0133_266_266_1.50_1.50.png
ADDED
|
samples/case66_day36_slice_0101_266_266_1.50_1.50.png
ADDED
|
samples/case67_day0_slice_0049_266_266_1.50_1.50.png
ADDED
|
samples/case67_day0_slice_0086_266_266_1.50_1.50.png
ADDED
|
samples/case74_day18_slice_0101_266_266_1.50_1.50.png
ADDED
|
samples/case74_day19_slice_0084_266_266_1.50_1.50.png
ADDED
|