rambo 6 mesiacov pred
rodič
commit
74ef9c3379

+ 1 - 0
python/mcu/DeviceControl.py

@@ -1382,6 +1382,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                     new_init_config["pre_delay"] = 0.0
                     new_init_config["after_delay"] = 0.0
                     new_init_config["led_switch"] = True
+                    new_init_config["turntable_angle"] = 0.0
                     new_init_config["is_wait"] = False
                     new_init_config["is_need_confirm"] = False
                     config_list.append(new_init_config)

+ 2 - 0
python/mcu/RemoteControlV2.py

@@ -253,12 +253,14 @@ class RemoteControlV2(BaseClass):
         else:
             print("开始单拍1")
             if record.image_index == 19:
+                self.msg_type = "photo_take"
                 self.sendSocketMessage(
                     code=1,
                     msg="单拍失败,单个货号最多允许拍摄20张产品图",
                     data=None,
                     device_status=2,
                 )
+                self.msg_type = "blue_tooth"
                 return
             image_index = record.image_index + 1
             self.photo_take_state = 1

+ 9 - 5
python/service/base_deal.py

@@ -136,6 +136,7 @@ class BaseDealImage(object):
     def check_folders_image_amount(self, all_goods_art_no_folder_data, image_order_list):
         print("*****************check_folders_image_amount************************")
         amount = len(image_order_list)
+        # print("图片数量检查===>", amount)
         message = ""
         for goods_art_no_folder_data in all_goods_art_no_folder_data:
             if goods_art_no_folder_data["label"] != "待处理":
@@ -148,11 +149,12 @@ class BaseDealImage(object):
                 _, e = os.path.splitext(pic_file_name)
                 if e in _Type:
                     image_num += 1
-            if image_num > amount:
-                goods_art_no_folder_data["label"] = "错误"
-                message += '货号{}:图片大于{}张~\n'.format(folder_name, amount)
-            if image_num < 2:
-                message += '货号{}:图片小于于2张~\n'.format(folder_name)
+            # print("图片数量判断===>", image_num)
+            # if image_num > amount:
+            #     goods_art_no_folder_data["label"] = "错误"
+            #     message += '货号{}:图片大于{}张~\n'.format(folder_name, amount)
+            # if image_num < 2:
+            #     message += '货号{}:图片小于于2张~\n'.format(folder_name)
 
         return all_goods_art_no_folder_data, message
 
@@ -804,6 +806,7 @@ class BaseDealImage(object):
 
                 print("image_index", image_index)
                 image_index = 99
+                curve_mask = True if "俯视" in image_dict["image_view"] else False
                 if generate_pic.run(image_path=original_image_path,
                                     cut_image_path=original_move_bg_image_path,
                                     out_path=out_path,
@@ -815,6 +818,7 @@ class BaseDealImage(object):
                                     out_process_path_2=out_process_path_2,
                                     max_box=max_box,
                                     logo_path=logo_path,
+                                    curve_mask=curve_mask
                                     ):
                     # self.show_progress_detail("货号图{} _{} 已完成800*800图片制作~".format(image_index, file_name))
                     callback_func("货号图{} _{} 已完成800*800图片制作~".format(image_index, file_name))

+ 289 - 88
python/service/generate_main_image/grenerate_main_image_test.py

@@ -1,12 +1,16 @@
 import os
 import copy
 import time
-from .image_deal_base_func import *
+from generate_main_image.image_deal_base_func import *
 from PIL import Image, ImageDraw
 from blend_modes import multiply
 import os
 import settings
 from functools import wraps
+from multi_threaded_image_saving import ImageSaver
+from get_mask_by_green import GetMask
+
+#
 
 
 def time_it(func):
@@ -15,7 +19,9 @@ def time_it(func):
         start_time = time.time()  # 记录开始时间
         result = func(*args, **kwargs)  # 调用原始函数
         end_time = time.time()  # 记录结束时间
-        print(f"Executing {func.__name__} took {end_time - start_time:.4f} seconds.")  # 打印耗时
+        print(
+            f"Executing {func.__name__} took {end_time - start_time:.4f} seconds."
+        )  # 打印耗时
         return result
 
     return wrapper
@@ -25,10 +31,11 @@ class GeneratePic(object):
     def __init__(self, is_test=False):
         # self.logger = MyLogger()
         self.is_test = is_test
+        self.saver = ImageSaver()
         pass
 
     @time_it
