Files
tw/services/service_qwen.py
2026-02-27 16:03:04 +08:00

219 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import time
import asyncio
import aiohttp
from PIL import Image
from pydantic import BaseModel
import logging
logger = logging.getLogger(__name__)
api_key = '8e32d44e3007447cb4be6ee52c5d3110'
class UploadInfo(BaseModel):
fileName: str
fileType: str
class CreateInfo(BaseModel):
taskId: str # 创建的任务 ID可用于查询状态或获取结果
taskStatus: str # 初始状态可能为QUEUED、RUNNING、FAILED
clientId: str # 平台内部标识,用于排错,无需关注
netWssUrl: str # WebSocket 地址(当前不稳定,不推荐使用)
promptTips: str # ComfyUI 校验信息(字符串格式的 JSON可用于识别配置异常节点
class RunHubResponse(BaseModel):
code: int # 状态码0 表示成功
msg: str # 提示信息
data: UploadInfo | CreateInfo | str | None = None # 数据对象
class Config:
extra = 'allow' # 允许添加额外字段
async def upload(img_path: str) -> RunHubResponse:
with open(img_path, 'rb') as f:
img_data = f.read()
form = aiohttp.FormData()
form.add_field('apiKey', api_key)
form.add_field('file', img_data, filename='image.jpg', content_type='image/jpeg')
form.add_field('fileType', 'image')
url = 'https://www.runninghub.cn/task/openapi/upload'
async with aiohttp.ClientSession() as session:
async with session.post(url, data=form) as resp:
response = await resp.json()
return RunHubResponse.model_validate(response)
async def create(workflow_id: str, node_info_list: list[dict[str, str]]) -> RunHubResponse:
url = 'https://www.runninghub.cn/task/openapi/create'
json_data = {'apiKey': api_key, 'workflowId': workflow_id, 'nodeInfoList': node_info_list}
async with aiohttp.ClientSession() as session:
async with session.post(url, json=json_data) as resp:
response = await resp.json()
return RunHubResponse.model_validate(response)
async def status(task_id: str) -> RunHubResponse:
# 查询状态
url = 'https://www.runninghub.cn/task/openapi/status'
payload = {'apiKey': api_key, 'taskId': task_id}
async with aiohttp.ClientSession() as session:
async with session.post(url, json=payload) as resp:
response = await resp.json()
# ["QUEUED","RUNNING","FAILED","SUCCESS"]
return RunHubResponse.model_validate(response)
async def outputs(task_id: str) -> dict:
# 获取结果
url = 'https://www.runninghub.cn/task/openapi/outputs'
payload = {'apiKey': api_key, 'taskId': task_id}
async with aiohttp.ClientSession() as session:
async with session.post(url, json=payload) as resp:
response = await resp.json()
return response
async def 花纹提取_api(img_path: str, save_path: str, prompt: str = '') -> bool:
"""
异步花纹提取API
Args:
img_path: 输入图片路径
save_path: 输出图片路径
prompt: 自定义提示词,为空则使用默认提示词
Returns:
bool: 处理是否成功
"""
try:
upload_res = await upload(img_path=img_path)
if upload_res.code != 0 or not upload_res.data:
logger.error(f"Qwen上传失败: code={upload_res.code}, msg={upload_res.msg}")
return False
# 确保 data 是 UploadInfo 类型
if not hasattr(upload_res.data, 'fileName'):
logger.error(f"Qwen上传返回数据格式错误: {upload_res.data}")
return False
logger.info(f"Qwen上传成功: {upload_res.data.fileName}")
workflow_id = '1980864078929379330'
if len(prompt) == 0:
prompt = '提取桌布上的花纹,自动补全空白,使得所有位置饱满并且完美衔接,去除所有的皱纹和扭曲和凸凹不平,图案自动摆正对齐并且铺平,使直线变得笔直,平行的花纹更有规律,没有残缺的花纹和折痕和断痕,铺满画布,完整的图案。简单的纯色背景'
node_info_list = [
{
'nodeId': '78',
'fieldName': 'image',
'fieldValue': upload_res.data.fileName,
},
{
'nodeId': '103',
'fieldName': 'text',
'fieldValue': prompt,
},
]
create_res = await create(workflow_id=workflow_id, node_info_list=node_info_list)
if create_res.code != 0 or not create_res.data:
logger.error(f"Qwen任务创建失败: code={create_res.code}, msg={create_res.msg}")
return False
# 确保 data 是 CreateInfo 类型
if not hasattr(create_res.data, 'taskId'):
logger.error(f"Qwen任务创建返回数据格式错误: {create_res.data}")
return False
task_id = create_res.data.taskId
logger.info(f"Qwen任务创建成功: {task_id}")
# 轮询检查状态
max_retries = 120 # 最多等待10分钟120次 * 5秒
retry_count = 0
while retry_count < max_retries:
status_res = await status(task_id=task_id)
if status_res.code == 0:
if status_res.data == 'QUEUED':
logger.info('Qwen队列排队中...')
elif status_res.data == 'RUNNING':
logger.info('Qwen正在处理中...')
elif status_res.data == 'FAILED':
logger.error(f'Qwen处理失败: {status_res}')
return False
elif status_res.data == 'SUCCESS':
logger.info('Qwen处理完成开始下载结果')
outputs_res = await outputs(task_id=task_id)
img_url = outputs_res['data'][0]['fileUrl']
# 下载结果图片
async with aiohttp.ClientSession() as session:
async with session.get(img_url) as resp:
img_data = await resp.read()
with open(save_path, 'wb') as f:
f.write(img_data)
logger.info(f"Qwen结果保存成功: {save_path}")
try:
from utils.api_cost_tracker import record
record("qwen_enhance", count=1)
except Exception:
pass
return True
await asyncio.sleep(5) # 每5秒检查一次
retry_count += 1
else:
logger.error(f'Qwen处理失败: {status_res}')
return False
logger.error(f"Qwen处理超时超过{max_retries * 5}")
return False
except Exception as e:
logger.error(f"Qwen花纹提取异常: {e}")
import traceback
logger.error(f"异常堆栈: {traceback.format_exc()}")
return False
async def 清晰化_api(img_path: str, save_path: str) -> bool:
"""
高清增强:对透视矫正后的图案进行清晰化处理。
使用与花纹提取相同的 ComfyUI 工作流,但提示词聚焦于清晰度增强。
Args:
img_path: 输入图片路径(透视矫正后的结果)
save_path: 输出图片路径
Returns:
bool: 处理是否成功
"""
prompt = (
"对这张已展平的图案进行高清增强处理:"
"提升整体清晰度和锐利度,修复模糊边缘,补全细节纹理,"
"使图案线条清晰笔直,颜色鲜艳均匀,"
"去除噪点和压缩痕迹,输出印刷级高质量平面图,"
"背景保持纯白色,不要改变图案内容和构图。"
)
return await 花纹提取_api(img_path=img_path, save_path=save_path, prompt=prompt)
# 测试代码(注释掉)
# if __name__ == "__main__":
# asyncio.run(花纹提取_api(img_path=r'1.jpg', save_path='save1.png', prompt=''))