90 lines
3.6 KiB
Python
90 lines
3.6 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
|
|
from fastapi import APIRouter, File, Form, HTTPException, UploadFile
|
|
|
|
from app.schemas.models import ImagingAnalyzeResponse
|
|
from app.services.monai_preprocess import load_image_bgr, preprocess
|
|
from app.services.report_generator import build_imaging_texts, generate_imaging_report, make_full_report
|
|
from app.schemas.models import ImagingReportRequest
|
|
from app.services.yolo_detector import get_detector
|
|
from app.services.yolo_manager import get_yolo_manager
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter(prefix="/imaging", tags=["imaging"])
|
|
|
|
|
|
@router.post("/analyze", response_model=ImagingAnalyzeResponse)
|
|
async def analyze_imaging(
|
|
file: UploadFile | None = File(default=None),
|
|
image_path: str | None = Form(default=None),
|
|
study_type: str = Form(default="CT"),
|
|
body_part: str = Form(default=""),
|
|
patient_summary: str = Form(default=""),
|
|
) -> ImagingAnalyzeResponse:
|
|
raw = await _read_bytes(file, image_path)
|
|
try:
|
|
image_bgr = load_image_bgr(raw)
|
|
prep = preprocess(image_bgr)
|
|
detector = get_detector()
|
|
detections = detector.detect(image_bgr, study_type=study_type)
|
|
annotated = detector.annotate(image_bgr, detections)
|
|
findings, diagnosis, recommendations, confidence = build_imaging_texts(
|
|
study_type, body_part, detections, detector.mode
|
|
)
|
|
report = generate_imaging_report(
|
|
ImagingReportRequest(
|
|
study_type=study_type,
|
|
body_part=body_part,
|
|
patient_summary=patient_summary,
|
|
preliminary_diagnosis=diagnosis,
|
|
findings=findings,
|
|
detections=detections,
|
|
confidence=confidence,
|
|
)
|
|
)
|
|
backend = prep.get("backend", "opencv")
|
|
model_version = f"yolo-{detector.mode}+{backend}+{report.model_version}"
|
|
try:
|
|
get_yolo_manager().record_inference(
|
|
mode=detector.mode,
|
|
detections=detections,
|
|
study_type=study_type,
|
|
model_version=model_version,
|
|
)
|
|
except Exception as e:
|
|
logger.warning("记录 YOLO 统计失败: %s", e)
|
|
return ImagingAnalyzeResponse(
|
|
detections=detections,
|
|
annotated_image_base64=annotated,
|
|
preliminary_diagnosis=report.impression or diagnosis,
|
|
findings=report.findings or findings,
|
|
recommendations=report.recommendations or recommendations,
|
|
confidence=confidence,
|
|
model_version=model_version,
|
|
mode=detector.mode, # type: ignore[arg-type]
|
|
full_report=report.full_report
|
|
or make_full_report(study_type, body_part, findings, diagnosis, recommendations, patient_summary),
|
|
)
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e)) from e
|
|
except Exception as e:
|
|
logger.exception("影像分析失败")
|
|
raise HTTPException(status_code=500, detail=f"影像分析失败: {e}") from e
|
|
|
|
|
|
async def _read_bytes(file: UploadFile | None, image_path: str | None) -> bytes:
|
|
if file is not None:
|
|
data = await file.read()
|
|
if not data:
|
|
raise HTTPException(status_code=400, detail="上传文件为空")
|
|
return data
|
|
if image_path:
|
|
path = Path(image_path)
|
|
if not path.is_file():
|
|
raise HTTPException(status_code=400, detail=f"影像路径不存在: {image_path}")
|
|
return path.read_bytes()
|
|
raise HTTPException(status_code=400, detail="请提供 file 或 image_path")
|