-    def get_mask_and_config(self, im_jpg: Image, im_png: Image):
+    def get_mask_and_config(self, im_jpg: Image, im_png: Image, curve_mask: bool):
         """
         步骤:
         1、尺寸进行对应缩小
@@ -48,27 +55,67 @@ class GeneratePic(object):
         # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
         image_high = im_jpg.height
         print("图片高度:", image_high)
+        # TODO 待移除
+        if settings.app:
+            settings.app.processEvents()
         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)
 
+        # 返回线条图片,以及最低位置
+        print("返回线条图片,以及最低位置")
+        # crop_image_box=(x1, y1, x2, y2),
+        if curve_mask:
+            crop_image_box = None
+        else:
+            # 不需要曲线部分的蒙版
+            crop_image_box = (x1, y1, x2, y2)
+        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,
+            crop_image_box=crop_image_box,
+        )
+        # TODO 待移除
+        if settings.app:
+            settings.app.processEvents()
+        print("66  制作蒙版")
         # 制作蒙版
         mask_line = cv2_to_pil(img_with_shifted_line)
-        mask = mask_line.convert('L')  # 转换为灰度图
+        mask = mask_line.convert("L")  # 转换为灰度图
         mask = ImageOps.invert(mask)
         # 蒙版扩边
-        mask = expand_mask(mask, expansion_radius=30, blur_radius=10)
-
+        print("72  蒙版扩边")
+        # 默认expansion_radius 65 blur_radius 45
+        mask = expand_or_shrink_mask(
+            pil_image=mask, expansion_radius=50, blur_radius=35
+        )
+
+        # =============使用绿色蒙版进行处理
+        if settings.IS_GET_GREEN_MASK:
+            print("============使用绿色蒙版进行处理")
+            mask = mask.convert("RGB")
+            white_bg = Image.new(mode="RGB", size=im_png.size, color=(0, 0, 0))
+            green_areas_mask_pil = GetMask().find_green_areas(cv2_jpg)
+            green_areas_mask_pil = expand_or_shrink_mask(
+                pil_image=green_areas_mask_pil, expansion_radius=15, blur_radius=5
+            )
+            mask.paste(white_bg, mask=green_areas_mask_pil.convert("L"))
+            mask = mask.convert("L")
+
+        # TODO 待移除
+        if settings.app:
+            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 待移除
+        if settings.app:
+            settings.app.processEvents()
         if self.is_test:
             _bg = bg.copy()
             draw = ImageDraw.Draw(_bg)
@@ -77,16 +124,12 @@ class GeneratePic(object):
             end_point = (_bg.width, lowest_y)  # 直线的结束点
             # 定义直线的颜色(R, G, B)
             line_color = (255, 0, 0)  # 红色
+            _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)
             # 绘制直线
             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")
@@ -103,25 +146,42 @@ class GeneratePic(object):
         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
+        k = copy.copy(settings.COLOR_GRADATION_CYCLES)
+        print("循环识别")
+        xunhuan = 0
         while k:
+            xunhuan += 1
+            if settings.app:
+                settings.app.processEvents()
             k -= 1
-            Midtones += 0.1
-            if Midtones > 1:
-                Midtones = 1
+            Midtones += 0.035
+            if Midtones > 1.7:
+                Midtones = 1.7
             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)
 
+            _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(
+                "循环识别:{},Midtones:{},Highlight:{},brightness_list:{}".format(
+                    xunhuan, Midtones, Highlight, brightness_list
+                )
+            )
             if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
                 break
-        print("Midtones,Highlight:", Midtones, Highlight)
 
         im_shadow = cv2_to_pil(_im_shadow)
 
@@ -130,8 +190,12 @@ class GeneratePic(object):
 
         # 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)
+        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)
 
@@ -143,7 +207,7 @@ class GeneratePic(object):
 
         return mask, config
 
-    def get_mask_and_config_beifen(self, im_jpg: Image, im_png: Image, out_image_path=None):
+    def get_mask_and_config_1_2025_05_18(self, im_jpg: Image, im_png: Image):
         """
         步骤:
         1、尺寸进行对应缩小
@@ -151,9 +215,7 @@ class GeneratePic(object):
         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()
@@ -163,31 +225,75 @@ class GeneratePic(object):
         # 查找每列的最低非透明点
         min_y_values = find_lowest_non_transparent_points(cv2_png)
         # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
+        image_high = im_jpg.height
+        print("图片高度:", image_high)
+        # TODO 待移除
+        if settings.app:
+            settings.app.processEvents()
         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)
