deal_one_image.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. import copy
  2. import os.path
  3. from module.other.module_online_data import GetOnlineData
  4. import time
  5. from module.other.log import MyLogger
  6. import os
  7. from module.remove_bg_pixian import RemoveBgPiXian
  8. from PIL import Image
  9. from module.other.pic import Picture
  10. from module.other.remove_bg_ali import RemoveBgALi
  11. import cv2
  12. import numpy as np
  13. class Base(object):
  14. def __init__(self, image_data, lock, windows, num):
  15. self.lock = lock
  16. self.windows = windows
  17. self.image_data = image_data
  18. self.num = num
  19. self.get_online_data = GetOnlineData()
  20. self.file_path = image_data["file_path"]
  21. self.file_name = image_data["file_name"]
  22. self.file = os.path.split(self.file_path)[1]
  23. self.is_once_data = {}
  24. self.logger = MyLogger().logger
  25. def add_log(self, text, _type="info"):
  26. self.logger.info("第{}个,图片名称:{},内容:{}".format(self.num, self.file, text))
  27. def show_image_info(self, data):
  28. data["file_path"] = self.file_path
  29. with self.lock:
  30. data = {"_type": "show_image_item_info",
  31. "data": data,
  32. }
  33. self.windows.signal_data.emit(data)
  34. def send_info(self, text="", is_success=None, _type="show_text_browser", need_point_return=False):
  35. with self.lock:
  36. if is_success is not None:
  37. if is_success:
  38. processing_failed = 0
  39. processing_successfully = 1
  40. else:
  41. processing_failed = 1
  42. processing_successfully = 0
  43. # 分数返回
  44. if need_point_return:
  45. # 分数返回
  46. if self.is_once("add_point"):
  47. print("第{}个,图片名称:{},内容:{}".format(self.num, self.file, "扣分返回"))
  48. self.dispose_point(_type="add")
  49. self.refresh_times(cumulative_frequency_times_change=-1)
  50. pass
  51. self.windows.signal_data.emit({"_type": "schedule",
  52. "data": {"processing_failed": processing_failed,
  53. "processing_successfully": processing_successfully,
  54. }})
  55. if text:
  56. data = {"_type": "show_text_browser",
  57. "data": text,
  58. }
  59. self.windows.signal_data.emit(data)
  60. def check_path(self, _path):
  61. if not os.path.exists(_path):
  62. os.mkdir(_path)
  63. return True
  64. def is_once(self, key):
  65. if key not in self.is_once_data:
  66. self.is_once_data[key] = 0
  67. return True
  68. return False
  69. def refresh_times(self, cumulative_frequency_times_change=0):
  70. data = {"_type": "refresh_times",
  71. "data": {"cumulative_frequency_times_change": cumulative_frequency_times_change
  72. },
  73. }
  74. self.windows.signal_data.emit(data)
  75. pass
  76. def dispose_point(self, _type):
  77. n = 3
  78. while n:
  79. n -= 1
  80. try:
  81. _r = self.get_online_data.dispose_point(_type)
  82. balance = _r["data"]["balance"]
  83. return True
  84. except:
  85. time.sleep(0.5)
  86. continue
  87. return False
  88. class DealOneImage(Base):
  89. def __init__(self, image_data, lock, windows, num):
  90. super().__init__(image_data, lock, windows, num)
  91. self.image_data = image_data
  92. self.lock = lock
  93. self.windows = windows
  94. self.num = num
  95. self.file_path = image_data["file_path"]
  96. self.file = os.path.split(self.file_path)[1]
  97. self.root_path = image_data["root_path"]
  98. self.r_pixian = RemoveBgPiXian()
  99. self.file_name = image_data["file_name"]
  100. def run(self):
  101. # 直接调用抠图
  102. # 1、增加获取key,2、key需要加密、3、429报错 重试再来拿一个KEY
  103. self.add_log("开始处理")
  104. self.show_image_info({"text": "处理中", "info": "", })
  105. if self.windows.windows.remaining_times <= 0:
  106. self.send_info(text="次数不足,处理失败", is_success=False)
  107. return
  108. # 检查图片上传是否有结束
  109. n = 60
  110. while 1:
  111. if self.windows.state != 1:
  112. self.show_image_info({"text": "已取消", "info": "", })
  113. self.send_info(text="用户主动终止", is_success=False)
  114. return
  115. n -= 1
  116. # print(n)
  117. if self.file_path in self.windows.upload_pic_dict:
  118. break
  119. else:
  120. time.sleep(1)
  121. if n <= 0:
  122. text = "处理超时"
  123. self.send_info(text=text, is_success=False)
  124. _data = {"text": "出错/超时",
  125. "info": "处理超时",
  126. }
  127. self.show_image_info(_data)
  128. return
  129. continue
  130. s = time.time()
  131. # print(self.upload_pic_dict[file_path])
  132. _flag = self.windows.upload_pic_dict[self.file_path]["flag"]
  133. if not _flag:
  134. self.add_log("未查到上传的图片地址")
  135. _data = {"text": "出错/超时",
  136. "info": "上传错误",
  137. }
  138. self.show_image_info(_data)
  139. self.send_info(text="上传错误", is_success=False)
  140. return
  141. image_deal_info = self.windows.upload_pic_dict[self.file_path]["image_deal_info"]
  142. original_im = self.windows.upload_pic_dict[self.file_path]["_im"]
  143. self.show_image_info({"text": "开始抠图", "info": "", })
  144. self.add_log("抠图中")
  145. with self.lock:
  146. self.windows.is_upload_pic_num -= 1
  147. # 抠图
  148. out_root_path = "{}/已扣图".format(self.root_path)
  149. self.check_path(out_root_path)
  150. # 直接调用pixian
  151. # second_cut_image,_ = self.r_pixian.run_by_image_url(image_url)
  152. try:
  153. balance = self.get_online_data.get_cutout_image_times()["balance"]
  154. self.add_log("查询balance:{}成功".format(balance))
  155. if balance <= 0:
  156. self.add_log("次数不足,处理失败")
  157. self.send_info(text="次数不足,处理失败", is_success=False)
  158. return
  159. except:
  160. self.add_log("查询balance失败")
  161. self.send_info(text="查询balance失败", is_success=False)
  162. return
  163. n = 0
  164. while 1:
  165. # 获取key
  166. if self.windows.state != 1:
  167. self.show_image_info({"text": "已取消", "info": "", })
  168. self.send_info(text="用户主动终止", is_success=False)
  169. return
  170. n += 1
  171. data = self.get_online_data.get_key_secret()
  172. key = (data["api_info"]["api_key"], data["api_info"]["api_serect"])
  173. self.add_log("查询key成功")
  174. if not key:
  175. _data = {"text": "出错/超时",
  176. "info": "多次获取key失败",
  177. }
  178. self.add_log(text="多次获取key失败")
  179. self.show_image_info(_data)
  180. self.send_info(text="处理失败,请联系管理员", is_success=False)
  181. return
  182. if self.is_once("sub_point"):
  183. # 调用扣分
  184. with self.lock:
  185. self.refresh_times(cumulative_frequency_times_change=1)
  186. f = self.dispose_point(_type="sub")
  187. if not f:
  188. self.add_log(text="多次获取调用余额扣减失败")
  189. self.send_info(text="多次获取调用余额扣减失败", is_success=False)
  190. _data = {"text": "出错/超时",
  191. "info": "多次获取调用余额扣减失败",
  192. }
  193. self.show_image_info(_data)
  194. return
  195. pixian_cutout_data = self.r_pixian.run_by_image_im(original_im, key)
  196. # pixian_cutout_data = {"status_code":200}
  197. if pixian_cutout_data["status_code"] == 200:
  198. second_cut_image = pixian_cutout_data["im"]
  199. # second_cut_image.save("xx.png")
  200. # second_cut_image = Image.open("xx.png")
  201. self.add_log(text="调用抠图完成")
  202. break
  203. elif pixian_cutout_data["status_code"] == 402:
  204. if n >= 2:
  205. _data = {"text": "出错/超时",
  206. "info": "多次抠图失败:{}".format(pixian_cutout_data["status_code"]),
  207. }
  208. self.add_log(text="多次抠图失败:{}".format(pixian_cutout_data["status_code"]))
  209. self.show_image_info(_data)
  210. self.send_info(text="处理失败,请联系管理员", is_success=False, need_point_return=True)
  211. if self.is_once("余额不足报错"):
  212. # todo 余额不足报错,钉钉消息通知
  213. self.get_online_data.send_message("Pixian:{} 余额不足".format(key))
  214. pass
  215. return
  216. self.add_log(text="抠图失败:{},延迟6秒".format(pixian_cutout_data["status_code"]))
  217. time.sleep(6)
  218. continue
  219. elif pixian_cutout_data["status_code"] == 429:
  220. if n >= 2:
  221. _data = {"text": "出错/超时",
  222. "info": "多次抠图失败:{}".format(pixian_cutout_data["status_code"]),
  223. }
  224. self.show_image_info(_data)
  225. self.add_log(text="多次抠图失败:{}".format(pixian_cutout_data["status_code"]))
  226. self.send_info(text="处理失败,请联系管理员", is_success=False, need_point_return=True)
  227. return
  228. self.add_log(text="抠图失败:{},延迟10秒".format(pixian_cutout_data["status_code"]))
  229. time.sleep(10)
  230. continue
  231. else:
  232. _data = {"text": "出错/超时", "info": "抠图异常", }
  233. self.show_image_info(_data)
  234. self.send_info(text="处理失败,请联系管理员", is_success=False, need_point_return=True)
  235. if "message" in pixian_cutout_data:
  236. text = "抠图异常,code:{},message:{}".format(pixian_cutout_data["status_code"],
  237. pixian_cutout_data["message"])
  238. else:
  239. text = "抠图异常,code:{}".format(pixian_cutout_data["status_code"])
  240. self.add_log(text)
  241. return
  242. # 拼接处理
  243. # print("耗时1:", time.time() - s)
  244. try:
  245. out_path = "{}/{}.png".format(out_root_path, self.file_name)
  246. if image_deal_info["二次抠图是否缩放"]:
  247. # print("图片尺寸还原")
  248. self.add_log(text="图片尺寸进行还原")
  249. original_im = image_deal_info["抠图扩边后PIL对象"]
  250. second_cut_image = self.picture_resize_to_original(second_cut_image, original_im)
  251. # second_cut_image = second_cut_image.resize(image_deal_info["抠图扩边后图片大小"])
  252. # 创建空白图片并粘贴回去
  253. _img_im = Image.new(mode="RGBA", size=image_deal_info["原始图片大小"], color=(0, 0, 0, 0))
  254. _img_im.paste(second_cut_image, box=(image_deal_info["抠图扩边后位置"][0], image_deal_info["抠图扩边后位置"][1]))
  255. _img_im.save(out_path)
  256. except BaseException as e:
  257. # print(e)
  258. text = "{} 图片处理错误,代码44".format(e)
  259. self.add_log(text)
  260. self.send_info(text=text, is_success=False, need_point_return=True)
  261. _data = {"text": "出错/超时",
  262. "info": text,
  263. }
  264. self.show_image_info(_data)
  265. return
  266. self.add_log(text="本张耗时:{}".format(time.time() - s))
  267. self.send_info(text="抠图已完成", is_success=True)
  268. self.show_image_info({"text": "已完成", "info": "", })
  269. def picture_resize_to_original(self, _img, original_im):
  270. """
  271. Parameters
  272. ----------
  273. _img 需要还原的PIL对象
  274. original_im 原图对象
  275. Returns
  276. -------
  277. """
  278. # 将抠图结果转成mask
  279. # 将抠图结果放大到原始图大小
  280. _img = _img.resize(original_im.size)
  281. new_big_mask = Image.new('RGB', _img.size, (0, 0, 0))
  282. white = Image.new('RGB', _img.size, (255, 255, 255))
  283. new_big_mask.paste(white, mask=_img.split()[3])
  284. # ---------制作选区缩小的mask
  285. mask = cv2.cvtColor(np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY) # 将PIL 格式转换为 CV对象
  286. mask[mask != 255] = 0
  287. # 黑白反转
  288. # mask = 255 - mask
  289. # 选区缩小10
  290. kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
  291. erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
  292. # -------再进行抠图处理
  293. mask = Image.fromarray(cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)) # CV 对象转 PIL
  294. transparent_im = Image.new('RGBA', original_im.size, (0, 0, 0, 0))
  295. transparent_im.paste(original_im, (0, 0), mask.convert('L'))
  296. # 上述抠图结果进行拼接
  297. _img.paste(transparent_im, (0, 0), transparent_im)
  298. return _img
  299. class DealOneImageBeforehand(Base):
  300. def __init__(self, image_data, lock, windows, num):
  301. super().__init__(image_data, lock, windows, num)
  302. self.image_data = image_data
  303. self.lock = lock
  304. self.windows = windows
  305. self.get_online_data = GetOnlineData()
  306. self.num = num
  307. self.file_path = image_data["file_path"]
  308. self.file = os.path.split(self.file_path)[1]
  309. self.root_path = image_data["root_path"]
  310. self.r_ali = RemoveBgALi()
  311. self.file_name = image_data["file_name"]
  312. def run(self):
  313. # 增加阿里调用报错的重试机制
  314. # a = 1/0
  315. # raise "11111111111111111"
  316. while 1:
  317. if self.windows.state != 1:
  318. self.show_image_info({"text": "已取消", "info": "", })
  319. return
  320. with self.lock:
  321. if self.windows.is_upload_pic_num >= 4:
  322. f = False
  323. else:
  324. self.windows.is_upload_pic_num += 1
  325. f = True
  326. if f:
  327. break
  328. else:
  329. time.sleep(1)
  330. continue
  331. image_deal_info = {}
  332. try:
  333. _data = {"text": "开始上传",
  334. "info": "",
  335. }
  336. self.show_image_info(_data)
  337. cut_image, image_deal_info = self.get_image_cut()
  338. f = True
  339. url = ""
  340. self.show_image_info({"text": "上传成功", "info": "", })
  341. except BaseException as e:
  342. # print(e)
  343. self.show_image_info({"text": "上传出错", "info": "{}".format(e), })
  344. f = False
  345. url = ""
  346. cut_image = ""
  347. # cut_image.save("1.jpg",format="JPEG")
  348. # raise 1
  349. with self.lock:
  350. self.windows.upload_pic_dict[self.file_path] = {"url": url,
  351. "image_deal_info": image_deal_info,
  352. "flag": f,
  353. "_im": cut_image, }
  354. def get_image_cut(self):
  355. original_pic = Picture(self.file_path)
  356. original_pic.im = self.get_image_orientation(original_pic.im)
  357. original_pic.x, original_pic.y = original_pic.im.size
  358. original_pic.im = original_pic.im.convert("RGB")
  359. image_deal_info = {}
  360. image_deal_info["原始图片大小"] = (original_pic.x, original_pic.y)
  361. if original_pic.x * original_pic.y < 1000000:
  362. cut_image = original_pic.im
  363. image_deal_info["抠图扩边后图片大小"] = cut_image.size
  364. image_deal_info["二次抠图是否缩放"] = False
  365. image_deal_info["抠图扩边后位置"] = (0, 0, original_pic.x, original_pic.y)
  366. else:
  367. self.add_log("开始预抠图处理")
  368. cut_image = self.r_ali.get_image_cut(file_path=None, out_file_path=None, original_im=original_pic.im)
  369. # cut_image.save("XX1.png")
  370. self.add_log("预抠图处理结束")
  371. x1, y1, x2, y2 = cut_image.getbbox()
  372. image_deal_info["鞋子原始位置"] = (x1, y1, x2, y2)
  373. o_w, o_h = cut_image.size
  374. image_deal_info["鞋子原始抠图后大小"] = (o_w, o_h)
  375. # 扩边处理
  376. _w, _h = x2 - x1, y2 - y1
  377. out_px = 0.025
  378. _w, _h = int(out_px * _w), int(out_px * _h)
  379. n_x1, n_y1, n_x2, n_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h
  380. if n_x1 < 0:
  381. n_x1 = 0
  382. if n_y1 < 0:
  383. n_y1 = 0
  384. if n_x2 > o_w:
  385. n_x2 = o_w
  386. if n_y2 > o_h:
  387. n_y2 = o_h
  388. image_deal_info["抠图扩边后位置"] = (n_x1, n_y1, n_x2, n_y2)
  389. cut_image = original_pic.im.crop(image_deal_info["抠图扩边后位置"])
  390. image_deal_info["抠图扩边后图片大小"] = cut_image.size
  391. x, y = image_deal_info["抠图扩边后图片大小"]
  392. # max_size = 32000000
  393. max_size = 12000000
  394. if x * y > max_size:
  395. r = max_size / (x * y)
  396. size = (int(x * r), int(y * r))
  397. self.add_log(text="图片进行压缩,压缩前:{},压缩后:{}".format(image_deal_info["抠图扩边后图片大小"], size))
  398. image_deal_info["抠图扩边后PIL对象"] = copy.deepcopy(cut_image)
  399. cut_image = cut_image.resize(size=size)
  400. # print(cut_image.size)
  401. # print(image_deal_info["抠图扩边后PIL对象"].size)
  402. image_deal_info["二次抠图是否缩放"] = True
  403. else:
  404. image_deal_info["二次抠图是否缩放"] = False
  405. return cut_image, image_deal_info
  406. def get_image_orientation(self, img):
  407. # 获取EXIF数据
  408. exif = img._getexif()
  409. if exif is not None:
  410. # EXIF标签274对应的是Orientation
  411. orientation = exif.get(0x0112)
  412. if orientation == 2:
  413. # 水平翻转
  414. img = img.transpose(Image.FLIP_LEFT_RIGHT)
  415. elif orientation == 3:
  416. # 旋转180度
  417. img = img.rotate(180, expand=True)
  418. elif orientation == 4:
  419. # 垂直翻转
  420. img = img.transpose(Image.FLIP_TOP_BOTTOM)
  421. elif orientation == 5:
  422. # 水平翻转后顺时针旋转90度
  423. img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_270)
  424. elif orientation == 6:
  425. # 顺时针旋转90度
  426. img = img.transpose(Image.ROTATE_270)
  427. elif orientation == 7:
  428. # 水平翻转后逆时针旋转90度
  429. img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_90)
  430. elif orientation == 8:
  431. # 逆时针旋转90度
  432. img = img.transpose(Image.ROTATE_90)
  433. else:
  434. print("没有EXIF数据或没有方向信息")
  435. orientation = 1
  436. return img