Selaa lähdekoodia

refactor(image): 优化图像处理流程和错误处理机制

- 移除测试环境特殊处理逻辑,统一异常捕获机制
- 重构抠图处理流程,支持多种抠图模式切换
- 新增云端阴影处理功能,集成UltraBackground服务
- 添加图片上传工具函数,优化图像处理性能
- 改进亮度检测算法,提升图像质量控制效果
- 优化代码结构,提高可维护性和扩展性
rambo 1 kuukausi sitten
vanhempi
commit
a55b619292

+ 23 - 29
python/service/base_deal.py

@@ -113,7 +113,7 @@ class BaseDealImage(object):
             callback_func("开始处理文件夹==========  {} ".format(folder_name))
 
             flag = None
-            if settings.IS_TEST:
+            try:
                 flag = self.shoes_run_one_folder_to_deal(
                     goods_art_no_folder_data=goods_art_no_folder_data,
                     resize_image_view=resize_image_view,
@@ -122,26 +122,16 @@ class BaseDealImage(object):
                     callback_func=callback_func,
                     windows=windows,
                 )
-            else:
-                try:
-                    flag = self.shoes_run_one_folder_to_deal(
-                        goods_art_no_folder_data=goods_art_no_folder_data,
-                        resize_image_view=resize_image_view,
-                        logo_path=logo_path,
-                        image_order_list=image_order_list,
-                        callback_func=callback_func,
-                        windows=windows,
-                    )
-                except BaseException as e:
-                    error_num += 1
-                    import traceback
-                    traceback.print_exc()
-                    logger.error(f"货号 {folder_name} 处理异常: {e}")
-                    callback_func(
-                        "货号:{} 图片生成处理异常,原因:{}".format(folder_name, e)
-                    )
-                    failed_folders.append(goods_art_no_folder_data)
-                    continue
+            except BaseException as e:
+                error_num += 1
+                import traceback
+                traceback.print_exc()
+                logger.error(f"货号 {folder_name} 处理异常: {e}")
+                callback_func(
+                    "货号:{} 图片生成处理异常,原因:{}".format(folder_name, e)
+                )
+                failed_folders.append(goods_art_no_folder_data)
+                continue
 
             # 判断处理结果
             if flag is None:
@@ -664,15 +654,19 @@ class BaseDealImage(object):
                                 }
                             )
                         else:
-                            if cutout_mode == "1":
-                                remove_pic_ins = RemoveBgALi()
-                            if cutout_mode == "3":
-                                remove_pic_ins = RemoveUltraBackground()
                             try:
-                                im = remove_pic_ins.get_image_cut(
-                                    file_path=original_image_path,
-                                    out_file_path=original_move_bg_image_path,
-                                )
+                                if cutout_mode == "1":
+                                    remove_pic_ins = RemoveBgALi()
+                                    im = remove_pic_ins.get_image_cut(
+                                        file_path=original_image_path,
+                                        out_file_path=original_move_bg_image_path,
+                                    )
+                                if cutout_mode == "3":
+                                    remove_pic_ins = RemoveUltraBackground()
+                                    im = remove_pic_ins.run_ultra_segment(
+                                        file_path=original_image_path,
+                                        out_file_path=original_move_bg_image_path,
+                                    )
                             except FunctionTimedOut as f:
                                 callback_func(
                                     "货号图{} 抠图处理超时~".format(file_name)

+ 239 - 118
python/service/grenerate_main_image_test.py

@@ -13,6 +13,9 @@ from .get_mask_by_green import GetMask
 from middleware import UnicornException
 from logger import logger
 from custom_plugins.plugins_mode.pic_deal import PictureProcessing
+from service.remove_bg_ali import Segment
+
+
 def time_it(func):
     @wraps(func)  # 使用wraps来保留原始函数的元数据信息
     def wrapper(*args, **kwargs):
@@ -33,8 +36,10 @@ class GeneratePic(object):
         self.is_test = is_test
         self.saver = ImageSaver()
         pass
+
     @time_it
-    def get_mask_and_config_v3(self, im_jpg: Image, im_png: Image, curve_mask: bool,grenerate_main_pic_brightness:int):
+    def get_mask_and_config_v3(self, im_jpg: Image, im_png: Image, curve_mask: bool,
+                               grenerate_main_pic_brightness: int):
         """
         步骤:
         1、尺寸进行对应缩小
@@ -46,7 +51,6 @@ class GeneratePic(object):
         im_jpg = to_resize(im_jpg, width=600)
         im_png = to_resize(im_png, width=600)
 
-
         # =========================两个蒙版叠加,删除上半部分的图
         # 获取透明图的左右点
         result = get_extremes_from_transparent(im_png)
@@ -109,10 +113,9 @@ class GeneratePic(object):
             brightness_value = brightness_check(img_gray=_im_shadow, mask=new_mask)
 
             print("循环识别:{},Midtones:{},Highlight:{},brightness_value:{}".format(xunhuan,
-                                                                                Midtones,
-                                                                                Highlight,
-                                                                                brightness_value))
-                                                                                    
+                                                                                    Midtones,
+                                                                                    Highlight,
+                                                                                    brightness_value))
 
             if brightness_value >= grenerate_main_pic_brightness:
                 # //GRENERATE_MAIN_PIC_BRIGHTNESS 亮度校验
@@ -140,6 +143,109 @@ class GeneratePic(object):
         }
 
         return return_mask, config
+
+    @time_it
+    def get_mask_and_config_v4_online(self, ori_im_jpg: Image, ori_im_png: Image, im_jpg: Image, im_png: Image,
+                                      image_mask_opacity, image_mask_grenerate_main_pic_brightness):
+        print("42------当前计算函数:get_mask_and_config_v4_online")
+        """
+        步骤:
+        1、尺寸进行对应缩小
+        2、查找并设定鞋底阴影蒙版
+        3、自动色阶检查亮度
+        4、输出自动色阶参数、以及放大的尺寸蒙版
+        """
+        # ===================尺寸进行对应缩小(提升处理速度)
+        ori_im_jpg = to_resize(ori_im_jpg, width=1800)
+        ori_im_png = to_resize(ori_im_png, width=1800)
+        # todo 上传云端返回mask
+        utrlImage = Segment()
+        try:
+            api_url = f"{settings.DOMAIN}/api/ai_image/segment_shadow/shadow_service"
+            matte_image_url = utrlImage.get_ultra_shadow(ori_im_jpg, ori_im_png, opacity=image_mask_opacity,
+                                                         bright_target=image_mask_grenerate_main_pic_brightness,
+                                                         api_url=api_url)
+            response = requests.get(matte_image_url)
+            pic = response.content
+            return_mask = Image.open(BytesIO(pic))  # 阿里返回的抠图结果 已转PIL对象
+        except Exception as e:
+            print("get_mask_and_config_v4_online 异常", e)
+            return None, None
+        # todo 上传云端
+        # ori_im_jpg ori_im_png
+        # return_mask
+
+        # ====================生成新的图片
+        print("get_mask_and_config_v4_online  生成新的图片", matte_image_url)
+
+        # mask 复原尺寸报错一致
+        return_mask = return_mask.resize(im_png.size)
+
+        bg = Image.new(mode="RGB", size=im_png.size, color=(255, 255, 255))
+        bg.paste(im=im_jpg, mask=return_mask)  # 只粘贴有阴影的地方
+        # bg.show()
+
+        # ==================自动色阶处理======================
+        # 对上述拼接后的图片进行自动色阶处理
+        _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR)
+        # 背景阴影
+        im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
+
+        # print("copy.copy(im_shadow)")
+        _im_shadow = copy.copy(im_shadow)
+        Midtones = 0.8
+        Highlight = 235
+        k = copy.copy(settings.COLOR_GRADATION_CYCLES)
+        print("get_mask_and_config_v4_online 开始循环识别")
+        xunhuan = 0
+        while k:
+            xunhuan += 1
+            k -= 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_value = get_png_brightness(img_gray=_im_shadow, mask=return_mask)
+
+            print(
+                "get_mask_and_config_v4_online----循环识别:{},Midtones:{},Highlight:{},brightness_value:{},阀值:{}".format(
+                    xunhuan,
+                    Midtones,
+                    Highlight,
+                    brightness_value,
+                    settings.GRENERATE_MAIN_PIC_BRIGHTNESS
+                ))
+
+            if brightness_value >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
+                break
+
+        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)
+        # only_shadow_img.show()
+        average_brightness = calculated_shadow_brightness(only_shadow_img)
+        print("get_mask_and_config_v4_online average_brightness:", average_brightness)
+
+        config = {
+            "Midtones": Midtones,
+            "Highlight": Highlight,
+            "average_brightness": average_brightness,
+        }
+        return return_mask, config
+
     @time_it
     def get_mask_and_config(self, im_jpg: Image, im_png: Image, curve_mask: bool):
         """
@@ -457,104 +563,106 @@ class GeneratePic(object):
         time.sleep(3)
         if output_queue is not None:
             output_queue.put(True)
-    def paste_img(self,image, top_img, base="nw", value=(0, 0), ):
-            """
-            {
-                "command": "paste_img",
-                "im": 需要粘贴的图片
-                "pos": {"plugins_mode": "relative",  # pixel
-                        "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
-                        "value": (100, 100),
-                        "percentage": (0.5, 0.5),
-                        },
-                "margins": (0, 0, 0, 0),  # 上下左右边距
-            }
-            """
-            value = (int(value[0]), int(value[1]))
-            # 处理默认值
-            base = "nw" if not base else base
-            top, down, left, right = 0, 0, 0, 0
-
-            # 基于右边,上下居中
-            if base == "ec" or base == "ce":
-                p_x = int(image.width - (top_img.width + value[0]))
-                p_y = int((image.height - top_img.height) / 2) + value[1]
-
-            # 基于顶部,左右居中
-            if base == "nc" or base == "cn":
-                # 顶部对齐
-                deviation_x, deviation_y = int((image.width - top_img.width) / 2), int(
-                    (image.height - top_img.height) / 2
-                )
-                p_x = deviation_x + value[0] + left
-                p_y = value[1]
-
-            # 基于右上角
-            if base == "en" or base == "ne":
-                p_x = int(image.width - (top_img.width + value[0])) + left
-                p_y = value[1]
-
-            # 基于左上角
-            if base == "nw" or base == "wn":
-                deviation_x, deviation_y = 0, 0
-                p_x, p_y = value
-
-            # 基于底部,左右居中
-            if base == "cs" or base == "sc":
-                deviation_x, deviation_y = int((image.width - top_img.width) / 2), int(
-                    (image.height - top_img.height) / 2
-                )
 
-                p_y = image.height - (top_img.height + value[1] + down)
-                p_x = deviation_x + value[0] + left
+    def paste_img(self, image, top_img, base="nw", value=(0, 0), ):
+        """
+        {
+            "command": "paste_img",
+            "im": 需要粘贴的图片
+            "pos": {"plugins_mode": "relative",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (100, 100),
+                    "percentage": (0.5, 0.5),
+                    },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        }
+        """
+        value = (int(value[0]), int(value[1]))
+        # 处理默认值
+        base = "nw" if not base else base
+        top, down, left, right = 0, 0, 0, 0
+
+        # 基于右边,上下居中
+        if base == "ec" or base == "ce":
+            p_x = int(image.width - (top_img.width + value[0]))
+            p_y = int((image.height - top_img.height) / 2) + value[1]
+
+        # 基于顶部,左右居中
+        if base == "nc" or base == "cn":
+            # 顶部对齐
+            deviation_x, deviation_y = int((image.width - top_img.width) / 2), int(
+                (image.height - top_img.height) / 2
+            )
+            p_x = deviation_x + value[0] + left
+            p_y = value[1]
+
+        # 基于右上角
+        if base == "en" or base == "ne":
+            p_x = int(image.width - (top_img.width + value[0])) + left
+            p_y = value[1]
+
+        # 基于左上角
+        if base == "nw" or base == "wn":
+            deviation_x, deviation_y = 0, 0
+            p_x, p_y = value
+
+        # 基于底部,左右居中
+        if base == "cs" or base == "sc":
+            deviation_x, deviation_y = int((image.width - top_img.width) / 2), int(
+                (image.height - top_img.height) / 2
+            )
 
-            # 上下左右居中
-            if base == "center" or base == "cc":
-                deviation_x, deviation_y = int((image.width - top_img.width) / 2), int(
-                    (image.height - top_img.height) / 2
-                )
-                p_x = deviation_x + value[0] + left
-                p_y = deviation_y + value[1] + top
+            p_y = image.height - (top_img.height + value[1] + down)
+            p_x = deviation_x + value[0] + left
 
-            # 基于左下角
-            if base == "sw" or base == "ws":
-                # deviation_x, deviation_y = 0, int((img.height - img_1.height))
-                p_x = value[0] + left
-                p_y = image.height - (top_img.height + value[1] + down)
+        # 上下左右居中
+        if base == "center" or base == "cc":
+            deviation_x, deviation_y = int((image.width - top_img.width) / 2), int(
+                (image.height - top_img.height) / 2
+            )
+            p_x = deviation_x + value[0] + left
+            p_y = deviation_y + value[1] + top
 
-            # 基于左边,上下居中
-            if base == "wc" or base == "cw":
-                p_x = value[0] + left
-                p_y = int((image.height - top_img.height) / 2) + value[1] + top
+        # 基于左下角
+        if base == "sw" or base == "ws":
+            # deviation_x, deviation_y = 0, int((img.height - img_1.height))
+            p_x = value[0] + left
+            p_y = image.height - (top_img.height + value[1] + down)
 
-            # 基于右下角
-            if base == "es" or base == "se":
-                p_x = int(image.width - (top_img.width + value[0])) + left
-                p_y = image.height - (top_img.height + value[1] + down) + top
+        # 基于左边,上下居中
+        if base == "wc" or base == "cw":
+            p_x = value[0] + left
+            p_y = int((image.height - top_img.height) / 2) + value[1] + top
 
-            try:
-                image.paste(top_img, box=(p_x, p_y), mask=top_img)
-            except:
-                image.paste(top_img, box=(p_x, p_y), mask=top_img.convert("RGBA"))
+        # 基于右下角
+        if base == "es" or base == "se":
+            p_x = int(image.width - (top_img.width + value[0])) + left
+            p_y = image.height - (top_img.height + value[1] + down) + top
+
+        try:
+            image.paste(top_img, box=(p_x, p_y), mask=top_img)
+        except:
+            image.paste(top_img, box=(p_x, p_y), mask=top_img.convert("RGBA"))
+
+        return image
 
-            return image
     @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="",
-        curve_mask=False,
-        **kwargs,
+            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:原始图
@@ -591,23 +699,35 @@ class GeneratePic(object):
         with Image.open(cut_image_path) as cut_image:
             # 复制图像以便后续操作
             cut_image = cut_image.copy()
+            cut_image_basic = cut_image.copy()
         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()
-        image_mask_config = settings.getSysConfigs("basic_configs", "image_mask_config", {"mode":0,"opacity":0.5,"grenerate_main_pic_brightness":254})
-        print("阴影图处理参数===>>>",image_mask_config)
-        image_mask_mode = image_mask_config.get("mode",0)
-        image_mask_opacity = float(image_mask_config.get("opacity",0.5))
-        image_mask_grenerate_main_pic_brightness = int(image_mask_config.get("grenerate_main_pic_brightness",254))
-        if image_mask_mode ==0:
+        image_mask_config = settings.getSysConfigs("basic_configs", "image_mask_config",
+                                                   {"mode": 0, "opacity": 0.5, "grenerate_main_pic_brightness": 254})
+        print("阴影图处理参数===>>>", image_mask_config)
+        image_mask_mode = image_mask_config.get("mode", 0)
+        image_mask_opacity = float(image_mask_config.get("opacity", 0.5))
+        image_mask_grenerate_main_pic_brightness = int(image_mask_config.get("grenerate_main_pic_brightness", 254))
+        if image_mask_mode == 0:
             shadow_mask, config = self.get_mask_and_config(
                 im_jpg=im_shadow, im_png=cut_image, curve_mask=curve_mask
             )
+        elif image_mask_mode == 1:
+            shadow_mask, config = self.get_mask_and_config_v3(im_jpg=im_shadow, im_png=cut_image, curve_mask=curve_mask,
+                                                              grenerate_main_pic_brightness=image_mask_grenerate_main_pic_brightness)
+        elif image_mask_mode == 2:
+            shadow_mask, config = self.get_mask_and_config_v4_online(ori_im_jpg=orign_im, ori_im_png=cut_image_basic,
+                                                                     im_jpg=im_shadow,
+                                                                     im_png=cut_image,
+                                                                     image_mask_opacity=image_mask_opacity,
+                                                                     image_mask_grenerate_main_pic_brightness=image_mask_grenerate_main_pic_brightness)
         else:
-            shadow_mask, config = self.get_mask_and_config_v3(im_jpg=im_shadow, im_png=cut_image, curve_mask=curve_mask,grenerate_main_pic_brightness=image_mask_grenerate_main_pic_brightness)
+            shadow_mask, config = self.get_mask_and_config_v3(im_jpg=im_shadow, im_png=cut_image, curve_mask=curve_mask,
+                                                              grenerate_main_pic_brightness=image_mask_grenerate_main_pic_brightness)
         print("242  need_time_2:{}".format(time.time() - _s))
 
         shadow_mask = shadow_mask.resize(im_shadow.size)
@@ -635,7 +755,7 @@ class GeneratePic(object):
 
         # ================处理阴影的亮度==================
         average_brightness = config["average_brightness"]
-        if image_mask_mode ==0:
+        if image_mask_mode == 0:
             if config["average_brightness"] < 180:
                 # 调整阴影亮度
                 backdrop_prepped = np.asfarray(
@@ -656,8 +776,8 @@ class GeneratePic(object):
             # im_shadow.show()
         else:
             backdrop_prepped = np.asfarray(
-                    Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255))
-                )
+                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)
             opacity_params = int(image_mask_opacity * 100)
