import settings import os try: is_test_plugins = settings.is_test_plugins except: is_test_plugins = False if is_test_plugins: from custom_plugins.plugins_mode.pic_deal import PictureProcessing else: from plugins_mode.pic_deal import PictureProcessing from PIL import Image import shutil from service.base import get_images, check_path, get_image_mask from natsort import ns, natsorted # import math from PIL import ImageFont import settings class DetailBase(object): def __init__( self, goods_no, goods_no_value: dict, out_put_dir, windows=None, excel_data=None, assigned_page_list=None, output_queue=None, ): self.goods_no = goods_no self.output_queue = output_queue self.out_put_dir = out_put_dir self.deal_pic_func_list = [] self.goods_no_value = goods_no_value self.root = "" self.windows = windows self.template_name = None print(goods_no_value) # 重新解析为新的数据结构 self.data = {} self.detailed_images = [] self.assigned_page_list = assigned_page_list self.overlay_pic_dict = {} self.init() # for goods_art_no_dict in self.goods_no_value["货号资料"]: # print(goods_art_no_dict) # # raise 1 if excel_data: ig_keys = ["模板名称"] for k, v in excel_data.items(): if k not in ig_keys: self.goods_no_value[k] = v def check_shoe_is_right_by_pixel(self, im=None, image_path=None): if im is None: im = Image.open(image_path) # 注意,只支持透明图 # 打开图像文件 im = im.crop(im.getbbox()) # image.show() # 获取图像第一行的像素数据 pixel_data = im.load() pix_list = [] h = int(im.height / 20) for i in range(im.width): _r, _g, _b, _a = pixel_data[i, h] if _a > 10: pix_list.append(i) left_f_num = 0 middle_w = int(im.width / 2) for i in pix_list: if i < middle_w: left_f_num += 1 else: left_f_num -= 1 if left_f_num > 0: return True else: return False def del_detail_folder(self): out_path = "{out_put_dir}/{goods_no}".format( out_put_dir=self.out_put_dir, goods_no=self.goods_no ) if not os.path.exists(out_path): return try: shutil.rmtree(out_path) except BaseException as e: print("删除文件夹失败", e) def run_all(self): if self.template_name: self.out_put_dir = "{}/{}".format(self.out_put_dir, self.template_name) print("===================detailed_images=================") # 如果没有指定页面,则删除指定目录下的对应的详情文件夹 if not self.assigned_page_list: self.del_detail_folder() detailed_images = self.deal_details() self.create_folder(self.out_put_dir) detail_path = "{out_put_dir}/{goods_no}/details".format( out_put_dir=self.out_put_dir, goods_no=self.goods_no ) self.create_folder(detail_path) self.save_to_png(detailed_images=detailed_images, detail_path=detail_path) # 生成拼接图 self.generate_spliced_picture() # ------------移动其他图片--------------------- # 获取主图模板列表 main_pic_path_list = DetailBase.get_temp_pic_info(root=self.root)[ "main_pic_path_list" ] if not main_pic_path_list: self.move_other_pic(move_main_pic=True) else: self.move_other_pic(move_main_pic=True) if not self.assigned_page_list: self.deal_all_main_pic() else: if "主图" in self.assigned_page_list: self.deal_all_main_pic() # ----------如果是红蜻蜓则创建同颜色下的其他货号颜色文件夹--------------- if settings.PROJECT == "红蜻蜓": if "data_all_goods_art_info" in self.goods_no_value: # 数据格式:[{'number': '14250232', 'goods_art_no': 'AC52001173', 'color': '杏色'}, ] for pic_data in self.goods_no_value["货号资料"]: if "颜色名称" not in pic_data: continue color_name = pic_data["颜色名称"] color_file_path = "{out_put_dir}/{goods_no}/{goods_number}".format( out_put_dir=self.out_put_dir, goods_no=self.goods_no, goods_number=pic_data["编号"], ) for i in self.goods_no_value["data_all_goods_art_info"]: if color_name in i["color"]: new_path = "{out_put_dir}/{goods_no}/{goods_number}".format( out_put_dir=self.out_put_dir, goods_no=self.goods_no, goods_number="NUM{}".format(i["number"]), ) if not os.path.exists(new_path): # 创建文件夹 os.makedirs(new_path) self.move_one_pic( color_file_path, new_path, "NUM{}".format(i["number"]), ) return True # 移动一张图片到新的文件夹 def move_one_pic(self, old_path, new_path, new_name): image_file = os.listdir(old_path)[0] old_image_path = "{}/{}".format(old_path, image_file) image_e = os.path.splitext(image_file)[1] new_image_path = "{}/{}{}".format(new_path, new_name, image_e) shutil.copy(old_image_path, new_image_path) # 生成各个详情图切片 def deal_details(self): detailed_images = [] for index, func in enumerate(self.deal_pic_func_list): image_pp = func() if not self.assigned_page_list: self.image_list_append(detailed_images, image_pp) else: index = "{}".format(index + 1) if index in self.assigned_page_list: self.image_list_append(detailed_images, image_pp) else: self.image_list_append(detailed_images, {"mes": "不生成"}) return [x for x in detailed_images if x] # 生成拼接的图片 def generate_spliced_picture(self): detail_path = "{out_put_dir}/{goods_no}/details".format( out_put_dir=self.out_put_dir, goods_no=self.goods_no ) if not os.path.exists(detail_path): return detailed_images = [] for image_data in get_images(detail_path): detailed_images.append(PictureProcessing(image_data["file_path"])) # 生成拼接图 img = self.add_pic(detailed_images) join_path = "{out_put_dir}/{goods_no}/拼接图".format( out_put_dir=self.out_put_dir, goods_no=self.goods_no ) self.create_folder(join_path) img.save("{}/1.jpg".format(join_path), format="JPEG") def image_list_append(self, image_list: list, data): self.check_state_end() if isinstance(data, list): image_list.extend(data) else: image_list.append(data) def save_to_png(self, detailed_images, detail_path): self.check_state_end() for index, pp in enumerate(detailed_images): if isinstance(pp, dict): continue pp.im.save( "{}/{}({}).png".format( detail_path, self.goods_no, str(index + 11).zfill(2) ) ) def check_state_end(self): if self.windows is not None: if self.windows.state == 99: raise "用户主动取消" @classmethod def get_temp_pic_info(cls, root): """ 获取详情页模板中的信息 """ main_pic_list = [] mask_pic_list = [] if os.path.exists(r"{}\main_image".format(root)): for _name in os.listdir(r"{}\main_image".format(root)): _path = r"{}\main_image\{}".format(root, _name) if os.path.isdir(_path): main_pic_list.append([x["file_path"] for x in get_images(_path)]) mask_pic_list.append( [x["file_path"] for x in get_image_mask(_path)] ) _l = get_images(r"{}\show".format(root)) temp_pic_path = _l[0]["file_path"] if _l else None other_pic_list = [x["file_path"] for x in get_images(r"{}".format(root))] return { "main_pic_path_list": main_pic_list, "temp_pic_path": temp_pic_path, "mask_pic_list": mask_pic_list, "other_pic_path_list": other_pic_list, } def init(self): for goods_art_no_value in self.goods_no_value["货号资料"]: self.data[goods_art_no_value["货号"]] = { "pics": goods_art_no_value["pics"], "pic_is_deal": {}, } def get_text_value(self, key, subsection_len=0): text = "" if key in self.goods_no_value: if self.goods_no_value[key]: text = str(self.goods_no_value[key]) text = text.replace(r"\n", "\n") # if key in ["跟高", "鞋宽", "帮高", "脚掌围", "鞋长"]: # if text: # text = text.split(".")[0] if subsection_len != 0: text = text.split("\n") text = [x for x in text if x] if len(text) == 2: text_1 = text[0] text_2 = text[1] return text_1, text_2 else: if text: text_1 = text[0] else: text_1 = "" text_2 = "" return text_1, text_2 return text def create_folder(self, path): if not os.path.exists(path): os.makedirs(path) def get_all_process_pics(self): """ 获取所有颜色的过程图片 data = [ {"货号": "", "素材": [{ "名称": "俯视", "抠图": "路径1", "阴影": "路径2" }, ]}, ] """ return_data = [] for goods_art_no in self.data: goods_art_no_dict = { "货号": goods_art_no, "素材": [], } # 图片数据重新排序 pic_data = [] for pic_name, pic_path in self.data[goods_art_no]["pics"].items(): root_path, file_name = os.path.split(pic_path) pic_data.append(file_name) pic_data = natsorted(pic_data, alg=ns.PATH) for file_name in pic_data: if "阴影" in file_name: _, action_name, _ = file_name.split("_") pic_path = self.data[goods_art_no]["pics"][ "{}-阴影".format(action_name) ] pic_cutout_path = self.data[goods_art_no]["pics"][ "{}-抠图".format(action_name) ] if os.path.exists(pic_path) and os.path.exists(pic_cutout_path): goods_art_no_dict["素材"].append( { "名称": action_name, "抠图": pic_cutout_path, "阴影": pic_path, } ) return_data.append(goods_art_no_dict) return return_data def get_overlay_pic_from_dict( self, goods_art_no, color_name, bg_color ) -> PictureProcessing: self.check_state_end() # 增加逻辑,获取任意货号下的组合图 if "组合" in color_name: goods_art_no, color_name = self.get_all_scene_list(goods_art_no, color_name) key = "{}-{}-{}".format(goods_art_no, color_name, bg_color) if key in self.overlay_pic_dict: return self.overlay_pic_dict[key] if goods_art_no in self.data: for pic_name, pic_path in self.data[goods_art_no]["pics"].items(): if "阴影" in pic_name: action_name = pic_name.replace("-阴影", "") if action_name == color_name: pp1 = PictureProcessing(pic_path) pp2 = PictureProcessing( self.data[goods_art_no]["pics"][ "{}-抠图".format(action_name) ] ) pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize( mode="pixel", base="width", value=1600 ) self.overlay_pic_dict[key] = pp1 if key in self.overlay_pic_dict: return self.overlay_pic_dict[key] def image_init(self, bg_color=(246, 246, 246)): # 制作一批素材图,添加背景色,并保留阴影,以及处理成最小尺寸 for goods_art_no in self.data: for pic_name, pic_path in self.data[goods_art_no]["pics"].items(): if "阴影" in pic_name: action_name = pic_name.replace("-阴影", "") pp1 = PictureProcessing(pic_path) pp2 = PictureProcessing( self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)] ) pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize( mode="pixel", base="width", value=1600 ) self.data[goods_art_no]["pic_is_deal"][action_name] = pp1 # 获取任意货号的场景图,优先取指定货号; # 调整,按顺序从货号列表中提取所有组合图 def get_all_scene_info(self, goods_art_no): data = [] # 收集所有组合图 # 找任意一个有组合图的货号 for goods_art_no_dict in self.goods_no_value["货号资料"]: _goods_art_no = goods_art_no_dict["货号"] _view_name_list = set([x.split("-")[0] for x in goods_art_no_dict["pics"]]) for _view_name in _view_name_list: if "组合" not in _view_name: continue return _goods_art_no return goods_art_no def get_all_scene_list(self, goods_art_no, view_name: str): if "组合" == view_name: view_name = "组合1" try: view_index = int(view_name.replace("组合", "")) - 1 except: return goods_art_no, "无法匹配" data = [] # 收集所有组合图 # 找任意一个有组合图的货号 for goods_art_no_dict in self.goods_no_value["货号资料"]: _goods_art_no = goods_art_no_dict["货号"] if _goods_art_no != goods_art_no: continue _view_name_list = set([x.split("-")[0] for x in goods_art_no_dict["pics"]]) for _view_name in _view_name_list: if "组合" not in _view_name: continue data.append( { "goods_art_no": _goods_art_no, "view_name": _view_name, "real_view_name": ( "组合1" if _view_name == "组合" else _view_name ), } ) if len(data) <= view_index: return goods_art_no, "无法匹配" else: data.sort(key=lambda x: x["real_view_name"], reverse=False) return data[view_index]["goods_art_no"], data[view_index]["view_name"] def image_one_pic(self, goods_art_no, name, bg_color=None, return_orign=None): # 增加逻辑,获取任意货号下的组合图 if "组合" in name: print("324==== goods_art_no, name", goods_art_no, name) goods_art_no, name = self.get_all_scene_list(goods_art_no, name) print("324 goods_art_no, name", goods_art_no, name) # 制作一批素材图,添加背景色,并保留阴影,以及处理成最小尺寸 for pic_name, pic_path in self.data[goods_art_no]["pics"].items(): if "阴影" in pic_name: action_name = pic_name.replace("-阴影", "") if name != action_name: continue pp1 = PictureProcessing(pic_path) pp2 = PictureProcessing( self.data[goods_art_no]["pics"]["{}-抠图".format(action_name)] ) if not return_orign: pp1 = pp1.get_overlay_pic(top_img=pp2, color=bg_color).resize( mode="pixel", base="width", value=1600 ) return pp1 else: return pp1, pp2 if not return_orign: return None else: return None, None def move_other_pic(self, move_main_pic=True): # ------------------------------移动其他图片------------------------------ goods_no_main_pic_number = 0 for goods_art_no_dict in self.goods_no_value["货号资料"]: if "800x800" not in goods_art_no_dict: continue if not goods_art_no_dict["800x800"]: continue goods_art_no = "" if "编号" in goods_art_no_dict: if goods_art_no_dict["编号"]: goods_art_no = goods_art_no_dict["编号"] if not goods_art_no: goods_art_no = goods_art_no_dict["货号"] # print("goods_art_no:", goods_art_no) # 移动颜色图===================== goods_art_no_f = "{}/{}/{}".format( self.out_put_dir, self.goods_no, goods_art_no ) self.create_folder(goods_art_no_f) # 放入一张主图 old_pic_path_1 = goods_art_no_dict["800x800"][0] shutil.copy( old_pic_path_1, "{}/{}{}".format( goods_art_no_f, goods_art_no, os.path.splitext(old_pic_path_1)[1] ), ) # 把其他主图放入作为款号图===================== if move_main_pic: for pic_path in goods_art_no_dict["800x800"]: goods_no_main_pic_number += 1 e = os.path.splitext(pic_path)[1] shutil.copy( pic_path, "{out_put_dir}/{goods_no}/{goods_no}({goods_no_main_pic_number}){e}".format( out_put_dir=self.out_put_dir, goods_no=self.goods_no, goods_no_main_pic_number=str( goods_no_main_pic_number + 10 ).zfill(2), e=e, ), ) def deal_all_main_pic(self): """ 处理主图模板,如存在出图模板则进行对应处理 """ # 获取主图模板列表 all_main_pic_path_list = DetailBase.get_temp_pic_info(root=self.root)[ "main_pic_path_list" ] if not all_main_pic_path_list: return mask_pic_list = DetailBase.get_temp_pic_info(root=self.root)["mask_pic_list"] data = self.get_all_process_pics() print("========deal_all_main_pic=========主图相关素材:") view_list = [ "组合", "组合2", "组合3", "组合4", "组合5", "组合6", "俯视", "侧视", "后跟", "鞋底", "内里", ] for _index, main_pic_path_list in enumerate(all_main_pic_path_list): self.check_state_end() out_path_root = "{out_put_dir}/{goods_no}/main_image_{_index}".format( out_put_dir=self.out_put_dir, goods_no=self.goods_no, _index=_index ) check_path(out_path_root) if mask_pic_list[_index]: mask_pic = mask_pic_list[_index][0] else: mask_pic = None goods_no_main_pic_number = 10 # g_index 为第几个颜色货号 for g_index, goods_art_no_dict in enumerate(data): goods_art_no = goods_art_no_dict["货号"] # =====================重新指定================================= _material_sort_dict = {} for index, material_dict in enumerate(goods_art_no_dict["素材"]): name = material_dict["名称"] _material_sort_dict[name] = material_dict # ====================================================== file_name_index = -1 for view_name in view_list: # 组合图比较特殊,为全局获取 if g_index != 0: if "组合" in view_name: continue if view_name not in _material_sort_dict: continue self.check_state_end() pp_jpg, pp_png = self.image_one_pic( goods_art_no, view_name, bg_color=None, return_orign=True ) if not pp_jpg: continue file_name_index += 1 # 获取对应主图模板 if len(main_pic_path_list) < file_name_index + 1: main_pic_path = main_pic_path_list[-1] else: main_pic_path = main_pic_path_list[file_name_index] pp_bg = PictureProcessing(main_pic_path) original_width = pp_bg.width if original_width != 1600: pp_bg = pp_bg.resize(value=1600) if mask_pic: mask_bg = PictureProcessing(mask_pic) mask_bg = mask_bg.resize(value=1600) mask_box_im = mask_bg.get_im() box_size = mask_box_im.getbbox() result_image = mask_box_im.crop(box_size) mask_width, mask_height = result_image.size mask_x, mask_y = box_size[0], box_size[1] else: mask_width, mask_height = pp_bg.size mask_width, mask_height = int(mask_width * 12 / 16), int( mask_height * 12 / 16 ) mask_x, mask_y = int((pp_bg.size[0] - mask_width) / 2), int( (pp_bg.size[1] - mask_height) / 2 ) if view_name != "后跟": pp_jpg = pp_jpg.resize(base_by_box=(mask_width, mask_height)) pp_png = pp_png.resize(base_by_box=(mask_width, mask_height)) # 计算粘贴的位置 mask的位置+图片在mask中的位置 p_x = mask_x + int((mask_width - pp_jpg.width) / 2) p_y = mask_y + int((mask_height - pp_jpg.height) / 2) pp_bg = pp_bg.to_overlay_pic_advance( mode="pixel", top_img=pp_jpg, base="nw", value=(p_x, p_y), top_png_img=pp_png, ) else: new_mask_width, new_mask_height = int(mask_width / 1.6), int( mask_height / 1.6 ) pp_jpg = pp_jpg.resize( base_by_box=(new_mask_width, new_mask_height) ) pp_png = pp_png.resize( base_by_box=(new_mask_width, new_mask_height) ) new_mask_x = int((mask_width - new_mask_width) / 2 + mask_x) new_mask_y = int((mask_height - new_mask_height) / 2 + mask_y) # 计算粘贴的位置 mask的位置+图片在mask中的位置 p_x = new_mask_x + int((new_mask_width - pp_jpg.width) / 2) p_y = new_mask_y + int((new_mask_height - pp_jpg.height) / 2) pp_bg = pp_bg.to_overlay_pic_advance( mode="pixel", top_img=pp_jpg, base="nw", value=(p_x, p_y), top_png_img=pp_png, ) goods_no_main_pic_number += 1 out_pic_path = "{out_path_root}/{goods_no}({goods_no_main_pic_number}){pic_mode}".format( out_path_root=out_path_root, goods_no=self.goods_no, goods_no_main_pic_number=goods_no_main_pic_number, pic_mode=settings.OUT_PIC_MODE, ) if settings.OUT_PIC_FACTOR > 1.0: print("图片锐化处理") pp_bg = pp_bg.sharpen_image(factor=settings.OUT_PIC_FACTOR) if original_width < 1600: pp_bg = pp_bg.resize(value=original_width) print("392 out_pic_path", out_pic_path) if settings.OUT_PIC_MODE == ".jpg": pp_bg.save_as_rgb(out_pic_path) elif settings.OUT_PIC_MODE == ".png": pp_bg.save_as_png(out_pic_path) else: pp_bg.save_as_other( out_pic_path, settings.OUT_PIC_MODE.split(".")[-1] ) def add_pic(self, detailed_images): self.check_state_end() todo_detailed_images = [] detailed_images = [x for x in detailed_images if x] if not detailed_images: return for i in detailed_images: if isinstance(i, list): for n in i: todo_detailed_images.append(n) else: todo_detailed_images.append(i) page_len = 0 for index, pp in enumerate(todo_detailed_images): page_len += pp.height bg_im = Image.new("RGB", (pp.width, page_len), (255, 255, 255)) n = 0 for index, pp in enumerate(todo_detailed_images): bg_im.paste(pp.im, (0, n)) n += pp.height return bg_im # 通用方法,用于写文字 def add_text_list(self, text_list, spacing=5, base="wn", base_width=1600): text_list = [x for x in text_list if x["text"]] # print(text_list) # spacing 行间距 text_image_list = [] max_w = 0 total_h = 0 for text_data in text_list: _pp = PictureProcessing("RGBA", (base_width, 1200), (255, 255, 255, 0)) if base == "wn" or base == "nw": align = "left" anchor = None value = (0, 250) if base == "cn" or base == "nc": align = "center" anchor = "mm" value = (int(base_width / 2), 250) if base == "en" or base == "ne": align = "right" anchor = "rs" value = (base_width - 10, 250) _pp = _pp.get_text_image_advanced( value=value, font=text_data["font"], text=text_data["text"], align=align, anchor=anchor, spacing=5, fill=text_data["fill"], return_mode="min_image", margins=(0, 0, 0, 0), ) text_image_list.append(_pp) if _pp.width > max_w: max_w = _pp.width total_h += _pp.height if "spacing" in text_data: total_h += text_data["spacing"] if not text_image_list: return None # bg = PictureProcessing("RGBA", (max_w, total_h * 3), (0, 0, 0, 0)) y = 0 for text_image, text_data in zip(text_image_list, text_list): bg = bg.paste_img(top_img=text_image, value=(0, y), base=base) y += spacing + text_image.height if "spacing" in text_data: y += text_data["spacing"] bg = bg.crop(mode="min") # _ = bg.paste_img_invert(top_img=PictureProcessing("RGB", (bg.width,bg.height), (255, 255, 255))) # _.show() return bg def generate_font_list_to_pic(self): font_path_list = [ r"resources\ttf\puhui\Bold.ttf", r"resources\ttf\puhui\Medium.ttf", r"resources\ttf\puhui\Heavy.ttf", r"resources\ttf\puhui\Light.ttf", r"resources\ttf\puhui\Regular.ttf", ] text_v_list = [ "这是一段话Bold", "这是一段话Medium", "这是一段话Heavy", "这是一段话Light", "这是一段话Regular", ] detailed_images = [] for font_path, text in zip(font_path_list, text_v_list): text_list = [] for size in range(26, 80, 2): font = ImageFont.truetype(font_path, size) text_list.append( { "text": "{}-字号{}".format(text, size), "font": font, "fill": (110, 110, 110), } ) text_image = self.add_text_list(text_list, spacing=15, base="nw") text_image = text_image.crop(mode="min") text_image = text_image.paste_img_invert( top_img=PictureProcessing("RGB", text_image.size, (255, 255, 255)) ) detailed_images.append(text_image) return PictureProcessing(im=self.add_pic(detailed_images)) # 图片分段,每段至少大于N长度 def pp_pic_subsection(self, pp: PictureProcessing, one_height=3200): total_height = pp.height now_height = 0 detailed_images = [] while 1: if now_height + one_height < total_height: h1 = now_height h2 = now_height + one_height bbox = (0, h1, pp.width, h2) # print("bbox1", bbox) detailed_images.append(pp.crop(bbox=bbox)) now_height = now_height + one_height continue if now_height + one_height >= total_height: h1 = now_height h2 = total_height bbox = (0, h1, pp.width, h2) # print("bbox2", bbox) detailed_images.append(pp.crop(bbox=bbox)) break return detailed_images