deal_one_image.py 16 KB

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