img_score_and_insert2.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. import asyncio
  2. import aiohttp
  3. import aiofiles
  4. import json
  5. import os
  6. from typing import Dict, Any, Tuple, List
  7. from datetime import datetime
  8. # --- 配置区域 (可根据需要修改) ---
  9. # INFERENCE_SERVICE_URL = "http://127.0.0.1:7744"
  10. # STORAGE_SERVICE_URL = "http://127.0.0.1:7745"
  11. INFERENCE_SERVICE_URL = "http://192.168.77.249:7744"
  12. STORAGE_SERVICE_URL = "http://192.168.77.249:7745"
  13. # 固定的处理类型映射
  14. SCORE_TYPES = [
  15. "front_corner_edge",
  16. "front_face",
  17. "back_corner_edge",
  18. "back_face"
  19. ]
  20. SCORE_TO_IMAGE_TYPE_MAP = {
  21. "front_corner_edge": "front_edge",
  22. "front_face": "front_face",
  23. "back_corner_edge": "back_edge",
  24. "back_face": "back_face"
  25. }
  26. # --- 辅助功能函数 (内部逻辑) ---
  27. async def call_api_with_file(
  28. session: aiohttp.ClientSession,
  29. url: str,
  30. file_path: str,
  31. params: Dict[str, Any] = None,
  32. form_fields: Dict[str, Any] = None
  33. ) -> Tuple[int, bytes]:
  34. """通用的文件上传API调用函数"""
  35. form_data = aiohttp.FormData()
  36. if form_fields:
  37. for key, value in form_fields.items():
  38. form_data.add_field(key, str(value))
  39. async with aiofiles.open(file_path, 'rb') as f:
  40. content = await f.read()
  41. form_data.add_field(
  42. 'file',
  43. content,
  44. filename=os.path.basename(file_path),
  45. content_type='image/jpeg'
  46. )
  47. try:
  48. async with session.post(url, data=form_data, params=params) as response:
  49. response_content = await response.read()
  50. if not response.ok:
  51. print(f"错误: 调用 {url} 失败, 状态码: {response.status}")
  52. return response.status, response_content
  53. except aiohttp.ClientConnectorError as e:
  54. print(f"错误: 无法连接到服务 {url} - {e}")
  55. return 503, b"Connection Error"
  56. async def process_single_image(
  57. session: aiohttp.ClientSession,
  58. image_path: str,
  59. score_type: str,
  60. is_reflect_card: str
  61. ) -> Dict[str, Any]:
  62. """处理单张图片:获取转正图和分数JSON"""
  63. print(f" 正在处理图片: {os.path.basename(image_path)} ({score_type})")
  64. # 1. 获取转正后的图片
  65. rectify_url = f"{INFERENCE_SERVICE_URL}/api/card_inference/card_rectify_and_center"
  66. rectify_status, rectified_image_bytes = await call_api_with_file(
  67. session, url=rectify_url, file_path=image_path
  68. )
  69. if rectify_status >= 300:
  70. raise Exception(f"获取转正图失败: {image_path}")
  71. # 2. 获取分数JSON
  72. score_url = f"{INFERENCE_SERVICE_URL}/api/card_score/score_inference"
  73. score_params = {
  74. "score_type": score_type,
  75. "is_reflect_card": is_reflect_card
  76. }
  77. score_status, score_json_bytes = await call_api_with_file(
  78. session,
  79. url=score_url,
  80. file_path=image_path,
  81. params=score_params
  82. )
  83. if score_status >= 300:
  84. raise Exception(f"获取分数JSON失败: {image_path}")
  85. score_json = json.loads(score_json_bytes)
  86. return {
  87. "score_type": score_type,
  88. "rectified_image": rectified_image_bytes,
  89. "score_json": score_json
  90. }
  91. async def create_card_set(session: aiohttp.ClientSession, card_name: str) -> int:
  92. """创建一个新的卡组并返回其ID"""
  93. url = f"{STORAGE_SERVICE_URL}/api/cards/created"
  94. params = {'card_name': card_name}
  95. print(f"\n[步骤 2] 创建卡组: '{card_name}'")
  96. try:
  97. async with session.post(url, params=params) as response:
  98. if response.ok:
  99. data = await response.json()
  100. card_id = data.get('id')
  101. if card_id is not None:
  102. print(f" -> 成功创建卡组 ID: {card_id}")
  103. return card_id
  104. raise Exception("响应中未找到 'id' 字段")
  105. else:
  106. raise Exception(f"状态码: {response.status}")
  107. except Exception as e:
  108. raise Exception(f"创建卡组失败: {e}")
  109. async def upload_processed_data(
  110. session: aiohttp.ClientSession,
  111. card_id: int,
  112. processed_data: Dict[str, Any]
  113. ):
  114. """上传单张转正图和对应的JSON"""
  115. score_type = processed_data['score_type']
  116. image_type = SCORE_TO_IMAGE_TYPE_MAP[score_type]
  117. url = f"{STORAGE_SERVICE_URL}/api/images/insert/{card_id}"
  118. form_data = aiohttp.FormData()
  119. form_data.add_field('image_type', image_type)
  120. form_data.add_field('json_data_str', json.dumps(processed_data['score_json'], ensure_ascii=False))
  121. form_data.add_field(
  122. 'image',
  123. processed_data['rectified_image'],
  124. filename='rectified.jpg',
  125. content_type='image/jpeg'
  126. )
  127. try:
  128. async with session.post(url, data=form_data) as response:
  129. if response.status == 201:
  130. print(f" -> 上传成功: {image_type}")
  131. else:
  132. print(f" -> 上传失败 ({image_type}): {response.status}")
  133. except Exception as e:
  134. print(f" -> 上传异常 ({image_type}): {e}")
  135. # --- 核心 API 函数 ---
  136. async def process_card_images(
  137. is_reflect: bool,
  138. front_face_path: str,
  139. front_edge_path: str,
  140. back_face_path: str,
  141. back_edge_path: str
  142. ) -> int:
  143. """
  144. 核心异步处理函数。
  145. :return: 成功创建的 card_id,如果失败返回 -1
  146. """
  147. # 0. 数据准备
  148. is_reflect_str = "true" if is_reflect else "false"
  149. # 注意:这里的顺序必须与全局 SCORE_TYPES 列表的顺序一一对应
  150. # SCORE_TYPES = ["front_corner_edge", "front_face", "back_corner_edge", "back_face"]
  151. path_list = [
  152. front_edge_path, # 对应 front_corner_edge
  153. front_face_path, # 对应 front_face
  154. back_edge_path, # 对应 back_corner_edge
  155. back_face_path # 对应 back_face
  156. ]
  157. # 检查路径是否存在
  158. for p in path_list:
  159. if not os.path.exists(p):
  160. print(f"错误: 文件路径不存在 -> {p}")
  161. return -1
  162. # 生成卡片名称
  163. card_name = f"临时测试-卡 {datetime.now().strftime('%Y-%m-%d_%H:%M:%S')}"
  164. async with aiohttp.ClientSession() as session:
  165. try:
  166. # 步骤 1: 并发处理图片 (转正 + 评分)
  167. print(f"--- 开始处理: {card_name} ---")
  168. print("[步骤 1] 并发图像处理...")
  169. process_tasks = []
  170. for path, s_type in zip(path_list, SCORE_TYPES):
  171. task = process_single_image(session, path, s_type, is_reflect_str)
  172. process_tasks.append(task)
  173. processed_results = await asyncio.gather(*process_tasks)
  174. # 步骤 2: 创建卡组
  175. card_id = await create_card_set(session, card_name)
  176. # 步骤 3: 并发上传结果
  177. print(f"\n[步骤 3] 上传数据到卡组 {card_id}...")
  178. upload_tasks = []
  179. for result in processed_results:
  180. task = upload_processed_data(session, card_id, result)
  181. upload_tasks.append(task)
  182. await asyncio.gather(*upload_tasks)
  183. print(f"--- 流程完成,卡组ID: {card_id} ---\n")
  184. return card_id
  185. except Exception as e:
  186. print(f"\n流程执行中发生错误: {e}")
  187. return -1
  188. # --- 同步封装函数 (方便直接调用) ---
  189. def run_card_processing_sync(
  190. is_reflect: bool,
  191. front_face_path: str,
  192. front_edge_path: str,
  193. back_face_path: str,
  194. back_edge_path: str
  195. ):
  196. """
  197. 同步包装函数,会自动处理 EventLoop。
  198. 如果你的代码不是异步的,直接调用这个函数即可。
  199. """
  200. if os.name == 'nt':
  201. asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
  202. return asyncio.run(process_card_images(
  203. is_reflect,
  204. front_face_path,
  205. front_edge_path,
  206. back_face_path,
  207. back_edge_path
  208. ))
  209. # --- 使用示例 ---
  210. if __name__ == "__main__":
  211. # 准备参数
  212. # !!!!!🪸是否反光
  213. my_is_reflect = False
  214. for img_num in range(1, 47):
  215. base_path = r"C:\Code\ML\Image\Card\_250915_many_capture_img\_250919_1500_no_reflect_nature_defect"
  216. p1 = os.path.join(base_path, f"{img_num}_front_coaxial_1_0.jpg")
  217. p2 = os.path.join(base_path, f"{img_num}_front_ring_0_1.jpg")
  218. p3 = os.path.join(base_path, f"{img_num}_back_coaxial_1_0.jpg")
  219. p4 = os.path.join(base_path, f"{img_num}_back_ring_0_1.jpg")
  220. # 调用函数
  221. print("开始调用函数...")
  222. final_card_id = run_card_processing_sync(
  223. is_reflect=my_is_reflect,
  224. front_face_path=p1,
  225. front_edge_path=p2,
  226. back_face_path=p3,
  227. back_edge_path=p4
  228. )
  229. if final_card_id != -1:
  230. print(f"调用成功,生成的卡片ID是: {final_card_id}")
  231. else:
  232. print("调用失败")