deal_cutout - 副本.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. import copy
  2. import random
  3. import time
  4. from concurrent.futures import as_completed, ThreadPoolExecutor
  5. import threading
  6. import os
  7. from module.remove_bg_pixian import RemoveBgPiXian
  8. from PySide6.QtWidgets import *
  9. from PySide6.QtCore import *
  10. from module.other.module_online_data import GetOnlineData
  11. import hashlib
  12. from PIL import Image
  13. from module.other.remove_bg_ali import RemoveBgALi
  14. from module.other.pic import Picture
  15. import io
  16. from module.other.log import logger
  17. from module.deal_one_image import DealOneImage
  18. class DealCutout(QThread):
  19. signal_data = Signal(dict)
  20. def __init__(self, windows):
  21. super().__init__()
  22. self.windows = windows
  23. self.lock = threading.Lock()
  24. self.r_ali = RemoveBgALi()
  25. self.r_pixian = RemoveBgPiXian()
  26. self.need_cutout_images = {}
  27. self.state = 2 # 1进行中 2停止
  28. self.get_online_data = GetOnlineData()
  29. self.is_upload_pic_num = 0
  30. self.is_deal_num = 0
  31. # 图片列表
  32. self.upload_pic_dict = {}
  33. def run(self):
  34. # self.total_num = len([x for x in self.need_cutout_images if x["need_cutout"]]) # 总条数
  35. # self.pending_processing = int(self.total_num) # 待处理
  36. # self.processing_failed = 0 # 处理失败
  37. # self.processing_successfully = 0 # 处理成功
  38. # self.processing_error = 0 # 处理异常
  39. self.get_online_data.refresh_headers()
  40. executor = ThreadPoolExecutor(max_workers=4)
  41. executor_pic_upload = ThreadPoolExecutor(max_workers=2)
  42. tasks = []
  43. tasks_2 = []
  44. self.state = 1
  45. self.is_upload_pic_num = 0
  46. self.is_deal_num = 0
  47. num = 0
  48. for image_data in self.need_cutout_images:
  49. if not image_data["need_cutout"]:
  50. continue
  51. num += 1
  52. task = executor.submit(DealOneImage(image_data=image_data, lock=self.lock, windows=self, num=num).run)
  53. tasks.append(task)
  54. task_2 = executor_pic_upload.submit(self.upload_pics, image_data=image_data)
  55. tasks_2.append(task_2)
  56. def send_info(self, text="", is_success=None, _type="show_text_browser"):
  57. with self.lock:
  58. if is_success is not None:
  59. if is_success:
  60. processing_failed = 0
  61. processing_successfully = 1
  62. else:
  63. processing_failed = 1
  64. processing_successfully = 0
  65. self.signal_data.emit({"_type": "schedule",
  66. "data": {"processing_failed": processing_failed,
  67. "processing_successfully": processing_successfully,
  68. }})
  69. if text:
  70. data = {"_type": "show_text_browser",
  71. "data": text,
  72. }
  73. self.signal_data.emit(data)
  74. def show_image_info(self, data):
  75. with self.lock:
  76. data = {"_type": "show_image_item_info",
  77. "data": data,
  78. }
  79. self.signal_data.emit(data)
  80. def add_log(self, text, _type="info"):
  81. with self.lock:
  82. logger.info(text)
  83. def run_with_thread222(self, image_data):
  84. with self.lock:
  85. self.is_deal_num += 1
  86. file_path = image_data["file_path"]
  87. self.add_log("开始处理第{}个,图片地址:{}".format(self.is_deal_num, file_path))
  88. self.send_info("\n开始处理:{}".format(file_path[-20:] if len(file_path) > 20 else file_path))
  89. _data = {"text": "处理中",
  90. "info": "",
  91. "file_path": file_path
  92. }
  93. self.show_image_info(_data)
  94. if self.windows.remaining_times <= 0:
  95. self.send_info(text="次数不足,处理失败", is_success=False)
  96. return
  97. # 检查图片上传是否有结束
  98. n = 90
  99. while 1:
  100. if self.state != 1:
  101. self.send_info(text="{} 用户主动终止".format(image_data["file_name"]), is_success=False)
  102. return
  103. time.sleep(1)
  104. n -= 1
  105. # print(n)
  106. if file_path in self.upload_pic_dict:
  107. break
  108. else:
  109. if n <= 0:
  110. text = "{} 图片上传处理超时".format(image_data["file_name"])
  111. self.send_info(text=text, is_success=False)
  112. logger.warning(text)
  113. _data = {"text": "出错/超时",
  114. "info": "处理超时",
  115. "file_path": file_path
  116. }
  117. self.show_image_info(_data)
  118. return
  119. continue
  120. s = time.time()
  121. # print(self.upload_pic_dict[file_path])
  122. _flag = self.upload_pic_dict[file_path]["flag"]
  123. if not _flag:
  124. self.add_log("第{}个 未查到上传的图片地址".format(self.is_deal_num))
  125. _data = {"text": "出错/超时",
  126. "info": "上传错误",
  127. "file_path": file_path
  128. }
  129. self.show_image_info(_data)
  130. text = "{} 上传错误".format(image_data["file_name"])
  131. self.send_info(text=text, is_success=False)
  132. return
  133. image_url = self.upload_pic_dict[file_path]["url"]
  134. image_deal_info = self.upload_pic_dict[file_path]["image_deal_info"]
  135. _data = {"text": "开始抠图",
  136. "info": "",
  137. "file_path": file_path
  138. }
  139. self.show_image_info(_data)
  140. self.add_log("第{}个 开始抠图".format(self.is_deal_num))
  141. with self.lock:
  142. self.is_upload_pic_num -= 1
  143. # todo 抠图
  144. out_root_path = "{}/已扣图".format(image_data["root_path"])
  145. self.check_path(out_root_path)
  146. print(image_url)
  147. try:
  148. generate_ids, remaining_times = self.get_online_data.remove_background(images_url=[image_url])
  149. self.add_log("第{}个 上报抠图任务成功,任务id:{}".format(self.is_deal_num, generate_ids))
  150. except BaseException as e:
  151. print(e)
  152. text = "{} 提交抠图请求错误".format(image_data["file_name"])
  153. print(text)
  154. self.add_log("第{}个 提交抠图请求错误".format(self.is_deal_num))
  155. self.send_info(text=text, is_success=False)
  156. _data = {"text": "出错/超时",
  157. "info": text,
  158. "file_path": file_path
  159. }
  160. self.show_image_info(_data)
  161. return
  162. with self.lock:
  163. self.signal_data.emit({"_type": "refresh_times",
  164. "data": {"remaining_times": remaining_times,
  165. "is_online": False
  166. }})
  167. # 查询抠图进度
  168. time.sleep(2)
  169. generate_ids = [str(x) for x in generate_ids]
  170. n = 60 * 5
  171. with self.lock:
  172. print(generate_ids)
  173. print(file_path, "查询抠图进度")
  174. self.add_log("第{}个 查询抠图进度,id:{}".format(self.is_deal_num, generate_ids))
  175. while 1:
  176. try:
  177. data = self.get_online_data.search_progress(generate_ids=generate_ids)
  178. pixian_status = data[0]["status"]
  179. except BaseException as e:
  180. print(e)
  181. text = "{} 查询抠图进度错误".format(image_data["file_name"])
  182. self.add_log("第{}个 查询抠图进度错误,id:{}".format(self.is_deal_num, generate_ids))
  183. print(text)
  184. logger.error("{};{}".format(text, e))
  185. self.send_info(text=text, is_success=False)
  186. _data = {"text": "出错/超时",
  187. "info": text,
  188. "file_path": file_path
  189. }
  190. self.show_image_info(_data)
  191. return
  192. # print(data)
  193. time.sleep(1)
  194. n -= 1
  195. if self.state != 1:
  196. self.send_info(text="{} 用户主动终止".format(image_data["file_name"]), is_success=False)
  197. return
  198. if n <= 0:
  199. result = False
  200. text = "{} 抠图300秒超时".format(image_data["file_name"])
  201. _data = {"text": "已完成",
  202. "info": text,
  203. "file_path": file_path
  204. }
  205. self.show_image_info(_data)
  206. self.send_info(text=text, is_success=False)
  207. _data = {"text": "出错/超时",
  208. "info": text,
  209. "file_path": file_path
  210. }
  211. self.show_image_info(_data)
  212. return
  213. if pixian_status == -1:
  214. text = "服务端抠图出错"
  215. self.send_info(text=text, is_success=False)
  216. _data = {"text": "出错/超时",
  217. "info": text,
  218. "file_path": file_path
  219. }
  220. self.show_image_info(_data)
  221. return
  222. if pixian_status == 2:
  223. result_image_url = data[0]["result_image_urls"][0]
  224. result = True
  225. # print(result_image_url)
  226. try:
  227. second_cut_image = self.get_online_data.download_picture(url=result_image_url, out_path=None)
  228. except BaseException as e:
  229. print(e)
  230. text = "{} 图片数据流获取错误".format(image_data["file_name"])
  231. print(text)
  232. logger.error("{};{}".format(text, e))
  233. self.send_info(text=text, is_success=False)
  234. _data = {"text": "出错/超时",
  235. "info": text,
  236. "file_path": file_path
  237. }
  238. self.show_image_info(_data)
  239. return
  240. break
  241. if result:
  242. # 拼接处理
  243. # print("耗时1:", time.time() - s)
  244. try:
  245. out_path = "{}/{}.png".format(out_root_path, image_data["file_name"])
  246. if image_deal_info["二次抠图是否缩放"]:
  247. print("图片尺寸还原")
  248. second_cut_image = second_cut_image.resize(image_deal_info["抠图扩边后图片大小"])
  249. # 创建空白图片并粘贴回去
  250. _img_im = Image.new(mode="RGBA", size=image_deal_info["原始图片大小"], color=(0, 0, 0, 0))
  251. _img_im.paste(second_cut_image, box=(image_deal_info["抠图扩边后位置"][0], image_deal_info["抠图扩边后位置"][1]))
  252. _img_im.save(out_path)
  253. except BaseException as e:
  254. print(e)
  255. text = "{} 图片处理错误,代码44".format(image_data["file_name"])
  256. print(text)
  257. logger.error("{};{}".format(text, e))
  258. self.send_info(text=text, is_success=False)
  259. _data = {"text": "出错/超时",
  260. "info": text,
  261. "file_path": file_path
  262. }
  263. self.show_image_info(_data)
  264. return
  265. with self.lock:
  266. print("耗时2:", time.time() - s)
  267. self.add_log("第{}个 抠图已完成,id:{},耗时:{}".format(self.is_deal_num, generate_ids, time.time() - s))
  268. self.send_info(text="{} 抠图已完成".format(image_data["file_name"]), is_success=True)
  269. _data = {"text": "已完成",
  270. "info": "",
  271. "file_path": file_path
  272. }
  273. self.show_image_info(_data)
  274. else:
  275. self.send_info(text="{} 处理失败,请联系管理员".format(image_data["file_name"]), is_success=False)
  276. _data = {"text": "出错/超时",
  277. "info": "处理失败,请联系管理员",
  278. "file_path": file_path
  279. }
  280. self.show_image_info(_data)
  281. def get_keys(self):
  282. # 执行顺序
  283. # 获取key、获取im对象、调用抠图、返回抠图结果
  284. n = 3
  285. while 1:
  286. if self.state != 1:
  287. return None
  288. key = self.get_online_data.get_keys()
  289. n -= 1
  290. if n <= 0:
  291. return None
  292. if not key:
  293. time.sleep(10)
  294. continue
  295. else:
  296. return key
  297. def run_with_thread(self, image_data):
  298. # 直接调用抠图
  299. # todo 1、增加获取key,2、key需要加密、3、429报错 重试再来拿一个KEY
  300. with self.lock:
  301. self.is_deal_num += 1
  302. is_deal_num = copy.copy(self.is_deal_num)
  303. file_path = image_data["file_path"]
  304. self.add_log("开始处理第{}个,图片地址:{}".format(is_deal_num, file_path))
  305. _data = {"text": "处理中",
  306. "info": "",
  307. "file_path": file_path
  308. }
  309. self.show_image_info(_data)
  310. if self.windows.remaining_times <= 0:
  311. self.send_info(text="次数不足,处理失败", is_success=False)
  312. return
  313. # 检查图片上传是否有结束
  314. n = 60
  315. while 1:
  316. if self.state != 1:
  317. self.send_info(text="{} 用户主动终止".format(image_data["file_name"]), is_success=False)
  318. return
  319. n -= 1
  320. # print(n)
  321. if file_path in self.upload_pic_dict:
  322. break
  323. else:
  324. time.sleep(1)
  325. if n <= 0:
  326. text = "{} 处理超时".format(image_data["file_name"])
  327. self.send_info(text=text, is_success=False)
  328. logger.warning(text)
  329. _data = {"text": "出错/超时",
  330. "info": "处理超时",
  331. "file_path": file_path
  332. }
  333. self.show_image_info(_data)
  334. return
  335. continue
  336. s = time.time()
  337. # print(self.upload_pic_dict[file_path])
  338. _flag = self.upload_pic_dict[file_path]["flag"]
  339. if not _flag:
  340. self.add_log("第{}个 未查到上传的图片地址".format(self.is_deal_num))
  341. _data = {"text": "出错/超时",
  342. "info": "上传错误",
  343. "file_path": file_path
  344. }
  345. self.show_image_info(_data)
  346. text = "{} 上传错误".format(image_data["file_name"])
  347. self.send_info(text=text, is_success=False)
  348. return
  349. # image_url = self.upload_pic_dict[file_path]["url"]
  350. image_deal_info = self.upload_pic_dict[file_path]["image_deal_info"]
  351. _im = self.upload_pic_dict[file_path]["_im"]
  352. _data = {"text": "开始抠图",
  353. "info": "",
  354. "file_path": file_path
  355. }
  356. self.show_image_info(_data)
  357. self.add_log("第{}个 开始抠图".format(self.is_deal_num))
  358. with self.lock:
  359. self.is_upload_pic_num -= 1
  360. # todo 抠图
  361. out_root_path = "{}/已扣图".format(image_data["root_path"])
  362. self.check_path(out_root_path)
  363. # 直接调用pixian
  364. # second_cut_image,_ = self.r_pixian.run_by_image_url(image_url)
  365. # todo 获取key
  366. n = 2
  367. while 1:
  368. key = self.get_keys()
  369. if not key:
  370. _data = {"text": "出错/超时",
  371. "info": "多次获取key失败",
  372. "file_path": file_path
  373. }
  374. self.show_image_info(_data)
  375. self.send_info(text="{} 处理失败,请联系管理员".format(image_data["file_name"]), is_success=False)
  376. return
  377. n -= 1
  378. second_cut_image, _ = self.r_pixian.run_by_image_im(_im, key)
  379. if second_cut_image is None:
  380. # todo 判断code
  381. if n <= 0:
  382. _data = {"text": "出错/超时",
  383. "info": "多次抠图失败:{}".format(_),
  384. "file_path": file_path
  385. }
  386. self.show_image_info(_data)
  387. self.send_info(text="{} 处理失败,请联系管理员".format(image_data["file_name"]), is_success=False)
  388. return
  389. time.sleep(6)
  390. continue
  391. else:
  392. break
  393. # 拼接处理
  394. # print("耗时1:", time.time() - s)
  395. try:
  396. out_path = "{}/{}.png".format(out_root_path, image_data["file_name"])
  397. if image_deal_info["二次抠图是否缩放"]:
  398. print("图片尺寸还原")
  399. second_cut_image = second_cut_image.resize(image_deal_info["抠图扩边后图片大小"])
  400. # 创建空白图片并粘贴回去
  401. _img_im = Image.new(mode="RGBA", size=image_deal_info["原始图片大小"], color=(0, 0, 0, 0))
  402. _img_im.paste(second_cut_image, box=(image_deal_info["抠图扩边后位置"][0], image_deal_info["抠图扩边后位置"][1]))
  403. _img_im.save(out_path)
  404. except BaseException as e:
  405. print(e)
  406. text = "{} 图片处理错误,代码44".format(image_data["file_name"])
  407. print(text)
  408. logger.error("{};{}".format(text, e))
  409. self.send_info(text=text, is_success=False)
  410. _data = {"text": "出错/超时",
  411. "info": text,
  412. "file_path": file_path
  413. }
  414. self.show_image_info(_data)
  415. return
  416. with self.lock:
  417. print("耗时2:", time.time() - s)
  418. self.send_info(text="{} 抠图已完成".format(image_data["file_name"]), is_success=True)
  419. _data = {"text": "已完成",
  420. "info": "",
  421. "file_path": file_path
  422. }
  423. self.show_image_info(_data)
  424. def upload_pics(self, image_data):
  425. # todo 1、增加阿里调用报错的重试机制
  426. while 1:
  427. if self.state != 1:
  428. return
  429. # print("self.is_upload_pic_num:", self.is_upload_pic_num)
  430. with self.lock:
  431. if self.is_upload_pic_num >= 4:
  432. f = False
  433. else:
  434. self.is_upload_pic_num += 1
  435. f = True
  436. if f:
  437. break
  438. else:
  439. time.sleep(1)
  440. continue
  441. file_path = image_data["file_path"]
  442. image_deal_info = {}
  443. try:
  444. _data = {"text": "开始上传",
  445. "info": "",
  446. "file_path": file_path
  447. }
  448. self.show_image_info(_data)
  449. cut_image, image_deal_info = self.get_image_cut(file_path)
  450. # buffer = io.BytesIO()
  451. # cut_image.save(buffer, format='JPEG')
  452. # buffer.seek(0)
  453. # url = self.get_online_data.upload_pic(file_path=None, buffer=buffer)
  454. f = True
  455. url = ""
  456. _data = {"text": "上传成功",
  457. "info": "",
  458. "file_path": file_path
  459. }
  460. self.show_image_info(_data)
  461. except BaseException as e:
  462. print(e)
  463. _data = {"text": "上传出错",
  464. "info": "{}".format(e),
  465. "file_path": file_path
  466. }
  467. self.show_image_info(_data)
  468. text = "{} 图片上传".format(image_data["file_name"])
  469. logger.error("{};{}".format(text, e))
  470. self.send_info(text=text, is_success=None)
  471. f = False
  472. url = ""
  473. cut_image = ""
  474. with self.lock:
  475. # print(file_path, "图片已上传")
  476. self.upload_pic_dict[file_path] = {"url": url,
  477. "image_deal_info": image_deal_info,
  478. "flag": f,
  479. "_im": cut_image, }
  480. def upload_pics2222(self, image_data):
  481. while 1:
  482. if self.state != 1:
  483. return
  484. # print("self.is_upload_pic_num:", self.is_upload_pic_num)
  485. with self.lock:
  486. if self.is_upload_pic_num >= 4:
  487. f = False
  488. else:
  489. self.is_upload_pic_num += 1
  490. f = True
  491. if f:
  492. break
  493. else:
  494. time.sleep(1)
  495. continue
  496. file_path = image_data["file_path"]
  497. image_deal_info = {}
  498. try:
  499. _data = {"text": "开始上传",
  500. "info": "",
  501. "file_path": file_path
  502. }
  503. self.show_image_info(_data)
  504. cut_image, image_deal_info = self.get_image_cut(file_path)
  505. buffer = io.BytesIO()
  506. cut_image.save(buffer, format='JPEG')
  507. buffer.seek(0)
  508. url = self.get_online_data.upload_pic(file_path=None, buffer=buffer)
  509. f = True
  510. # url = ""
  511. _data = {"text": "上传成功",
  512. "info": "",
  513. "file_path": file_path
  514. }
  515. self.show_image_info(_data)
  516. except BaseException as e:
  517. print(e)
  518. _data = {"text": "上传出错",
  519. "info": "{}".format(e),
  520. "file_path": file_path
  521. }
  522. self.show_image_info(_data)
  523. text = "{} 图片上传".format(image_data["file_name"])
  524. logger.error("{};{}".format(text, e))
  525. self.send_info(text=text, is_success=None)
  526. f = False
  527. url = ""
  528. cut_image = ""
  529. with self.lock:
  530. # print(file_path, "图片已上传")
  531. self.upload_pic_dict[file_path] = {"url": url,
  532. "image_deal_info": image_deal_info,
  533. "flag": f,
  534. "_im": cut_image, }
  535. def get_image_cut(self, file_path):
  536. original_pic = Picture(file_path)
  537. original_pic.im = original_pic.im.convert("RGB")
  538. image_deal_info = {}
  539. image_deal_info["原始图片大小"] = (original_pic.x, original_pic.y)
  540. if original_pic.x * original_pic.y < 1000000:
  541. cut_image = original_pic.im
  542. image_deal_info["抠图扩边后图片大小"] = cut_image.size
  543. image_deal_info["二次抠图是否缩放"] = False
  544. image_deal_info["抠图扩边后位置"] = (0, 0, original_pic.x, original_pic.y)
  545. else:
  546. # print("alilkoutu1")
  547. cut_image = self.r_ali.get_image_cut(file_path=None, out_file_path=None, original_im=original_pic.im)
  548. # print("alilkoutu2")
  549. x1, y1, x2, y2 = cut_image.getbbox()
  550. image_deal_info["鞋子原始位置"] = (x1, y1, x2, y2)
  551. o_w, o_h = cut_image.size
  552. image_deal_info["鞋子原始抠图后大小"] = (o_w, o_h)
  553. # 扩边处理
  554. _w, _h = x2 - x1, y2 - y1
  555. out_px = 0.06
  556. _w, _h = int(out_px * _w), int(out_px * _h)
  557. n_x1, n_y1, n_x2, n_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h
  558. if n_x1 < 0:
  559. n_x1 = 0
  560. if n_y1 < 0:
  561. n_y1 = 0
  562. if n_x2 > o_w:
  563. n_x2 = o_w
  564. if n_y2 > o_h:
  565. n_y2 = o_h
  566. image_deal_info["抠图扩边后位置"] = (n_x1, n_y1, n_x2, n_y2)
  567. cut_image = original_pic.im.crop(image_deal_info["抠图扩边后位置"])
  568. image_deal_info["抠图扩边后图片大小"] = cut_image.size
  569. x, y = image_deal_info["抠图扩边后图片大小"]
  570. max_size = 32000000
  571. # max_size = 1000000
  572. if x * y > max_size:
  573. r = max_size / (x * y)
  574. size = (int(x * r), int(y * r))
  575. print(size)
  576. cut_image = cut_image.resize(size=size)
  577. image_deal_info["二次抠图是否缩放"] = True
  578. else:
  579. image_deal_info["二次抠图是否缩放"] = False
  580. return cut_image, image_deal_info
  581. def check_path(self, _path):
  582. if not os.path.exists(_path):
  583. os.mkdir(_path)
  584. return True
  585. def md5_of_image(self, image_path):
  586. # 打开图片文件
  587. with open(image_path, 'rb') as f:
  588. # 读取文件内容
  589. data = f.read()
  590. # 计算MD5值
  591. md5 = hashlib.md5(data).hexdigest()
  592. return md5