第二代仪评指标联识别接口
This commit is contained in:
@@ -485,7 +485,7 @@ def get_ticket_image_list(user_id):
|
||||
"created_at": MyUtils.format_datetime(row[0]),
|
||||
"file_name": row[1],
|
||||
"resolution": row[2],
|
||||
"size": round(row[3], 2),
|
||||
"size": round(row[3] / 1024, 2),
|
||||
"name": row[4],
|
||||
"moisture_content": row[5],
|
||||
"cocoon_weight": row[6],
|
||||
@@ -493,7 +493,6 @@ def get_ticket_image_list(user_id):
|
||||
"fresh_shell_weight": row[8],
|
||||
"sample_count": row[9],
|
||||
"barcode": row[10],
|
||||
# "oss_url": f"http://{SERVER_PATH_OSS}:9000/image-ticket/{row[11]}",
|
||||
"oss_url": get_temp_url("image-ticket", row[11]),
|
||||
"net_weight_total": row[12],
|
||||
"evaluator": row[13],
|
||||
@@ -513,6 +512,7 @@ def insert_ticket_image(
|
||||
moisture_content,
|
||||
cocoon_weight,
|
||||
defective_pupa_count,
|
||||
dead_pupa_count,
|
||||
fresh_shell_weight,
|
||||
sample_count,
|
||||
barcode,
|
||||
@@ -527,11 +527,11 @@ def insert_ticket_image(
|
||||
"""
|
||||
INSERT INTO ticket_images (
|
||||
created_by, file_name, resolution, size, name,
|
||||
moisture_content, cocoon_weight, defective_pupa_count,
|
||||
moisture_content, cocoon_weight, defective_pupa_count, dead_pupa_count,
|
||||
fresh_shell_weight, sample_count, barcode, oss,
|
||||
net_weight_total, evaluator, reviewer, created_at
|
||||
)
|
||||
VALUES (%s, %s, %s, %s, %s,
|
||||
VALUES (%s, %s, %s, %s, %s, %s,
|
||||
%s, %s, %s, %s, %s,
|
||||
%s, %s, %s, %s, %s, NOW())
|
||||
RETURNING id
|
||||
@@ -545,6 +545,7 @@ def insert_ticket_image(
|
||||
moisture_content,
|
||||
cocoon_weight,
|
||||
defective_pupa_count,
|
||||
dead_pupa_count,
|
||||
fresh_shell_weight,
|
||||
sample_count,
|
||||
barcode,
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import json
|
||||
import re
|
||||
|
||||
from langchain.schema import HumanMessage
|
||||
|
||||
from config.llm import *
|
||||
from llm.ticketLLM import decode_barcode
|
||||
|
||||
|
||||
def get_ticket_response_v2(image_url: str, needBarcode: bool = False):
|
||||
# 构建 prompt
|
||||
prompt_text = f"""
|
||||
你是一位专业的图像分析AI。你的任务是仔细分析提供的图片内容,并按JSON格式输出结果。
|
||||
|
||||
## JSON输出结构及字段说明:
|
||||
# 蚕茧检测信息数据模型
|
||||
|
||||
该模型用于描述蚕茧检测信息,每条记录包含以下字段:
|
||||
|
||||
## 1. 下足茧重量 (`cocoon_weight`)
|
||||
- **类型**:浮点数
|
||||
- **描述**:下足茧的重量,单位为克(可带小数)
|
||||
|
||||
## 2. 非蛹粒数 (`defective_pupa_count`)
|
||||
- **类型**:整数
|
||||
- **描述**:不合格蛹的数量,即非好蛹的个数
|
||||
|
||||
## 3. 僵蛹粒数 (`dead_pupa_count`)
|
||||
- **类型**:整数
|
||||
- **描述**:僵蚕蛹的数量
|
||||
|
||||
## 4. 鲜壳量 (`fresh_shell_weight`)
|
||||
- **类型**:浮点数
|
||||
- **描述**:鲜壳重量,单位为克(可带小数)
|
||||
|
||||
## 5. 小样粒数 (`sample_count`)
|
||||
- **类型**:整数
|
||||
- **描述**:检测使用的小样数量,即用于检测的茧粒数
|
||||
|
||||
---
|
||||
|
||||
### 注意事项
|
||||
- 所有字段都是必填的(required),在 JSON 实例中必须提供值
|
||||
- 浮点数字段可以包含小数,整数字段只能是整数
|
||||
- `evaluator` 和 `reviewer` 可以为空字符串,但字段必须存在
|
||||
- 如果图片与上述内容无关,则字段值为默认值,例如0、0.0、""
|
||||
|
||||
最后,只输出严格的 JSON 格式,不要包含其他文字、markdown等内容。
|
||||
"""
|
||||
messages = [
|
||||
HumanMessage(
|
||||
content=[
|
||||
{"type": "text", "text": prompt_text},
|
||||
{"type": "image_url", "image_url": {"url": image_url}},
|
||||
]
|
||||
)
|
||||
]
|
||||
# 直接调用模型
|
||||
llmRes = llmVision.invoke(messages).content
|
||||
# 去掉 ```json 和 ``` 包裹
|
||||
cleaned = re.sub(r"^```json\s*|\s*```$", "", llmRes.strip())
|
||||
# 解析 JSON
|
||||
jsonRes = json.loads(cleaned)
|
||||
print("needBarcode:", needBarcode)
|
||||
if needBarcode:
|
||||
jsonRes["barcode"] = decode_barcode(image_url)
|
||||
return jsonRes
|
||||
@@ -0,0 +1,7 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class F8ImageRequestV2(BaseModel):
|
||||
title: str
|
||||
image: str
|
||||
needBarcode: bool
|
||||
@@ -5,6 +5,7 @@ from fastapi import APIRouter
|
||||
from config.app import F8_SERVER_USER_ID
|
||||
from models.BaseResponse import BaseResponse
|
||||
from models.F8ImageRequest import F8ImageRequest
|
||||
from models.F8ImageRequestV2 import F8ImageRequestV2
|
||||
from service.vision import process_ticket_image
|
||||
from utils import MyUtils
|
||||
|
||||
@@ -20,6 +21,29 @@ async def cocoonTicket(data: F8ImageRequest):
|
||||
img_bytes = base64.b64decode(input_data)
|
||||
json_data = await MyUtils.async_task(
|
||||
process_ticket_image,
|
||||
1,
|
||||
True,
|
||||
img_bytes,
|
||||
f"{data.title}.jpg",
|
||||
data.title,
|
||||
F8_SERVER_USER_ID,
|
||||
)
|
||||
return BaseResponse(data=json_data)
|
||||
except Exception as e:
|
||||
return BaseResponse(status=False, message=f"解析失败: {str(e)}", data=None)
|
||||
|
||||
|
||||
@f8Router.post("/createTicketImageTaskV2")
|
||||
async def cocoonTicket(data: F8ImageRequestV2):
|
||||
input_data = data.image
|
||||
if "," in input_data:
|
||||
input_data = input_data.split(",")[1]
|
||||
try:
|
||||
img_bytes = base64.b64decode(input_data)
|
||||
json_data = await MyUtils.async_task(
|
||||
process_ticket_image,
|
||||
2,
|
||||
data.needBarcode,
|
||||
img_bytes,
|
||||
f"{data.title}.jpg",
|
||||
data.title,
|
||||
|
||||
@@ -36,7 +36,7 @@ async def createTicketImageTask(
|
||||
try:
|
||||
contents = await file.read()
|
||||
json_data = await MyUtils.async_task(
|
||||
process_ticket_image, contents, file.filename, projectName, user_id
|
||||
process_ticket_image, 1, True, contents, file.filename, projectName, user_id
|
||||
)
|
||||
return BaseResponse(data=json_data)
|
||||
except Exception as e:
|
||||
|
||||
@@ -5,10 +5,13 @@ import config.minIO as minIO
|
||||
import db.postgres as pg
|
||||
from config.minIO import minio_client
|
||||
from llm.ticketLLM import *
|
||||
from llm.ticketLLMv2 import get_ticket_response_v2
|
||||
|
||||
|
||||
def process_ticket_image(
|
||||
img_bytes: bytes,
|
||||
version: int,
|
||||
needBarcode: bool = False,
|
||||
img_bytes=None,
|
||||
file_name: str = None,
|
||||
project_name: str = None,
|
||||
user_id: UUID = None,
|
||||
@@ -18,6 +21,8 @@ def process_ticket_image(
|
||||
"""
|
||||
|
||||
# 上传到 OSS,使用 UUID 做对象名
|
||||
if img_bytes is None:
|
||||
img_bytes = []
|
||||
object_name = str(uuid.uuid4())
|
||||
file_bytes = BytesIO(img_bytes)
|
||||
bucket_name = "image-ticket"
|
||||
@@ -28,15 +33,16 @@ def process_ticket_image(
|
||||
oss_url = minIO.get_temp_url(bucket_name, object_name)
|
||||
|
||||
# 调用分析方法获取 JSON
|
||||
json_data = get_ticket_response(oss_url)
|
||||
# 解析条码
|
||||
barcode = decode_barcode(BytesIO(img_bytes))
|
||||
json_data["barcode"] = barcode
|
||||
|
||||
if version == 1:
|
||||
json_data = get_ticket_response(oss_url)
|
||||
elif version == 2:
|
||||
json_data = get_ticket_response_v2(oss_url, needBarcode)
|
||||
else:
|
||||
json_data = get_ticket_response(oss_url)
|
||||
# 获取图片分辨率和大小
|
||||
img = Image.open(BytesIO(img_bytes))
|
||||
resolution = f"{img.width}x{img.height}"
|
||||
size_kb = len(img_bytes) / 1024
|
||||
size_kb = round(len(img_bytes) / 1024, 2)
|
||||
|
||||
# 插入数据库
|
||||
pg.insert_ticket_image(
|
||||
@@ -45,12 +51,13 @@ def process_ticket_image(
|
||||
resolution=resolution,
|
||||
size=size_kb,
|
||||
name=project_name if project_name else object_name[:8],
|
||||
dead_pupa_count=json_data.get("moisture_content") if version == 2 else 0,
|
||||
moisture_content=json_data.get("moisture_content"),
|
||||
cocoon_weight=json_data.get("cocoon_weight"),
|
||||
defective_pupa_count=json_data.get("defective_pupa_count"),
|
||||
fresh_shell_weight=json_data.get("fresh_shell_weight"),
|
||||
sample_count=json_data.get("sample_count"),
|
||||
barcode=barcode,
|
||||
barcode=json_data.get("barcode"),
|
||||
oss=object_name,
|
||||
net_weight_total=json_data.get("net_weight_total"),
|
||||
evaluator=json_data.get("evaluator"),
|
||||
|
||||
Reference in New Issue
Block a user