@@ -679,7 +799,7 @@ class GeneratePic(object):
                 out_image_1 = out_image_1.transpose(Image.FLIP_LEFT_RIGHT)
 
             self.saver.save_image(
-                image=out_image_1, file_path=out_process_path_1, quality=100,dpi=(350, 350), _format="PNG"
+                image=out_image_1, file_path=out_process_path_1, quality=100, dpi=(350, 350), _format="PNG"
             )
             # save_image_by_thread(image=out_image_1, out_path=out_process_path_1)
             # out_image_1.save(out_process_path_1)
@@ -691,7 +811,7 @@ class GeneratePic(object):
                 out_image_2 = out_image_2.transpose(Image.FLIP_LEFT_RIGHT)
 
             self.saver.save_image(
-                image=out_image_2, file_path=out_process_path_2, quality=100,dpi=(350, 350), _format="PNG"
+                image=out_image_2, file_path=out_process_path_2, quality=100, dpi=(350, 350), _format="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)
@@ -717,7 +837,8 @@ class GeneratePic(object):
             offset_x, offset_y = _x - (cut_image.width - _w) / 2, _y - (cut_image.height - _h) / 2,
             # print("中心偏移量:", offset_x, offset_y)
             # 透明底最小矩形
-            scale_rate = self.get_scale(base_by_box=(bg_size[0] - image_margin * 2, bg_size[1] - image_margin * 2), image_size=(_w, _h))
+            scale_rate = self.get_scale(base_by_box=(bg_size[0] - image_margin * 2, bg_size[1] - image_margin * 2),
+                                        image_size=(_w, _h))
             # 计算缩放比例,以及顶点相对位置
             # print("缩放比例:", scale_rate)
             # 偏移量
@@ -751,7 +872,6 @@ class GeneratePic(object):
                         im_shadow = to_resize(_im=im_shadow, high=1400)
                         cut_image = to_resize(_im=cut_image, high=1400)
 
-
         # 创建底层背景
         # 用户可设置的颜色值参数
         # image_bg = Image.new("RGB", bg_size, rgb_color)
@@ -759,10 +879,10 @@ class GeneratePic(object):
         # image_bg = self.paste_img(image=image_bg, top_img=cut_image, base="cc", value=(_offset_x * -1, _offset_y * -1))
         image_bg = PictureProcessing("RGB", bg_size, rgb_color)
         image_bg = image_bg.to_overlay_pic_advance(mode="pixel",
-                                                    top_img=PictureProcessing(im=im_shadow),
-                                                    base="cc",
-                                                    value=(_offset_x * -1, _offset_y * -1),
-                                                    top_png_img=PictureProcessing(im=cut_image),)
+                                                   top_img=PictureProcessing(im=im_shadow),
+                                                   base="cc",
+                                                   value=(_offset_x * -1, _offset_y * -1),
+                                                   top_png_img=PictureProcessing(im=cut_image), )
         image_bg = image_bg.im
         image_bg_x, image_bg_y = image_bg.size
         image_x, image_y = im_shadow.size