-
+        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,
+            crop_image_box=(x1, y1, x2, y2),
+        )
+        # TODO 待移除
+        if settings.app:
+            settings.app.processEvents()
+        print("66  制作蒙版")
         # 制作蒙版
-        mask = cv2_to_pil(img_with_shifted_line)
-        mask = mask.convert('L')  # 转换为灰度图
+        mask_line = cv2_to_pil(img_with_shifted_line)
+        mask = mask_line.convert("L")  # 转换为灰度图
         mask = ImageOps.invert(mask)
         # 蒙版扩边
-        mask = expand_mask(mask, expansion_radius=30, blur_radius=10)
-
+        print("72  蒙版扩边")
+        # 默认expansion_radius 65 blur_radius 45
+        mask = expand_or_shrink_mask(
+            pil_image=mask, expansion_radius=50, 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 待移除
+        if settings.app:
+            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)  # 粘贴有阴影的地方
-
-        # 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()
+        # TODO 待移除
+        if settings.app:
+            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()
@@ -197,35 +303,69 @@ class GeneratePic(object):
         _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.62
+        Midtones = 0.7
         Highlight = 235
-        k = 10
+        k = 12
+        print("循环识别")
         while k:
+            print("循环识别:{}".format(k))
+            if settings.app:
+                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)
+            _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:
+            if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
                 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)
+
+        # ========================================================
+        # 计算阴影的亮度,用于确保阴影不要太黑
+
+        # 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 my_test(self,**kwargs):
+    def my_test(self, **kwargs):
         if "output_queue" in kwargs:
             output_queue = kwargs["output_queue"]
         else:
@@ -235,9 +375,23 @@ class GeneratePic(object):
             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对象
+    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="",
+        curve_mask=False,
+        **kwargs,
+    ):  # im 为cv对象
         """
         image_path:原始图
         cut_image_path:抠图结果 与原始图尺寸相同
@@ -249,6 +403,7 @@ class GeneratePic(object):
         out_process_path_1=None, 有阴影的图片,白底非透明
         out_process_path_2=None, 已抠图的图片
         resize_mode=0,1,2 主体缩小尺寸
+        curve_mask 为True时,表示为对鞋曲线部分的mask,不做剪裁
         """
         if "output_queue" in kwargs:
             output_queue = kwargs["output_queue"]
@@ -268,24 +423,33 @@ class GeneratePic(object):
 
         # ================自动色阶处理
         _s = time.time()
-        shadow_mask, config = self.get_mask_and_config(im_jpg=im_shadow, im_png=cut_image)
+        shadow_mask, config = self.get_mask_and_config(
+            im_jpg=im_shadow, im_png=cut_image, curve_mask=curve_mask
+        )
         print("242  need_time_2:{}".format(time.time() - _s))
 
         shadow_mask = shadow_mask.resize(im_shadow.size)
 
         # =====抠图,形成新的阴影背景图=====
-        _new_im_shadow = Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255))
+        # 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)
+        _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)
 
@@ -293,7 +457,9 @@ class GeneratePic(object):
         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)))
+            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()
@@ -302,8 +468,10 @@ class GeneratePic(object):
             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')
+            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()
 
         # 把原图粘贴回去,避免色差
@@ -317,14 +485,24 @@ class GeneratePic(object):
             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)
+
+            self.saver.save_image(
+                image=out_image_1, file_path=out_process_path_1, save_mode="png"
+            )
+            # save_image_by_thread(image=out_image_1, out_path=out_process_path_1)
+            # 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)
+
+            self.saver.save_image(
+                image=out_image_2, file_path=out_process_path_2, save_mode="png"
+            )
+            # save_image_by_thread(image=out_image_2, out_path=out_process_path_2, save_mode="png")
+            # out_image_2.save(out_process_path_2)
 
         # 不生成主图时直接退出
         if not out_path:
@@ -409,13 +587,36 @@ class GeneratePic(object):
             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)
+            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")
+            if settings.OUT_PIC_QUALITY == "普通":
+                self.saver.save_image(
+                    image=image_bg,
+                    file_path=out_path,
+                    save_mode="jpg",
+                    quality=None,
+                    dpi=None,
+                    _format="JPEG",
+                )
+                # save_image_by_thread(image_bg, out_path, save_mode="jpg", quality=None, dpi=None, _format="JPEG")
+                # image_bg.save(out_path, format="JPEG")
+            else:
+                self.saver.save_image(
+                    image=image_bg,
+                    file_path=out_path,
+                    save_mode="jpg",
+                    quality=100,
+                    dpi=(300, 300),
+                    _format="JPEG",
+                )
+                # save_image_by_thread(image_bg, out_path, save_mode="jpg", quality=100, dpi=(300, 300), _format="JPEG")
+                # image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG")
         else:
