qwen3.5 优化
This commit is contained in:
136
fastAPI_tarot.py
136
fastAPI_tarot.py
@@ -22,6 +22,7 @@ import re
|
||||
import asyncio
|
||||
import shutil
|
||||
import subprocess
|
||||
import ast
|
||||
from datetime import datetime
|
||||
from typing import Optional, List, Dict, Any
|
||||
from contextlib import asynccontextmanager
|
||||
@@ -288,6 +289,35 @@ def append_to_history(req_type: str, prompt: str, status: str, result_path: str
|
||||
print(f"Failed to write history: {e}")
|
||||
|
||||
|
||||
def extract_json_from_response(text: str) -> dict:
|
||||
"""
|
||||
Robustly extract JSON from text, handling:
|
||||
1. Markdown code blocks (```json ... ```)
|
||||
2. Single quotes (Python dict style) via ast.literal_eval
|
||||
"""
|
||||
try:
|
||||
# 1. Try to find JSON block
|
||||
json_match = re.search(r'```json\s*(.*?)\s*```', text, re.DOTALL)
|
||||
if json_match:
|
||||
clean_text = json_match.group(1).strip()
|
||||
else:
|
||||
# Try to find { ... } block if no markdown
|
||||
match = re.search(r'\{.*\}', text, re.DOTALL)
|
||||
if match:
|
||||
clean_text = match.group(0).strip()
|
||||
else:
|
||||
clean_text = text.strip()
|
||||
|
||||
# 2. Try standard JSON
|
||||
return json.loads(clean_text)
|
||||
except Exception as e1:
|
||||
# 3. Try ast.literal_eval for single quotes
|
||||
try:
|
||||
return ast.literal_eval(clean_text)
|
||||
except Exception as e2:
|
||||
# 4. Fail
|
||||
raise ValueError(f"Could not parse JSON: {e1} | {e2} | Content: {text[:100]}...")
|
||||
|
||||
def translate_to_sam3_prompt(text: str) -> str:
|
||||
"""
|
||||
使用 Qwen 模型将中文提示词翻译为英文
|
||||
@@ -567,13 +597,13 @@ def recognize_card_with_qwen(image_path: str) -> dict:
|
||||
|
||||
if response.status_code == 200:
|
||||
content = response.output.choices[0].message.content[0]['text']
|
||||
import json
|
||||
try:
|
||||
clean_content = content.replace("```json", "").replace("```", "").strip()
|
||||
result = json.loads(clean_content)
|
||||
result = extract_json_from_response(content)
|
||||
result["model_used"] = QWEN_MODEL
|
||||
return result
|
||||
except:
|
||||
return {"raw_response": content}
|
||||
except Exception as e:
|
||||
print(f"JSON Parse Error in recognize_card: {e}")
|
||||
return {"raw_response": content, "error": str(e), "model_used": QWEN_MODEL}
|
||||
else:
|
||||
return {"error": f"API Error: {response.code} - {response.message}"}
|
||||
|
||||
@@ -602,13 +632,13 @@ def recognize_spread_with_qwen(image_path: str) -> dict:
|
||||
|
||||
if response.status_code == 200:
|
||||
content = response.output.choices[0].message.content[0]['text']
|
||||
import json
|
||||
try:
|
||||
clean_content = content.replace("```json", "").replace("```", "").strip()
|
||||
result = json.loads(clean_content)
|
||||
result = extract_json_from_response(content)
|
||||
result["model_used"] = QWEN_MODEL
|
||||
return result
|
||||
except:
|
||||
return {"raw_response": content, "spread_name": "Unknown"}
|
||||
except Exception as e:
|
||||
print(f"JSON Parse Error in recognize_spread: {e}")
|
||||
return {"raw_response": content, "error": str(e), "spread_name": "Unknown", "model_used": QWEN_MODEL}
|
||||
else:
|
||||
return {"error": f"API Error: {response.code} - {response.message}"}
|
||||
|
||||
@@ -951,6 +981,10 @@ async def recognize_tarot(
|
||||
processor = request.app.state.processor
|
||||
|
||||
try:
|
||||
# 在执行 GPU 操作前,切换到线程中运行,避免阻塞主线程(虽然 SAM3 推理在 CPU 上可能已经很快,但为了保险)
|
||||
# 注意:processor 内部调用了 torch,如果是在 GPU 上,最好不要多线程调用同一个 model
|
||||
# 但这里只是推理,且是单次请求。
|
||||
# 如果是 CPU 推理,run_in_executor 有助于防止阻塞 loop
|
||||
inference_state = processor.set_image(image)
|
||||
output = processor.set_text_prompt(state=inference_state, prompt="tarot card")
|
||||
masks, boxes, scores = output["masks"], output["boxes"], output["scores"]
|
||||
@@ -975,15 +1009,25 @@ async def recognize_tarot(
|
||||
main_file_path = None
|
||||
main_file_url = None
|
||||
|
||||
# Step 0: 牌阵识别
|
||||
# Step 0: 牌阵识别 (异步启动)
|
||||
spread_info = {"spread_name": "Unknown"}
|
||||
spread_task = None
|
||||
if main_file_path:
|
||||
# 使用原始图的一份拷贝给 Qwen 识别牌阵
|
||||
temp_raw_path = os.path.join(output_dir, "raw_for_spread.jpg")
|
||||
image.save(temp_raw_path)
|
||||
spread_info = recognize_spread_with_qwen(temp_raw_path)
|
||||
|
||||
# 将同步调用包装为异步任务
|
||||
loop = asyncio.get_event_loop()
|
||||
spread_task = loop.run_in_executor(None, recognize_spread_with_qwen, temp_raw_path)
|
||||
|
||||
if detected_count != expected_count:
|
||||
# 如果数量不对,等待牌阵识别完成(如果已启动)再返回
|
||||
if spread_task:
|
||||
try:
|
||||
spread_info = await spread_task
|
||||
except Exception as e:
|
||||
print(f"Spread recognition failed: {e}")
|
||||
|
||||
duration = time.time() - start_time
|
||||
append_to_history("tarot-recognize", f"expected: {expected_count}", "failed", result_path=f"results/{request_id}/{main_filename}" if main_file_url else None, details=f"Detected {detected_count}, expected {expected_count}", duration=duration)
|
||||
return JSONResponse(
|
||||
@@ -1005,21 +1049,47 @@ async def recognize_tarot(
|
||||
append_to_history("tarot-recognize", f"expected: {expected_count}", "failed", details=f"Crop Error: {str(e)}", duration=duration)
|
||||
raise HTTPException(status_code=500, detail=f"抠图处理错误: {str(e)}")
|
||||
|
||||
# 遍历每张卡片进行识别
|
||||
# 遍历每张卡片进行识别 (并发)
|
||||
tarot_cards = []
|
||||
|
||||
# 1. 准备任务列表
|
||||
loop = asyncio.get_event_loop()
|
||||
card_tasks = []
|
||||
|
||||
for obj in saved_objects:
|
||||
fname = obj["filename"]
|
||||
file_path = os.path.join(output_dir, fname)
|
||||
|
||||
# 调用 Qwen-VL 识别 (串行)
|
||||
recognition_res = recognize_card_with_qwen(file_path)
|
||||
|
||||
# 创建异步任务
|
||||
# 使用 lambda 来延迟调用,确保参数传递正确
|
||||
task = loop.run_in_executor(None, recognize_card_with_qwen, file_path)
|
||||
card_tasks.append(task)
|
||||
|
||||
# 2. 等待所有卡片识别任务完成
|
||||
# 同时等待牌阵识别任务 (如果还在运行)
|
||||
if card_tasks:
|
||||
all_card_results = await asyncio.gather(*card_tasks)
|
||||
else:
|
||||
all_card_results = []
|
||||
|
||||
if spread_task:
|
||||
try:
|
||||
# 如果之前没有await spread_task,这里确保它完成
|
||||
# 注意:如果 detected_count != expected_count 分支已经 await 过了,这里不会重复执行
|
||||
# 但那个分支有 return,所以这里肯定是还没 await 的
|
||||
spread_info = await spread_task
|
||||
except Exception as e:
|
||||
print(f"Spread recognition failed: {e}")
|
||||
|
||||
# 3. 组装结果
|
||||
for i, obj in enumerate(saved_objects):
|
||||
fname = obj["filename"]
|
||||
file_url = str(request.url_for("static", path=f"results/{request_id}/{fname}"))
|
||||
|
||||
tarot_cards.append({
|
||||
"url": file_url,
|
||||
"is_rotated": obj["is_rotated_by_algorithm"],
|
||||
"orientation_status": "corrected_to_portrait" if obj["is_rotated_by_algorithm"] else "original_portrait",
|
||||
"recognition": recognition_res,
|
||||
"recognition": all_card_results[i],
|
||||
"note": obj["note"]
|
||||
})
|
||||
|
||||
@@ -1083,14 +1153,26 @@ async def segment_face(
|
||||
|
||||
# 调用独立服务进行处理
|
||||
try:
|
||||
result = human_analysis_service.process_face_segmentation_and_analysis(
|
||||
processor=processor,
|
||||
image=image,
|
||||
prompt=final_prompt,
|
||||
output_base_dir=RESULT_IMAGE_DIR,
|
||||
qwen_model=QWEN_MODEL,
|
||||
analysis_prompt=PROMPTS["face_analysis"]
|
||||
)
|
||||
# 使用新增加的异步并发函数
|
||||
if hasattr(human_analysis_service, "process_face_segmentation_and_analysis_async"):
|
||||
result = await human_analysis_service.process_face_segmentation_and_analysis_async(
|
||||
processor=processor,
|
||||
image=image,
|
||||
prompt=final_prompt,
|
||||
output_base_dir=RESULT_IMAGE_DIR,
|
||||
qwen_model=QWEN_MODEL,
|
||||
analysis_prompt=PROMPTS["face_analysis"]
|
||||
)
|
||||
else:
|
||||
# 回退到同步
|
||||
result = human_analysis_service.process_face_segmentation_and_analysis(
|
||||
processor=processor,
|
||||
image=image,
|
||||
prompt=final_prompt,
|
||||
output_base_dir=RESULT_IMAGE_DIR,
|
||||
qwen_model=QWEN_MODEL,
|
||||
analysis_prompt=PROMPTS["face_analysis"]
|
||||
)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
Reference in New Issue
Block a user