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")