-            # quality=quality
-            image_bg.save(out_path, quality=100)
+            self.saver.save_image(image=image_bg, file_path=out_path, save_mode="png")
+            # image_bg.save(out_path)
 
         if output_queue is not None:
             output_queue.put(True)

+ 40 - 0
python/service/generate_main_image/image_deal_base_func.py

@@ -71,6 +71,46 @@ def expand_mask(mask, expansion_radius=5, blur_radius=0):
     return mask
 
 
+def expand_or_shrink_mask(pil_image, expansion_radius=5, iterations=1, blur_radius=0):
+    """
+    对输入的PIL黑白图像(掩膜)进行膨胀或腐蚀操作,以扩大或缩小前景区域。
+
+    :param pil_image: 输入的PIL黑白图像对象
+    :param expansion_radius: 结构元素大小,默认是一个3x3的小正方形;负值表示收缩
+    :param iterations: 操作迭代次数,默认为1次
+    :param blur_radius: 高斯模糊的半径,默认不应用模糊
+    :return: 修改后的PIL黑白图像对象
+    """
+
+    # 将PIL图像转换为numpy数组,并确保其为8位无符号整数类型
+    img_np = np.array(pil_image).astype(np.uint8)
+
+    # 如果不是二值图像,则应用阈值处理
+    if len(np.unique(img_np)) > 2:  # 检查是否为二值图像
+        _, img_np = cv2.threshold(img_np, 127, 255, cv2.THRESH_BINARY)
+
+    # 定义结构元素(例如正方形)
+    abs_expansion_radius = abs(expansion_radius)
+    kernel = np.ones((abs_expansion_radius, abs_expansion_radius), np.uint8)
+
+    # 根据expansion_radius的符号选择膨胀或腐蚀操作
+    if expansion_radius >= 0:
+        modified_img_np = cv2.dilate(img_np, kernel, iterations=iterations)
+    else:
+        modified_img_np = cv2.erode(img_np, kernel, iterations=iterations)
+
+    # 如果提供了blur_radius,则应用高斯模糊
+    if blur_radius > 0:
+        modified_img_np = cv2.GaussianBlur(
+            modified_img_np, (blur_radius * 2 + 1, blur_radius * 2 + 1), 0
+        )
+
+    # 将numpy数组转换回PIL图像
+    modified_pil_image = Image.fromarray(modified_img_np)
+
+    return modified_pil_image
+
+
 def find_lowest_non_transparent_points(cv2_png):
     # cv2_png 为cv2格式的带有alpha通道的图片
 

+ 43 - 0
python/service/get_mask_by_green.py

@@ -0,0 +1,43 @@
+from .generate_main_image.image_deal_base_func import *
+
+
+class GetMask(object):
+    def __init__(self):
+        pass
+
+    def find_green_areas(self, cv2_jpg):
+        # pp_jpg = PictureProcessing(image_path)
+        # pp_jpg = pp_jpg.resize(value=800)
+        # cv2_jpg = pil_to_cv2(pp_jpg.im)
+
+        # 将图像从BGR格式转换为HSV格式
+        hsv_image = cv2.cvtColor(cv2_jpg, cv2.COLOR_BGR2HSV)
+
+        # 定义绿色范围
+        lower_green = np.array([35, 43, 46])
+        upper_green = np.array([77, 255, 255])
+
+        # 根据颜色范围创建掩膜
+        mask = cv2.inRange(hsv_image, lower_green, upper_green)
+        # 查找轮廓
+        contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
+        # 创建空白图像用于绘制符合条件的区域
+        green_areas_mask = np.zeros_like(mask)
+        for contour in contours:
+            area = cv2.contourArea(contour)
+            if area > 100:  # 只选择面积大于100的区域
+                cv2.drawContours(green_areas_mask, [contour], -1, 255, -1)
+
+        # 转换为PIL图像
+        green_areas_mask_pil = Image.fromarray(green_areas_mask)
+        # pp_bg = PictureProcessing("RGB", pp_jpg.size, (255, 255, 255))
+        # bg_im = pp_bg.im
+        # bg_im.paste(pp_jpg.im, mask=green_areas_mask_pil)
+        return green_areas_mask_pil
+
+
+if __name__ == '__main__':
+    image_path = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\output\测试阴影\1\MD251202_6951(1).jpg"
+    # 使用函数并传入图片路径
+    mask_image = GetMask().find_green_areas(image_path)
+    mask_image.show()  # 显示结果

