diff --git a/docs/architecture-analytical.html b/docs/architecture-analytical.html new file mode 100644 index 0000000..fd85ff3 --- /dev/null +++ b/docs/architecture-analytical.html @@ -0,0 +1,446 @@ + + architecture-analytical + + + + + + + + + + + + + + + + +
+ +

Аналитический документ: DXA Quality Assessment System

+

Содержание

+
    +
  1. Обзор системы
  2. +
  3. Архитектура
  4. +
  5. Компоненты системы
  6. +
  7. Пайплайн обработки
  8. +
  9. API Endpoints
  10. +
  11. Модели и алгоритмы
  12. +
  13. PlantUML Диаграммы
  14. +
+
+

Обзор системы

+

Система DXA Quality Assessment — это медицинский AI-сервис для автоматизированной оценки качества исследований DXA (денситометрия костей). Система анализирует DICOM файлы и определяет:

+ +

Целевое назначение

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ПараметрЗначение
ТипМедицинский AI сервис
Входные данныеDICOM файлы (DXA исследования)
Выходные данныеJSON / XLSX отчеты
Тип классификацииБинарный (OK / Violation)
Целевые регионыПозвоночник (L1-L4), Бедро (левое/правое)
+
+

Архитектура

+

Высокоуровневая архитектура

+
┌─────────────────────────────────────────────────────────────────────────┐
+│                          DXA Quality Assessment                          │
+├─────────────────────────────────────────────────────────────────────────┤
+│                                                                          │
+│  ┌──────────────┐    ┌──────────────┐    ┌──────────────────────────┐ │
+│  │   Клиент     │───▶│   FastAPI    │───▶│   Orchestrator           │ │
+│  │  (Web UI)    │    │   Server     │    │   (Pipeline Control)    │ │
+│  └──────────────┘    └──────────────┘    └───────────┬──────────────┘ │
+│                                                        │                 │
+│                                    ┌───────────────────┼───────────────┐ │
+│                                    ▼                   ▼               ▼ │
+│                           ┌───────────────┐  ┌─────────────┐  ┌──────────┐ │
+│                           │ DXA Classifier │  │  Segmentor │  │ Quality  │ │
+│                           │   (ResNet18)   │  │ (Threshold) │  │  Scorer  │ │
+│                           └───────────────┘  └─────────────┘  └──────────┘ │
+│                                                                          │
+│  ┌────────────────────────────────────────────────────────────────────┐  │
+│  │                    Detailed Assessment Module                      │  │
+│  │  ┌─────────┐  ┌──────────┐  ┌─────────┐  ┌───────┐  ┌────────┐ │  │
+│  │  │  Motion │  │ Artifact │  │ Position│  │  ROI  │  │Rotation│ │  │
+│  │  │Detector │  │ Detector │  │Validator│  │ Check │  │ Checker│ │  │
+│  │  └─────────┘  └──────────┘  └─────────┘  └───────┘  └────────┘ │  │
+│  └────────────────────────────────────────────────────────────────────┘  │
+│                                                                          │
+└─────────────────────────────────────────────────────────────────────────┘
+

Технологический стек

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
КомпонентТехнология
BackendPython 3.10, FastAPI, Uvicorn
ML/DLPyTorch, torchvision (ResNet18)
Image ProcessingPIL, OpenCV, pydicom, scipy
Data Handlingpandas, openpyxl
ContainerizationDocker
+
+

Компоненты системы

+

1. API Layer (src/main.py)

+

Точка входа — FastAPI приложение с REST эндпоинтами.

+

Основные функции:

+ +
# Основной API эндпоинт
+@app.post("/api/v1/analyze/detailed")
+async def analyze_dicom_detailed(file: UploadFile = File(...)):
+    # 1. Загрузка модели
+    # 2. Предобработка DICOM
+    # 3. Инференс ResNet18
+    # 4. Генерация сегментации
+    # 5. Детальная оценка качества
+    # 6. Формирование ответа
+

2. DXA Classifier (src/dxa/model.py)

+

Модель для бинарной классификации качества изображения.

+

Архитектура:

+
Input (224x224x3)
+    │
+    ▼
+ResNet18 (pretrained on ImageNet)
+    │
+    ├── Remove final FC layer
+    │
+    ▼
+Classifier Head:
+    ├── Dropout(0.3)
+    ├── Linear(512 → 256)
+    ├── ReLU
+    ├── Dropout(0.3)
+    └── Linear(256 → 2)
+    │
+    ▼
+Output: [prob_OK, prob_Violation]
+

Параметры модели:

+ +

3. Segmentator (Простая реализация)

+

Для сегментации используется простой пороговый метод:

+
# Threshold-based segmentation
+threshold = np.percentile(image, 90)
+segmentation = (image > threshold).astype(np.uint8)
+

Планируется: Замена на UNet или TotalSegmentator для более точной сегментации.

+

4. Detailed Assessment (src/quality/detailed_assessment.py)

+

Модуль детальной оценки качества включает:

+

4.1 Motion Detection

+ +

4.2 Artifact Detection

+ +

4.3 Region-Specific Checks

+

Для позвоночника (spine):

+ +

Для бедра (hip):

+ +

4.4 ROI Validation

+ +

5. Region Detector (src/dxa/inference.py)

+

Определение анатомической области по содержимому изображения:

+

Алгоритм:

+
    +
  1. Пороговая бинаризация (95 перцентиль)
  2. +
  3. Вычисление bounding box яркой области
  4. +
  5. Соотношение сторон bbox: +
      +
    • < 1.5 → spine (более квадратная область)
    • +
    • ≥ 1.5 → hip (вытянутая область)
    • +
    +
  6. +
  7. Для hip: определение левого/правого по асимметрии яркости
  8. +
