Browse Source

临时体积哦啊

zhangyh 8 months ago
parent
commit
f7da72b310

+ 38 - 0
python/api.py

@@ -7,6 +7,7 @@ from utils.hlm_http_request import forward_request
 from sockets.socket_client import socket_manager
 from mcu.DeviceControl import DeviceControl
 import time
+from service.base_deal import  BaseDealImage
 
 @app.get("/")
 async def index():
@@ -98,3 +99,40 @@ async def forwardRequest(request: HlmForwardRequest):
         raise UnicornException(e)
     except Exception as e:
         raise UnicornException(e)
+
+
+    @app.post('/handle_detail')
+    async def handle_detail(request: Request):
+
+        image_dir = "{}/data".format(os.getcwd())
+        baseDealImage = BaseDealImage(image_dir=image_dir)
+        result = baseDealImage.dealMoveImage(image_dir=image_dir, callback_func=None)
+
+
+
+
+        params = json.dump(request.query_params)
+        #{'image_dir': 'D:/phpstudy_pro/WWW/auto_photo/output/2024-11-18', 'image_order': '俯视,侧视,后跟,鞋底,内里', 'is_check_number': True, 'resize_image_view': '后跟', 'cutout_mode': '1', 'logo_path': '', 'special_goods_art_no_folder_line': '', 'is_use_excel': True, 'excel_path': '', 'is_check_color_is_all': True, 'assigned_page_dict': {}, 'temp_class': {'huilima-2': <class 'detail_template.huilima.detail_huilima2.DetailPicGet'>, 'huilima-3': <class 'detail_template.huilima.detail_huilima3.DetailPicGet'>, 'huilima-4': <class 'detail_template.huilima.detail_huilima4.DetailPicGet'>, 'huilima-1': <class 'detail_template.huilima.detail_huilima1.DetailPicGet'>}, 'temp_name': 'huilima-2', 'temp_name_list': ['huilima-2', 'huilima-3', 'huilima-4', 'huilima-1'], 'target_error_folder': 'D:/phpstudy_pro/WWW/auto_photo/output/2024-11-18/软件-生成详情错误'}
+
+        config_data = {
+            'image_dir': params['image_dir'],
+            'image_order': params['image_order'],
+            'is_check_number': params['is_check_number'],
+            'resize_image_view': params['resize_image_view'],
+            'cutout_mode': '1',
+            'logo_path': params['logo_path'],
+            'special_goods_art_no_folder_line': '',
+            'is_use_excel': params['is_use_excel'],
+            'excel_path': params['excel_path'],
+            'is_check_color_is_all': params['is_check_color_is_all'],
+            'assigned_page_dict': {},
+            'temp_class': {
+                'huilima-2': 'detail_template.huilima.detail_huilima2.DetailPicGet',
+                'huilima-3': 'detail_template.huilima.detail_huilima3.DetailPicGet',
+                'huilima-4': 'detail_template.huilima.detail_huilima4.DetailPicGet',
+                'huilima-1': 'detail_template.huilima.detail_huilima1.DetailPicGet'
+            },
+            'temp_name': 'huilima-2',
+            'temp_name_list': ['huilima-2', 'huilima-3', 'huilima-4', 'huilima-1'],
+            'target_error_folder': 'D:/phpstudy_pro/WWW/auto_photo/output/2024-11-18/软件-生成详情错误'
+        }

+ 475 - 0
python/service/base.py

@@ -0,0 +1,475 @@
+import os
+import copy
+import configparser
+from module.base_mode.module_aes import Aes
+import exifread
+from natsort import ns, natsorted
+import shutil
+from hashlib import sha256, md5
+import requests
+from datetime import datetime
+
+
+# 获取digicam的路径
+def check_install_path(other):
+    path_list = []
+    path_list.append(other)
+    path_list.append(r"D:\Program Files (x86)\digiCamControl\CameraControl.exe")
+    path_list.append(r"C:\Program Files (x86)\digiCamControl\CameraControl.exe")
+    path_list.append(r"D:\Program Files\digiCamControl\CameraControl.exe")
+    path_list.append(r"C:\Program Files\digiCamControl\CameraControl.exe")
+    for i in path_list:
+        if os.path.exists(i):
+            return i
+    return ""
+
+
+# 输入文件夹,并检查是否是一个正常的图片文件夹。
+def check_goods_folder(folder_path):
+    all_files = os.listdir(folder_path)
+    for file in all_files:
+        file_path = "{}/{}".format(folder_path, file)
+        if not os.path.isdir(file_path):
+            continue
+        if "原始图" in os.listdir(file_path):
+            return folder_path
+    # 上述检查不通过,可能是选择目录错误
+    if "原始图" in all_files:
+        root_path, _ = os.path.split(folder_path)
+        return root_path
+    return None
+
+
+def download_file(url, file_path):
+    try:
+        root_path, file_name = os.path.split(file_path)
+        check_path(root_path)
+        response = requests.get(url)
+        _content = response.content
+        with open(file_path, 'wb') as f:
+            f.write(_content)
+        print("下载成功:{}".format(file_path))
+    except:
+        print("下载失败:{}".format(file_path))
+
+
+def calculate_sha256(file_path):
+    """Calculate the sha256 hash of the given file."""
+    sha256_hash = sha256()
+    try:
+        with open(file_path, "rb") as f:
+            # Read and update hash string value in blocks of 4K
+            for byte_block in iter(lambda: f.read(4096), b""):
+                sha256_hash.update(byte_block)
+            return sha256_hash.hexdigest()
+    except FileNotFoundError:
+        print(f"The file {file_path} does not exist.")
+        return None
+    except Exception as e:
+        print(f"An error occurred: {e}")
+        return None
+
+
+def get_modified_time(file_path):
+    # 获取文件最后修改的时间戳
+    timestamp = os.path.getmtime(file_path)
+    # 将时间戳转换为datetime对象,并格式化为指定格式的字符串
+    modified_time = datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')
+    return modified_time
+
+
+def download_file_to_io(url):
+    try:
+        # 发送GET请求
+        response = requests.get(url)
+        # 检查请求是否成功
+        response.raise_for_status()  # 如果响应状态码不是200,会抛出HTTPError异常
+        # 返回响应内容,以文本形式(对于HTML、JSON等文本内容)
+        return response.text
+        # 或者返回原始的二进制内容(对于图片、文件等非文本内容)
+        # return response.content
+    except requests.RequestException as e:
+        print(f"An error occurred: {e}")
+        return None
+
+
+def get_md5(file_path):
+    data_md5 = None
+    if os.path.isfile(file_path):
+        f = open(file_path, 'rb')
+        md5_obj = md5()
+        md5_obj.update(f.read())
+        hash_code = md5_obj.hexdigest()
+        f.close()
+        data_md5 = str(hash_code).lower()
+    return data_md5
+
+
+def list_dir(path):
+    listdir = os.listdir(path)
+    return natsorted(listdir, alg=ns.PATH)
+
+
+# 通用串口数据解析器
+def get_data_from_receive_data(receive_data, start, len_data, data_magnification=1):
+    # data_magnification 数据放大倍数,或缩小倍数,默认为1
+    try:
+        if len_data == 1:
+            data = receive_data[start]
+            return data * data_magnification
+        elif len_data == 2:
+            data = receive_data[start] << 8 | receive_data[start + 1]
+            return data * data_magnification
+        elif len_data == 4:
+            data = receive_data[start] << 24 | receive_data[start + 1] << 16 | receive_data[start + 2] << 8 | \
+                   receive_data[start + 3]
+            return data * data_magnification
+        return None
+    except:
+        return None
+
+
+def get_images(path):
+    _Type = [".png", ".PNG", ".jpg", ".JPG", ".gif", ".GIF", ".jpge", ".JPGE", ".CR2"]
+    image_list = []  # 过滤非图片数据
+    for _file in list_dir(path):
+        file_name, e = os.path.splitext(_file)
+        file_path = "{}/{}".format(path, _file)
+        if os.path.isdir(file_path):
+            continue
+        if e in _Type and "mask" not in file_name:
+            image_list.append(
+                {
+                    "file_path": file_path,
+                    "file_name": file_name,
+                    "file": _file,
+                    "root_path": path,
+                    "e": e,
+                }
+            )
+    return image_list
+
+
+def get_image_mask(path):
+    _Type = [".png", ".PNG", ".jpg", ".JPG", ".gif", ".GIF", ".jpge", ".JPGE", ".CR2"]
+    image_list = []  # 过滤非图片数据
+    for _file in list_dir(path):
+        file_name, e = os.path.splitext(_file)
+        file_path = "{}/{}".format(path, _file)
+        if os.path.isdir(file_path):
+            continue
+        if e in _Type and file_name == "mask":
+            image_list.append(
+                {
+                    "file_path": file_path,
+                    "file_name": file_name,
+                    "file": _file,
+                    "root_path": path,
+                    "e": e,
+                }
+            )
+    return image_list
+
+
+# 删除文件夹下的所有文件
+def remove_all_file(directory):
+    try:
+        shutil.rmtree(directory)
+        os.makedirs(directory)
+    except Exception as e:
+        print(f'Failed to clear directory {directory}. Reason: {e}')
+
+
+# 删除文件
+def remove_files(file_path):
+    try:
+        if os.path.exists(file_path):
+            os.remove(file_path)
+            return True
+    except BaseException as e:
+        print("base 188", e)
+    return False
+
+
+def get_folder(path):
+    folder_list = []
+    for _file in list_dir(path):
+        file_path = "{}/{}".format(path, _file)
+        if os.path.isdir(file_path):
+            folder_list.append(
+                {"folder_path": file_path,
+                 "folder_name": _file,
+                 "root_path": path,
+                 "label": "待处理",  # 是否需要继续处理
+                 }
+            )
+    return folder_list
+
+
+# 获取所有货号颜色 文件夹
+def get_all_goods_art_no_folder(path):
+    folder_list = []
+    if not os.path.exists(path):
+        return folder_list
+    if os.path.isfile(path):
+        return folder_list
+
+    temp_folder_list = get_folder(path)
+    for folder_data in temp_folder_list:
+        _p = "{}/原始图".format(folder_data["folder_path"])
+        if os.path.exists(_p):
+            folder_list.append(folder_data)
+    return folder_list
+
+
+def get_date_time_original(file_path):
+    with open(file_path, "rb") as file_data:
+        tags = exifread.process_file(file_data)
+        if "EXIF DateTimeOriginal" in tags:
+            return str(tags["EXIF DateTimeOriginal"])
+        else:
+            return False
+
+
+def get_data_from_hqt(goods_number_list, get_online_data_ins):
+    _goods_number_list = copy.deepcopy(goods_number_list)
+    _list = []
+    # 单次请求数少于20个
+    goods_number_dict = {}
+
+    while _goods_number_list:
+        goods_art_no = _goods_number_list.pop()
+        if "NUM" in goods_art_no:
+            goods_art_no = goods_art_no.replace("NUM", "")
+
+        _list.append(goods_art_no)
+        if len(_list) == 20 or len(_goods_number_list) == 0:
+            online_goods_art_data = get_online_data_ins.get_goods_art_no_info(
+                numbers_list=_list
+            )
+            if online_goods_art_data:
+                for number in online_goods_art_data:
+                    goods_number_dict["NUM" + number] = online_goods_art_data[number]
+            _list = []
+    return goods_number_dict
+
+
+def get_data_from_hqt_with_goods_art_no(goods_art_no_list, get_online_data_ins):
+    _goods_art_no_list = copy.deepcopy(goods_art_no_list)
+    _list = []
+    # 单次请求数少于20个
+    goods_art_no_dict = {}
+
+    while _goods_art_no_list:
+        goods_art_no = _goods_art_no_list.pop()
+
+        _list.append(goods_art_no)
+        if len(_list) == 20 or len(_goods_art_no_list) == 0:
+            online_goods_art_data = get_online_data_ins.get_goods_art_no_info(
+                goods_art_list=_list
+            )
+            if online_goods_art_data:
+                for _goods_art_no in online_goods_art_data:
+                    goods_art_no_dict[_goods_art_no] = online_goods_art_data[
+                        _goods_art_no
+                    ]
+            _list = []
+    return goods_art_no_dict
+
+
+def load_config_form_ini(config_path):
+    config = configparser.ConfigParser()
+    if os.path.exists(config_path):
+        try:
+            config.read(config_path, encoding="utf-8")
+        except:
+            config.read(config_path)
+
+    return config
+
+
+def set_config_to_int(config_ins, config_path, data_dict, section="basicSetup"):
+    for i in data_dict:
+        config_ins.set(section=section, option=i, value=data_dict[i])
+    try:
+        config_ins.write(open(config_path, "w", encoding="utf-8"))
+    except:
+        config_ins.write(open(config_path, "w"))
+    # try:
+    #     config_ins.read(config_path, encoding="utf-8")
+    # except:
+    #     config_ins.read(config_path)
+    return config_ins
+
+
+def get_config(config_ins, key, section="basicSetup"):
+    config_dict1 = config_ins.items(section)
+    __config_dict = {}
+    for i, k in config_dict1:
+        __config_dict[i] = k
+    if key in __config_dict:
+        return __config_dict[key]
+    else:
+        return None
+
+
+def get_config_by_items(config_dict):
+    __config_dict = {}
+    for i, k in config_dict:
+        __config_dict[i] = k
+    return __config_dict
+
+
+def get_dict_value(_dict, key, default=None):
+    if key in _dict:
+        return _dict[key]
+    else:
+        return default
+
+
+def set_key(authorization, SecretKey, SecretIv):
+    # --------------------用户身份--------------
+    if authorization:
+        authorization = Aes().get_key(authorization, SecretKey, SecretIv)
+    with open("key", "w") as f:
+        f.write(authorization)
+    return authorization
+
+
+def print_dic(_dict):
+    for i, v in _dict.items():
+        print("{}:{}".format(i, v))
+    print("\n")
+
+
+def check_path(_path):
+    if not os.path.exists(_path):
+        # 创建多级目录
+        os.makedirs(_path, exist_ok=True)
+        # os.mkdir(_path)
+    return True
+
+
+def move_folders(path_list, target_folder):
+    if not os.path.exists(target_folder):
+        os.makedirs(target_folder)
+    for source_folder in path_list:
+        shutil.move(source_folder, target_folder)
+
+
+# 给定一个图片路径,如果 是原始图下的则返回对应已扣图等信息。否则只返回基础信息
+def get_cutout_image_info(source_image_path):
+    if not os.path.exists(source_image_path):
+        return None
+    if os.path.isdir(source_image_path):
+        return None
+
+    data = {}
+    source_root_path, source_file = os.path.split(source_image_path)
+    source_file_name, source_file_extension = os.path.splitext(source_file)
+    print("---------------source_root_path--------------------")
+    print(source_root_path)
+    print(source_root_path[-3:])
+    # if source_root_path[-3:] != "原始图":
+    #     return None
+
+    data["source_image_path"] = source_image_path
+    data["source_root_path"] = source_root_path
+    data["source_file"] = source_file
+    data["source_file_name"] = source_file_name
+    data["source_file_extension"] = source_file_extension
+
+    cutout_image_path = "{}/原始图_已抠图/{}.png".format(source_root_path[:-3], source_file_name)
+    if not os.path.exists(cutout_image_path):
+        data["cutout_image_path"] = None
+        return data
+
+    cutout_image_root_path, cutout_image_file = os.path.split(cutout_image_path)
+    cutout_image_file_name, cutout_image_file_extension = os.path.splitext(cutout_image_file)
+    data["cutout_image_path"] = cutout_image_path
+    data["cutout_image_root_path"] = cutout_image_root_path
+    data["cutout_image_file"] = cutout_image_file
+    data["cutout_image_file_name"] = cutout_image_file_name
+    data["cutout_image_file_extension"] = cutout_image_file_extension
+    return data
+
+
+# 指定一个列表,指定一个需要移动的数据列表,移动到指定列表中的位置
+def move_elements_inplace(lst, indices, target_index, is_real_same_folder=False):
+    """
+    在原地将列表中指定位置的两个元素移动到目标位置。
+
+    :param lst: 要操作的列表
+    :param indices: 一个包含两个整数的元组或列表,表示要移动的元素的索引
+    :param target_index: 目标插入位置的索引
+    """
+
+    if not isinstance(indices, (list, tuple)) or len(indices) == 0:
+        raise ValueError("Indices must be a non-empty list or tuple of integers.")
+
+    # 添加到末尾
+    if target_index == len(lst):
+        if is_real_same_folder:
+            # 倒序取出所有内容,并添加到末尾
+            indices.sort(reverse=True)
+            temp = []
+            for i in indices:
+                temp.append(lst.pop(i))
+            while temp:
+                lst.append(temp.pop(-1))
+
+        # 重新写入索引
+        for index, image_label in enumerate(lst):
+            image_label.image_index = index
+        return
+
+    # 检查索引是否有效,并排序以确保按照正确的顺序处理
+    valid_indices = sorted(set(indices))  # 去重并排序
+    if not all(0 <= idx < len(lst) for idx in valid_indices):
+        raise IndexError("One or more indices are out of range.")
+
+    if not (0 <= target_index <= len(lst)):
+        raise IndexError("Target index is out of range.")
+
+    elements_to_move = [lst[idx] for idx in valid_indices]
+
+    # 如果目标位置在所有待移动元素的最大索引之后,则不需要调整目标位置
+    max_idx = max(valid_indices)
+    if max_idx < target_index:
+        pass
+    else:
+        # 计算需要减少的位置数(因为删除元素后,后续元素会向前移动)
+        shift_count = sum(1 for idx in valid_indices if idx < target_index)
+        target_index -= shift_count
+
+    # 移除元素(从大索引开始以避免影响小索引处的元素)
+    for idx in reversed(valid_indices):
+        del lst[idx]
+
+    # 插入元素到目标位置,保持它们原来的相对顺序
+    for i, element in enumerate(elements_to_move):
+        lst.insert(target_index + i, element)
+
+    # 重新写入索引
+    for index, image_label in enumerate(lst):
+        image_label.image_index = index
+
+
+# 比较两个相同格式的时间字符串
+def compare_two_times(time_str1, time_str2):
+    # 定义时间格式
+    time_format = "%Y-%m-%d %H:%M:%S"
+    # 将时间字符串解析为datetime对象
+    time1 = datetime.strptime(time_str1, time_format)
+    time2 = datetime.strptime(time_str2, time_format)
+
+    # 比较两个时间
+    if time1 > time2:
+        # print(f"{time_str1} 比 {time_str2} 新。")
+        return "left_new"
+    elif time1 < time2:
+        # print(f"{time_str2} 比 {time_str1} 新。")
+        return "right_new"
+    else:
+        # print("两个时间相等。")
+        return "is_same"