+ 250 - 120
python/service/grenerate_main_image_test.py

@@ -7,7 +7,8 @@ from blend_modes import multiply
 import os
 import settings
 from functools import wraps
-
+from multi_threaded_image_saving import ImageSaver
+from get_mask_by_green import GetMask
 
 def time_it(func):
     @wraps(func)  # 使用wraps来保留原始函数的元数据信息
@@ -15,7 +16,9 @@ def time_it(func):
         start_time = time.time()  # 记录开始时间
         result = func(*args, **kwargs)  # 调用原始函数
         end_time = time.time()  # 记录结束时间
-        print(f"Executing {func.__name__} took {end_time - start_time:.4f} seconds.")  # 打印耗时
+        print(
+            f"Executing {func.__name__} took {end_time - start_time:.4f} seconds."
+        )  # 打印耗时
         return result
 
     return wrapper
@@ -25,10 +28,11 @@ class GeneratePic(object):
     def __init__(self, is_test=False):
         # self.logger = MyLogger()
         self.is_test = is_test
+        self.saver = ImageSaver()
         pass
 
     @time_it
-    def get_mask_and_config(self, im_jpg: Image, im_png: Image):
+    def get_mask_and_config(self, im_jpg: Image, im_png: Image, curve_mask: bool):
         """
         步骤:
         1、尺寸进行对应缩小
@@ -48,44 +52,55 @@ class GeneratePic(object):
         # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
         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=None)
-        # TODO 待移除
-        # settings.app.processEvents()
+        # crop_image_box=(x1, y1, x2, y2),
+        if curve_mask:
+            crop_image_box = None
+        else:
+            # 不需要曲线部分的蒙版
+            crop_image_box = (x1, y1, x2, y2)
+        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,
+            crop_image_box=crop_image_box,
+        )
         print("66  制作蒙版")
         # 制作蒙版
         mask_line = cv2_to_pil(img_with_shifted_line)
-        mask = mask_line.convert('L')  # 转换为灰度图
+        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
+        # 默认expansion_radius 65 blur_radius 45
+        mask = expand_or_shrink_mask(
+            pil_image=mask, expansion_radius=50, blur_radius=35
+        )
+
+        # =============使用绿色蒙版进行处理
+        if settings.IS_GET_GREEN_MASK:
+            print("============使用绿色蒙版进行处理")
+            mask = mask.convert("RGB")
+            white_bg = Image.new(mode="RGB", size=im_png.size, color=(0, 0, 0))
+            green_areas_mask_pil = GetMask().find_green_areas(cv2_jpg)
+            green_areas_mask_pil = expand_or_shrink_mask(
+                pil_image=green_areas_mask_pil, expansion_radius=15, blur_radius=5
+            )
+            mask.paste(white_bg, mask=green_areas_mask_pil.convert("L"))
+            mask = mask.convert("L")
 
-        # 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)
@@ -94,16 +109,12 @@ class GeneratePic(object):
             end_point = (_bg.width, lowest_y)  # 直线的结束点
             # 定义直线的颜色(R, G, B)
             line_color = (255, 0, 0)  # 红色
+            _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)
             # 绘制直线
             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")
@@ -124,26 +135,38 @@ class GeneratePic(object):
         _im_shadow = copy.copy(im_shadow)
         Midtones = 0.7
         Highlight = 235
-        k = 8
+        k = copy.copy(settings.COLOR_GRADATION_CYCLES)
         print("循环识别")
+        xunhuan = 0
         while k:
-            # TODO 待移除
-            print("循环识别:{}".format(k))
-            # settings.app.processEvents()
+            xunhuan += 1
+            if settings.app:
+                settings.app.processEvents()
             k -= 1
-            Midtones += 0.1
-            if Midtones > 1:
-                Midtones = 1
+            Midtones += 0.035
+            if Midtones > 1.7:
+                Midtones = 1.7
             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)
 
+            _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(
+                "循环识别:{},Midtones:{},Highlight:{},brightness_list:{}".format(
+                    xunhuan, Midtones, Highlight, brightness_list
+                )
+            )
             if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
                 break
-        print("Midtones,Highlight:", Midtones, Highlight)
 
         im_shadow = cv2_to_pil(_im_shadow)
 
@@ -152,8 +175,12 @@ class GeneratePic(object):
 
         # 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)
+        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)
 
@@ -165,7 +192,7 @@ class GeneratePic(object):
 
         return mask, config
 
-    def get_mask_and_config_beifen(self, im_jpg: Image, im_png: Image, out_image_path=None):
+    def get_mask_and_config_1_2025_05_18(self, im_jpg: Image, im_png: Image):
         """
         步骤:
         1、尺寸进行对应缩小
