import os import copy import time from module.view_control.generate_main_image.image_deal_base_func import * from PIL import Image, ImageDraw from blend_modes import multiply import os from module.log.log import MyLogger import settings from functools import wraps def time_it(func): @wraps(func) # 使用wraps来保留原始函数的元数据信息 def wrapper(*args, **kwargs): start_time = time.time() # 记录开始时间 result = func(*args, **kwargs) # 调用原始函数 end_time = time.time() # 记录结束时间 print(f"Executing {func.__name__} took {end_time - start_time:.4f} seconds.") # 打印耗时 return result return wrapper class GeneratePic(object): def __init__(self, is_test=False): # self.logger = MyLogger() self.is_test = is_test pass @time_it def get_mask_and_config(self, im_jpg: Image, im_png: Image): """ 步骤: 1、尺寸进行对应缩小 2、查找并设定鞋底阴影蒙版 3、自动色阶检查亮度 4、输出自动色阶参数、以及放大的尺寸蒙版 """ # ===================尺寸进行对应缩小(提升处理速度) im_jpg = to_resize(im_jpg, width=800) im_png = to_resize(im_png, width=800) x1, y1, x2, y2 = im_png.getbbox() cv2_png = pil_to_cv2(im_png) # =====================设定鞋底阴影图的蒙版 # 查找每列的最低非透明点 min_y_values = find_lowest_non_transparent_points(cv2_png) # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小 image_high = im_jpg.height print("图片高度:", image_high) # TODO 待移除 settings.app.processEvents() cv2_jpg = pil_to_cv2(im_jpg) # 返回线条图片,以及最低位置 print("返回线条图片,以及最低位置") img_with_shifted_line, lowest_y = draw_shifted_line(image=cv2_jpg, min_y_values=min_y_values, shift_amount=15, one_line_pos=(x1, x2), line_color=(0, 0, 0), line_thickness=20, app=settings.app) # TODO 待移除 settings.app.processEvents() print("66 制作蒙版") # 制作蒙版 mask_line = cv2_to_pil(img_with_shifted_line) mask = mask_line.convert('L') # 转换为灰度图 mask = ImageOps.invert(mask) # 蒙版扩边 print("72 蒙版扩边") mask = expand_or_shrink_mask(pil_image=mask, expansion_radius=65, blur_radius=35) # mask1 = expand_mask(mask, expansion_radius=30, blur_radius=10) # mask1.save("mask1.png") # mask2 = expand_or_shrink_mask(pil_image=mask, expansion_radius=60, blur_radius=30) # mask2.save("mask2.png") # raise 11 # TODO 待移除 settings.app.processEvents() # ====================生成新的图片 print("84 生成新的图片") bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255)) bg.paste(im_png, mask=im_png) bg.paste(im_jpg, mask=mask) # 粘贴有阴影的地方 # TODO 待移除 settings.app.processEvents() if self.is_test: _bg = bg.copy() draw = ImageDraw.Draw(_bg) # 定义直线的起点和终点坐标 start_point = (0, lowest_y) # 直线的起始点 end_point = (_bg.width, lowest_y) # 直线的结束点 # 定义直线的颜色(R, G, B) line_color = (255, 0, 0) # 红色 # 绘制直线 draw.line([start_point, end_point], fill=line_color, width=1) # mask.show() # bg = pil_to_cv2(bg) # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2) # bg = cv2_to_pil(bg) _r = Image.new(mode="RGBA", size=im_png.size, color=(246, 147, 100, 255)) mask_line = mask_line.convert('L') # 转换为灰度图 mask_line = ImageOps.invert(mask_line) _bg.paste(_r, mask=mask) _bg.show() # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png") # bg.show() # ==================自动色阶处理====================== # 对上述拼接后的图片进行自动色阶处理 bg = bg.convert("RGB") _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR) # 背景阴影 im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY) print("image_high lowest_y", image_high, lowest_y) if lowest_y < 0 or lowest_y >= image_high: lowest_y = image_high - 1 print("image_high lowest_y", image_high, lowest_y) rows = [lowest_y] # 需要检查的像素行 print("copy.copy(im_shadow)") _im_shadow = copy.copy(im_shadow) Midtones = 0.7 Highlight = 235 k = 8 print("循环识别") while k: # TODO 待移除 print("循环识别:{}".format(k)) settings.app.processEvents() k -= 1 Midtones += 0.1 if Midtones > 1: Midtones = 1 Highlight -= 3 _im_shadow = levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight, OutShadow=0, OutHighlight=255, Dim=3) brightness_list = calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows) print(brightness_list) if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS: break print("Midtones,Highlight:", Midtones, Highlight) im_shadow = cv2_to_pil(_im_shadow) # ======================================================== # 计算阴影的亮度,用于确保阴影不要太黑 # 1、图片预处理,只保留阴影 only_shadow_img = im_shadow.copy() only_shadow_img.paste(Image.new(mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)), mask=im_png) average_brightness = calculated_shadow_brightness(only_shadow_img) print("average_brightness:", average_brightness) config = { "Midtones": Midtones, "Highlight": Highlight, "average_brightness": average_brightness, } return mask, config def get_mask_and_config_beifen(self, im_jpg: Image, im_png: Image, out_image_path=None): """ 步骤: 1、尺寸进行对应缩小 2、查找并设定鞋底阴影蒙版 3、自动色阶检查亮度 4、输出自动色阶参数、以及放大的尺寸蒙版 """ # ===================尺寸进行对应缩小 orign_x, orign_y = im_jpg.size im_jpg = to_resize(im_jpg, width=800) im_png = to_resize(im_png, width=800) x1, y1, x2, y2 = im_png.getbbox() cv2_png = pil_to_cv2(im_png) # =====================设定鞋底阴影图的蒙版 # 查找每列的最低非透明点 min_y_values = find_lowest_non_transparent_points(cv2_png) # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小 cv2_jpg = pil_to_cv2(im_jpg) # 返回线条图片,以及最低位置 img_with_shifted_line, lowest_y = draw_shifted_line(image=cv2_jpg, min_y_values=min_y_values, shift_amount=15, one_line_pos=(x1, x2), line_color=(0, 0, 0), line_thickness=20) # 制作蒙版 mask = cv2_to_pil(img_with_shifted_line) mask = mask.convert('L') # 转换为灰度图 mask = ImageOps.invert(mask) # 蒙版扩边 mask = expand_mask(mask, expansion_radius=30, blur_radius=10) # ====================生成新的图片 bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255)) bg.paste(im_png, mask=im_png) bg.paste(im_jpg, mask=mask) # 粘贴有阴影的地方 # bg = pil_to_cv2(bg) # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2) # bg = cv2_to_pil(bg) # bg.show() # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png") # bg.show() # ==================自动色阶处理====================== # 对上述拼接后的图片进行自动色阶处理 bg = bg.convert("RGB") _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR) # 背景阴影 im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY) rows = [lowest_y] # 需要检查的像素行 _im_shadow = copy.copy(im_shadow) Midtones = 0.62 Highlight = 235 k = 10 while k: k -= 1 Midtones += 0.1 if Midtones > 1: Midtones = 1 Highlight -= 3 _im_shadow = levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight, OutShadow=0, OutHighlight=255, Dim=3) brightness_list = calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows) print(brightness_list) if brightness_list[0] >= 254: break print("Midtones,Highlight:", Midtones, Highlight) config = (Midtones, Highlight) im_shadow = cv2_to_pil(_im_shadow) im_shadow.paste(im_png, (0, 0), im_png) # 把原图粘贴回去,避免色差 if out_image_path: im_shadow.save(out_image_path) return mask, config def my_test(self, **kwargs): if "output_queue" in kwargs: output_queue = kwargs["output_queue"] else: output_queue = None time.sleep(3) if output_queue is not None: output_queue.put(True) @time_it def run(self, image_path, cut_image_path, out_path, image_deal_mode=0, image_index=99, out_pic_size=1024, is_logo=True, out_process_path_1=None, out_process_path_2=None, resize_mode=None, max_box=None, logo_path="", **kwargs): # im 为cv对象 """ image_path:原始图 cut_image_path:抠图结果 与原始图尺寸相同 out_path:输出主图路径 image_deal_mode:图片处理模式,1表示需要镜像处理 image_index:图片顺序索引 out_pic_size:输出图片宽度大小 is_logo=True 是否要添加logo水印 out_process_path_1=None, 有阴影的图片,白底非透明 out_process_path_2=None, 已抠图的图片 resize_mode=0,1,2 主体缩小尺寸 """ if "output_queue" in kwargs: output_queue = kwargs["output_queue"] else: output_queue = None # ==========先进行剪切原图 _s = time.time() orign_im = Image.open(image_path) # 原始图 print("242 need_time_1:{}".format(time.time() - _s)) orign_x, orign_y = orign_im.size cut_image = Image.open(cut_image_path) # 原始图的已扣图 cut_image, new_box = get_mini_crop_img(img=cut_image) im_shadow = orign_im.crop(new_box) # 切图 new_x, new_y = im_shadow.size # ================自动色阶处理 _s = time.time() shadow_mask, config = self.get_mask_and_config(im_jpg=im_shadow, im_png=cut_image) print("242 need_time_2:{}".format(time.time() - _s)) shadow_mask = shadow_mask.resize(im_shadow.size) # =====抠图,形成新的阴影背景图===== # TODO 待移除 settings.app.processEvents() _new_im_shadow = Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255)) _new_im_shadow.paste(im_shadow, mask=shadow_mask) # 粘贴有阴影的地方 # _new_im_shadow.show() _new_im_shadow = pil_to_cv2(_new_im_shadow) _new_im_shadow = cv2.cvtColor(_new_im_shadow, cv2.COLOR_BGR2GRAY) _new_im_shadow = levels_adjust(img=_new_im_shadow, Shadow=0, Midtones=config["Midtones"], Highlight=config["Highlight"], OutShadow=0, OutHighlight=255, Dim=3) im_shadow = cv2_to_pil(_new_im_shadow) # ================处理阴影的亮度================== average_brightness = config["average_brightness"] if config["average_brightness"] < 180: # 调整阴影亮度 backdrop_prepped = np.asfarray(Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255))) im_shadow = im_shadow.convert("RGBA") source_prepped = np.asfarray(im_shadow) # im_shadow.show() opacity = (average_brightness - 30) / 160 opacity = max(0.5, min(opacity, 1)) print("阴影透明度:{}%".format(int(opacity * 100))) blended_np = multiply(backdrop_prepped, source_prepped, opacity=int(opacity * 100) / 100) im_shadow = Image.fromarray(np.uint8(blended_np)).convert('RGB') # im_shadow.show() # 把原图粘贴回去,避免色差 im_shadow.paste(cut_image, (0, 0), mask=cut_image) # _new_im_shadow.show() # ===========处理其他==================== # 保存带有阴影的底图,没有logo if out_process_path_1: out_image_1 = im_shadow.copy() if image_deal_mode == 1: out_image_1 = out_image_1.transpose(Image.FLIP_LEFT_RIGHT) out_image_1.save(out_process_path_1) # 保存抠图结果,没有底图,没有logo if out_process_path_2: out_image_2 = cut_image.copy() if image_deal_mode == 1: out_image_2 = out_image_2.transpose(Image.FLIP_LEFT_RIGHT) out_image_2.save(out_process_path_2) # 不生成主图时直接退出 if not out_path: return True # im_shadow.show() # =====================主图物体的缩放依据大小 if max_box: im_shadow = to_resize(_im=im_shadow, width=max_box[0], high=max_box[1]) cut_image = to_resize(_im=cut_image, width=max_box[0], high=max_box[1]) else: if resize_mode is None: im_shadow = to_resize(_im=im_shadow, width=1400, high=1400) cut_image = to_resize(_im=cut_image, width=1400, high=1400) elif resize_mode == 1: im_shadow = to_resize(_im=im_shadow, width=1400, high=1400) cut_image = to_resize(_im=cut_image, width=1400, high=1400) elif resize_mode == 2: # todo 兼容长筒靴等,将图片大小限制在一个指定的box内 im_shadow = to_resize(_im=im_shadow, width=650) cut_image = to_resize(_im=cut_image, width=650) # 再次检查需要约束缩小到一定高度,适应长筒靴 _im_x, _im_y = cut_image.size if _im_y > 1400: im_shadow = to_resize(_im=im_shadow, high=1400) cut_image = to_resize(_im=cut_image, high=1400) # if im_shadow.height <= im_shadow.width * 1.2: # im_shadow = to_resize(_im=im_shadow, width=650) # cut_image = to_resize(_im=cut_image, width=650) # else: # im_shadow = to_resize(_im=im_shadow, high=1400) # cut_image = to_resize(_im=cut_image, high=1400) if image_deal_mode == 1: # 翻转 im_shadow = im_shadow.transpose(Image.FLIP_LEFT_RIGHT) cut_image = cut_image.transpose(Image.FLIP_LEFT_RIGHT) # 创建底层背景 image_bg = Image.new("RGB", (1600, 1600), (255, 255, 255)) image_bg_x, image_bg_y = image_bg.size image_x, image_y = im_shadow.size _x = int((image_bg_x - image_x) / 2) _y = int((image_bg_y - image_y) / 2) image_bg.paste(im_shadow, (_x, _y)) image_bg.paste(cut_image, (_x, _y), cut_image) # 再叠加原图避免色差 if "小苏" in settings.Company: # 所有主图加logo is_logo = True if is_logo: # logo_path = "" # if settings.PROJECT == "红蜻蜓": # logo_path = r"resources\LOGO\HQT\logo.png" # elif settings.PROJECT == "惠利玛": # if "小苏" in settings.Company: # logo_path = r"resources\LOGO\xiaosushuoxie\logo.png" # elif "惠利玛" in settings.Company: # logo_path = r"resources\LOGO\HLM\logo.png" # else: # pass if not logo_path: logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0)) else: if os.path.exists(logo_path): logo_im = Image.open(logo_path) else: logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0)) image_bg.paste(logo_im, (0, 0), logo_im) # image_bg = image_bg.resize((out_pic_size, out_pic_size), Image.BICUBIC) if settings.OUT_PIC_FACTOR > 1.0: print("图片锐化处理") image_bg = sharpen_image(image_bg, factor=settings.OUT_PIC_FACTOR) if out_pic_size < 1600: image_bg = image_bg.resize((out_pic_size, out_pic_size), resample=settings.RESIZE_IMAGE_MODE) if settings.OUT_PIC_MODE == ".jpg": image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG") else: # quality=quality image_bg.save(out_path, quality=100) if output_queue is not None: output_queue.put(True) return True