+ 1011 - 0
python/service/base_deal.py

@@ -0,0 +1,1011 @@
+import json
+from module_generate_goods_art_no_table import GenerateGoodsArtNoTable
+
+from grenerate_main_image_test import GeneratePic
+from threading import Lock
+
+import settings
+from collections import defaultdict
+from remove_bg_ali import RemoveBgALi, Picture
+from deal_cutout import DealCutout
+
+
+from data import DataModeAutoDealPics
+import time
+from image_pic_deal import OnePicDeal
+
+from natsort import natsorted
+import os
+import  shutil
+
+"""
+照片自动货号匹配 将图片放置在指定文件夹下,并自动对应不同的货号进行整理
+"""
+
+_Type = ['.png', '.PNG', '.jpg', '.JPG', '.gif', '.GIF', ".jpge", ".JPGE"]
+
+
+class BaseDealImage(object):
+    def __init__(self, image_dir=None):
+        self.goods_images_count_dict = defaultdict(int)
+        self.dataModeMatchPhoto = DataModeMatchPhoto()
+        # 数据模型
+        self.data_mode_auto_deal_pics = DataModeAutoDealPics()
+        self.image_dir = image_dir
+        pass
+
+    def run_main(self, all_goods_art_no_folder_data, callback_func=None, cutout_mode=None,
+                 resize_image_view=None, windows=None, logo_path=None, image_order_list=None):
+        # 对所有缺失已抠图的进行抠图处理
+        self.run_cutout_image(all_goods_art_no_folder_data=all_goods_art_no_folder_data,
+                              callback_func=callback_func,
+                              cutout_mode=cutout_mode,
+                              windows=windows,
+                              )
+        error_num = 0
+        successful_num = 0
+        for goods_art_no_folder_data in all_goods_art_no_folder_data:
+            if goods_art_no_folder_data["label"] != "待处理":
+                continue
+            if windows:
+                if windows.state != 1:
+                    break
+            folder_name = goods_art_no_folder_data["folder_name"]
+            callback_func("开始处理文件夹==========  {} ".format(folder_name))
+            if settings.IS_TEST:
+                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,
+                                                         )
+                if flag is None:
+                    callback_func("货号:{} 数据异常".format(folder_name))
+                else:
+                    if flag:
+                        successful_num += 1
+                        callback_func("货号:{} 图片生成处理成功".format(folder_name))
+                    else:
+                        error_num += 1
+                        callback_func("货号:{} 图片生成处理失败".format(folder_name))
+            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,
+                                                             )
+                    if flag is None:
+                        callback_func("货号:{} 数据异常".format(folder_name))
+                    else:
+                        if flag:
+                            successful_num += 1
+                            callback_func("货号:{} 图片生成处理成功".format(folder_name))
+                        else:
+                            error_num += 1
+                            callback_func("货号:{} 图片生成处理失败".format(folder_name))
+                except BaseException as e:
+                    error_num += 1
+                    callback_func("货号:{} 图片生成处理异常,原因:{}".format(folder_name, e))
+        callback_func("处理成功:{}个,失败:{}".format(successful_num, error_num))
+
+    def checkImageAmount(self, image_dir: str, amount: int, todo_goods_art_no_folder_name_list=None) -> dict:
+        result = {'code': 0, 'msg': '', 'data': {}}
+        for goods_art_no_folder in self.list_dir(image_dir):
+            # 指定内容检查
+            if todo_goods_art_no_folder_name_list is not None:
+                if goods_art_no_folder not in todo_goods_art_no_folder_name_list:
+                    continue
+
+            if not os.path.isdir("{}/{}".format(image_dir, goods_art_no_folder)):
+                continue
+            if "软件" in goods_art_no_folder:
+                continue
+            if "无法" in goods_art_no_folder:
+                continue
+            if "原始图" not in self.list_dir("{}/{}".format(image_dir, goods_art_no_folder)):
+                result['data'][goods_art_no_folder] = '文件夹下,没有 原始图 文件夹\n'
+                continue
+
+            # 计算单个文件夹原图数量
+            images = [x for x in self.list_dir("{}/{}/原始图".format(image_dir, goods_art_no_folder))]
+            image_num = 0
+            for pic_file_name in images:
+                _, e = os.path.splitext(pic_file_name)
+                if e in _Type:
+                    image_num += 1
+            # if self.number_pictures != 0:
+            if image_num > amount:
+                result['data'][goods_art_no_folder] = '货号图片大于{}张~\n'.format(amount)
+            if image_num < 2:
+                result['data'][goods_art_no_folder] = '货号图片小于于2张~\n'
+        if result['data']:
+            result['code'] = 1
+        return result
+
+    def check_folders_image_amount(self, all_goods_art_no_folder_data, image_order_list):
+        amount = len(image_order_list)
+        message = ""
+        for goods_art_no_folder_data in all_goods_art_no_folder_data:
+            if goods_art_no_folder_data["label"] != "待处理":
+                continue
+            folder_path = goods_art_no_folder_data["folder_path"]
+            folder_name = goods_art_no_folder_data["folder_name"]
+            images = [x for x in self.list_dir("{}/原始图".format(folder_path))]
+            image_num = 0
+            for pic_file_name in images:
+                _, 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)
+
+        return all_goods_art_no_folder_data, message
+
+    def check_one_folder_image_amount(self, folder_data, amount: int):
+        # 计算单个文件夹原图数量
+        images = [x for x in self.list_dir("{}/原始图".format(folder_data["folder_path"]))]
+        image_num = 0
+        for pic_file_name in images:
+            _, e = os.path.splitext(pic_file_name)
+            if e in _Type:
+                image_num += 1
+        if image_num > amount:
+            return False, '货号{}:图片大于{}张~\n'.format(folder_data["folder_name"], amount)
+        if image_num < 2:
+            return False, '货号{}:图片小于于2张~\n'.format(folder_data["folder_name"])
+        return True, ""
+
+    # 指定的图片顺序
+    def getImageOrder(self, image_order: str, resize_image_view: str):
+        imageOrderList = image_order.replace(",", ",").replace(' ', '').replace('图', '').split(",")
+        if len(set(imageOrderList)) != len(imageOrderList):
+            return {'code': 1, 'msg': '图片位置与顺序重复,请检查您的输入'}
+
+        for val in imageOrderList:
+            if val not in ["俯视", "侧视", "后跟", "鞋底", "内里", "组合", "组合2", "组合3", "组合4", "组合5"]:
+                return {'code': 1, 'msg': '可选项为:俯视,侧视,后跟,鞋底,内里,组合,组合2,组合3,组合4,组合5'}
+
+        if resize_image_view not in imageOrderList:
+            return {'code': 1, 'msg': '缩小的步骤必须是你填写的图片顺序中'}
+
+        return {'code': 0, 'msg': 'sucess', 'imageOrderList': imageOrderList}
+
+    def shoes_run_one_folder_to_deal(self,
+                                     goods_art_no_folder_data,
+                                     image_order_list: list,
+                                     resize_image_view: str,
+                                     logo_path="",
+                                     windows=None,
+                                     callback_func=None):
+        """
+        操作步骤:
+        1、查询每个图片的角度
+        2、
+        """
+
+        is_successful = True
+
+        folder_path = goods_art_no_folder_data["folder_path"]
+        folder_name = goods_art_no_folder_data["folder_name"]
+        all_original_images = self.get_images("{}/原始图".format(folder_path))
+        self.check_path('{}/800x800'.format(folder_path))
+        self.crate_all_folders(folder_path)
+
+        if not all_original_images:
+            return None
+            # _ = ["俯视", "侧视", "后跟", "鞋底", "内里"]
+        for index, image_dict in enumerate(all_original_images):
+            if index < len(image_order_list):
+                image_dict["image_view"] = image_order_list[index]
+            else:
+                image_dict["image_view"] = '其他{}'.format(len(image_order_list) - index + 1)
+
+        # ====================处理所有图片的顺序====================
+        # ["俯视", "侧视", "后跟", "鞋底", "内里"]
+        _config = {"俯视": 1,
+                   "侧视": 2,
+                   "后跟": 3,
+                   "鞋底": 4,
+                   "内里": 5, }
+
+        r = time.time()
+        n = 0
+        _index = 0
+        # 检查是否有重复的角度
+        _d_views = []
+        f = True
+        for image_dict in all_original_images:
+            n += 1
+            _index += 1
+            image_dict["old_file_name"] = image_dict["file_name"]
+            image_dict["random_name"] = "{}-{}".format(r, n)
+            if image_dict["image_view"] in _config:
+                if image_dict["image_view"] not in _d_views:
+                    _d_views.append(image_dict["image_view"])
+                else:
+                    callback_func("货号:{} 处理失败".format(folder_name))
+                    # self.show_progress_detail("货号图{}  存在多个{} 角度~".format(goods_art_no_folder, image_dict["image_view"]))
+                    return None
+
+                image_dict["index"] = _config[image_dict["image_view"]]
+            else:
+                image_dict["index"] = _index
+
+        all_original_images.sort(key=lambda x: x["index"])
+        #  ==========直接进行处理=============
+        i_n = 0
+        image_index = 0  # 图片顺序
+        is_image_deal_mode = 0
+
+        # 删除目录再新建
+        if os.path.exists('{}/阴影图处理'.format(folder_path)):
+            shutil.rmtree('{}/阴影图处理'.format(folder_path))
+
+        self.crate_all_folders(folder_path)
+
+        for image_dict in all_original_images:
+            if windows:
+                if windows.state != 1:
+                    return None
+            i_n += 1
+            image_index += 1
+            original_image_path = "{}/原始图/{}{}".format(folder_path, image_dict["file_name"], image_dict["e"])
+            file_name = image_dict["file_name"]
+            print("正在处理,货号:{}".format(folder_path))
+
+            # self.show_progress_detail("正在处理,货号:{}".format(file_name))
+            # 该文件在800images下没有时,则进行生成新的抠图
+            # 检查是否存在已抠图文件,如没有再去抠图
+
+            original_move_bg_image_path = "{}/原始图_已抠图/{}{}".format(folder_path, image_dict["file_name"], ".png")
+
+            # 此处判断鞋子是否为左右脚
+            if image_index == 1:
+                is_image_deal_mode = 0
+                if OnePicDeal().check_shoe_is_right(image_path=original_move_bg_image_path):
+                    is_image_deal_mode = 1  # 1表示要镜像,0表示不做镜像
+
+            """进行800image 生成"""
+            generate_pic = GeneratePic()
+
+            if settings.OUT_PIC_MODE == ".jpg":
+                out_path = "{}/800x800/{}{}".format(folder_path, file_name, ".jpg")
+            else:
+                out_path = "{}/800x800/{}{}".format(folder_path, file_name, ".png")
+
+            out_process_path_1 = "{}/阴影图处理/{}_{}_阴影{}".format(folder_path, file_name,
+                                                              image_dict["image_view"], ".png")
+
+            out_process_path_2 = "{}/阴影图处理/{}_{}_抠图{}".format(folder_path, file_name,
+                                                              image_dict["image_view"], ".png")
+
+            resize_mode = 1
+            max_box = None
+
+            print("------------1", image_dict["image_view"], resize_image_view)
+            if image_dict["image_view"] == resize_image_view:
+                print(image_dict["image_view"], resize_image_view)
+                resize_mode = 2
+
+            if settings.Mode == "皮具":
+                max_box = (1000, 1200)
+
+            if resize_mode == 2:
+                print(
+                    is_image_deal_mode,
+                    resize_mode,
+                    settings.OUT_PIC_SIZE,
+                    True if i_n == 1 else False,
+                    max_box
+                )
+
+            if not generate_pic.run(image_path=original_image_path,
+                                    cut_image_path=original_move_bg_image_path,
+                                    out_path=out_path,
+                                    image_deal_mode=is_image_deal_mode,
+                                    resize_mode=resize_mode,
+                                    out_pic_size=settings.OUT_PIC_SIZE,
+                                    is_logo=True if i_n == 1 else False,
+                                    out_process_path_1=out_process_path_1,
+                                    out_process_path_2=out_process_path_2,
+                                    max_box=max_box,
+                                    logo_path=logo_path,
+                                    ):
+                is_successful = False
+        if is_successful:
+            return True
+        else:
+            return False
+
+    def to_upload_pic(self, file_path, is_resize=True):
+        file_name = os.path.split(file_path)[1]
+        e = os.path.splitext(file_name)[1][1:]
+
+        im = Picture(file_path)
+        if im.x > 500:
+            im.resize(width=500)
+
+        _ = {"jpg": "JPEG",
+             "JPG": "JPEG",
+             "JPEG": "JPEG",
+             "jpeg": "JPEG",
+             "png": "PNG",
+             "PNG": "PNG", }
+        e = _[e]
+        image_io = im.save_to_io(e)
+        goods_data = {"file_path": os.path.split(file_path)[1],
+                      "image_io": image_io,
+                      "e": e
+                      }
+        url = self.data_mode_image_cut.get_online_data.upload_pic(goods_data=goods_data)
+        return url
+
+    def get_images(self, path):
+        image_list = []  # 过滤非图片数据
+        for _file in self.list_dir(path):
+            file_name, e = os.path.splitext(_file)
+            if e in _Type:
+                image_list.append({"file_path": "{}/{}".format(path, _file),
+                                   "file_name": file_name,
+                                   "e": e})
+        return image_list
+
+    def crate_all_folders(self, root_path):
+        path_list = ["800x800", "原始图_已抠图", "阴影图处理"]
+        for i in path_list:
+            path = "{}/{}".format(root_path, i)
+            self.check_path(path)
+
+    def run_cutout_image(self, all_goods_art_no_folder_data, callback_func=None, cutout_mode=1, windows=None):
+        """
+        处理所有的抠图
+        """
+        callback_func('开始处理抠图~')
+        error_goods_art_no_folder = []
+        for goods_art_no_folder_data in all_goods_art_no_folder_data:
+            if goods_art_no_folder_data["label"] != "待处理":
+                continue
+            folder_path = goods_art_no_folder_data["folder_path"]
+            self.crate_all_folders(folder_path)
+            # 检查是否存在已抠图文件,如没有再去抠图
+            images = [x for x in self.list_dir("{}/原始图".format(folder_path))]
+            cutImageList = []
+            for pic_file_name in images:
+                if windows:
+                    if windows.state != 1:
+                        break
+                # 根据名称判断,没有抠图过的,进行统计
+                file_name, suffix = os.path.splitext(pic_file_name)
+                if suffix in _Type:
+                    original_image_path = "{}/原始图/{}".format(folder_path, pic_file_name)
+                    original_move_bg_image_path = "{}/原始图_已抠图/{}{}".format(folder_path, file_name, ".png")
+                    if not os.path.exists(original_move_bg_image_path):
+                        # 没有抠图文件,进行抠图生成
+                        callback_func("正在抠图 货号:{}".format(file_name))
+                        if cutout_mode == '2':
+                            cutImageList.append({
+                                "file_name": file_name,  # 文件名
+                                "file_e": suffix,  # 后缀,.jpg
+                                "file_path": original_image_path,  # 完整路径
+                                "file": '{}{}'.format(file_name, suffix),  # 图片文件名,带后缀
+                                "need_cutout": True,  # 必须,需要抠图
+                                "out_path": original_move_bg_image_path
+                            })
+                        else:
+                            remove_pic_ins = RemoveBgALi()
+                            if settings.IS_TEST:
+                                im = remove_pic_ins.get_image_cut(file_path=original_image_path,
+                                                                  out_file_path=original_move_bg_image_path)
+                            else:
+                                try:
+                                    im = remove_pic_ins.get_image_cut(file_path=original_image_path,
+                                                                      out_file_path=original_move_bg_image_path)
+                                except FunctionTimedOut as f:
+                                    callback_func("货号图{} 抠图处理超时~".format(file_name))
+                                    im = None
+                                except BaseException as e:
+                                    callback_func("货号图{} 抠图处理失败,原因{}".format(file_name, e))
+                                    im = None
+
+                            if not im:
+                                callback_func("货号图{} 抠图处理失败~".format(file_name))
+                                continue
+                            else:
+                                callback_func("货号图{} 抠图完成~".format(file_name))
+
+            if cutout_mode == '2':
+                dealCutout = DealCutout(windows=None)
+                dealCutout.need_cutout_images = cutImageList
+                dealCutout.run()
+                while True:
+                    time.sleep(1)
+                    if windows:
+                        if windows.state != 1:
+                            break
+                    if dealCutout.state == 3:
+                        if len(dealCutout.resultData) != len(cutImageList):
+                            error_goods_art_no_folder.append(folder_path)
+                        break
+
+        if error_goods_art_no_folder:
+            print("以下货号抠图失败~\n {}".format(error_goods_art_no_folder))
+            callback_func("以下货号抠图失败~\n {}".format(error_goods_art_no_folder))
+        else:
+            callback_func("完成抠图处理")
+
+    def checkCutoutImage(self, image_dir: str, todo_goods_art_no_folder_name_list=None):
+        """
+        进行图片检查,不合规的直接提示
+        """
+        error_goods_art_no_folder = []
+        self.check_path("{}/软件-处理失败".format(image_dir))
+
+        for goods_art_no_folder in self.list_dir(image_dir):
+            # 指定内容检查
+            if todo_goods_art_no_folder_name_list is not None:
+                if goods_art_no_folder not in todo_goods_art_no_folder_name_list:
+                    continue
+
+            if not os.path.isdir("{}/{}".format(image_dir, goods_art_no_folder)):
+                continue
+            if "软件" in goods_art_no_folder:
+                continue
+            if "无法" in goods_art_no_folder:
+                continue
+            if "原始图" not in self.list_dir("{}/{}".format(image_dir, goods_art_no_folder)):
+                error_goods_art_no_folder.append(goods_art_no_folder)
+                continue
+
+            self.check_path("{}/{}/原始图_已抠图".format(image_dir, goods_art_no_folder))
+            self.check_path("{}/{}/800x800".format(image_dir, goods_art_no_folder))
+            self.check_path("{}/{}/阴影图处理".format(image_dir, goods_art_no_folder))
+
+        if error_goods_art_no_folder:
+            self.move_folders(path_list=["{}/{}".format(self.image_dir, x) for x in error_goods_art_no_folder],
+                              target_folder="{}/软件-处理失败".format(self.image_dir))
+            return False
+
+    def move_folders(self, path_list, target_folder):
+        for source_folder in path_list:
+            shutil.move(source_folder, target_folder)
+
+    def rename_folder_for_hqt(self, all_goods_art_no_folder_data):
+        """
+        步骤:
+        规整红蜻蜓的文件名
+        重新按文件名进行命名
+        """
+        goods_art_no_list = []
+        for goods_art_no_folder_data in all_goods_art_no_folder_data:
+            if "@" not in goods_art_no_folder_data["folder_name"]:
+                goods_art_no_folder_data["label"] = "待处理"
+                goods_art_no_list.append(goods_art_no_folder_data["folder_name"])
+            else:
+                goods_art_no_folder_data["label"] = "不处理"
+
+        if goods_art_no_list:
+            # goods_art_no_dict 文件夹与货号的字典
+            goods_art_no_dict = self.dataModeMatchPhoto.get_data_from_hqt_with_goods_art_no(
+                goods_art_no_list=goods_art_no_list)
+            for goods_art_no_folder_data in all_goods_art_no_folder_data:
+                if goods_art_no_folder_data["label"] != "待处理":
+                    continue
+                goods_art_no_folder = goods_art_no_folder_data["folder_name"]
+                if goods_art_no_folder in goods_art_no_dict:
+                    print(goods_art_no_folder)
+                    old_folder_path = goods_art_no_folder_data["folder_path"]
+                    new_folder_name = "{}@NUM{}".format(goods_art_no_folder,
+                                                        goods_art_no_dict[goods_art_no_folder]["编号"])
+                    new_folder_path = "{}/{}".format(goods_art_no_folder_data["root_path"], new_folder_name)
+                    try:
+                        os.rename(old_folder_path, new_folder_path)
+                        goods_art_no_folder_data["folder_path"] = new_folder_path
+                        goods_art_no_folder_data["folder_name"] = new_folder_path
+                        goods_art_no_folder_data["label"] = "待处理"
+                    except BaseException as e:
+                        goods_art_no_folder_data["label"] = "不处理"
+                        print("521 文件夹重名命失败:{}".format(e))
+
+        # 重新规整修改图片名称
+        for goods_art_no_folder_data in all_goods_art_no_folder_data:
+            if goods_art_no_folder_data["label"] != "待处理":
+                continue
+            goods_art_no_folder = goods_art_no_folder_data["folder_name"]
+            _img_all = self.list_dir("{}/原始图".format(goods_art_no_folder_data["folder_path"]))
+            index = 0
+            for _file in _img_all:
+                file_name, suffix = os.path.splitext(_file)
+                if suffix in _Type:
+                    index += 1
+                    folder_path = goods_art_no_folder_data["folder_path"]
+                    new_file_name = "{}({}){}".format(goods_art_no_folder, index, suffix)
+                    new_path = "{}/原始图/{}".format(folder_path, new_file_name)
+                    old_path = "{}/原始图/{}".format(folder_path, _file)
+                    crop_new_path = "{}/原始图_已抠图/{}".format(folder_path, "{}({}).png".format(goods_art_no_folder, index))
+                    crop_old_path = "{}/原始图_已抠图/{}".format(folder_path, "{}.png".format(file_name))
+
+                    if old_path != new_path:
+                        # 存在货号命名错误的,进行修正
+                        try:
+                            if os.path.exists(old_path):
+                                os.rename(old_path, new_path)
+                            if os.path.exists(crop_old_path):
+                                os.rename(crop_old_path, crop_new_path)
+                        except BaseException as e:
+                            goods_art_no_folder_data["label"] = "不处理"
+                            print("550 文件夹重名命失败:{}".format(e))
+
+    def cutImagePiju(self, image_dir: str, image_order='', is_check_number=True, is_filter=True, resize_image_view='后跟',
+                     callback_func=None, event=None, todo_goods_art_no_folder_name_list=None):
+        """
+        1、遍历文件夹,基于生成的结果图看哪些需要进行抠图等处理
+        2、压缩并上传平台获取抠图
+        3、抠图处理成白底图
+        4、做成800*800/200*200
+        :return:
+        """
+        logo_path = ""
+
+        res = self.getImageOrder(image_order=image_order, resize_image_view=resize_image_view)
+        if res['code'] != 0:
+            callback_func(res['msg'])
+            return {'code': 1, 'msg': res['msg']}
+
+        imageOrderList = res['imageOrderList']
+
+        """扫描文档,检查有哪些需要进行抠图等处理"""
+        self.lock = Lock()
+        to_do_images_total = 0
+        error_goods_art_no_folder = []
+        for goods_art_no_folder in self.list_dir(image_dir):
+            # 指定内容检查
+            if todo_goods_art_no_folder_name_list is not None:
+                if goods_art_no_folder not in todo_goods_art_no_folder_name_list:
+                    continue
+
+            if not os.path.isdir("{}/{}".format(image_dir, goods_art_no_folder)):
+                continue
+
+            self.check_path("{}/{}/原始图".format(image_dir, goods_art_no_folder))
+            self.check_path("{}/{}/原始图_已抠图".format(image_dir, goods_art_no_folder))
+            self.check_path("{}/{}/800x800".format(image_dir, goods_art_no_folder))
+            self.check_path("{}/{}/阴影图处理".format(image_dir, goods_art_no_folder))
+
+            # 遍历原始图片文件夹
+            all_original_images = [x for x in
+                                   self.list_dir(
+                                       "{}/{}/原始图".format(image_dir, goods_art_no_folder))]
+            # 检查已抠图文件夹
+            all_moved_images = [os.path.splitext(x)[0] for x in
+                                self.list_dir(
+                                    "{}/{}/原始图_已抠图".format(image_dir, goods_art_no_folder))]
+
+            all_800images = [os.path.splitext(x)[0] for x in
+                             self.list_dir(
+                                 "{}/{}/800x800".format(image_dir, goods_art_no_folder))]
+            if is_check_number and len(imageOrderList) != len(all_original_images):
+                callback_func("{} 文件夹下图片数量与订单数量不一致,请检查!".format(goods_art_no_folder))
+                return {'code': 1, 'msg': '{} 文件夹下图片数量与订单数量不一致,请检查!'.format(goods_art_no_folder)}
+
+            all_800images = []
+            image_num = 0
+            for pic_file_name in all_original_images:
+                if pic_file_name not in all_800images:
+                    # 根据名称判断,没有抠图过的,进行统计
+                    _, e = os.path.splitext(pic_file_name)
+                    print(e)
+                    if e in _Type:
+                        image_num += 1
+                        print("----------》", goods_art_no_folder, pic_file_name)
+                        to_do_images_total += 1
+            # if image_num > 5:
+            #     error_goods_art_no_folder.append(goods_art_no_folder)
+
+        # if error_goods_art_no_folder:
+        #     self.show_progress_detail("以下货号图片张数超过5张~\n {}".format(error_goods_art_no_folder))
+        #     self.set_state(state_value=2)
+        #     return
+
+        if to_do_images_total > 0:
+            # self.progress_sign.emit({"type": "处理图片 抠图、加工等", "progress_bar_value": 0})
+
+            for goods_art_no_folder in self.list_dir(image_dir):
+                # 指定内容检查
+                if todo_goods_art_no_folder_name_list is not None:
+                    if goods_art_no_folder not in todo_goods_art_no_folder_name_list:
+                        continue
+
+                if not os.path.isdir('{}/{}'.format(image_dir, goods_art_no_folder)):
+                    continue
+
+                self.run_one_folder_to_deal(goods_art_no_folder=goods_art_no_folder,
+                                            image_dir=image_dir,
+                                            image_order=image_order,
+                                            resize_image_view=resize_image_view,
+                                            callback_func=callback_func,
+                                            logo_path=logo_path,
+                                            )
+        else:
+            # self.show_progress_detail("没有需要处理的图片~")
+            callback_func('没有需要处理的图片~')
+        # self.set_state(state_value=2)
+        return {'code': 0, 'msg': 'ok'}
+
+    def run_one_folder_to_deal(self, goods_art_no_folder, image_dir, image_order, resize_image_view,
+                               callback_func=None, logo_path=""):
+
+        _img_all = self.list_dir("{}/{}/原始图".format(image_dir, goods_art_no_folder))
+        all_original_images = []  # 过滤非图片数据
+        index = 0
+        for _file in _img_all:
+
+            file_name, e = os.path.splitext(_file)
+            if e in _Type:
+                index += 1
+                new_file_name = "{}({}){}".format(goods_art_no_folder, index, e)
+                new_path = "{}/{}/原始图/{}".format(image_dir, goods_art_no_folder, new_file_name)
+                old_path = "{}/{}/原始图/{}".format(image_dir, goods_art_no_folder, _file)
+                if old_path != new_path:
+                    # 存在货号命名错误的,进行修正
+                    try:
+                        os.rename(old_path, new_path)
+                    except:
+                        pass
+                all_original_images.append(new_file_name)
+
+        if os.path.exists("{}/{}/原始图/镜像.txt".format(image_dir, goods_art_no_folder)):
+            file_mirror_mark = True
+        else:
+            file_mirror_mark = None
+        # if goods_art_no_folder == "AC51016112":
+        #     print(file_mirror_mark)
+        #     raise 111
+
+        all_moved_images = [os.path.splitext(x)[0] for x in
+                            self.list_dir("{}/{}/原始图_已抠图".format(image_dir, goods_art_no_folder))]
+        all_800images = [os.path.splitext(x)[0] for x in
+                         self.list_dir(
+                             "{}/{}/800x800".format(image_dir, goods_art_no_folder))]
+        all_800images = []
+
+        # 检查哪些图片没有做过抠图处理
+        i_n = 0
+        _name_list = ["视角{}".format(x) for x in range(1, len(all_original_images) + 1)]
+        # if goods_art_no_folder == "AC51028001":
+        #     _name_list = ["正视", "45度", "侧视", "后视", "底视", "其他1", "其他2", "其他3"]
+
+        image_index = 0  # 图片顺序
+        is_image_deal_mode = 0
+        max_box = None
+
+        for file in all_original_images:
+            i_n += 1
+            image_index += 1
+            original_image_path = "{}/{}/原始图/{}".format(image_dir, goods_art_no_folder, file)
+            file_name = os.path.splitext(file)[0]
+            """
+            当第三张就是为后跟
+            """
+            if file_name not in all_800images:  # 所有都重新生成
+                # if goods_art_no_folder != "AC51016112":
+                #     continue
+                goods_art_no_folder_path = "{}/{}".format(image_dir, goods_art_no_folder)
+                print("正在处理,货号:{}".format(goods_art_no_folder_path))
+                # self.show_progress_detail("正在处理,货号:{}".format(file_name))
+                callback_func("正在处理,货号:{}".format(file_name))
+                # 该文件在800images下没有时,则进行生成新的抠图
+                # 检查是否存在已抠图文件,如没有再去抠图
+                original_move_bg_image_path = "{}/原始图_已抠图/{}{}".format(goods_art_no_folder_path, file_name,
+                                                                       ".png")
+
+                image_deal_mode = 0  # 默认图片不做镜像处理
+                if not os.path.exists(original_move_bg_image_path):
+                    # 没有抠图文件,进行抠图生成
+                    # self.show_progress_detail("正在抠图 货号:{}".format(file_name))
+                    callback_func("正在抠图 货号:{}".format(file_name))
+                    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 not im:
+                        # self.show_progress_detail("货号图{} 抠图处理失败~".format(file_name))
+                        callback_func("货号图{} 抠图处理失败~".format(file_name))
+                        continue
+
+                if image_index == 1:
+                    is_image_deal_mode = 0
+                    if settings.Mode == "鞋类":
+                        goods_class = "鞋"
+                        # 如果图片已存在,则需要通过加载图片判断是否为左右脚
+                        if OnePicDeal().check_shoe_is_right(image_path=original_move_bg_image_path):
+                            image_deal_mode = 1  # 1表示要镜像,0表示不做镜像
+                            is_image_deal_mode = 1
+                    if settings.Mode == "皮具":
+                        # 图片对应的商品类型
+                        # goods_class = self.get_goods_class(goods_art_no_folder, original_image_path)
+                        max_box = (1000, 1200)
+                        # _ = {"AC51028001": "女包",
+                        #      "AC51028002": "男包",
+                        #      "AC51028003": "皮带",
+                        #      "AC51028004": "女包"}
+                        # if goods_class in _:
+                        #     goods_class = _[goods_class]
+                        # else:
+                        #     goods_class = "女包"
+                        #
+                        # _ = {"女包": (1000, 1200),
+                        #      "男包": (1000, 1200),
+                        #      "皮带": (1000, 1000), }
+                        # max_box = _[goods_class]
+
+                # 获取图片信息非必要程序,用于处理图片模式
+                date_time_original = self.get_date_time_original(original_image_path)  # 获取照片拍照时间
+
+                if date_time_original:
+                    # 基于照片的时间,与数据库匹配goods_art_no
+                    self.lock.acquire()
+                    _data = self.dataModeMatchPhoto.get_goods_art_no(date_time_original)
+                    self.lock.release()
+
+                    if _data:
+                        # 能匹配上数据库
+                        goods_art_no, _image_index, _image_deal_mode = _data
+                        if _image_index < 10:
+                            image_index = _image_index
+
+                        if _image_deal_mode == 1:
+                            image_deal_mode = 1
+                        # print(goods_art_no, image_index, image_deal_mode)
+
+                if file_mirror_mark:
+                    image_deal_mode = 1
+
+                """进行800image 生成"""
+                generate_pic = GeneratePic()
+                if settings.OUT_PIC_MODE == ".jpg":
+                    out_path = "{}/800x800/{}{}".format(goods_art_no_folder_path, file_name, ".jpg")
+                else:
+                    out_path = "{}/800x800/{}{}".format(goods_art_no_folder_path, file_name, ".png")
+
+                out_process_path_1 = "{}/阴影图处理/{}_{}_阴影{}".format(goods_art_no_folder_path, file_name,
+                                                                  _name_list[i_n - 1], ".png")
+                out_process_path_2 = "{}/阴影图处理/{}_{}_抠图{}".format(goods_art_no_folder_path, file_name,
+                                                                  _name_list[i_n - 1], ".png")
+
+                print("image_index", image_index)
+                image_index = 99
+                if generate_pic.run(image_path=original_image_path,
+                                    cut_image_path=original_move_bg_image_path,
+                                    out_path=out_path,
+                                    image_deal_mode=is_image_deal_mode,
+                                    image_index=image_index,
+                                    out_pic_size=settings.OUT_PIC_SIZE,
+                                    is_logo=True if i_n == 1 else False,
+                                    out_process_path_1=out_process_path_1,
+                                    out_process_path_2=out_process_path_2,
+                                    max_box=max_box,
+                                    logo_path=logo_path,
+                                    ):
+                    # self.show_progress_detail("货号图{} _{} 已完成800*800图片制作~".format(image_index, file_name))
+                    callback_func("货号图{} _{} 已完成800*800图片制作~".format(image_index, file_name))
+                else:
+                    # self.show_progress_detail("货号图{} _{}  图片生成处理失败~".format(image_index, file_name))
+                    callback_func("货号图{} _{}  图片生成处理失败~".format(image_index, file_name))
+
+                # 完成处理的图片进度
+                self.lock.acquire()
+                # self.set_progress()
+                self.lock.release()
+
+    def dealMoveImage(self, image_dir: str, callback_func=None) -> dict:
+        if not self.check_path(image_dir=image_dir + "/历史"):
+            return {'code': 1, 'msg': '文件夹创建失败', 'data': {}}
+
+        # 遍历目标文件夹,获取有拍摄信息的图片,并按拍摄时间排序
+        files = self.list_dir(image_dir)
+        original_photo_list = []  # 原始图片列表
+        for file in files:
+            # -----图片清洗
+            file_path = image_dir + "/" + file
+            if os.path.isdir(file_path):  # 忽略文件夹
+                continue
+            file_name, suffix = os.path.splitext(file)
+            if suffix not in _Type:  # 非图片进行移除
+                shutil.move(file_path, image_dir + "/历史/" + file)
+                continue
+
+            date_time_original = self.get_date_time_original(file_path)  # 获取照片拍照时间
+            if date_time_original:
+                # 基于照片的时间,与数据库匹配goods_art_no
+                _data = self.dataModeMatchPhoto.get_goods_art_no(date_time_original)
+                if _data:
+                    # 能匹配上数据库
+                    goods_art_no, image_index, image_deal_mode = _data
+                    print("832 与数据库匹配goods_art_no", file_name, date_time_original, goods_art_no)
+                    original_photo_list.append({"file_path": file_path,
+                                                "file": file,
+                                                "date_time_original": date_time_original,
+                                                "goods_art_no": goods_art_no,
+                                                "image_index": image_index,
+                                                "real_goods_art_no": "",
+                                                "real_goods_number": "",
+                                                })
+                else:
+                    # 匹配不上报错
+                    # self.show_progress_detail("图片:{} 无法对应货号,不做处理".format(file))
+                    if callback_func:
+                        callback_func("图片:{} 无对应货号".format(file))
+                    # shutil.move(photo_dict["file_path"], self.image_dir + "/历史/" + photo_dict["file"])
+                    continue
+            else:
+                shutil.move(file_path, image_dir + "/历史/" + file)
+
+        if not original_photo_list:
+            # self.show_progress_detail("没有任何匹配的图片~")
+            if callback_func:
+                callback_func('没有任何匹配的图片')
+            # self.set_state(state_value=2)
+            return {"code": 1, "msg": "没有任何匹配的图片", 'data': {}}
+
+        if settings.PROJECT == "红蜻蜓":
+            # 批量请求货号图信息
+            goods_art_no_list = [x["goods_art_no"] for x in original_photo_list]
+            goods_art_no_list = list(set(goods_art_no_list))
+            goods_art_no_list = [x for x in goods_art_no_list if "NUM" not in x]
+
+            if goods_art_no_list:
+                goods_art_no_dict = self.dataModeMatchPhoto.get_data_from_hqt_with_goods_art_no(
+                    goods_art_no_list=goods_art_no_list)
+
+                for i in original_photo_list:
+                    if i["goods_art_no"] in goods_art_no_dict:
+                        i["real_goods_art_no"] = i["goods_art_no"]
+                        i["real_goods_number"] = "NUM{}".format(goods_art_no_dict[i["goods_art_no"]]["编号"])
+
+            # 批量请求编号对应信息
+            goods_number_list = [x["goods_art_no"] for x in original_photo_list]
+            goods_number_list = list(set(goods_number_list))
+            goods_number_list = [x for x in goods_number_list if "NUM" in x]
+
+            if goods_number_list:
+                goods_number_dict = self.dataModeMatchPhoto.get_data_from_hqt(goods_number_list=goods_number_list)
+                for i in original_photo_list:
+                    if i["goods_art_no"] in goods_number_dict:
+                        i["real_goods_number"] = i["goods_art_no"]
+                        i["real_goods_art_no"] = goods_number_dict[i["goods_art_no"]]["商品货号"]
+
+        # 排序需要基于拍照的文件序号进行处理
+        original_photo_list.sort(
+            key=lambda x: "{}-{}-{}".format(x["goods_art_no"], x["image_index"], x["file"]))
+
+        # print(original_photo_list)
+        # 对有拍摄信息的图片进行数据库比对,如有比对上,则移动至货号文件夹,否则移入历史文件夹
+        total_num = len(original_photo_list)
+        # 当天日期作为文件夹
+        seconds = time.time()
+        output_path = "output/{f_name}".format(f_name=time.strftime("%Y-%m-%d", time.localtime(seconds)))
+
+        # 遍历每个匹配好的数据进行处理
+        n = 0
+        for photo_dict in original_photo_list:
+            n += 1
+            # 进度条
+            goods_art_no = photo_dict["goods_art_no"]
+            original_image_path = photo_dict["file_path"]
+            # 输出货号文件夹
+            if photo_dict["real_goods_art_no"]:
+                goods_art_no = "{}@{}".format(photo_dict["real_goods_art_no"], photo_dict["real_goods_number"])
+
+            goods_art_no_path = "{output_path}/{goods_art_no}".format(output_path=output_path,
+                                                                      goods_art_no=goods_art_no)
+
+            # 创建货号下的一系列文件夹
+            self.create_folder(goods_art_no_path)
+
+            # 重命名并进行移动
+            print("开始移动:{}  {} 命名为:{}".format(goods_art_no, original_image_path, goods_art_no_path))
+            self.move_images(goods_art_no, goods_art_no_path, original_image_path)  # 货号、货号文件路径、原始图路径
+            time.sleep(0.2)
+
+
+            # self.progress_sign.emit({"type": "移动原始图片", "progress_bar_value": int(n / total_num * 100)})
+            # self.show_progress_detail("货号{} 相关文件夹创建完成,已移动原图~".format(goods_art_no))
+            if callback_func:
+                callback_func("货号{} 相关文件夹创建完成,已移动原图~".format(goods_art_no))
+
+            print("已完成移动处理")
+
+        if n != 0:
+            # if settings.MattingPics:
+            #     # 检查所有未处理的货号文件夹,查看是否有完成图片加工处理
+            #     self.deal_images()
+
+            # 自动生成一个货号表
+            print("output_path", output_path)
+            GenerateGoodsArtNoTable.deal(output_path)
+
+        # 完成处理
+        # self.set_state(state_value=2)
+        return {'code': 0, 'msg': '处理完成', 'target_path': output_path, 'data': {}}
+
+    def check_path(self, image_dir: str):
+        if not os.path.exists(image_dir):
+            os.mkdir(image_dir)
+        return True
+
+    def get_date_time_original(self, file_path):
+        with open(file_path, 'rb') as file_data:
+            tags = exifread.process_file(file_data)
+            if "EXIF DateTimeOriginal" in tags:
+                return str(tags["EXIF DateTimeOriginal"])
+            else:
+                return False
+
+    def create_folder(self, path):
+        def check_folder(__path):
+            if not os.path.exists(__path):
+                os.makedirs(__path)
+                return False
+            return True
+
+        # 文件夹不存在,创建货号子集文件夹
+        if not check_folder(path):
+            for name in ["原始图", "原始图_已抠图", "800x800", "200images"]:
+                other_path = path + "/" + name
+                check_folder(other_path)
+
+    def move_images(self, goods_art_no, goods_art_no_path, old_image_path):
+        """
+        步骤:
+        1、移动到原始图
+        Args:
+            goods_art_no:
+            goods_art_no_path:
+            old_image_path:
+
+        Returns:
+
+        """
+        # 移动到原始图
+        file = os.path.split(old_image_path)[1]
+        # 扩展名
+        e = os.path.splitext(file)[1]
+        # 获取图片序列
+        self.goods_images_count_dict[goods_art_no] += 1
+        # A9999(1).jpg
+        new_file_name = "{}({})".format(goods_art_no, self.goods_images_count_dict[goods_art_no])
+        original_image_path = "{}/原始图/{}{}".format(goods_art_no_path, new_file_name, e)
+        # 移动图片
+        shutil.move(old_image_path, original_image_path)
+
+    def pixianRemoveImageBg(self, file_path: str, out_file_path: str, callbackek_func=None):
+        url = self.dataModeMatchPhoto.get_online_data.uploadImage(local_path=file_path)
+
+        remonveUrl = settings.AIGC_DOMAIN + '/api/ai_image/main/remove_background'
+        param = {'base_image': url}
+        post_headers = {"Authorization": settings.Authorization,
+                        "Content-Length": "",
+                        "Content-Type": "application/json",
+                        "Accept": "application/json"}
+
+        result = requests.post(remonveUrl, data=json.dumps(param), headers=post_headers).json()
+        print(result)
+        if "code" in result and result['code'] == 0:
+            response = requests.get(result['data']['image'][0])
+            with open(out_file_path, 'wb') as file:
+                file.write(response.content)
+                return result['data']['image'][0]
+        else:
+            callbackek_func("精细化抠图处理失败 {}".format(result['message']))
+            return ''
+
+    def list_dir(self, path):
+        listdir = os.listdir(path)
+        return natsorted(listdir, alg=ns.PATH)

