deal_one_image.py 16 KB

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