+
+

Пайплайн обработки

+

Основной пайплайн

+

DXA Quality Assessment PipelineUpload DICOM fileParse DICOM metadata- Normalize to 0-1- Convert to 3-channel- Resize to 224x224Preprocess image- Get prediction (OK/Violation)- Get confidence scoresModel Inference (ResNet18)- bbox_aspect ratio analysis- left/right brightness ratioDetermine Anatomical Region- Threshold-based (90th percentile)Generate Segmentation- Motion detection- Artifact detection- Region-specific checks- ROI validationDetailed Quality Assessment- correct- position_error- artifact_motion- artifact_other- labeling_error- incomplete_view- roi_error- rotationDetermine Violation TypeGenerate Response

Детальная диаграмма последовательности

+

Sequence: Detailed AnalysisUserFastAPIDXA ModelRegion DetectorSegmentatorQuality AssessorUserUserFastAPIFastAPIDXA ModelDXA ModelRegion DetectorRegion DetectorSegmentatorSegmentatorQuality AssessorQuality AssessorPOST /api/v1/analyze/detailedpreprocess_dicom()predict(image)[prediction, confidence]determine_region(image)"spine" | "hip_left" | "hip_right"segment(image)segmentation_maskgenerate_quality_report()- detect_motion_blur()- detect_artifacts()- check_spine_completeness() / check_hip_completeness()- check_hip_rotation()- check_roi_boundaries()- determine_violation_type()quality_reportconvert_to_serializable()JSON response


+

API Endpoints

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodEndpointDescription
GET/Web interface
GET/api/v1/healthHealth check
POST/api/v1/analyzeBasic analysis
POST/api/v1/analyze/detailedDetailed analysis with metrics
POST/api/v1/analyze/srDICOM SR report
POST/api/v1/batchBatch processing
POST/api/v1/exportExport to XLSX
+

Response Format (Detailed)

+
{
+  "anatomical_region": "spine",
+  "quality_class": 1,
+  "quality_label": "Violation detected",
+  "violation_type": "artifact_motion",
+  "reason": "Обнаружен артефакт движения (размытие)",
+  "confidence": 0.85,
+  "confidence_per_class": {
+    "correct": 0.15,
+    "violation": 0.85
+  },
+  "view_quality": "full",
+  "metrics": {
+    "motion": {
+      "motion_detected": true,
+      "blur_laplacian": 0.001,
+      "severity": "HIGH"
+    },
+    "artifacts": {
+      "any_detected": false,
+      "metal_detected": false
+    },
+    "roi_check": {
+      "valid": true
+    }
+  },
+  "overall_quality": "POOR",
+  "severity": "HIGH"
+}
+

+

Модели и алгоритмы

+

Типы нарушений

+

Для позвоночника:

+ +

Для бедра:

+ +

Метрики детекции движения

+ + + + + + + + + + + + + + + + + + + + + + + + + +
МетрикаПорогОписание
Laplacian variance< 0.002Низкая дисперсия = размытие
FFT high-freq ratio< 0.3Низкая высокочастотная энергия
Edge duplicationboolДублирование краев
+

Алгоритм определения региона

+

Error 400: Cannot find if (Assumed diagram type: activity) (line: 22)


+

PlantUML Диаграммы

+

Диаграмма компонентов

+

Web ClientFastAPI ServerDXA ModelsQuality AssessmentFile SystemWeb UIUpload HandlerPreprocessorResponse BuilderResNet18 ClassifierRegion DetectorSegmentatorMotion DetectorArtifact DetectorPosition ValidatorROI CheckerRotation CheckerDICOM FilesModel WeightsHTTPRead/WriteInferenceQuality Metrics

Диаграмма классов (основные сущности)

+

DXAQualityClassifierbackbone: strnum_classes: intforward(x: Tensor) -> Tensorextract_features(x: Tensor) -> TensorDXAQualityModelmodel: DXAQualityClassifierdevice: strpredict(images: Tensor) -> (preds, probs)train_epoch(loader)validate(loader)DetailedAssessmentdetect_motion_blur(image) -> Dictdetect_artifacts(image, segmentation) -> Dictcheck_spine_completeness(segmentation) -> Dictcheck_hip_completeness(segmentation) -> Dictcheck_hip_rotation(segmentation, image) -> Dictcheck_roi_boundaries(segmentation, shape) -> Dictgenerate_quality_report(...) -> DictRegionDetectordetermine_region_from_image(image) -> strdetermine_anatomical_region(dcm_path) -> strusesprovides region

Диаграмма развертывания

+

Client LayerApplication LayerML PipelineInfrastructureBrowserWeb UI (HTML/JS)FastAPIUvicornDXA ClassifierSegmentatorQuality AssessorCPU/GPUFile SystemDockerPyTorchNumPy/SciPyModels/Data


+

Потоки данных

+

InputPreprocessingML ModelsQuality AnalysisOutputDICOM FileNormalization3-Channel ConvResize 224x224ResNet18Region DetectorThreshold SegMotion DetectionArtifact DetectionCompleteness CheckROI ValidationJSON ResponseXLSX Export


+

Ограничения и планы развития

+

Текущие ограничения

+
    +
  1. Сегментация — простая пороговая обработка, требует замены на Deep Learning (UNet/TotalSegmentator)
  2. +
  3. Модель — бинарная классификация, нужно расширение до многоклассовой для типов нарушений
  4. +
  5. Dataset — ~100 исследований, требуется расширение до 500+
  6. +
  7. F1 Score — текущий ~0.27, требует улучшения
  8. +
+