+ 28 - 0
python/service/data.py

@@ -0,0 +1,28 @@
+import settings
+from module.data_mode.data_metaclass import DataBaseModel
+from PIL import Image
+from io import BytesIO
+
+
+class DataModeAutoDealPics(DataBaseModel):
+    def __init__(self):
+        super().__init__()
+
+    def check_is_right_foot_by_api(self, image):
+        image = image.convert('RGB')
+
+        re_x = int(640)
+        re_y = int(image.height * re_x / image.width)
+        image = image.resize((re_x, re_y))
+        e = "JPEG"
+
+        img = BytesIO()
+        image.save(img, format=e)  # format: PNG or JPEG
+        img.seek(0)  # rewind to the start
+
+        image_url = self.get_online_data.upload_image_by_io(image_io=img)
+        if settings.IS_TEST:
+            print("识别左右脚,{}".format(image_url))
+        # 识别左右脚
+        r_data = self.get_online_data.yolo_shoes_category(image_url=image_url)
+        return r_data

+ 134 - 0
python/service/deal_cutout.py

@@ -0,0 +1,134 @@
+import os.path
+import time
+from concurrent.futures import ThreadPoolExecutor, wait
+import threading
+from import_qt_mode import *
+
+from module.cutout_mode.deal_one_image import DealOneImage, DealOneImageBeforehand
+from module.log.log import MyLogger
+from module.online_request.module_online_data import GetOnlineDataHLM
+
+
+class DealCutout(QThread):
+    signal_data = Signal(dict)
+
+    def __init__(self, windows):
+        super().__init__()
+        self.windows = windows
+        self.lock = threading.Lock()
+        self.need_cutout_images = {}
+        self.state = 2  # 1进行中 2停止  状态 用于中途取消, 3已结束
+        # 当前阿里预处理后的图片数量(未消费)
+        self.is_upload_pic_num = 0
+        self.is_deal_num = 0
+        # 图片列表
+        self.upload_pic_dict = {}
+        self.logger = MyLogger().logger
+
+        self.remaining_times = 0  # 剩余总次数
+        self.get_online_data = GetOnlineDataHLM()
+
+        self.resultData = [] # 最终的结果
+
+    def refresh_times(self, is_online=True, remaining_times=None):
+        # 刷新剩余次数
+        if remaining_times is not None:
+            self.remaining_times = remaining_times
+
+        if is_online:
+            _ = self.get_online_data.get_cutout_image_times()
+            if _ is False:
+                self.remaining_times = 0
+            else:
+                if "balance" in _:
+                    self.remaining_times = _["balance"]
+
+        if self.remaining_times <= 0:
+            return False
+        return True
+
+    def send_sign(self, data):
+        # show_info    complete
+        self.signal_data.emit(data)
+
+    def check_before(self):
+        self.refresh_times()
+        if self.remaining_times <= 0:
+            self.send_sign({"type": "show_info",
+                            "message": "精细化抠图余量不足",
+                            })
+            return False
+
+        return True
+
+    def run(self):
+        """
+        need_cutout_images 结构:
+        [
+        "file_name": file_name, # 文件名
+        "file_e": file_e, # 后缀,.jpg
+        "file_path": image_path, # 完整路径
+        "file": file, # 图片文件名,带后缀
+        "need_cutout": True,# 必须,需要抠图
+        "out_path":图片输出路径
+        ]
+        """
+        if not self.check_before():
+            self.signal_data.emit({"_type": "complete",
+                                   "data": []})
+            self.state = 3
+            return
+
+        executor = ThreadPoolExecutor(max_workers=4)
+        executor_pic_upload = ThreadPoolExecutor(max_workers=2)
+
+        tasks_1 = []
+        tasks_2 = []
+        self.state = 1
+        self.resultData = []
+        self.is_upload_pic_num = 0
+        self.is_deal_num = 0
+        num = 0
+        for image_data in self.need_cutout_images:
+            if not image_data["need_cutout"]:
+                continue
+            num += 1
+            task_1 = executor.submit(DealOneImage(image_data=image_data, lock=self.lock, windows=self, num=num).run)
+            tasks_1.append(task_1)
+
+            task_2 = executor_pic_upload.submit(
+                DealOneImageBeforehand(image_data=image_data, lock=self.lock, windows=self, num=num).run)
+            tasks_2.append(task_2)
+        self.check_thread(tasks_1, tasks_2)
+
+    def __del__(self):
+        self.state = 2
+
+    def check_thread(self, *tasks_list):
+        time.sleep(2)
+        success_image_path = []
+        while 1:
+            f = True
+            for tasks in tasks_list:
+                done, not_done = wait(tasks)
+                if not_done:
+                    time.sleep(2)
+                    f = False
+                    continue
+
+                for task in done:
+                    try:
+                        image_path = task.result()
+                        if image_path:
+                            if isinstance(image_path, str):
+                                if os.path.exists(image_path):
+                                    success_image_path.append(image_path)
+                    except BaseException as e:
+                        self.logger.info("有线程出错:{}".format(e))
+            if f:
+                break
+
+        self.signal_data.emit({"_type": "complete",
+                               "data": success_image_path})
+        self.resultData = success_image_path
+        self.state = 3

