Initial scoliosis pipeline (without models)\

This commit is contained in:
Roman
2026-01-23 20:35:07 +05:00
commit 83dfbcc238
11 changed files with 1638 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
import os
import threading
from typing import Optional
import torch
import torch.nn as nn
from torchvision import models
from ultralytics import YOLO
from config import load_config
_LOCK = threading.Lock()
_CACHE = {}
_CFG = load_config()
def _cache_key(kind: str, path: str, device: Optional[str]) -> str:
return f"{kind}|{os.path.abspath(path)}|{device or 'auto'}"
def _load_shufflenet(path: str, device: Optional[str]):
"""Загружает shufflenet‑классификатор из state_dict"""
sd = torch.load(path, map_location=device or "cpu")
num_classes = sd["fc.weight"].shape[0]
candidates = [
models.shufflenet_v2_x0_5(weights=None),
models.shufflenet_v2_x1_0(weights=None),
]
model = None
for m in candidates:
m.fc = nn.Linear(m.fc.in_features, num_classes)
try:
m.load_state_dict(sd, strict=True)
model = m
break
except Exception:
continue
if model is None:
raise RuntimeError(f"Cannot load state_dict from {path} into supported shufflenet variants")
model.eval()
if device:
model.to(device)
return model
def _load_mobilenet_v3_large(path: str, device: Optional[str]):
"""Загружает mobilenet_v3_large из чекпойнта, настраивает число классов, переносит на device, ставит eval()"""
ckpt = torch.load(path, map_location=device or "cpu")
sd = ckpt.get("model_state", ckpt)
num_classes = sd["classifier.3.weight"].shape[0]
model = models.mobilenet_v3_large(weights=None)
model.classifier[3] = nn.Linear(model.classifier[3].in_features, num_classes)
model.load_state_dict(sd, strict=True)
model.eval()
if device:
model.to(device)
return model
def get_detection_model(device: Optional[str] = None, model_path: Optional[str] = None):
"""Получает YOLO‑детектор: берёт путь (из аргумента или конфига), достаёт из кеша или загружает и кэширует"""
path = model_path or _CFG.models.detection
key = _cache_key("detection", path, device)
with _LOCK:
if key not in _CACHE:
model = YOLO(path)
if device:
try:
model.to(device)
except Exception:
if hasattr(model, "model"):
model.model.to(device)
_CACHE[key] = model
return _CACHE[key]
def get_classification_model(device: Optional[str] = None, model_path: Optional[str] = None):
"""Получает классификатор stitched/single (mobilenet): кеш + загрузка при первом вызове"""
path = model_path or _CFG.models.classification
key = _cache_key("classification", path, device)
with _LOCK:
if key not in _CACHE:
_CACHE[key] = _load_mobilenet_v3_large(path, device)
return _CACHE[key]
def get_projection_model(device: Optional[str] = None, model_path: Optional[str] = None):
"""Получает классификатор проекции (shufflenet): кеш + загрузка при первом вызове"""
path = model_path or _CFG.models.projection
key = _cache_key("projection", path, device)
with _LOCK:
if key not in _CACHE:
_CACHE[key] = _load_shufflenet(path, device)
return _CACHE[key]