@@ -809,7 +929,7 @@ class GeneratePic(object):
             if dot_index != -1:
                 # 拆分文件路径和后缀
                 file_without_suffix = out_path[:dot_index]
-                suffix = out_path[dot_index + 1 :]
+                suffix = out_path[dot_index + 1:]
             else:
                 file_without_suffix = out_path
                 suffix = ""
@@ -818,8 +938,8 @@ class GeneratePic(object):
             image_size_str = str(imageSize)
             new_file_path = f"{file_without_suffix}_{image_size_str}.{suffix}"
             image_bg = image_bg.resize(
-                    (image_size_int, image_size_int), resample=settings.RESIZE_IMAGE_MODE
-                )
+                (image_size_int, image_size_int), resample=settings.RESIZE_IMAGE_MODE
+            )
             if image_size_int < 3000:
                 if out_pci_mode == ".jpg":
                     self.saver.save_image(
@@ -872,11 +992,12 @@ class GeneratePic(object):
             if output_queue is not None:
                 output_queue.put(True)
         return True
-    def get_scale(self,base_by_box, image_size):
+
+    def get_scale(self, base_by_box, image_size):
         box_width, box_height = int(base_by_box[0]), int(base_by_box[1])
         width, height = image_size[0], image_size[1]
         if box_width / box_height < width / height:
             scale = box_width / width
         else:
             scale = box_height / height
-        return scale
+        return scale

+ 67 - 13
python/service/image_deal_base_func.py

@@ -1,8 +1,32 @@
 import cv2
-import numpy as np
+from io import BytesIO
+import numpy as np, requests
 from PIL import Image, ImageEnhance, ImageFilter, ImageOps, ImageDraw, ImageChops, ImageStat
 import settings
 
+
+def uploadImage(im: Image) -> str:
+    img_byte_io = BytesIO()
+    # 根据图片模式选择保存格式
+    if im.mode == 'RGBA':
+        im.save(img_byte_io, format='PNG')
+    else:
+        im.save(img_byte_io, format='JPEG')
+    img_byte_io.seek(0)  # 重置指针到开头
+
+    post_headers = {"Authorization": settings.USER_TOKEN}
+    url = settings.DOMAIN + "/api/upload"
+
+    # 使用字节流上传
+    resultData = requests.post(
+        url,
+        files={"file": ("image.jpg", img_byte_io, "image/jpeg")},
+        headers=post_headers
+    ).json()
+
+    return resultData["data"]["url"]
+
+
 # 锐化图片
 def sharpen_image(img, factor=1.0):
     # 创建一个ImageEnhance对象
@@ -26,7 +50,7 @@ def to_resize(_im, width=None, high=None) -> Image:
     else:
         re_y = int(high)
         re_x = int(_im_x * re_y / _im_y)
-    _im = _im.resize((re_x, re_y),resample=settings.RESIZE_IMAGE_MODE)
+    _im = _im.resize((re_x, re_y), resample=settings.RESIZE_IMAGE_MODE)
     return _im
 
 
@@ -60,6 +84,7 @@ def get_mini_crop_img(img):
     box = (new_x1, new_y1, new_x2, new_y2)
     return img, box
 
+
 def expand_or_shrink_mask(pil_image, expansion_radius=5, iterations=1, blur_radius=0):
     """
     对输入的PIL黑白图像(掩膜)进行膨胀或腐蚀操作,以扩大或缩小前景区域。
@@ -97,6 +122,7 @@ def expand_or_shrink_mask(pil_image, expansion_radius=5, iterations=1, blur_radi
 
     return modified_pil_image
 
+
 def expand_mask(mask, expansion_radius=5, blur_radius=0):
     # 对蒙版进行膨胀处理
     mask = mask.filter(ImageFilter.MaxFilter(expansion_radius * 2 + 1))
@@ -124,14 +150,14 @@ def find_lowest_non_transparent_points(cv2_png):
 
 
 def draw_shifted_line(
-    image,
-    min_y_values,
-    shift_amount=15,
-    one_line_pos=(0, 100),
-    line_color=(0, 0, 0),
-    line_thickness=20,
-    app=None,
-    crop_image_box=None,
+        image,
+        min_y_values,
+        shift_amount=15,
+        one_line_pos=(0, 100),
+        line_color=(0, 0, 0),
+        line_thickness=20,
+        app=None,
+        crop_image_box=None,
 ):
     """
     image:jpg cv2格式的原始图
@@ -165,7 +191,7 @@ def draw_shifted_line(
             shoe_width = crop_image_box[2] - crop_image_box[0]
             _half_show_width = int(shoe_width * 0.15)
             valid_x[: crop_image_box[0] + _half_show_width] = False
-            valid_x[crop_image_box[2] - _half_show_width :] = False
+            valid_x[crop_image_box[2] - _half_show_width:] = False
 
     x_coords = np.arange(image.shape[1])[valid_x]
     y_start = shifted_min_y_values[valid_x]
@@ -272,6 +298,8 @@ def calculate_average_brightness_opencv(img_gray, rows_to_check):
             print(f"警告:行号{row}超出图片范围,已跳过。")
 
     return brightness_list
+
+
 def get_extremes_from_transparent(img, alpha_threshold=10):
     """
     直接从透明图获取最左和最右的XY坐标
@@ -347,6 +375,8 @@ def get_extremes_from_transparent(img, alpha_threshold=10):
     }
 
     return result
+
+
 def create_polygon_mask_from_points(img, left_point, right_point):
     """
     根据两个点和图片边界创建多边形mask
@@ -400,6 +430,7 @@ def transparent_to_mask_pil(img, threshold=0, is_invert=False):
         mask = a.point(lambda x: 255 if x <= threshold else 0)
     return mask
 
+
 # 两个MASK取交集
 def mask_intersection(mask1: Image.Image, mask2: Image.Image) -> Image.Image:
     """
@@ -426,10 +457,11 @@ def mask_intersection(mask1: Image.Image, mask2: Image.Image) -> Image.Image:
 
     return Image.fromarray(result, mode='L')
 
+
 def brightness_check(img_gray, mask):
     img_gray = cv2_to_pil(img_gray)
     img = Image.new("RGBA", img_gray.size, (255, 255, 255, 0))
-    img.paste(im=img_gray,mask=mask)
+    img.paste(im=img_gray, mask=mask)
     data = np.array(img)  # shape: (H, W, 4)
     # 分离通道
     r, g, b, a = data[..., 0], data[..., 1], data[..., 2], data[..., 3]
@@ -443,4 +475,26 @@ def brightness_check(img_gray, mask):
     luminance = 0.299 * r[mask] + 0.587 * g[mask] + 0.114 * b[mask]
 
     # 返回平均亮度
-    return float(np.mean(luminance))
+    return float(np.mean(luminance))
+
+
+def get_png_brightness(img_gray, mask):
+    # 计算非透明区域的平均亮度
+    # transparent_im = Image.new('RGB', img.size, (0, 0, 0))
+    img_gray = cv2_to_pil(img_gray)
+    _im = Image.new("RGB", img_gray.size, (0, 0, 0))
+    _im.paste(im=img_gray, mask=mask)
+
+    # img = Image.open(img)
+    # _im = Image.new('RGB', img.size, (0, 0, 0))
+    # _im.paste(img, (0, 0), img)
+    # _im.show()
+    # raise 1
+    img = cv2.cvtColor(np.asarray(_im), cv2.COLOR_RGB2BGR)
+
+    hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
+    H, S, V = cv2.split(hsv)
+    # print(V[0])
+    v = V[V != 0]  # 亮度非零的值
+    average_v = sum(v) / len(v)
+    return average_v

+ 247 - 60
python/service/remove_bg_ali.py

@@ -16,7 +16,8 @@ import numpy as np
 from func_timeout import func_set_timeout
 from func_timeout import FunctionTimedOut
 from .multi_threaded_image_saving import ImageSaver
-import settings
+import settings, time, math
+from logger import logger
 
 # 自己的
 AccessKeyId = "LTAI5tCk4p881X8hymj2FYFk"
@@ -92,7 +93,16 @@ class Segment(object):
 
     def get_image_orientation(self, img):
         # 获取EXIF数据
-        exif = img._getexif()
+        exif = None
+        try:
+            if hasattr(img, 'getexif'):
+                exif_data = img.getexif()
+                if exif_data:
+                    exif = dict(exif_data)
+            elif hasattr(img, '_getexif'):
+                exif = img._getexif()
+        except Exception:
+            print("获取EXIF数据失败", img)
         if exif is not None:
             # EXIF标签274对应的是Orientation
             orientation = exif.get(0x0112)
@@ -155,12 +165,42 @@ class Segment(object):
             return None
         return cutout_image
 
+    def get_ultra_shadow(self, original_image, cutout_image, opacity, bright_target, api_url=None):
+        image_original_url = uploadImage(original_image)
+        image_cutcou_url = uploadImage(cutout_image)
+        # imageUrl = imageUrl + "?x-oss-process=image/auto-orient,1"
+        if not settings.USER_TOKEN:
+            print("错误:USER_TOKEN 未配置或为空")
+            return None
+        print("阴影图处理,图片上传成功", image_original_url)
+        post_headers = {"Authorization": "Bearer " + settings.USER_TOKEN}
+        data = {
+            "oraginal_image": image_original_url,
+            "cutout_image": image_cutcou_url,
+            "opacity": opacity,
+            "bright_target": bright_target
+        }
+        print("阴影处理参数:", data)
+        response = requests.post(
+            api_url, json=data, headers=post_headers
+        )
+        print(f"响应状态码: {response.status_code}")
+        print(f"响应内容: {response.text[:500]}")  # 只打印前500字符
+
+        resultData = response.json()
+        print("旗舰版抠图请求", resultData)
+        # 安全地获取返回值
+        data = resultData.get("data")
+        if data is None:
+            print("旗舰版抠图返回的data为空")
+            return None
+        matte_image = data.get("matte_image")
+        if matte_image is None:
+            print("旗舰版 阴影处理 返回的 matte_image为空")
+            return None
+        return matte_image
+
     def get_no_bg_goods(self, file_path=None, _im=None):
-        # file_path = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\change_color_2\test\_MG_9061.jpg"
-        # file_path_1 = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\change_color_2\test\_MG_9061_resize.png"
-        # if file_path:
-        #     img = open(file_path, 'rb')
-        # if _im:
         # https://blog.csdn.net/weixin_43411585/article/details/107780941
         im = _im
         # im.save(file_path)
@@ -168,13 +208,6 @@ class Segment(object):
         im.save(img, format='JPEG')  # format: PNG or JPEG
         img.seek(0)  # rewind to the start
 
-        # img = img_byte.getvalue()  # im对象转为二进制流
-        # with open(file_path, "wb") as binary_file:
-        #     binary_file.write(im.tobytes())
-
-        # file_path = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\change_color_2\test\1.png"
-        # img = open(file_path, 'rb')
-
         request = imageseg_20191230_models.SegmentCommodityAdvanceRequest()
         request.image_urlobject = img
         client = self.create_client()
@@ -406,26 +439,28 @@ class RemoveUltraBackground:
         }
         self.saver = ImageSaver()
         self.segment = Segment()
+        self.r_ali = RemoveBgALi()
+        self.logger = logger
 
     @func_set_timeout(40)
-    def get_image_cut(self, file_path, out_file_path=None):
-        original_pic = Picture(file_path)
-        original_pic.im = self.segment.get_image_orientation(original_pic.im)
+    def get_image_cut_ultra(self, out_file_path=None, im_image=None):
+        original_pic = Picture(in_path=None, im=im_image)
+        original_pic.im = self.segment.get_image_orientation(im_image)
         original_pic.x, original_pic.y = original_pic.im.size
         if original_pic.im.mode != "RGB":
             print("抠图图片不能是PNG")
             return None
 
         new_pic = copy.copy(original_pic)
-        after_need_resize = False
-        if new_pic.x > new_pic.y:
-            if new_pic.x > 2000:
-                after_need_resize = True
-                new_pic.resize(2000)
-        else:
-            if new_pic.y > 2000:
-                after_need_resize = True
-                new_pic.resize_by_heigh(heigh=2000)
+        # after_need_resize = False
+        # if new_pic.x > new_pic.y:
+        #     if new_pic.x > 2000:
+        #         after_need_resize = True
+        #         new_pic.resize(2000)
+        # else:
+        #     if new_pic.y > 2000:
+        #         after_need_resize = True
+        #         new_pic.resize_by_heigh(heigh=2000)
         print("使用旗舰版抠图")
         try:
             api_url = f"{settings.DOMAIN}{self.api_url}"
@@ -434,48 +469,200 @@ class RemoveUltraBackground:
             print("旗舰版抠图异常:", e)
             # 处理失败,需要删除过程图片
             return None
+        if image_url is None:
+            return None
         # 字节流转PIL对象
         print("image_url", image_url)
         response = requests.get(image_url)
         pic = response.content
         _img_im = Image.open(BytesIO(pic))  # 阿里返回的抠图结果 已转PIL对象
-        # 原图更大,则需要执行CV处理
-        if after_need_resize:
-            # 将抠图结果转成mask
-            # _img_im = Image.open(_path)
-            # 将抠图结果放大到原始图大小
-            _img_im = _img_im.resize(original_pic.im.size)
-            new_big_mask = Image.new('RGB', _img_im.size, (0, 0, 0))
-            white = Image.new('RGB', _img_im.size, (255, 255, 255))
-            new_big_mask.paste(white, mask=_img_im.split()[3])
+        # # 原图更大,则需要执行CV处理
+        # if after_need_resize:
+        #     # 将抠图结果转成mask
+        #     # _img_im = Image.open(_path)
+        #     # 将抠图结果放大到原始图大小
+        #     _img_im = _img_im.resize(original_pic.im.size)
+        #     new_big_mask = Image.new('RGB', _img_im.size, (0, 0, 0))
+        #     white = Image.new('RGB', _img_im.size, (255, 255, 255))
+        #     new_big_mask.paste(white, mask=_img_im.split()[3])
+        #
+        #     # ---------制作选区缩小的mask
+        #     # mask = cv2.imread(mask_path)
+        #     # mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
+        #     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_pic.im.size, (0, 0, 0, 0))
+        #     # original_pic.im.show()
+        #     # mask.show()
+        #     transparent_im.paste(original_pic.im, (0, 0), mask.convert('L'))
+        #     # transparent_im.show()
+        #     # 上述抠图结果进行拼接
+        #     _img_im.paste(transparent_im, (0, 0), transparent_im)
+        return _img_im
 
-            # ---------制作选区缩小的mask
-            # mask = cv2.imread(mask_path)
-            # mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
-            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)
+    def add_log(self, text, _type="info"):
+        self.logger.info(
+            f"旗舰抠图,{text}"
+        )
 
-            # -------再进行抠图处理
-            mask = Image.fromarray(cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA))  # CV 对象转 PIL
-            transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
-            # original_pic.im.show()
-            # mask.show()
-            transparent_im.paste(original_pic.im, (0, 0), mask.convert('L'))
-            # transparent_im.show()
-            # 上述抠图结果进行拼接
-            _img_im.paste(transparent_im, (0, 0), transparent_im)
-        # 原图更大,则需要执行CV处理
-        if out_file_path:
-            self.saver.save_image(
-                image=_img_im, file_path=out_file_path,
-                quality=100, dpi=(350, 350), _format="PNG"
+    @func_set_timeout(40)
+    def get_image_cut_ali(self, file_path):
+        original_pic = Picture(in_path=file_path, im=None)
+        original_pic.im = self.segment.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
             )
-        return _img_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 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
+
+    def run_ultra_segment(self, file_path, out_file_path):
+        # 直接调用抠图
+        time.sleep(0.01)
+        # 1、增加获取key,2、key需要加密、3、429报错 重试再来拿一个KEY
+        self.add_log("开始处理")
+        cut_image_ali, image_deal_info = self.get_image_cut_ali(file_path)
+        ultra_cutout_data = self.get_image_cut_ultra(out_file_path, cut_image_ali)
+
+        if ultra_cutout_data is None:
+            return None
+
+        try:
+            if image_deal_info["二次抠图是否缩放"]:
+                # print("图片尺寸还原")
+                self.add_log(text="图片尺寸进行还原")
+                original_im = image_deal_info["抠图扩边后PIL对象"]
+                second_cut_image = self.picture_resize_to_original(
+                    ultra_cutout_data, original_im
+                )
+            else:
+                second_cut_image = ultra_cutout_data
+            # 创建空白图片并粘贴回去
+            _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(out_file_path, dpi=(350, 350))
+            return _img_im
+
+        except BaseException as e:
+            # print(e)
+            text = "{} 图片处理错误,代码49990".format(e)
+            self.add_log(text)
+            return
 
 
 if __name__ == '__main__':