+ 447 - 0
python/service/grenerate_main_image_test.py

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

+ 639 - 0
python/service/image_pic_deal.py

@@ -0,0 +1,639 @@
+"""
+"""
+
+import cv2
+import numpy as np
+from PIL import Image, ImageDraw, ImageFont
+from module.view_control.auto_deal_pics.data import DataModeAutoDealPics
+
+
+class OnePicDeal(object):
+    def __init__(self):
+        # 数据模型
+        self.data_mode_auto_deal_pics = DataModeAutoDealPics()
+
+    def check_shoe_is_right(self, im=None, image_path=None):
+        # 先进行API识别左右脚
+        if im is None:
+            im = Image.open(image_path)
+        try:
+            r_data = self.data_mode_auto_deal_pics.check_is_right_foot_by_api(image=im)
+        except BaseException as e:
+            r_data = None
+            print("20", e)
+
+        flag = self.check_shoe_is_right_by_pixel(im=im)
+        if r_data:
+            if "拖鞋" in r_data:
+                flag = flag is not True
+        if flag:
+            print("自动识别----->这是左脚")
+        else:
+            print("自动识别----->这是右脚")
+        return flag
+
+    def check_shoe_is_right_by_pixel(self, im=None, image_path=None):
+        if im is None:
+            im = Image.open(image_path)
+        # 注意,只支持透明图
+        # 打开图像文件
+        im = im.crop(im.getbbox())
+        # image.show()
+        # 获取图像第一行的像素数据
+        pixel_data = im.load()
+        pix_list = []
+        h = int(im.height / 20)
+        for i in range(im.width):
+            _r, _g, _b, _a = pixel_data[i, h]
+            if _a > 10:
+                pix_list.append(i)
+
+        left_f_num = 0
+        middle_w = int(im.width / 2)
+        for i in pix_list:
+            if i < middle_w:
+                left_f_num += 1
+            else:
+                left_f_num -= 1
+        if left_f_num > 0:
+            return True
+        else:
+            return False
+
+    def how_to_use(self):
+        # 单图缩放处理
+        a = {"command": "resize",
+             "plugins_mode": "relative",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+             "base_im": {"im": "im"},  # 参考基于其他图 PIL格式
+             "base": "width",  # base:pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+             "value": 649,  # 固定值,如果为百分比,则为0
+             "percentage": 0, }  # 百分比
+
+        # 单图圆角处理
+        a = {"command": "radius",  # radius
+             "plugins_mode": "relative",  # pixel 相对(短边),或绝对像素
+             "circular_pos": (0, 1, 0, 1),  # 从左上角顺时针,记录圆角数量
+             "value": 649,  # 固定值,如果为百分比,则为0
+             "percentage": 0, }  # 百分比
+
+        # 单图处理成圆形
+        a = {"command": "circular",  # circular
+             }
+
+        # 单图旋转处理
+        a = {"command": "rotate",
+             "plugins_mode": "",
+             "value": 649,  # 固定值 顺时针
+             }
+
+        # 图片粘贴处理
+        a = {
+            "command": "paste_img",
+            "img": {"im": "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),  # 上下左右边距
+        }
+
+        # 图片剪裁处理
+        a = {
+            "command": "crop_img",
+            "img": {"im": "im"},
+            "pos": {"plugins_mode": "relative",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (100, 100, 10, 10),
+                    },
+            "color_fill": (255, 255, 255)
+        }
+
+        # 图片添加文字
+        a = {
+            "command": "add_text",
+            "pos": {"plugins_mode": "relative",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (100, 100),
+                    "percentage": 0, },
+            "font": "",
+            "text": "",
+            "anchor": "",  # mm 为居中 ma 为左右居中,上面居顶
+            "align": "对齐方式",
+            "direction": "文本的方向",
+            "max_len_one_line": "单行长度",
+            "spacing": 10,
+            "fill": "文字颜色",
+        }
+
+    def add_text(self, img: Image, command):
+        draw_1 = ImageDraw.Draw(img)
+        # 定义字体,你需要有一个.ttf字体文件
+        font = command["font"]
+        text = command["text"]
+        spacing = 4 if not command["spacing"] else command["spacing"]
+        fill = command["fill"]
+        anchor = None if not command["anchor"] else command["anchor"]
+        align = "left" if not command["align"] else command["align"]  # left, center 或 right
+        _, _, text_width, text_height = draw_1.textbbox((0, 0), text, font=font)
+
+        xy = (0, 0)
+        if command["pos"]["plugins_mode"] == "pixel":
+            value = command["pos"]["value"]
+            xy = value
+
+        draw_1.multiline_text(xy, text,
+                              fill=fill,
+                              font=font,
+                              anchor=anchor,
+                              spacing=spacing,
+                              align=align,
+                              direction=None,
+                              features=None,
+                              language=None,
+                              stroke_width=0,
+                              stroke_fill=None,
+                              embedded_color=False)
+        return img
+
+    def resize(self, img: Image, command):
+        if command["plugins_mode"] == "pixel":
+            if command["base"] == "width":
+                img = self.to_resize(img, width=command["value"])
+            if command["base"] == "high":
+                img = self.to_resize(img, height=command["value"])
+        # 相对值
+        if command["plugins_mode"] == "relative":
+            base_im = command["base_im"]["im"]
+            if command["base"] == "width":
+                img = self.to_resize(img, width=img.width * command["percentage"] if not base_im else int(
+                    base_im.width * command["percentage"]))
+            if command["base"] == "height":
+                img = self.to_resize(img, width=img.height * command["percentage"] if not base_im else int(
+                    base_im.height * command["percentage"]))
+            # by_im确保能塞进参考图内
+            if command["base"] == "by_im":
+                percentage = 1 if not command["percentage"] else command["percentage"]
+                box_width, box_height = int(base_im.width * percentage), int(base_im.height * percentage)
+                width, height = img.width, img.height
+                if box_width / box_height < width / height:
+                    scale = box_width / width
+                else:
+                    scale = box_height / height
+                img = img.resize((int(width * scale), int(height * scale)))
+        # img.show()
+        return img
+
+    def paste_img(self, img: Image, command):
+        # 粘贴绝对像素
+        base = "nw" if not command["pos"]["base"] else command["pos"]["base"]
+        value = command["pos"]["value"]
+        percentage = (0, 0) if not command["pos"]["percentage"] else command["pos"]["percentage"]
+
+        if command["margins"]:
+            top, down, left, right = command["margins"]
+        else:
+            top, down, left, right = 0, 0, 0, 0
+
+        if percentage != (0, 0):  # percentage 不按占比模式
+            if base in ("nw", "wn", "wc", "cw", "nc", "cn", "center"):
+                value = (int(img.width), int(img.height))
+            if base in ("sw", "ws", "sc", "cs", "center"):
+                value = (int(img.width), -1 * int(img.height))
+            if base in ("ec", "ce"):
+                value = (int(img.width), int(img.height))
+
+        img_1 = command["img"]["im"]
+        if command["pos"]["plugins_mode"] == "pixel":
+            if base == "ec" or "ce":
+                p_x = int(img.width - img_1.width) + value[0]
+                p_y = value[1]
+            if base == "nw" or "wn":
+                deviation_x, deviation_y = 0, 0
+                p_x, p_y = value
+
+            if base == "cs" or "sc":
+                p_x, p_y = value
+            if base == "center":
+                deviation_x, deviation_y = int((img.width - img_1.width) / 2), int((img.height - img_1.height) / 2)
+                p_x = deviation_x + value[0] + left
+                p_y = deviation_y + 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 = img.height - (img_1.height + value[1] + down)
+            if base == "wc" or base == "cw":
+                p_x = value[0] + left
+                p_y = int((img.height - img_1.height) / 2) + value[1] + top
+
+            try:
+                img.paste(img_1, (p_x, p_y), img_1)
+            except:
+                img.paste(img_1, (p_x, p_y), img_1.convert("RGBA"))
+
+        return img
+
+    def crop_img(self, img: Image, command):
+        base = "nw" if not command["pos"]["base"] else command["pos"]["base"]
+        # print(base)
+        value = command["pos"]["value"]
+        percentage = command["pos"]["percentage"]
+
+        if command["margins"]:
+            top, down, left, right = command["margins"]
+        else:
+            top, down, left, right = 0, 0, 0, 0
+
+        out_img_size = (value[2], value[3])
+        # 默认填充色
+        color_fill = command["color_fill"]
+        if not color_fill:
+            color_fill = (0, 0, 0)
+
+        if command["pos"]["plugins_mode"] == "pixel":
+            if base == "nw" or "wn":
+                box = value
+            if base == "sw" or base == "ws":
+                # deviation_x, deviation_y = 0, int((img.height - img_1.height))
+                box = (value[0], img.height - (value[1] + value[3]), value[2], value[3])
+                print(box)
+
+            if base == "se" or base == "es":
+                box = (img.width - (value[0] + value[2]), img.height - (value[1] + value[3]), value[2], value[3])
+                print(box)
+            box = [box[0], box[1], box[0] + box[2], box[1] + box[3]]
+
+        print("box", box)
+        print("img.width", img.width)
+        print("img.height", img.height)
+
+        out_img = img.crop(box=box)
+        print(out_img.size)
+        # out_img.show()
+        if box[0] < 0:
+            out_img.paste(Image.new("RGB", (-1 * box[0], out_img.height), color_fill), (0, 0))
+        # print(img.width, box)
+
+        if box[2] > img.width:
+            # print(box[2] - img.width, img.height)
+            i = Image.new("RGB", (box[2] - img.width, out_img.height), color_fill)
+            out_img.paste(i, (img.width - box[0], 0))
+        if box[1] < 0:
+            out_img.paste(Image.new("RGB", (img.width, -1 * box[1]), color_fill), (0, 0))
+
+        if box[3] > img.height:
+            out_img.paste(Image.new("RGB", (out_img.width, box[3] - img.height), color_fill),
+                          (0, img.height - box[1]))
+
+        # bg_img = Image.new("RGB", out_img_size, color_fill)
+        # if box[0] < 0:
+        #     x = -1 * box[0]
+        # elif box[2] > img.width:
+        #     x = img.width - box[2]
+        # else:
+        #     x = 0
+        #
+        # if box[1] < 0:
+        #     y = -1 * box[1]
+        # elif box[2] > img.height:
+        #     y = img.height - box[1]
+        # else:
+        #     y = 0
+        # bg_img.paste(img, box=(x, y), )
+
+        # print(box)
+        # img = img.crop(box=box)
+        # out_img.show()
+        return out_img
+
+    def radius(self, img: Image, command):
+        # 单图圆角处理
+
+        radii = command["value"]
+        if radii > img.width / 2:
+            radii = int(img.width / 2)
+        if radii > img.height / 2:
+            radii = int(img.height / 2)
+
+        # 画圆(用于分离4个角)
+        circle = Image.new('L', (radii * 2, radii * 2), 0)  # 创建一个黑色背景的画布
+        draw = ImageDraw.Draw(circle)
+        draw.ellipse((0, 0, radii * 2, radii * 2), fill=255)  # 画白色圆形
+
+        # 原图
+        img = img.convert("RGBA")
+        w, h = img.size
+
+        # 画4个角(将整圆分离为4个部分)
+        alpha = Image.new('L', img.size, 255)
+        _pos = command["circular_pos"]
+        if not _pos:
+            _pos = (1, 1, 1, 1)
+        for index, i in enumerate(_pos):
+            if index == 0 and i == 1:
+                alpha.paste(circle.crop((0, 0, radii, radii)), (0, 0))  # 左上角
+            if index == 1 and i == 1:
+                alpha.paste(circle.crop((radii, 0, radii * 2, radii)), (w - radii, 0))  # 右上角
+            if index == 2 and i == 1:
+                alpha.paste(circle.crop((radii, radii, radii * 2, radii * 2)), (w - radii, h - radii))  # 右下角
+            if index == 3 and i == 1:
+                alpha.paste(circle.crop((0, radii, radii, radii * 2)), (0, h - radii))  # 左下角
+        # alpha.show()
+        img.putalpha(alpha)  # 白色区域透明可见,黑色区域不可见
+        return img
+
+    def to_resize(self, _im, width=None, height=None):
+        _im_x, _im_y = _im.size
+        if width and height:
+            if _im_x >= _im_y:
+                height = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(height)
+            re_x = int(_im_x * re_y / _im_y)
+        _im = _im.resize((re_x, re_y))
+        return _im
+
+    def add_pic(self, detailed_images):
+        if not detailed_images:
+            return
+        page_len = 0
+        for index, im in enumerate(detailed_images):
+            page_len += im.height
+        bg_im = Image.new("RGB", (im.width, page_len), (255, 255, 255))
+
+        n = 0
+        for index, im in enumerate(detailed_images):
+            bg_im.paste(im, (0, n))
+            n += im.height
+        # bg_im.show()
+        return bg_im
+
+    def get_goods_pos(self, im, cut_image):
+        # 保留多余内容
+        old_x, old_y = im.size
+        x1, y1, x2, y2 = cut_image.getbbox()
+        goods_w, goods_h = x2 - x1, y2 - y1
+        _w, _h = int(goods_w / 10), int(goods_h / 10)  # 上下左右扩展位置
+        new_x1, new_y1, new_x2, new_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h  # 防止超限
+        new_x1 = 0 if new_x1 < 0 else new_x1
+        new_y1 = 0 if new_y1 < 0 else new_y1
+        new_x2 = old_x if new_x2 > old_x else new_x2
+        new_y2 = old_y if new_y2 > old_y else new_y2
+        # 剪切掉多余的内容,保留阴影
+        im = im.crop((new_x1, new_y1, new_x2, new_y2))  # 切图
+        return im
+
+    def deal_one_pic(self, data=None, is_show=False):
+        """
+        通用图片处理器
+        1、图片位置处理
+        输出拼接后的图片,以及拼接后商品所属位置
+        """
+        for command in data:
+            if command["command"] == "paste_img":
+                img1 = command["img1"]["im"]
+                img2 = command["img2"]["im"]
+                if "resize" in command["img2"]:
+                    if "width" in command["img2"]["resize"]:
+                        img2 = self.to_resize(img2, width=command["img2"]["resize"]["width"])
+                        if is_show:
+                            img2.show()
+                img1.paste(img2, command["img2"]["pos"])
+            if command["command"] == "image_crop":
+                img1 = img1.crop(command["image_crop"])
+        img = img1
+        return img
+
+    def deal_one_pic_2(self, orign_im, data=None):
+        """
+        通用图片处理器
+        1、基于某原始图,进行加工,包括粘贴、单图处理等逻辑
+        """
+        # 单图缩放处理
+        a = {"command": "resize",
+             "plugins_mode": "relative",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+             "base_im": "im",  # 参考基于其他图 PIL格式
+             "base": "width",  # base:pixel,width,height,by_long_side,by_short_side基于长边、基于短边  (基于短边时,则缩放到指定尺寸)
+             "value": 649,  # 固定值,如果为百分比,则为0
+             "percentage": 0, }  # 百分比
+
+        # 单图圆角处理
+        a = {"command": "radius",  # radius
+             "plugins_mode": "relative",  # pixel 相对(短边),或绝对像素
+             "circular_pos": (0, 1, 0, 1),  # 从左上角顺时针,记录圆角数量
+             "value": 649,  # 固定值,如果为百分比,则为0
+             "percentage": 0, }  # 百分比
+
+        # 单图处理成圆形
+        a = {"command": "circular",  # circular
+             }
+
+        # 单图旋转处理
+        a = {"command": "rotate",
+             "plugins_mode": "",
+             "value": 649,  # 固定值 顺时针
+             }
+
+        # 图片粘贴处理
+        a = {
+            "command": "paste_img",
+            "img": {"im": "im"},
+            "pos": {"plugins_mode": "relative",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (100, 100),
+                    "percentage": 0, },
+            "margins": (0, 0, 0, 0),  # 上下左右边距
+        }
+
+        # 图片剪裁处理
+        a = {
+            "command": "crop_img",
+            "img": {"im": "im"},
+            "plugins_mode": "relative",  # pixel
+            "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+            "box": (0, 0, 0, 0),
+        }
+
+        # 图片添加文字
+        a = {
+            "command": "add_text",
+            "pos": {"plugins_mode": "relative",  # pixel
+                    "base": "center",  # nw,nc,ne,ec ... 各个方向参考点
+                    "value": (100, 100),
+                    "percentage": 0, },
+            "font": "",
+            "text": "",
+            "align": "对齐方式",
+            "direction": "文本的方向",
+            "fill": "文字颜色",
+        }
+
+        # 图片渐变处理
+        # https://blog.csdn.net/skying159/article/details/119532479
+
+        def get_value(data_dict, key):
+            return 1
+
+        r_dict = {"nw": 0,
+                  }
+
+        _r = 0
+        for command in data:
+            if command["command"] == "paste_img":
+                img1 = command["img1"]["im"]
+                img2 = command["img2"]["im"]
+                if "resize" in command["img2"]:
+                    if "width" in command["img2"]["resize"]:
+                        img2 = self.to_resize(img2, width=command["img2"]["resize"]["width"])
+                if "pos" in command["img2"]:
+                    base = command["img2"]["base"]
+                    value = command["img2"]["value"]
+                    if command["img2"]["plugins_mode"] == "relative":
+                        # 相对位置处理,居中比较特殊
+                        # 其他的先进行旋转与镜像,处理后,进行反向操作
+                        x, y = 0, 0
+                        if base == "center":
+                            x, y = int((img1.width - img2.width) / 2), int((img1.height - img2.height) / 2)
+
+                        if base == "nw" or base == "nw":
+                            pass
+
+                        w, h = img1.width * value[0], img1.height * value[0]
+
+                img1.paste(img2, command["img2"]["pos"])
+                if _r != 0:
+                    img1 = img1.rotate(_r * -1)
+                continue
+            if command["command"] == "image_crop":
+                img1 = img1.crop(command["image_crop"])
+                continue
+        img = img1
+        return img
+
+    def get_overlay_pic(self, pil_img_1, pil_img_2, color):
+        im_w, im_h = pil_img_1.size
+        cv_im = cv2.cvtColor(np.asarray(pil_img_1), cv2.COLOR_RGB2BGR)
+
+        # 创建一张纯色底图
+        image_white = Image.new("RGB", (im_w, im_h), color)
+
+        cv_image_white = cv2.cvtColor(np.asarray(image_white), cv2.COLOR_RGB2BGR)
+        new_im = self.to_color_2(cv_image_white, cv_im)
+        # new_im = cv2.addWeighted(new_im, 0.7, cv_im_2, 0.3, 0)
+        # new_im = cv2.add(new_im, cv_im_2)
+
+        new_im = Image.fromarray(cv2.cvtColor(new_im, cv2.COLOR_BGR2RGB))
+        new_im.paste(pil_img_2, (0, 0), pil_img_2)
+        return new_im
+
+    def to_color_2(self, target, blend):  # 正片叠底
+        return np.array(np.multiply(target / 256, blend / 256) * 256, dtype=np.uint8)
+
+    def to_color_1(self, img1, img2):  # PS颜色模式
+        img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV)
+        img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2HSV)
+        img2[:, :, 0] = img1[:, :, 0]
+        img2[:, :, 1] = img1[:, :, 1]
+        res = cv2.cvtColor(img2, cv2.COLOR_HSV2BGR)
+        return res
+
+
+if __name__ == '__main__':
+    from collections import defaultdict
+
+
+    def set_dict(one_dict):
+        _ = defaultdict(str)
+        for i, v in one_dict.items():
+            if isinstance(v, dict):
+                v = set_dict(v)
+            _[i] = v
+        return _
+
+
+    def deal_one_pic(img: Image, data=None):
+        """
+        通用图片处理器
+        1、基于某原始图,进行加工,包括粘贴、单图处理等逻辑
+        """
+        data = [set_dict(x) for x in data]
+
+        for command in data:
+            if command["command"] == "resize":
+                img = OnePicDeal().resize(img, command)
+                continue
+            if command["command"] == "paste_img":
+                img = OnePicDeal().paste_img(img, command)
+                continue
+            if command["command"] == "crop_img":
+                img = OnePicDeal().crop_img(img, command)
+                continue
+            if command["command"] == "radius":
+                img = OnePicDeal().radius(img, command)
+                continue
+            if command["command"] == "add_text":
+                img = OnePicDeal().add_text(img, command)
+                continue
+
+        return img
+
+
+    image_path = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\IPC\output\-秋季订货会夏季补充给葛明明\NUM24106316\阴影图处理\NUM24106316(3)_后跟_阴影.png"
+    to_paste_img = Image.open(image_path)
+    data = []
+    bg_color = (246, 246, 246)
+    # 单图缩放处理
+    view = "后跟"
+    data.append({"command": "resize",
+                 "plugins_mode": "pixel",  # pixel 相对(宽度、高度、其他参考图),或绝对像素
+                 "base_im": {"im": ""},  # 参考基于其他图 PIL格式
+                 "base": "width",  # base:pixel,width,height,by_im 基于长边、基于短边  (基于短边时,则缩放到指定尺寸)by_im确保能塞进参考图内
+                 "value": 1300 if view == "后跟" else 2200,  # 固定值,如果为百分比,则为0
+                 "percentage": 0, })  # 百分比
+
+    view_dict = {"俯视": (0, 0, 1100, 1200),
+                 "内里": (-1, -100, 1100, 1200),
+                 "后跟": (0, -100, 1100, 1200),
+                 "鞋底": (0, -100, 1100, 1200),
+                 }
+
+    data.append({
+        "command": "crop_img",
+        "img": "",
+        "pos": {"plugins_mode": "pixel",  # pixel
+                "base": "sw",  # nw,nc,ne,ec ... 各个方向参考点
+                "value": view_dict[view],
+                },
+        "color_fill": bg_color,
+    })
+
+    # 处理圆角
+    data.append({"command": "radius",  # radius
+                 "plugins_mode": "relative",  # pixel 相对(短边),或绝对像素
+                 "circular_pos": (1, 0, 0, 1),  # 从左上角顺时针,记录圆角数量
+                 "value": 100,  # 固定值,如果为百分比,则为0
+                 "percentage": 0, })  # 百分比
+
+    to_paste_img = deal_one_pic(to_paste_img, data)
+    # print(to_paste_img.size)
+
+    # 粘贴到白底图上
+    img = Image.new("RGB", (1200, 1316), (255, 255, 255))
+    data = []
+    data.append({
+        "command": "paste_img",
+        "img": {"im": to_paste_img},
+        "pos": {"plugins_mode": "pixel",  # pixel  relative
+                "base": "wc",  # nw,nc,ne,ec,... 各个方向参考点
+                "value": (100, 0),
+                "percentage": "",
+                },
+        "margins": (0, 0, 0, 0),  # 上下左右边距
+    })
+    img = deal_one_pic(img, data)
+    img.show()

