import copy import os.path # from module.other.module_online_data import GetOnlineData from module.online_request.module_online_data import GetOnlineDataHLM import time from module.log.log import MyLogger import os from PIL import Image # from module.other.remove_bg_ali import RemoveBgALi from module.base_mode.remove_bg_pixian import RemoveBgPiXian from module.base_mode.remove_bg_ali import RemoveBgALi from module.base_mode.remove_bg_ali import Picture import cv2 import numpy as np import settings import math class Base(object): def __init__(self, image_data, lock, windows, num): self.lock = lock self.windows = windows self.image_data = image_data self.num = num self.get_online_data = GetOnlineDataHLM() self.file_path = image_data["file_path"] self.file_name = image_data["file_name"] self.file = os.path.split(self.file_path)[1] self.is_once_data = {} self.logger = MyLogger().logger def add_log(self, text, _type="info"): self.logger.info("第{}个,图片名称:{},内容:{}".format(self.num, self.file, text)) def send_info(self, text="", is_success=None, _type="show_info", need_point_return=False): with self.lock: if is_success is not None: if is_success: self.windows.remaining_times = self.windows.remaining_times - 1 else: # 分数返回 if need_point_return: # 分数返回 if self.is_once("add_point"): print("第{}个,图片名称:{},内容:{}".format(self.num, self.file, "扣分返回")) self.dispose_point(_type="add") self.windows.remaining_times = self.windows.remaining_times + 1 if text: data = {"_type": _type, "data": text, } self.windows.send_sign(data) def refresh_times(self, cumulative_frequency_times_change): if cumulative_frequency_times_change > 0: self.windows.remaining_times = self.windows.remaining_times - 1 def check_path(self, _path): if not os.path.exists(_path): os.mkdir(_path) return True def is_once(self, key): if key not in self.is_once_data: self.is_once_data[key] = 0 return True return False def dispose_point(self, _type): n = 3 while n: n -= 1 try: _r = self.get_online_data.dispose_point(_type) balance = _r["data"]["balance"] return True except: time.sleep(0.5) continue return False class DealOneImage(Base): def __init__(self, image_data, lock, windows, num): super().__init__(image_data, lock, windows, num) self.image_data = image_data self.lock = lock self.windows = windows self.num = num self.file_path = image_data["file_path"] self.file = os.path.split(self.file_path)[1] self.r_pixian = RemoveBgPiXian() self.file_name = image_data["file_name"] self.out_path = image_data["out_path"] def run(self): # 直接调用抠图 # 1、增加获取key,2、key需要加密、3、429报错 重试再来拿一个KEY self.add_log("开始处理") self.send_info(text="{} 处理中".format(self.file_name)) if self.windows.remaining_times <= 0: self.send_info(text="次数不足,处理失败", is_success=False) return # 检查图片上传是否有结束 n = 60 while 1: if self.windows.state != 1: return n -= 1 if self.file_path in self.windows.upload_pic_dict: break else: time.sleep(1) if n <= 0: self.send_info(text="{} 处理超时", is_success=False) return continue s = time.time() # print(self.upload_pic_dict[file_path]) _flag = self.windows.upload_pic_dict[self.file_path]["flag"] if not _flag: self.add_log("未查到上传的图片地址") self.send_info(text="{} 上传错误", is_success=False) return image_deal_info = self.windows.upload_pic_dict[self.file_path]["image_deal_info"] original_im = self.windows.upload_pic_dict[self.file_path]["_im"] self.add_log("抠图中") with self.lock: self.windows.is_upload_pic_num -= 1 try: balance = self.get_online_data.get_cutout_image_times()["balance"] self.add_log("查询balance:{}成功".format(balance)) if balance <= 0: self.add_log("次数不足,处理失败") self.send_info(text="次数不足,处理失败", is_success=False) return except: self.add_log("查询balance失败") self.send_info(text="查询balance失败", is_success=False) return n = 0 while 1: # 获取key if self.windows.state != 1: return n += 1 data = self.get_online_data.get_key_secret() key = (data["api_info"]["api_key"], data["api_info"]["api_serect"]) self.add_log("查询key成功") if not key: _data = {"text": "出错/超时", "info": "多次获取key失败", } self.add_log(text="多次获取key失败") self.send_info(text="{} 处理失败,请联系管理员".format(self.file_name), is_success=False) return if self.is_once("sub_point"): # 调用扣分 with self.lock: self.refresh_times(cumulative_frequency_times_change=1) f = self.dispose_point(_type="sub") if not f: self.add_log(text="多次获取调用余额扣减失败") self.send_info(text="多次获取调用余额扣减失败", is_success=False) return pixian_cutout_data = self.r_pixian.run_by_image_im(original_im, key) if pixian_cutout_data["status_code"] == 200: second_cut_image = pixian_cutout_data["im"] self.add_log(text="调用抠图完成") break elif pixian_cutout_data["status_code"] == 402: if n >= 2: self.add_log(text="多次抠图失败:{}".format(pixian_cutout_data["status_code"])) self.send_info(text="处理失败,请联系管理员", is_success=False, need_point_return=True) if self.is_once("余额不足报错"): # 余额不足报错,钉钉消息通知 self.get_online_data.send_message("Pixian:{} 余额不足".format(key)) pass return self.add_log(text="抠图失败:{},延迟6秒".format(pixian_cutout_data["status_code"])) time.sleep(6) continue elif pixian_cutout_data["status_code"] == 429: if n >= 2: self.add_log(text="多次抠图失败:{}".format(pixian_cutout_data["status_code"])) self.send_info(text="处理失败,请联系管理员", is_success=False, need_point_return=True) return self.add_log(text="{}抠图失败:{},延迟10秒".format(self.file_name, pixian_cutout_data["status_code"])) time.sleep(10) continue else: self.send_info(text="{} 处理失败,请联系管理员".format(self.file_name), is_success=False, need_point_return=True) if "message" in pixian_cutout_data: text = "抠图异常,code:{},message:{}".format(pixian_cutout_data["status_code"], pixian_cutout_data["message"]) else: text = "抠图异常,code:{}".format(pixian_cutout_data["status_code"]) self.add_log(text) return # 拼接处理 # print("耗时1:", time.time() - s) try: if image_deal_info["二次抠图是否缩放"]: # print("图片尺寸还原") self.add_log(text="图片尺寸进行还原") original_im = image_deal_info["抠图扩边后PIL对象"] second_cut_image = self.picture_resize_to_original(second_cut_image, original_im) # 创建空白图片并粘贴回去 _img_im = Image.new(mode="RGBA", size=image_deal_info["原始图片大小"], color=(0, 0, 0, 0)) _img_im.paste(second_cut_image, box=(image_deal_info["抠图扩边后位置"][0], image_deal_info["抠图扩边后位置"][1])) _img_im.save(self.out_path) self.send_info(text="{} 抠图已完成".format(self.file_name), is_success=True) return self.file_path except BaseException as e: # print(e) text = "{} 图片处理错误,代码44".format(e) self.add_log(text) self.send_info(text=text, is_success=False, need_point_return=True) return def picture_resize_to_original(self, _img, original_im): """ Parameters ---------- _img 需要还原的PIL对象 original_im 原图对象 Returns ------- """ # 将抠图结果转成mask # 将抠图结果放大到原始图大小 _img = _img.resize(original_im.size,resample=1) new_big_mask = Image.new('RGB', _img.size, (0, 0, 0)) white = Image.new('RGB', _img.size, (255, 255, 255)) new_big_mask.paste(white, mask=_img.split()[3]) # ---------制作选区缩小的mask mask = cv2.cvtColor(np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY) # 将PIL 格式转换为 CV对象 mask[mask != 255] = 0 # 黑白反转 # mask = 255 - mask # 选区缩小10 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10)) erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel) # -------再进行抠图处理 mask = Image.fromarray(cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)) # CV 对象转 PIL transparent_im = Image.new('RGBA', original_im.size, (0, 0, 0, 0)) transparent_im.paste(original_im, (0, 0), mask.convert('L')) # 上述抠图结果进行拼接 _img.paste(transparent_im, (0, 0), transparent_im) return _img class DealOneImageBeforehand(Base): def __init__(self, image_data, lock, windows, num): super().__init__(image_data, lock, windows, num) self.image_data = image_data self.lock = lock self.windows = windows self.get_online_data = GetOnlineDataHLM() self.num = num self.file_path = image_data["file_path"] self.file = os.path.split(self.file_path)[1] self.r_ali = RemoveBgALi() self.file_name = image_data["file_name"] def run(self): while 1: if self.windows.state != 1: return with self.lock: if self.windows.is_upload_pic_num >= 4: f = False else: self.windows.is_upload_pic_num += 1 f = True if f: break else: time.sleep(1) continue image_deal_info = {} try: cut_image, image_deal_info = self.get_image_cut() f = True url = "" except BaseException as e: f = False url = "" cut_image = "" with self.lock: self.windows.upload_pic_dict[self.file_path] = {"url": url, "image_deal_info": image_deal_info, "flag": f, "_im": cut_image, } def get_image_cut(self): original_pic = Picture(self.file_path) original_pic.im = self.get_image_orientation(original_pic.im) original_pic.x, original_pic.y = original_pic.im.size original_pic.im = original_pic.im.convert("RGB") image_deal_info = {} image_deal_info["原始图片大小"] = (original_pic.x, original_pic.y) # 原始图过小,则不需要使用阿里进行预处理 if original_pic.x * original_pic.y < 1000000: cut_image = original_pic.im image_deal_info["抠图扩边后图片大小"] = cut_image.size image_deal_info["二次抠图是否缩放"] = False image_deal_info["抠图扩边后位置"] = (0, 0, original_pic.x, original_pic.y) else: self.add_log("开始预抠图处理") cut_image = self.r_ali.get_image_cut(file_path=None, out_file_path=None, original_im=original_pic.im) self.add_log("预抠图处理结束") x1, y1, x2, y2 = cut_image.getbbox() image_deal_info["鞋子原始位置"] = (x1, y1, x2, y2) o_w, o_h = cut_image.size image_deal_info["鞋子原始抠图后大小"] = (o_w, o_h) # 扩边处理 _w, _h = x2 - x1, y2 - y1 out_px = 0.025 _w, _h = int(out_px * _w), int(out_px * _h) n_x1, n_y1, n_x2, n_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h if n_x1 < 0: n_x1 = 0 if n_y1 < 0: n_y1 = 0 if n_x2 > o_w: n_x2 = o_w if n_y2 > o_h: n_y2 = o_h image_deal_info["抠图扩边后位置"] = (n_x1, n_y1, n_x2, n_y2) cut_image = original_pic.im.crop(image_deal_info["抠图扩边后位置"]) image_deal_info["抠图扩边后图片大小"] = cut_image.size x, y = image_deal_info["抠图扩边后图片大小"] # 12000000 max_size = settings.MAX_PIXIAN_SIZE if x * y > max_size: r = math.sqrt(max_size) / math.sqrt(x * y) r = r*0.9 size = (int(x * r), int(y * r)) # print("图片:{} pixian触发二次缩放,原尺寸{}*{},新尺寸:{}".format(self.file_name, x, y, size)) self.add_log(text="图片进行压缩,压缩前:{},压缩后:{}".format(image_deal_info["抠图扩边后图片大小"], size)) image_deal_info["抠图扩边后PIL对象"] = copy.deepcopy(cut_image) cut_image = cut_image.resize(size=size,resample=1) # print(cut_image.size) # print(image_deal_info["抠图扩边后PIL对象"].size) image_deal_info["二次抠图是否缩放"] = True else: image_deal_info["二次抠图是否缩放"] = False return cut_image, image_deal_info def get_image_orientation(self, img): # 获取EXIF数据 exif = img._getexif() if exif is not None: # EXIF标签274对应的是Orientation orientation = exif.get(0x0112) if orientation == 2: # 水平翻转 img = img.transpose(Image.FLIP_LEFT_RIGHT) elif orientation == 3: # 旋转180度 img = img.rotate(180, expand=True) elif orientation == 4: # 垂直翻转 img = img.transpose(Image.FLIP_TOP_BOTTOM) elif orientation == 5: # 水平翻转后顺时针旋转90度 img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_270) elif orientation == 6: # 顺时针旋转90度 img = img.transpose(Image.ROTATE_270) elif orientation == 7: # 水平翻转后逆时针旋转90度 img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_90) elif orientation == 8: # 逆时针旋转90度 img = img.transpose(Image.ROTATE_90) else: print("没有EXIF数据或没有方向信息") orientation = 1 return img