@@ -173,9 +200,7 @@ class GeneratePic(object):
         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()
@@ -185,31 +210,63 @@ class GeneratePic(object):
         # 查找每列的最低非透明点
         min_y_values = find_lowest_non_transparent_points(cv2_png)
         # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
+        image_high = im_jpg.height
+        print("图片高度:", image_high)
         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)
-
+        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,
+            crop_image_box=(x1, y1, x2, y2),
+        )
+        print("66  制作蒙版")
         # 制作蒙版
-        mask = cv2_to_pil(img_with_shifted_line)
-        mask = mask.convert('L')  # 转换为灰度图
+        mask_line = cv2_to_pil(img_with_shifted_line)
+        mask = mask_line.convert("L")  # 转换为灰度图
         mask = ImageOps.invert(mask)
         # 蒙版扩边
-        mask = expand_mask(mask, expansion_radius=30, blur_radius=10)
+        print("72  蒙版扩边")
+        # 默认expansion_radius 65 blur_radius 45
+        mask = expand_or_shrink_mask(
+            pil_image=mask, expansion_radius=50, 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
 
         # ====================生成新的图片
+        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)  # 粘贴有阴影的地方
-
-        # 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()
+        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()
@@ -219,31 +276,65 @@ class GeneratePic(object):
         _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.62
+        Midtones = 0.7
         Highlight = 235
-        k = 10
+        k = 12
+        print("循环识别")
         while k:
+            print("循环识别:{}".format(k))
+            if settings.app:
+                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)
+            _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:
+            if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
                 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)
+
+        # ========================================================
+        # 计算阴影的亮度,用于确保阴影不要太黑
+
+        # 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
 
@@ -257,10 +348,23 @@ class GeneratePic(object):
             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=[1600], is_logo=True, out_process_path_1=None, out_process_path_2=None,
-            resize_mode=None, max_box=None, logo_path="", **kwargs):  # im 为cv对象
-        print("****************************处理图像核心********************************\n")
+    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="",
+        curve_mask=False,
+        **kwargs,
+    ):  # im 为cv对象
         """
         image_path:原始图
         cut_image_path:抠图结果 与原始图尺寸相同
@@ -272,6 +376,7 @@ class GeneratePic(object):
         out_process_path_1=None, 有阴影的图片,白底非透明
         out_process_path_2=None, 已抠图的图片
         resize_mode=0,1,2 主体缩小尺寸
+        curve_mask 为True时,表示为对鞋曲线部分的mask,不做剪裁
         """
         if "output_queue" in kwargs:
             output_queue = kwargs["output_queue"]
@@ -291,26 +396,31 @@ class GeneratePic(object):
 
         # ================自动色阶处理
         _s = time.time()
-        shadow_mask, config = self.get_mask_and_config(im_jpg=im_shadow, im_png=cut_image)
+        shadow_mask, config = self.get_mask_and_config(
+            im_jpg=im_shadow, im_png=cut_image, curve_mask=curve_mask
+        )
         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 = 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)
+        _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)
 
@@ -318,7 +428,9 @@ class GeneratePic(object):
         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)))
+            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()
@@ -327,8 +439,10 @@ class GeneratePic(object):
             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')
+            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()
 
         # 把原图粘贴回去,避免色差
@@ -342,14 +456,24 @@ class GeneratePic(object):
             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)
+
+            self.saver.save_image(
+                image=out_image_1, file_path=out_process_path_1, save_mode="png"
+            )
+            # save_image_by_thread(image=out_image_1, out_path=out_process_path_1)
+            # 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)
+
+            self.saver.save_image(
+                image=out_image_2, file_path=out_process_path_2, save_mode="png"
+            )
+            # save_image_by_thread(image=out_image_2, out_path=out_process_path_2, save_mode="png")
+            # out_image_2.save(out_process_path_2)
 
         # 不生成主图时直接退出
         if not out_path:
@@ -432,32 +556,38 @@ class GeneratePic(object):
         if settings.OUT_PIC_FACTOR > 1.0:
             print("图片锐化处理")
             image_bg = sharpen_image(image_bg, factor=settings.OUT_PIC_FACTOR)