+ 313 - 0
python/service/module_generate_goods_art_no_table.py

@@ -0,0 +1,313 @@
+from UI.generate_goods_art_no_table.generate_goods_art_no_table_ui import (
+    Ui_Form as generate_goods_art_no_table_Ui_Form,
+)
+from import_qt_mode import *
+import os
+import time
+import threading
+import xlsxwriter
+import shutil
+from module.base_mode.pic_deal import Picture
+from PIL import Image
+from module.view_control.MineQWidget import MineQWidget
+from module.base_mode.excel_base_func import *
+
+
+class GenerateGoodsArtNoTable(MineQWidget, generate_goods_art_no_table_Ui_Form):
+    # 货号表生成
+    progress_sign = Signal(dict)
+    info_sign = Signal(str)
+
+    def __init__(self):
+        super().__init__()
+        self.is_del = False
+        self.setupUi(self)
+        # 0禁用  1进行中  2已结束
+        self.state = 2
+        self.init()
+        self.show()
+
+    def init(self):
+        self.label_4.setText("请选择需要生成货号表的文件夹路径")
+        self.label_3.setText("请选择目录")
+        self.label_3.mousePressEvent = self.get_img_dir
+        self.textBrowser_2.hide()
+
+        self.progressBar.setValue(0)
+        self.progressBar.setMaximum(100)
+        self.progressBar.setValue(0)
+        self.progressBar.hide()
+        self.label_5.setText("")
+
+        self.progress_sign.connect(self.show_progress)
+        self.pushButton.clicked.connect(self.run)
+        self.textBrowser_2.hide()
+
+    def get_img_dir(self, *args):
+        folder = QFileDialog.getExistingDirectory(self, "选取文件夹", "./")
+        self.label_4.setText(folder)
+
+    def show_progress(self, data):
+        progress_bar_value = data["progress_bar_value"]
+        self.label_5.setText(data["type"])
+        self.progressBar.setValue(progress_bar_value)
+        pass
+
+    def check(self):
+        _path = self.image_dir + "/历史"
+        if not os.path.exists(_path):
+            os.mkdir(_path)
+        return True
+
+    def set_state(self, state_value: int):
+        # 0禁用  1进行中  2已结束
+        if state_value not in [0, 1, 2]:
+            return
+        self.state = state_value
+        if self.state == 0:
+            self.pushButton.setText("执行中")
+            self.pushButton.setEnabled(False)
+        if self.state == 1:
+            self.progressBar.show()
+            self.textBrowser_2.show()
+            self.pushButton.setText("执行中")
+            self.pushButton.setEnabled(False)
+            self.textBrowser_2.clear()
+        if self.state == 2:
+            self.pushButton.setText("执行完毕")
+            self.pushButton.setEnabled(True)
+            self.progressBar.hide()
+
+    def run(self):
+        self.set_state(state_value=1)
+        self.t = threading.Thread(target=self.run_by_thread, args=())
+        self.t.start()
+
+    def show_progress_detail(self, text):
+        self.textBrowser_2.append(text)
+
+    def run_by_thread(self, dir_path=None):
+        if not dir_path:
+            dir_path = self.label_4.text()
+        if not dir_path:
+            self.show_progress_detail("请选择文件夹")
+            self.set_state(state_value=2)
+            return
+
+        if not os.path.exists(dir_path):
+            self.show_progress_detail("该文件夹不存在")
+            self.set_state(state_value=2)
+            return
+        GenerateGoodsArtNoTable.deal(dir_path)
+        # 完成处理
+        self.set_state(state_value=2)
+
+    def save_as_excel(self, out_excel_data, out_excel_path=None):
+        self.show_progress_detail("开始尝试导出Excel文件~~~~")
+
+        def close_book(_book):
+            try:
+                _book.close()
+            except BaseException as e:
+                print(e)
+                self.show_progress_detail("请先关闭文件:{}".format(out_excel_path))
+                return False
+            return True
+
+        options = {
+            "default_format_properties": {
+                "align": "left",
+                "valign": "vcenter",
+                "text_wrap": True,
+            }
+        }
+        book = xlsxwriter.Workbook(filename=out_excel_path, options=options)
+        sheet = book.add_worksheet("sheet1")
+        # sheet.freeze_panes(1, 2)
+        sheet.set_column("B:B", 17)
+
+        sheet.write_row("A1", ["货号", "缩略图"])
+        for index, data in enumerate(out_excel_data):
+            # print(data)
+            goods_no, image_file = data
+            try:
+                im = Image.open(image_file)
+                im_x, im_y = im.size
+                image_width = 100
+                image_height = int(im_y * image_width / im_x)
+                sheet.set_row(index + 1, 95)
+                x_scale = round(image_width / im_x, 2)  # 固定宽度/要插入的原始图片宽
+                y_scale = round(image_height / im_y, 2)  # 固定高度/要插入的原始图片高
+
+                sheet.insert_image(
+                    index + 1,
+                    1,
+                    image_file,
+                    {
+                        "x_scale": x_scale,
+                        "y_scale": y_scale,
+                        "x_offset": 5,
+                        "y_offset": 5,
+                    },
+                )
+            except:
+                self.show_progress_detail("图片异常,{}".format(image_file))
+                pass
+            sheet.write_row("A{}".format(index + 2), [goods_no])
+
+        close_book(book)
+        self.show_progress_detail("已保存文件至:{}".format(out_excel_path))
+        # while 1:
+        #     if self.is_del:
+        #         break
+        #
+        #     if not close_book(book):
+        #         time.sleep(1)
+        #         self.show_progress_detail("请先关闭文件:{}".format(out_excel_path))
+        #     else:
+        #         self.show_progress_detail("已保存文件至:{}".format(out_excel_path))
+        #         break
+
+    @classmethod
+    def deal(cls, dir_path):
+        def close_book(_book):
+            try:
+                _book.close()
+            except BaseException as e:
+                print(e)
+                return False
+            return True
+
+        # print("dir_path", dir_path)
+        out_excel_data = []
+        for goods_art_no_folder in os.listdir(dir_path):  # 遍历货号文件夹集合
+            if not os.path.isdir(
+                "{}/{}".format(dir_path, goods_art_no_folder)
+            ):  # 非文件夹进行过滤
+                continue
+            if "软件" in goods_art_no_folder:
+                continue
+
+            # print("goods_art_no_folder", goods_art_no_folder)
+            # 如果存在800的主图,则优先进行使用
+            big_image_folder_path = "{}/{}/800x800".format(
+                dir_path, goods_art_no_folder
+            )
+            if not os.path.exists(big_image_folder_path):
+                os.mkdir(big_image_folder_path)
+            all_big_images = os.listdir(big_image_folder_path)
+            goods_pic_total = len(all_big_images)
+            _Type = [".png", ".PNG", ".jpg", ".JPG", ".gif", ".GIF", ".jpge", ".JPGE"]
+            thumb_image_file_path = None
+            # print("all_big_images",all_big_images)
+            if all_big_images:
+                for _file in all_big_images:
+                    # print(_file)
+                    file_name, e = os.path.splitext(_file)
+                    # print(file_name, e)
+                    if e in _Type:
+                        thumb_image_file_path = "{}/{}/800x800/{}".format(
+                            dir_path, goods_art_no_folder, _file
+                        )
+                        break
+
+            # 如果不存在主图则进行使用原始图
+            if thumb_image_file_path is None:
+                _path = "{}/{}/原始图".format(dir_path, goods_art_no_folder)
+                if not os.path.exists(_path):
+                    continue
+
+                all_original_images = os.listdir(_path)  # 遍历货号原始图文件夹
+                goods_pic_total = len(all_original_images)
+                if not all_original_images:
+                    continue
+                image_file = all_original_images[0]  # 取第一个货号图
+                image_file_path = "{}/{}/原始图/{}".format(
+                    dir_path, goods_art_no_folder, image_file
+                )
+
+                if not os.path.exists(
+                    "{}/{}/200images".format(dir_path, goods_art_no_folder)
+                ):
+                    os.mkdir("{}/{}/200images".format(dir_path, goods_art_no_folder))
+
+                thumb_image_file_path = "{}/{}/200images/{}".format(
+                    dir_path, goods_art_no_folder, image_file
+                )
+                if not os.path.exists(thumb_image_file_path):
+                    # 开始触发进行压缩生成文件
+                    shutil.copy(image_file_path, thumb_image_file_path)  # 复制文件
+                    pic = Picture(thumb_image_file_path)
+                    pic.resize(width=600)
+                    pic.save_img(thumb_image_file_path)
+            # print("thumb_image_file_path", thumb_image_file_path)
+
+            goods_number = ""
+            if "@" in goods_art_no_folder:
+                _ = goods_art_no_folder.split("@")
+                goods_art_no_folder = _[0]
+                goods_number = _[1].replace("NUM", "")
+
+            out_excel_data.append(
+                [
+                    goods_number,
+                    goods_art_no_folder,
+                    thumb_image_file_path,
+                    goods_pic_total,
+                ]
+            )
+
+        if out_excel_data:
+            out_excel_path = "{}/货号表-{}.xlsx".format(dir_path, time.time())
+            options = {
+                "default_format_properties": {
+                    "align": "left",
+                    "valign": "vcenter",
+                    "text_wrap": True,
+                }
+            }
+            book = xlsxwriter.Workbook(filename=out_excel_path, options=options)
+            sheet = book.add_worksheet("sheet1")
+            # sheet.freeze_panes(1, 2)
+            sheet.set_column("B:B", 17)
+            sheet.set_column("D:D", 20)
+
+            sheet.write_row("A1", ["编号", "原货号", "新货号", "缩略图", "原始图张数"])
+            for index, data in enumerate(out_excel_data):
+                # print(data)
+                goods_number, goods_art_no, image_file, goods_pic_total = data
+                try:
+                    im = Image.open(image_file)
+                    im_x, im_y = im.size
+                    image_width = 100
+                    image_height = int(im_y * image_width / im_x)
+                    sheet.set_row(index + 1, 95)
+                    x_scale = round(
+                        image_width / im_x, 2
+                    )  # 固定宽度/要插入的原始图片宽
+                    y_scale = round(
+                        image_height / im_y, 2
+                    )  # 固定高度/要插入的原始图片高
+                    sheet.insert_image(
+                        index + 1,
+                        3,
+                        image_file,
+                        {
+                            "x_scale": x_scale,
+                            "y_scale": y_scale,
+                            "x_offset": 5,
+                            "y_offset": 5,
+                            "object_position":1,
+                        },
+                    )
+
+
+                except:
+                    pass
+                sheet.write_row("A{}".format(index + 2), [goods_number])
+                sheet.write_row("B{}".format(index + 2), [goods_art_no])
+                sheet.write_row("E{}".format(index + 2), [goods_pic_total])
+            close_book(book)
+
+    def __del__(self):
+        self.is_del = True