Планируемые улучшения

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
КомпонентПлан
Region DetectorОтдельная модель для детекции региона
SegmentatorUNet или TotalSegmentator
Quality ClassifierРасширение на 8 классов нарушений
Violation ClassifierОтдельная модель для классификации типа нарушения
Artifact DetectorCNN для детекции металла/имплантатов
VisualizationGrad-CAM для внимания модели
+
+

Заключение

+

Система DXA Quality Assessment представляет собой полнофункциональный AI-сервис для автоматизированной оценки качества DXA исследований. Архитектура построена на принципах модульности и расширяемости, что позволяет поэтапно улучшать отдельные компоненты.

+

Основные характеристики:

+ + +
+ + + + + + + + + + \ No newline at end of file diff --git a/docs/architecture-analytical.md b/docs/architecture-analytical.md new file mode 100644 index 0000000..befc39a --- /dev/null +++ b/docs/architecture-analytical.md @@ -0,0 +1,652 @@ +# Аналитический документ: DXA Quality Assessment System + +## Содержание + +1. [Обзор системы](#обзор-системы) +2. [Архитектура](#архитектура) +3. [Компоненты системы](#компоненты-системы) +4. [Пайплайн обработки](#пайплайн-обработки) +5. [API Endpoints](#api-endpoints) +6. [Модели и алгоритмы](#модели-и-алгоритмы) +7. [PlantUML Диаграммы](#plantuml-диаграммы) + +--- + +## Обзор системы + +Система DXA Quality Assessment — это медицинский AI-сервис для автоматизированной оценки качества исследований DXA (денситометрия костей). Система анализирует DICOM файлы и определяет: + +- **Анатомическую область**: позвоночник (spine) или бедро (hip_left/hip_right) +- **Качество изображения**: OK (класс 0) или нарушение (класс 1) +- **Тип нарушения**: движение, артефакты, позиционирование, ROI, ротация + +### Целевое назначение + +| Параметр | Значение | +|----------|----------| +| Тип | Медицинский AI сервис | +| Входные данные | DICOM файлы (DXA исследования) | +| Выходные данные | JSON / XLSX отчеты | +| Тип классификации | Бинарный (OK / Violation) | +| Целевые регионы | Позвоночник (L1-L4), Бедро (левое/правое) | + +--- + +## Архитектура + +### Высокоуровневая архитектура + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ DXA Quality Assessment │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │ +│ │ Клиент │───▶│ FastAPI │───▶│ Orchestrator │ │ +│ │ (Web UI) │ │ Server │ │ (Pipeline Control) │ │ +│ └──────────────┘ └──────────────┘ └───────────┬──────────────┘ │ +│ │ │ +│ ┌───────────────────┼───────────────┐ │ +│ ▼ ▼ ▼ │ +│ ┌───────────────┐ ┌─────────────┐ ┌──────────┐ │ +│ │ DXA Classifier │ │ Segmentor │ │ Quality │ │ +│ │ (ResNet18) │ │ (Threshold) │ │ Scorer │ │ +│ └───────────────┘ └─────────────┘ └──────────┘ │ +│ │ +│ ┌────────────────────────────────────────────────────────────────────┐ │ +│ │ Detailed Assessment Module │ │ +│ │ ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌───────┐ ┌────────┐ │ │ +│ │ │ Motion │ │ Artifact │ │ Position│ │ ROI │ │Rotation│ │ │ +│ │ │Detector │ │ Detector │ │Validator│ │ Check │ │ Checker│ │ │ +│ │ └─────────┘ └──────────┘ └─────────┘ └───────┘ └────────┘ │ │ +│ └────────────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### Технологический стек + +| Компонент | Технология | +|-----------|------------| +| Backend | Python 3.10, FastAPI, Uvicorn | +| ML/DL | PyTorch, torchvision (ResNet18) | +| Image Processing | PIL, OpenCV, pydicom, scipy | +| Data Handling | pandas, openpyxl | +| Containerization | Docker | + +--- + +## Компоненты системы + +### 1. API Layer (`src/main.py`) + +Точка входа — FastAPI приложение с REST эндпоинтами. + +**Основные функции:** +- Прием DICOM файлов через multipart/form-data +- Предобработка изображений (нормализация, ресайз до 224x224) +- Инференс модели +- Формирование ответов + +```python +# Основной API эндпоинт +@app.post("/api/v1/analyze/detailed") +async def analyze_dicom_detailed(file: UploadFile = File(...)): + # 1. Загрузка модели + # 2. Предобработка DICOM + # 3. Инференс ResNet18 + # 4. Генерация сегментации + # 5. Детальная оценка качества + # 6. Формирование ответа +``` + +### 2. DXA Classifier (`src/dxa/model.py`) + +Модель для бинарной классификации качества изображения. + +**Архитектура:** +``` +Input (224x224x3) + │ + ▼ +ResNet18 (pretrained on ImageNet) + │ + ├── Remove final FC layer + │ + ▼ +Classifier Head: + ├── Dropout(0.3) + ├── Linear(512 → 256) + ├── ReLU + ├── Dropout(0.3) + └── Linear(256 → 2) + │ + ▼ +Output: [prob_OK, prob_Violation] +``` + +**Параметры модели:** +- Backbone: ResNet18 (ImageNet pretrained) +- Input: 224x224 RGB +- Output: 2 класса (OK / Violation) +- Dropout: 0.3 + +### 3. Segmentator (Простая реализация) + +Для сегментации используется простой пороговый метод: + +```python +# Threshold-based segmentation +threshold = np.percentile(image, 90) +segmentation = (image > threshold).astype(np.uint8) +``` + +**Планируется:** Замена на UNet или TotalSegmentator для более точной сегментации. + +### 4. Detailed Assessment (`src/quality/detailed_assessment.py`) + +Модуль детальной оценки качества включает: + +#### 4.1 Motion Detection +- **Laplacian variance** — дисперсия лапласиана (ниже = размытие) +- **FFT blur** — анализ высокочастотной энергии (ниже = размытие) +- **Edge duplication** — проверка "призрачных" краев + +#### 4.2 Artifact Detection +- **Metal detection** — яркие пятна (>99.5 перцентиль) +- **Implant detection** — линейные структуры +- **Cement detection** — локальные яркие области +- **Calcification** — малые яркие пятна + +#### 4.3 Region-Specific Checks + +**Для позвоночника (spine):** +- Проверка количества позвонков (3-4 для поясничного отдела) +- Проверка полноты (не обрезаны ли позвонки) +- Проверка выравнивания позвонков +- Проверка контуров позвонков + +**Для бедра (hip):** +- Проверка полноты видимости (шейка бедра, головка) +- Проверка ротации (угол главной оси) +- Проверка соотношения сторон + +#### 4.4 ROI Validation +- Проверка отступов от краев +- Проверка размера ROI + +### 5. Region Detector (`src/dxa/inference.py`) + +Определение анатомической области по содержимому изображения: + +**Алгоритм:** +1. Пороговая бинаризация (95 перцентиль) +2. Вычисление bounding box яркой области +3. Соотношение сторон bbox: + - < 1.5 → **spine** (более квадратная область) + - ≥ 1.5 → **hip** (вытянутая область) +4. Для hip: определение левого/правого по асимметрии яркости + +--- + +## Пайплайн обработки + +### Основной пайплайн + +```plantuml +@startuml +title DXA Quality Assessment Pipeline + +start +:Upload DICOM file; +:Parse DICOM metadata; +:Preprocess image; + +note right + - Normalize to 0-1 + - Convert to 3-channel + - Resize to 224x224 +end note + +:Model Inference (ResNet18); +note right + - Get prediction (OK/Violation) + - Get confidence scores +end note + +:Determine Anatomical Region; +note right + - bbox_aspect ratio analysis + - left/right brightness ratio +end note + +:Generate Segmentation; +note right + - Threshold-based (90th percentile) +end note + +:Detailed Quality Assessment; + +note right + - Motion detection + - Artifact detection + - Region-specific checks + - ROI validation +end note + +:Determine Violation Type; + +note right + - correct + - position_error + - artifact_motion + - artifact_other + - labeling_error + - incomplete_view + - roi_error + - rotation +end note + +:Generate Response; +stop + +@enduml +``` + +### Детальная диаграмма последовательности + +```plantuml +@startuml +title Sequence: Detailed Analysis + +actor User +participant "FastAPI" as API +participant "DXA Model" as Model +participant "Region Detector" as Region +participant "Segmentator" as Seg +participant "Quality Assessor" as QA + +User -> API: POST /api/v1/analyze/detailed +API -> API: preprocess_dicom() +API -> Model: predict(image) +Model --> API: [prediction, confidence] + +API -> Region: determine_region(image) +Region --> API: "spine" | "hip_left" | "hip_right" + +API -> Seg: segment(image) +Seg --> API: segmentation_mask + +API -> QA: generate_quality_report() +note over QA + - detect_motion_blur() + - detect_artifacts() + - check_spine_completeness() / check_hip_completeness() + - check_hip_rotation() + - check_roi_boundaries() + - determine_violation_type() +end note + +QA --> API: quality_report + +API -> API: convert_to_serializable() +API --> User: JSON response + +@enduml +``` + +--- + +## API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/` | Web interface | +| GET | `/api/v1/health` | Health check | +| POST | `/api/v1/analyze` | Basic analysis | +| POST | `/api/v1/analyze/detailed` | Detailed analysis with metrics | +| POST | `/api/v1/analyze/sr` | DICOM SR report | +| POST | `/api/v1/batch` | Batch processing | +| POST | `/api/v1/export` | Export to XLSX | + +### Response Format (Detailed) + +```json +{ + "anatomical_region": "spine", + "quality_class": 1, + "quality_label": "Violation detected", + "violation_type": "artifact_motion", + "reason": "Обнаружен артефакт движения (размытие)", + "confidence": 0.85, + "confidence_per_class": { + "correct": 0.15, + "violation": 0.85 + }, + "view_quality": "full", + "metrics": { + "motion": { + "motion_detected": true, + "blur_laplacian": 0.001, + "severity": "HIGH" + }, + "artifacts": { + "any_detected": false, + "metal_detected": false + }, + "roi_check": { + "valid": true + } + }, + "overall_quality": "POOR", + "severity": "HIGH" +} +``` + +--- + +## Модели и алгоритмы + +### Типы нарушений + +**Для позвоночника:** +- `correct` — качество соответствует норме +- `position_error` — ошибка позиционирования +- `artifact_motion` — артефакт движения (размытие) +- `artifact_other` — другие артефакты +- `labeling_error` — ошибка маркировки +- `incomplete_view` — неполный вид +- `roi_error` — ошибка ROI + +**Для бедра:** +- `correct` — качество соответствует норме +- `position_error` — ошибка позиционирования +- `rotation` — нарушение ротации +- `artifact_motion` — артефакт движения +- `artifact_other` — другие артефакты +- `roi_error` — ошибка ROI +- `incomplete_view` — неполный вид + +### Метрики детекции движения + +| Метрика | Порог | Описание | +|---------|-------|----------| +| Laplacian variance | < 0.002 | Низкая дисперсия = размытие | +| FFT high-freq ratio | < 0.3 | Низкая высокочастотная энергия | +| Edge duplication | bool | Дублирование краев | + +### Алгоритм определения региона + +```plantuml +@startuml +title Region Detection Algorithm + +start +:Load DICOM image; +:Normalize to 0-1; +:Threshold at 95th percentile; + +if (binary.sum > 0?) then (yes) + :Calculate bounding box; + :Compute bbox_aspect = height / width; + + if (bbox_aspect < 1.5) then (yes) + :return "spine"; + else (no) + if (bbox_aspect < 1.8) then (yes) + :Calculate symmetry; + if (symmetry > 0.35) then (yes) + :return "spine"; + else (no) + :return "hip"; + end + else (no) + :Calculate left/right brightness ratio; + + if (ratio > 1.3) then (yes) + :return "hip_right"; + else if (ratio < 0.7) then (yes) + :return "hip_left"; + else (no) + :return "hip"; + end + end + end +else (no) + :Fallback: height < 270 → hip; + :return "spine"; +end + +stop + +@enduml +``` + +--- + +## PlantUML Диаграммы + +### Диаграмма компонентов + +```plantuml +@startuml +!theme plain +skinparam componentStyle uml2 + +component "Web Client" as Client { + [Web UI] +} + +component "FastAPI Server" as API { + [Upload Handler] + [Preprocessor] + [Response Builder] +} + +component "DXA Models" as Models { + [ResNet18 Classifier] + [Region Detector] + [Segmentator] +} + +component "Quality Assessment" as QA { + [Motion Detector] + [Artifact Detector] + [Position Validator] + [ROI Checker] + [Rotation Checker] +} + +database "File System" as FS { + [DICOM Files] + [Model Weights] +} + +Client -down-> API : HTTP +API -down-> FS : Read/Write +API -right-> Models : Inference +Models -down-> QA : Quality Metrics + +@enduml +``` + +### Диаграмма классов (основные сущности) + +```plantuml +@startuml +!theme plain + +class DXAQualityClassifier { + +backbone: str + +num_classes: int + +forward(x: Tensor) -> Tensor + +extract_features(x: Tensor) -> Tensor +} + +class DXAQualityModel { + +model: DXAQualityClassifier + +device: str + +predict(images: Tensor) -> (preds, probs) + +train_epoch(loader) + +validate(loader) +} + +class DetailedAssessment { + +detect_motion_blur(image) -> Dict + +detect_artifacts(image, segmentation) -> Dict + +check_spine_completeness(segmentation) -> Dict + +check_hip_completeness(segmentation) -> Dict + +check_hip_rotation(segmentation, image) -> Dict + +check_roi_boundaries(segmentation, shape) -> Dict + +generate_quality_report(...) -> Dict +} + +class RegionDetector { + +determine_region_from_image(image) -> str + +determine_anatomical_region(dcm_path) -> str +} + +DXAQualityModel --> DXAQualityClassifier +DXAQualityModel ..> DetailedAssessment : uses +RegionDetector ..> DetailedAssessment : provides region + +@enduml +``` + +### Диаграмма развертывания + +```plantuml +@startuml +!theme plain +skinparam rectangle { + BackgroundColor #White + BorderColor #Black +} + +rectangle "Client Layer" { + rectangle "Browser" as Browser + rectangle "Web UI (HTML/JS)" as WebUI +} + +rectangle "Application Layer" { + rectangle "FastAPI" as API + rectangle "Uvicorn" as Uvicorn +} + +rectangle "ML Pipeline" { + rectangle "DXA Classifier" as Classifier + rectangle "Segmentator" as Segmentator + rectangle "Quality Assessor" as Assessor +} + +rectangle "Infrastructure" { + rectangle "CPU/GPU" as Compute + rectangle "File System" as Storage + rectangle "Docker" as Docker +} + +Browser -right-> WebUI +WebUI -right-> API +API -right-> Uvicorn +Uvicorn -right-> Classifier +Uvicorn -right-> Segmentator +Classifier -right-> Assessor +Classifier -up-> Compute : PyTorch +Segmentator -up-> Compute : NumPy/SciPy +API -down-> Storage : Models/Data +Compute -up-> Docker + +@enduml +``` + +--- + +## Потоки данных + +```plantuml +@startuml +!theme plain +skinparam ranksep 50 +skinparam nodesep 50 + +node "Input" { + [DICOM File] +} + +node "Preprocessing" { + [Normalization] + [3-Channel Conv] + [Resize 224x224] +} + +node "ML Models" { + [ResNet18] + [Region Detector] + [Threshold Seg] +} + +node "Quality Analysis" { + [Motion Detection] + [Artifact Detection] + [Completeness Check] + [ROI Validation] +} + +node "Output" { + [JSON Response] + [XLSX Export] +} + +[DICOM File] --> [Normalization] +[Normalization] --> [3-Channel Conv] +[3-Channel Conv] --> [Resize 224x224] + +[Resize 224x224] --> [ResNet18] +[Resize 224x224] --> [Region Detector] +[Resize 224x224] --> [Threshold Seg] + +[ResNet18] --> [Quality Analysis] +[Region Detector] --> [Quality Analysis] +[Threshold Seg] --> [Quality Analysis] + +[Quality Analysis] --> [JSON Response] +[Quality Analysis] --> [XLSX Export] + +@enduml +``` + +--- + +## Ограничения и планы развития + +### Текущие ограничения + +1. **Сегментация** — простая пороговая обработка, требует замены на Deep Learning (UNet/TotalSegmentator) +2. **Модель** — бинарная классификация, нужно расширение до многоклассовой для типов нарушений +3. **Dataset** — ~100 исследований, требуется расширение до 500+ +4. **F1 Score** — текущий ~0.27, требует улучшения + +### Планируемые улучшения + +| Компонент | План | +|-----------|------| +| Region Detector | Отдельная модель для детекции региона | +| Segmentator | UNet или TotalSegmentator | +| Quality Classifier | Расширение на 8 классов нарушений | +| Violation Classifier | Отдельная модель для классификации типа нарушения | +| Artifact Detector | CNN для детекции металла/имплантатов | +| Visualization | Grad-CAM для внимания модели | + +--- + +## Заключение + +Система DXA Quality Assessment представляет собой полнофункциональный AI-сервис для автоматизированной оценки качества DXA исследований. Архитектура построена на принципах модульности и расширяемости, что позволяет поэтапно улучшать отдельные компоненты. + +Основные характеристики: +- ✅ RESTful API с FastAPI +- ✅ Глубокое обучение (ResNet18) +- ✅ Детальная оценка качества с метриками +- ✅ Поддержка нескольких типов нарушений +- ✅ Экспорт в XLSX/CSV +- ✅ Docker контейнеризация diff --git a/docs/architecture-analytical.png b/docs/architecture-analytical.png new file mode 100644 index 0000000..f3fd6ac Binary files /dev/null and b/docs/architecture-analytical.png differ diff --git a/docs/architecture-analytical_001.png b/docs/architecture-analytical_001.png new file mode 100644 index 0000000..50fdae7 Binary files /dev/null and b/docs/architecture-analytical_001.png differ diff --git a/docs/architecture-analytical_002.png b/docs/architecture-analytical_002.png new file mode 100644 index 0000000..4784a76 Binary files /dev/null and b/docs/architecture-analytical_002.png differ diff --git a/docs/architecture-analytical_003.png b/docs/architecture-analytical_003.png new file mode 100644 index 0000000..9519af8 Binary files /dev/null and b/docs/architecture-analytical_003.png differ diff --git a/docs/architecture-analytical_004.png b/docs/architecture-analytical_004.png new file mode 100644 index 0000000..abe152a Binary files /dev/null and b/docs/architecture-analytical_004.png differ diff --git a/docs/architecture-analytical_005.png b/docs/architecture-analytical_005.png new file mode 100644 index 0000000..44ffd64 Binary files /dev/null and b/docs/architecture-analytical_005.png differ diff --git a/docs/architecture-analytical_006.png b/docs/architecture-analytical_006.png new file mode 100644 index 0000000..f29290c Binary files /dev/null and b/docs/architecture-analytical_006.png differ diff --git a/requirements.txt b/requirements.txt index 31f914e..bdf488d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,37 +1,72 @@ -# Core dependencies -torch>=2.0.0 -torchvision>=0.15.0 -numpy>=1.24.0 - -# Image processing -Pillow>=10.0.0 -opencv-python-headless>=4.8.0 - -# Medical imaging -pydicom>=2.4.0 -pydicom-seg>=0.4.0 -nibabel>=5.0.0 -SimpleITK>=2.2.0 - -# Deep learning / models -torchvision>=0.15.0 -timm>=0.9.0 - -# Data handling -pandas>=2.0.0 -openpyxl>=3.1.0 - -# Visualization -matplotlib>=3.7.0 - -# API / serving -fastapi>=0.100.0 -uvicorn>=0.23.0 -python-multipart>=0.0.6 - -# Progress bars -tqdm>=4.65.0 - -# Utils -scipy>=1.10.0 -scikit-learn>=1.3.0 +annotated-doc==0.0.5 +annotated-types==0.7.0 +anyio==4.12.1 +attrs==26.1.0 +certifi==2026.7.22 +click==8.1.8 +contourpy==1.3.0 +cycler==0.12.1 +et_xmlfile==2.0.0 +exceptiongroup==1.3.1 +fastapi==0.128.8 +filelock==3.19.1 +fonttools==4.60.2 +fsspec==2025.10.0 +h11==0.16.0 +hf-xet==1.6.0 +httpcore==1.0.9 +httpx==0.28.1 +huggingface_hub==1.8.0 +idna==3.18 +importlib_resources==6.5.2 +Jinja2==3.1.6 +joblib==1.5.3 +jsonschema==3.2.0 +kiwisolver==1.4.7 +markdown-it-py==3.0.0 +MarkupSafe==3.0.3 +matplotlib==3.9.4 +mdurl==0.1.2 +monai==1.5.2 +mpmath==1.3.0 +networkx==3.2.1 +nibabel==5.3.3 +numpy==1.26.4 +opencv-python-headless==4.11.0.86 +openpyxl==3.1.5 +packaging==26.3 +pandas==2.3.3 +pillow==11.3.0 +pydantic==2.13.4 +pydantic_core==2.46.4 +pydicom==2.4.4 +pydicom-seg==0.4.1 +Pygments==2.21.0 +pyparsing==3.3.2 +pyrsistent==0.20.0 +python-dateutil==2.9.0.post0 +python-multipart==0.0.20 +pytz==2026.3.post1 +PyYAML==6.0.3 +rich==15.0.0 +safetensors==0.7.0 +scikit-learn==1.6.1 +scipy==1.13.1 +shellingham==1.5.4 +simpleitk==2.5.6 +six==1.17.0 +starlette==0.49.3 +sympy==1.14.0 +threadpoolctl==3.7.0 +timm==1.0.29 +torch==2.8.0 +torchvision==0.23.0 +TotalSegmentator==2.18.0 +tqdm==4.70.0 +typer==0.23.2 +typing-inspection==0.4.2 +typing_extensions==4.16.0 +tzdata==2026.3 +unicorn==2.1.4 +uvicorn==0.39.0 +zipp==3.23.1 diff --git a/src/api/static/index.html b/src/api/static/index.html index 6cef7de..548dbf8 100644 --- a/src/api/static/index.html +++ b/src/api/static/index.html @@ -256,7 +256,20 @@ -
+
+ +
+ +
+ + +
+