-        for imageSize in out_pic_size:
-            if imageSize < 1600:
-                image_bg = image_bg.resize(
-                    (imageSize, imageSize), resample=settings.RESIZE_IMAGE_MODE
+
+        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":
+            if settings.OUT_PIC_QUALITY == "普通":
+                self.saver.save_image(
+                    image=image_bg,
+                    file_path=out_path,
+                    save_mode="jpg",
+                    quality=None,
+                    dpi=None,
+                    _format="JPEG",
                 )
-            dot_index = out_path.rfind(".")
-            if dot_index != -1:
-                # 拆分文件路径和后缀
-                file_without_suffix = out_path[:dot_index]
-                suffix = out_path[dot_index + 1 :]
+                # save_image_by_thread(image_bg, out_path, save_mode="jpg", quality=None, dpi=None, _format="JPEG")
+                # image_bg.save(out_path, format="JPEG")
             else:
-                file_without_suffix = out_path
-                suffix = ""
-
-            # 单独拼接字符串示例
-            new_file_path = f"{file_without_suffix}_{imageSize}.{suffix}"
-            if settings.OUT_PIC_MODE == ".jpg":
-                image_bg.save(
-                    new_file_path,
+                self.saver.save_image(
+                    image=image_bg,
+                    file_path=out_path,
+                    save_mode="jpg",
                     quality=100,
                     dpi=(300, 300),
-                    format="JPEG",
+                    _format="JPEG",
                 )
-            else:
-                # quality=quality
-                image_bg.save(new_file_path, quality=100)
+                # save_image_by_thread(image_bg, out_path, save_mode="jpg", quality=100, dpi=(300, 300), _format="JPEG")
+                # image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG")
+        else:
+            self.saver.save_image(image=image_bg, file_path=out_path, save_mode="png")
+            # image_bg.save(out_path)
 
         if output_queue is not None:
             output_queue.put(True)

+ 101 - 0
python/service/multi_threaded_image_saving.py

@@ -0,0 +1,101 @@
+import settings
+import threading
+from concurrent.futures import ThreadPoolExecutor, Future
+from typing import List, Dict, Any
+import time
+from PIL import Image
+
+
+class ImageSaver:
+    instance = None
+    init_flag = None
+
+    def __init__(self):
+        """
+        初始化ImageSaver对象。
+
+        :param max_workers: 线程池中最大工作线程数,默认为4。
+        """
+        """此处设计为,如果已经存在实例时,不再执行初始化"""
+        if self.init_flag:
+            return
+        else:
+            self.init_flag = True
+
+        self.executor = ThreadPoolExecutor(max_workers=settings.IMAGE_SAVE_MAX_WORKERS)
+        self.tasks_dict = {}
+        self.lock = threading.Lock()
+
+    def save_image(self, image: Image, file_path: str, **kwargs) -> Future[Any]:
+        """
+        将图片数据插入任务队列,并返回一个Future对象。
+
+        :param image_data: 图片的二进制数据。
+        :param filename: 图片保存的文件名。
+        :return: 返回一个Future对象,用于查询任务状态。
+        """
+        future = self.executor.submit(self._save_image_worker, image, file_path, **kwargs)
+        with self.lock:
+            self.tasks_dict[file_path] = {"is_completed": False,
+                                          "create_time": time.time(),
+                                          "is_error": False,
+                                          "error_info": "",
+                                          }
+        return future
+
+    def _save_image_worker(self, image: Image, file_path: str, **kwargs) -> None:
+        """
+        实际执行保存图片的任务。
+
+        :param image_data: 图片的二进制数据。
+        :param filename: 图片保存的文件名。
+        """
+        try:
+            self.save_image_by_thread_run(image=image, out_path=file_path, **kwargs)
+            with self.lock:
+                self.tasks_dict[file_path]["is_completed"] = True
+        except Exception as e:
+            print(f"Error saving {file_path}: {e}")
+            with self.lock:
+                self.tasks_dict[file_path]["is_completed"] = True
+                self.tasks_dict[file_path]["is_error"] = True
+                self.tasks_dict[file_path]["error_info"] = "{}".format(e)
+
+    def save_image_by_thread_run(self, image: Image, out_path, save_mode="png", quality=None, dpi=None, _format="JPEG",
+                                 **kwargs):
+        if save_mode == "png":
+            image.save(out_path)
+        else:
+            if quality:
+                if dpi:
+                    image.save(out_path, quality=quality, dpi=dpi, format=_format)
+                else:
+                    image.save(out_path, quality=quality, format=_format)
+            else:
+                image.save(out_path, format=_format)
+
+    def get_completed_images(self, file_path):
+        """
+        获取已完成保存的图片列表。
+
+        :return: 已完成保存的图片文件名列表。
+        """
+        with self.lock:
+            if file_path in self.tasks_dict:
+                return self.tasks_dict[file_path]
+        return None
+
+    def get_pending_images(self) -> List[str]:
+        """
+        获取尚未完成保存的图片列表。
+
+        :return: 尚未完成保存的图片文件名列表。
+        """
+        with self.lock:
+            return [file_path for file_path, _value in self.tasks_dict.items() if not _value["is_completed"]]
+
+    def __new__(cls, *args, **kwargs):
+        """如果当前没有实例时,调用父类__new__方法,生成示例,有则返回保存的内存地址。"""
+        if not cls.instance:
+            cls.instance = super().__new__(cls)
+        return cls.instance

+ 7 - 2
python/service/remove_bg_ali.py

@@ -15,6 +15,7 @@ import cv2
 import numpy as np
 from func_timeout import func_set_timeout
 from func_timeout import FunctionTimedOut
+from multi_threaded_image_saving import ImageSaver
 
 # 自己的
 AccessKeyId = 'LTAI5t7GVSbV5GuqUo935v4f'
@@ -142,9 +143,10 @@ class Picture:
 
 class RemoveBgALi(object):
     def __init__(self):
+        self.saver = ImageSaver()
         self.segment = Segment()
 
-    @func_set_timeout(30)
+    @func_set_timeout(40)
     def get_image_cut(self, file_path, out_file_path=None, original_im=None):
         if original_im:
             original_pic = Picture(in_path=None, im=original_im)
@@ -211,7 +213,10 @@ class RemoveBgALi(object):
             _img_im.paste(transparent_im, (0, 0), transparent_im)
             # _img_im.show("11111111111111111111111")
         if out_file_path:
-            _img_im.save(out_file_path)
+            self.saver.save_image(
+                image=_img_im, file_path=out_file_path, save_mode="png"
+            )
+            # _img_im.save(out_file_path)
         return _img_im
 
     def get_image_cut1(self, file_path, out_file_path=None):

+ 25 - 0
python/settings.py

@@ -231,3 +231,28 @@ import importlib
 
 
 # loaded_plugins = [load_plugin(p.lstrip(".")) for p in plugins]
+
+
+GRENERATE_MAIN_PIC_BRIGHTNESS = int(
+    getSysConfigs("other_configs", "grenerate_main_pic_brightness", 254)
+)  # 色阶是否调整到位判断
+SHADOW_PROCESSING = int(
+    getSysConfigs("other_configs", "shadow_processing", 0)
+)  # 0表示要直线和曲线,1 表示只要直线
+LOWER_Y = int(
+    getSysConfigs("other_configs", "lower_y", 4)
+)  # 鞋底以下多少距离作为阴影蒙版
+CHECK_LOWER_Y = int(
+    getSysConfigs("other_configs", "check_lower_y", 4)
+)  # 检测亮度区域,倒数第几行
+IS_GET_GREEN_MASK = (
+    True
+    if getSysConfigs("other_configs", "is_get_green_mask", "否") == "是"
+    else False
+)  # 是否进行绿幕抠图
+IMAGE_SAVE_MAX_WORKERS = int(
+    getSysConfigs("other_configs", "image_save_max_workers", 4)
+)  # 批量保存的线程大小
+COLOR_GRADATION_CYCLES = int(
+    getSysConfigs("other_configs", "color_gradation_cycles", 22)
+)  # 色阶处理循环次数

+ 1 - 1
python/sys_configs.json

@@ -9,7 +9,7 @@
     },
     {
         "key": "other_configs",
-        "value": "{\"product_type\":\"鞋类\",\"cutout_mode\":\"普通抠图\",\"device_speed\":\"二档\",\"running_mode\":\"普通模式\"}"
+        "value": "{\"product_type\":\"鞋类\",\"cutout_mode\":\"普通抠图\",\"device_speed\":\"二档\",\"running_mode\":\"普通模式\",\"grenerate_main_pic_brightness\":254,\"shadow_processing\":0,\"lower_y\":4,\"check_lower_y\":4,\"is_get_green_mask\":\"否\",\"image_save_max_workers\":4,\"color_gradation_cycles\":22}"
     },
     {
         "key": "action_configs",