+ 268 - 0
python/service/remove_bg_ali.py

@@ -0,0 +1,268 @@
+import copy
+import json
+import os
+from PIL import Image
+from alibabacloud_imageseg20191230.client import Client as imageseg20191230Client
+from alibabacloud_imageseg20191230.models import SegmentCommodityAdvanceRequest
+from alibabacloud_imageseg20191230 import models as imageseg_20191230_models
+from alibabacloud_tea_util.models import RuntimeOptions
+from alibabacloud_tea_openapi import models as open_api_models
+from alibabacloud_tea_openapi.models import Config
+from alibabacloud_tea_util import models as util_models
+import requests
+from io import BytesIO
+import cv2
+import numpy as np
+from func_timeout import func_set_timeout
+from func_timeout import FunctionTimedOut
+
+# 自己的
+AccessKeyId = 'LTAI5t7GVSbV5GuqUo935v4f'
+AccessKeySecret = 'IAe8CMw5PTp61zrV2rEYeLtKRVdL3A'
+
+# 惠利玛公司的KEY
+# AccessKeyId = 'LTAI5tCk4p881X8hymj2FYFk'
+# AccessKeySecret = 'rQMgHwciTN4Gusbpt8CM8tflgsxh1V'
+
+
+# https://help.aliyun.com/zh/viapi/developer-reference/python?spm=a2c4g.11186623.0.i0#task-2252575
+# pip install alibabacloud_goodstech20191230
+# pip install alibabacloud_tea_openapi
+# pip install alibabacloud_tea_util
+
+class Segment(object):
+    def __init__(self):
+        self.client = self.create_client()
+
+    def get_no_bg_common(self, file_path):
+        # 初始化RuntimeObject
+        runtime_option = RuntimeOptions()
+        try:
+            # 场景一:文件在本地
+            img = open(file_path, 'rb')
+            # 使用完成之后记得调用img.close()关闭流
+            # 场景二,使用任意可访问的url
+            # url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/ocr/RecognizeBankCard/yhk1.jpg'
+            # img = io.BytesIO(urlopen(url).read())
+            # 4、初始化Request,这里只是以RecognizeBankCard为例,其他能力请使用相应能力对应的类
+            request = SegmentCommodityAdvanceRequest()
+            request.image_urlobject = img
+
+            # 5、调用api,注意,recognize_bank_card_advance需要更换为相应能力对应的方法名。方法名是根据能力名称按照一定规范形成的,如能力名称为SegmentCommonImage,对应方法名应该为segment_common_image_advance。
+            response = self.client.segment_common_image_advance(request, runtime_option)
+            # 获取整体结果
+            # print(response.body)
+            img.close()
+            return response.body
+            # 获取单个字段,这里只是一个例子,具体能力下的字段需要看具体能力的文档
+            # print(response.body.data.card_number)
+            # tips: 可通过response.body.__dict__查看属性名称
+        except Exception as error:
+            # 获取整体报错信息
+            print("error", error)
+            return None
+            # 获取单个字段
+            # print(error.code)
+            # tips: 可通过error.__dict__查看属性名称
+
+    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)
+        img = BytesIO()
+        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()
+        # 5、调用api,注意,recognize_bank_card_advance需要更换为相应能力对应的方法名。方法名是根据能力名称按照一定规范形成的,如能力名称为SegmentCommonImage,对应方法名应该为segment_common_image_advance。
+        runtime = util_models.RuntimeOptions()
+        response = client.segment_commodity_advance(request, runtime)
+        # img.close()
+        # print("1111111111111", response.body)
+        return response.body
+
+    def create_client(self):
+        """
+        使用AK&SK初始化账号Client
+        @param access_key_id:
+        @param access_key_secret:
+        @return: Client
+        @throws Exception
+        """
+        config = open_api_models.Config(
+            # 必填,您的 AccessKey ID,
+            access_key_id=AccessKeyId,
+            # 必填,您的 AccessKey Secret,
+            access_key_secret=AccessKeySecret
+        )
+        # 访问的域名
+        config.endpoint = f'imageseg.cn-shanghai.aliyuncs.com'
+        return imageseg20191230Client(config)
+
+
+class Picture:
+    def __init__(self, in_path, im=None):
+        if im:
+            self.im = im
+        else:
+            self.im = Image.open(in_path)
+        self.x, self.y = self.im.size
+        # print(self.x, self.y)
+
+    def save_img(self, outpath, quality=90):
+        # self.im = self.im.convert("RGB")
+        self.im.save(outpath, quality=quality)
+
+    def resize(self, width):
+        re_x = int(width)
+        re_y = int(self.y * re_x / self.x)
+        self.im = self.im.resize((re_x, re_y), Image.BICUBIC)
+        self.x, self.y = self.im.size
+
+    def resize_by_heigh(self, heigh):
+        re_y = int(heigh)
+        re_x = int(self.x * re_y / self.y)
+        self.im = self.im.resize((re_x, re_y), Image.BICUBIC)
+        self.x, self.y = self.im.size
+
+
+class RemoveBgALi(object):
+    def __init__(self):
+        self.segment = Segment()
+
+    @func_set_timeout(30)
+    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)
+        else:
+            original_pic = Picture(file_path)
+
+        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)
+
+        # new_pic.im.show()
+        body = self.segment.get_no_bg_goods(file_path=None, _im=new_pic.im)
+        body = eval(str(body))
+        try:
+            image_url = body["Data"]["ImageURL"]
+        except BaseException as e:
+            print("阿里抠图错误:", e)
+            # 处理失败,需要删除过程图片
+            return None
+        # 字节流转PIL对象
+        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])
+
+            # ---------制作选区缩小的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)
+            # _img_im.show("11111111111111111111111")
+        if out_file_path:
+            _img_im.save(out_file_path)
+        return _img_im
+
+    def get_image_cut1(self, file_path, out_file_path=None):
+        original_pic = Picture(file_path)
+        new_pic = copy.copy(original_pic)
+        if new_pic.x > 2000:
+            new_pic.resize(2000)
+        # new_pic.im.show()
+        body = self.segment.get_no_bg_goods(file_path=out_file_path, _im=new_pic.im)
+        body = eval(str(body))
+        try:
+            image_url = body["Data"]["ImageURL"]
+        except BaseException as e:
+            print("阿里抠图错误:", e)
+            # 处理失败,需要删除过程图片
+            return None
+        # 字节流转PIL对象
+        response = requests.get(image_url)
+        pic = response.content
+        _img_im = Image.open(BytesIO(pic))  # 阿里返回的抠图结果 已转PIL对象
+
+        if original_pic.x > 2000:
+            # 原图更大,则需要执行CV处理
+            # _img_im.show()
+            # 对mask进行放大,然后进行抠图处理
+            print("对mask进行放大,然后进行抠图处理")
+            transparent_im = Image.new('RGBA', original_pic.im.size, (0, 0, 0, 0))
+            # original_pic.im.show()
+            # mask.show()
+            _img_im = _img_im.resize((original_pic.x, original_pic.y))
+            # _img_im.show()
+            transparent_im.paste(original_pic.im, (0, 0), mask=_img_im)
+            # transparent_im.show()
+            # transparent_im.show()
+            _img_im = transparent_im
+            # 上述抠图结果进行拼接
+            # _img_im.paste(transparent_im, (0, 0), transparent_im)
+            pass
+
+        _img_im.save(out_file_path)
+        return _img_im
+
+    def download_picture(self, url, out_path):
+        response = requests.get(url)
+        pic = response.content
+        with open(out_path, 'wb') as f:
+            f.write(pic)
+
+
+if __name__ == '__main__':
+    r = RemoveBgALi()
+    path = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\IPC\test\171112057820408.png"
+    out_path = "{}._no_bg-out.png".format(path)
+    r.get_image_cut(path, out_file_path=out_path)