Идентификаторы

+
+ +
+
+
@@ -265,7 +278,7 @@
- +

Детали качества

@@ -273,33 +286,101 @@
- - -
-

Метрики

-
+ + +
+

Уверенность модели

+
- - -
-

Причина

-
+ + +
+

Общая оценка

+
- - -
-

Визуализация

-
-
-

Оригинал

-
-
-

Маска ROI

-
+ + +
+

+ Движение (Motion) +

+
+ +
+
+ + +
+

+ Артефакты +

+
+ +
+
+ + +
+

+ Позиция +

+
+ +
+
+ + +
+

+ ROI +

+
+ +
+
+ + + + + + +
+ + +
+

Заключение

+
+ +
+
+ + +
+

Визуализация

+
+
+

Оригинал

+
+
+

Маска ROI

diff --git a/src/api/static/js/dxa-app.js b/src/api/static/js/dxa-app.js index b2a86ad..31b4b8f 100644 --- a/src/api/static/js/dxa-app.js +++ b/src/api/static/js/dxa-app.js @@ -359,74 +359,265 @@ document.getElementById('closeDetail')?.addEventListener('click', () => { // View detailed analysis for a file async function viewDetail(result) { detailSection.classList.remove('hidden'); - - // Populate basic info - const regionLabel = result.anatomical_region === 'spine' ? 'Позвоночник' : - result.anatomical_region === 'hip_left' ? 'Бедро левое' : - result.anatomical_region === 'hip_right' ? 'Бедро правое' : - result.anatomical_region === 'hip' ? 'Бедро' : result.anatomical_region || '—'; - - document.getElementById('detailBasic').innerHTML = ` -
Файл:${result.filename || '—'}
-
Регион:${regionLabel}
-
UID исследования:${(result.study_uid || '').substring(0, 20)}...
- `; - - // Populate quality details + + // Region label helper + const getRegionLabel = (region) => { + const labels = { + 'spine': 'Позвоночник', + 'hip_left': 'Бедро левое', + 'hip_right': 'Бедро правое', + 'hip': 'Бедро (общее)', + 'unknown': 'Неизвестно' + }; + return labels[region] || region || '—'; + }; + + // Violation type label + const getViolationLabel = (type) => { + const labels = { + 'correct': 'Корректно', + 'position_error': 'Ошибка позиционирования', + 'artifact_motion': 'Артефакт движения', + 'artifact_other': 'Другие артефакты', + 'labeling_error': 'Ошибка маркировки', + 'incomplete_view': 'Неполный вид', + 'roi_error': 'Ошибка ROI', + 'rotation': 'Неправильный поворот' + }; + return labels[type] || type || '—'; + }; + + // Quality badge const qualityClass = result.quality_class === 0 ? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' : result.quality_class === 1 ? 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400' : 'bg-gray-100 text-gray-800'; - - const violationType = result.violation_type || (result.quality_class === 1 ? 'quality_violation_detected' : 'correct'); - const reason = result.reason || (result.quality_class === 0 ? 'Качество соответствует норме' : 'Нарушение качества'); - - document.getElementById('detailQuality').innerHTML = ` -
Класс:${result.quality_class === 0 ? 'OK' : 'Нарушение'}
-
Тип нарушения:${violationType}
-
Уверенность:${result.confidence ? (result.confidence * 100).toFixed(1) + '%' : '—'}
-
Вид:${result.view_quality || 'unknown'}
+ const qualityLabel = result.quality_class === 0 ? 'OK' : result.quality_class === 1 ? 'Нарушение' : 'Ошибка'; + + document.getElementById('detailBadge').innerHTML = ` + + ${qualityLabel} + + ${result.quality_label || ''} `; - - // Populate metrics + + // UIDs + document.getElementById('detailUIDs').innerHTML = ` +
Study UID:${result.study_uid || '—'}
+
Image UID:${result.image_uid || '—'}
+ `; + + // Basic info + document.getElementById('detailBasic').innerHTML = ` +
Файл:${result.filename || '—'}
+
Регион:${getRegionLabel(result.anatomical_region)}
+
Вид снимка:${result.view_quality || 'unknown'}
+ `; + + // Quality details + document.getElementById('detailQuality').innerHTML = ` +
Тип нарушения:${getViolationLabel(result.violation_type)}
+
Класс:${result.quality_class}
+ `; + + // Confidence + const confCorrect = result.confidence_per_class?.correct || 0; + const confViolation = result.confidence_per_class?.violation || 0; + document.getElementById('detailConfidence').innerHTML = ` +
+
Верный:${(confCorrect * 100).toFixed(1)}%
+
+
+
+
Нарушение:${(confViolation * 100).toFixed(1)}%
+
+
+
+
+ `; + + // Overall quality + const overallClass = result.overall_quality === 'GOOD' ? 'text-green-600' : + result.overall_quality === 'FAIR' ? 'text-yellow-600' : + result.overall_quality === 'POOR' ? 'text-red-600' : 'text-gray-600'; + const severityClass = result.severity === 'LOW' ? 'bg-green-100 text-green-800 dark:bg-green-900/30' : + result.severity === 'MEDIUM' ? 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30' : + result.severity === 'HIGH' ? 'bg-red-100 text-red-800 dark:bg-red-900/30' : 'bg-gray-100'; + + document.getElementById('detailOverall').innerHTML = ` +
Качество:${result.overall_quality || '—'}
+
Серьёзность:${result.severity || '—'}
+ `; + + // Motion metrics const metrics = result.metrics || {}; const motion = metrics.motion || {}; - const artifacts = metrics.artifacts || {}; - const roi = metrics.roi_check || {}; - - document.getElementById('detailMetrics').innerHTML = ` + const blurLaplacian = motion.blur_laplacian?.toFixed(4) || '—'; + const blurFFT = motion.blur_fft?.toFixed(4) || '—'; + + document.getElementById('detailMotion').innerHTML = `
Движение
- ${motion.motion_detected ? 'Обнаружено' : 'Нет'} + ${motion.motion_detected ? 'Да' : 'Нет'}
- ${motion.severity ? `
Severity: ${motion.severity}
` : ''}
-
Артефакты
-
-
- ${artifacts.any_detected ? 'Обнаружены' : 'Нет'} -
- ${artifacts.metal_detected ? `
Металл: да
` : ''} - ${artifacts.implant_detected ? `
Имплантат: да
` : ''} +
Blur Laplacian
+ ${blurLaplacian} + ${motion.is_blurred_laplacian ? 'размыто' : ''}
-
ROI
+
Blur FFT
+ ${blurFFT} + ${motion.is_blurred_fft ? 'размыто' : ''} +
+
+
Дублирование краёв
-
- ${roi.valid === false ? 'Проблема' : 'OK'} +
+ ${motion.edge_duplication ? 'Да' : 'Нет'}
`; - - // Populate reason - const reasonClass = result.quality_class === 0 ? 'bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-200' : - 'bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200'; + + // Artifacts + const artifacts = metrics.artifacts || {}; + document.getElementById('detailArtifacts').innerHTML = ` +
+
Металл
+
+
+ ${artifacts.metal_detected ? 'Да' : 'Нет'} +
+
+
+
Имплантат
+
+
+ ${artifacts.implant_detected ? 'Да' : 'Нет'} +
+
+
+
Цемент
+
+
+ ${artifacts.cement_detected ? 'Да' : 'Нет'} +
+
+
+
Кальцификаты
+
+
+ ${artifacts.calcification_detected ? 'Да' : 'Нет'} +
+
+
+
Локальные дефекты
+
+
+ ${artifacts.local_defects?.length || 0} +
+
+ `; + + // Position + const position = metrics.position || {}; + const posValid = position.valid !== false; + document.getElementById('detailPosition').innerHTML = ` +
Центр:${position.center_pixels ? `[${position.center_pixels[0]}, ${position.center_pixels[1]}]` : '—'}
+
Отклонение:${position.deviation?.toFixed(4) || '—'}
+
Размер объекта:${position.object_size?.toFixed(4) || '—'}
+
Валидность:${posValid ? 'OK' : 'Ошибка'}
+ `; + + // ROI + const roi = metrics.roi_check || {}; + const roiValid = roi.valid !== false; + document.getElementById('detailROI').innerHTML = ` +
Валидность:${roiValid ? 'OK' : 'Проблема'}
+
Размер:${roi.size ? `${roi.size.width}x${roi.size.height}` : '—'}
+
BBox:${roi.bounding_box ? `x:${roi.bounding_box.x_min}-${roi.bounding_box.x_max}, y:${roi.bounding_box.y_min}-${roi.bounding_box.y_max}` : '—'}
+ ${roi.issues?.length ? `
Проблемы:
    ${roi.issues.map(i => `
  • ${i}
  • `).join('')}
` : ''} + `; + + // Spine completeness + const spine = result.spine_completeness || {}; + const spineSection = document.getElementById('spineSection'); + if (spine && Object.keys(spine).length > 0) { + spineSection.classList.remove('hidden'); + document.getElementById('detailSpine').innerHTML = ` +
+
Позвонков
+ ${spine.vertebrae_count || '—'} +
+
+
Полный вид
+
+
+ ${spine.is_complete ? 'Да' : 'Нет'} +
+
+
+
Валидность
+
+
+ ${spine.valid ? 'OK' : 'Ошибка'} +
+
+
+
Проблемы
+ ${spine.issues?.length || 0} +
+ `; + } else { + spineSection.classList.add('hidden'); + } + + // Hip completeness + const hip = result.hip_completeness || {}; + const hipRotation = result.hip_rotation || {}; + const hipSection = document.getElementById('hipSection'); + if ((hip && Object.keys(hip).length > 0) || (hipRotation && Object.keys(hipRotation).length > 0)) { + hipSection.classList.remove('hidden'); + document.getElementById('detailHip').innerHTML = ` +
+
Полный вид
+
+
+ ${hip.is_complete ? 'Да' : 'Нет'} +
+
+
+
Соотношение сторон
+ ${hip.aspect_ratio?.toFixed(2) || '—'} +
+
+
Угол поворота
+ ${hipRotation.angle ? hipRotation.angle.toFixed(1) + '°' : '—'} +
+
+
Валидность
+
+
+ ${(hip.valid && hipRotation.valid) ? 'OK' : 'Ошибка'} +
+
+ `; + } else { + hipSection.classList.add('hidden'); + } + + // Reason + const reasonClass = result.quality_class === 0 ? 'bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-200 border border-green-200 dark:border-green-800' : + 'bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200 border border-red-200 dark:border-red-800'; document.getElementById('detailReason').innerHTML = ` - ${reason} + + + ${result.reason || (result.quality_class === 0 ? 'Качество соответствует норме' : 'Нарушение качества')} + `; - + // Scroll to detail detailSection.scrollIntoView({ behavior: 'smooth' }); } diff --git a/src/training/train_model.py b/src/training/train_model.py index b574ef0..0e1d501 100644 --- a/src/training/train_model.py +++ b/src/training/train_model.py @@ -203,7 +203,7 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument('--data-root', default='dataset_hack/НД_для_обучения') parser.add_argument('--annotation', default='dataset_hack/НД_для_обучения/разметка.xlsx') - parser.add_argument('--model-type', default='quality', choices=['quality', 'region', 'violation']) + parser.add_argument('--model-type', default='violation', choices=['quality', 'region', 'violation']) parser.add_argument('--epochs', type=int, default=15) parser.add_argument('--batch-size', type=int, default=8) parser.add_argument('--lr', type=float, default=1e-4)