|
|
@@ -44,3 +44,45 @@ async def tongue_diagnose(
|
|
|
return {"code": 500, "message": "舌诊分析失败"}
|
|
|
finally:
|
|
|
os.unlink(tmp.name)
|
|
|
+
|
|
|
+
|
|
|
+@router.post("/tongue/detect")
|
|
|
+async def tongue_detect(file: UploadFile = File(...)):
|
|
|
+ """舌象照片分类: 判断上传图片是否为舌头(舌象)照片,用于图片上传分流。
|
|
|
+
|
|
|
+ 返回 {"is_tongue": bool}。非舌象(报告截图/文档/其他部位)返回 false。
|
|
|
+ 分类失败时返回 false(安全降级:走通用报告流程,不误伤正常报告)。
|
|
|
+ """
|
|
|
+ import base64, json, re, os
|
|
|
+ content = await file.read()
|
|
|
+ try:
|
|
|
+ ext = os.path.splitext(file.filename or "tongue.jpg")[1] or ".jpg"
|
|
|
+ b64 = base64.b64encode(content).decode()
|
|
|
+ data_url = f"data:image/{ext[1:]};base64,{b64}"
|
|
|
+
|
|
|
+ from src.llm.client import get_vision_llm
|
|
|
+ from langchain_core.messages import HumanMessage
|
|
|
+
|
|
|
+ prompt = (
|
|
|
+ "你是一个医疗图片分类助手。请判断这张图片是否是人的舌头(舌象/舌诊)照片。"
|
|
|
+ "舌象照片特征:舌体清晰可见、呈舌形、舌面颜色偏红/粉、可能带有舌苔。"
|
|
|
+ "以下都不是舌象照片:纸质报告/体检报告截图、文档、身体其他部位、食物、物体。"
|
|
|
+ "只输出 JSON,不要任何额外文字或 Markdown 包裹:{\"is_tongue\": true 或 false}"
|
|
|
+ )
|
|
|
+ llm = get_vision_llm()
|
|
|
+ resp = llm.invoke([HumanMessage(content=[
|
|
|
+ {"type": "text", "text": prompt},
|
|
|
+ {"type": "image_url", "image_url": {"url": data_url}},
|
|
|
+ ])])
|
|
|
+ raw = (resp.content or "").strip()
|
|
|
+ m = re.search(r"\{.*\}", raw, re.DOTALL)
|
|
|
+ parsed = json.loads(m.group(0)) if m else {}
|
|
|
+ val = parsed.get("is_tongue")
|
|
|
+ if isinstance(val, bool):
|
|
|
+ is_tongue = val
|
|
|
+ else:
|
|
|
+ is_tongue = str(val).strip().lower() in ("true", "yes", "1")
|
|
|
+ return {"code": 200, "data": {"is_tongue": is_tongue}}
|
|
|
+ except Exception as e:
|
|
|
+ logger.error("舌象分类失败: %s", e, exc_info=True)
|
|
|
+ return {"code": 200, "data": {"is_tongue": False}}
|