Bladeren bron

抠图 以及文件夹处理

rambo 8 maanden geleden
bovenliggende
commit
1fca57d6d1
35 gewijzigde bestanden met toevoegingen van 8338 en 163 verwijderingen
  1. 2 1
      python/.gitignore
  2. 57 30
      python/api.py
  3. 6 0
      python/models.py
  4. 726 0
      python/service/AutoDealPics.py
  5. 8 8
      python/service/base.py
  6. 7 7
      python/service/base_deal.py
  7. 400 2
      python/service/data.py
  8. 295 0
      python/service/data_metaclass.py
  9. 6 6
      python/service/deal_cutout.py
  10. 48 55
      python/service/deal_image.py
  11. 5 6
      python/service/deal_one_image.py
  12. 176 0
      python/service/detail_func.py
  13. 84 0
      python/service/detail_temp.py
  14. 11 11
      python/service/excel_base_func.py
  15. 313 0
      python/service/generate_goods_art_no_table/module_generate_goods_art_no_table.py
  16. 422 0
      python/service/generate_main_image/grenerate_main_image_test.py
  17. 216 0
      python/service/generate_main_image/image_deal_base_func.py
  18. 481 0
      python/service/generate_main_image/module_generate_main_image.py
  19. 1 1
      python/service/grenerate_main_image_test.py
  20. 2 1
      python/service/image_pic_deal.py
  21. 123 0
      python/service/load_plugins.py
  22. 512 0
      python/service/manual_image_matching/goods_art_image_list.py
  23. 332 0
      python/service/manual_image_matching/image_label.py
  24. 153 0
      python/service/manual_image_matching/m_image_matching_cotrol.py
  25. 42 0
      python/service/manual_image_matching/m_image_matching_data.py
  26. 1046 0
      python/service/match_and_cutout_mode_control/base_deal_image_v2.py
  27. 1219 0
      python/service/match_and_cutout_mode_control/module_matching_photos_v2.py
  28. 8 0
      python/service/matching_photos/data.py
  29. 847 0
      python/service/matching_photos/module_matching_photos.py
  30. 2 2
      python/service/module_generate_goods_art_no_table.py
  31. 423 0
      python/service/online_request/module_online_data.py
  32. 230 0
      python/service/remove_bg_pixian.py
  33. 22 23
      python/service/run_main.py
  34. 11 10
      python/service/upload_pic.py
  35. 102 0
      python/utils/utils_func.py

+ 2 - 1
python/.gitignore

@@ -5,4 +5,5 @@ venv/
 resources/
 custom_plugins/
 *.db
-*.pdf
+*.pdf
+output/*

+ 57 - 30
python/api.py

@@ -7,14 +7,15 @@ from logger import logger
 from serial.tools import list_ports
 from model import PhotoRecord
 from utils.hlm_http_request import forward_request
+from utils.utils_func import check_path
 from sockets.socket_client import socket_manager
 from mcu.DeviceControl import DeviceControl
-import time
+import time,shutil
 from sqlalchemy import and_, asc, desc
 
 from service.deal_image import DealImage
 from databases import DeviceConfig, SqlQuery, CRUD, select
-
+from service.run_main import RunMain
 
 @app.get("/")
 async def index():
@@ -111,42 +112,68 @@ async def forwardRequest(request: HlmForwardRequest):
 
 
 @app.post("/handle_detail")
-async def handle_detail(request: Request):
-
+async def handle_detail(request: Request, params: HandlerDetail):
+    goods_art_no = params.goods_art_no
+    session = SqlQuery()
+    pr = CRUD(PhotoRecord)
+    images = pr.read_all(session,conditions={"goods_art_no": goods_art_no})
+    if not images:
+        raise UnicornException("没有可用货号数据")
     image_dir = "{}/data".format(os.getcwd())
+    for itemImg in images:
+        if not os.path.exists(image_dir+"/"+os.path.basename(itemImg.image_path)):
+            shutil.copy(itemImg.image_path, image_dir)
+    check_path(image_dir)
     dealImage = DealImage(image_dir)
     dealImage.header = request.headers
     print(">>>>>>>>>>>>>>>>>请求参数")
     print(dealImage.header)
-    result = dealImage.dealMoveImage(image_dir=image_dir, callback_func=None)
-    return result
-
+    resFlag,path = dealImage.dealMoveImage(
+        image_dir=image_dir, callback_func=None, goods_art_no=goods_art_no
+    )
+    if not resFlag:
+        return {
+            "code": 0,
+            "msg": path,
+            "data": 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/软件-生成详情错误'}
+    # #{'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/软件-生成详情错误'
-    # }
+    config_data = {
+        "image_dir": path,
+        "image_order": "俯视,侧视,后跟,鞋底,内里",
+        "is_check_number": True,
+        "resize_image_view": "后跟",
+        "cutout_mode": "1",
+        "logo_path": "",
+        "special_goods_art_no_folder_line": "",
+        "is_use_excel": False,
+        "excel_path": "",
+        "is_check_color_is_all": True,
+        "assigned_page_dict": {},
+        "temp_class": {
+            "huilima-2": "plugins.detail_template.huilima.detail_huilima2.DetailPicGet",
+            "huilima-3": "plugins.detail_template.huilima.detail_huilima3.DetailPicGet",
+            "huilima-4": "plugins.detail_template.huilima.detail_huilima4.DetailPicGet",
+            "huilima-1": "plugins.detail_template.huilima.detail_huilima1.DetailPicGet",
+        },
+        "temp_name": "huilima-2",
+        "temp_name_list": ["huilima-2", "huilima-3", "huilima-4", "huilima-1"],
+        "target_error_folder": f"{path}/软件-生成详情错误",
+    }
+    run_main = RunMain()
+    run_main.check_before_cutout(config_data)
 
 
 @app.post("/get_device_configs", description="获取可执行程序命令列表")

+ 6 - 0
python/models.py

@@ -55,3 +55,9 @@ class SysConfigParams(BaseModel):
     """系统配置"""
     key: str = Field(default=None, description="类型")
     value: str = Field(default=None, description="json数据")
+
+
+class HandlerDetail(BaseModel):
+    """获取可执行程序命令列表"""
+
+    goods_art_no: str = Field(default=None, description="货号")

+ 726 - 0
python/service/AutoDealPics.py

@@ -0,0 +1,726 @@
+import os
+
+
+#
+from .data import DataModeGenerateDetail
+from .base_deal import BaseDealImage
+from .run_main import RunMain
+from utils.utils_func import check_path
+from .upload_pic import UploadPic
+
+# 手动移动图片等操作
+from .manual_image_matching.m_image_matching_cotrol import (
+    MainMatchingWindow,
+)
+
+# 每个详情的模板
+from .detail_temp import TempItem
+
+from .load_plugins import LoadAllPlugins
+
+from functools import partial
+from threading import Lock
+
+# from module.view_control.MineQWidget import MineQWidget, DialogShow
+# from module.base_mode.base import *
+import threading
+import settings
+
+
+def queren_get_config():
+    config_data = {}
+    # config_data["cutout_is_enable"] = self.ui.cutout_is_enable.isChecked()
+    # config_data["cutout_is_pass"] = self.ui.cutout_is_pass.isChecked()
+    # config_data["detail_is_enable"] = self.ui.detail_is_enable.isChecked()
+    # config_data["detail_is_pass"] = self.ui.detail_is_pass.isChecked()
+    # config_data["upload_is_enable"] = self.ui.upload_is_enable.isChecked()
+    # config_data["upload_is_pass"] = self.ui.upload_is_pass.isChecked()
+
+    # # 保存配置
+    # settings.GOODS_DETAIL_CUTOUT_IS_ENABLE = self.ui.cutout_is_enable.isChecked()
+    # settings.GOODS_DETAIL_CUTOUT_IS_PASS = self.ui.cutout_is_pass.isChecked()
+    # settings.GOODS_DETAIL_DETAIL_IS_ENABLE = self.ui.detail_is_enable.isChecked()
+    # settings.GOODS_DETAIL_DETAIL_IS_PASS = self.ui.detail_is_pass.isChecked()
+    # settings.GOODS_DETAIL_UPLOAD_IS_ENABLE = self.ui.upload_is_enable.isChecked()
+    # settings.GOODS_DETAIL_UPLOAD_IS_PASS = self.ui.upload_is_pass.isChecked()
+
+    data_dict = {
+        # "goods_detail_cutout_is_enable": (
+        #     "是" if settings.GOODS_DETAIL_CUTOUT_IS_ENABLE else "否"
+        # ),
+        # "goods_detail_cutout_is_pass": (
+        #     "是" if settings.GOODS_DETAIL_CUTOUT_IS_PASS else "否"
+        # ),
+        # "goods_detail_detail_is_enable": (
+        #     "是" if settings.GOODS_DETAIL_DETAIL_IS_ENABLE else "否"
+        # ),
+        # "goods_detail_detail_is_pass": (
+        #     "是" if settings.GOODS_DETAIL_DETAIL_IS_PASS else "否"
+        # ),
+        # "goods_detail_upload_is_enable": (
+        #     "是" if settings.GOODS_DETAIL_UPLOAD_IS_ENABLE else "否"
+        # ),
+        # "goods_detail_upload_is_pass": (
+        #     "是" if settings.GOODS_DETAIL_UPLOAD_IS_PASS else "否"
+        # ),
+        "goods_detail_cutout_is_enable": "是",
+        "goods_detail_cutout_is_pass": "是",
+        "goods_detail_detail_is_enable": "是",
+        "goods_detail_detail_is_pass": "是",
+        "goods_detail_upload_is_enable": "是",
+        "goods_detail_upload_is_pass": "是",
+    }
+    settings.set_config(data_dict=data_dict, section="goods_detail")
+    return config_data
+
+
+# 抠图与详情生成
+class AutoDealPics:
+    # progress_sign = Signal(dict)
+    # text_show = Signal(str)
+    # progress_show_sign = Signal(bool)
+    # progress_end_sign = Signal()
+    # send_dialog_sign = Signal()
+    # change_state_sign = Signal(int)
+    # run_end_sign = Signal(dict)
+
+    def __init__(self, windows=None, top_windows=None):
+        super().__init__()
+        self.windows = windows
+        self.top_windows = top_windows
+        # self.ui = generate_goods_no_detail_pic_Ui_Form()
+        # self.ui.setupUi(self)
+        # self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
+
+        self.run_main = RunMain(windows=self)
+        # self.run_main.run_end_sign.connect(self.deal_run_end_sign)
+        # self.run_main.show_dialog_sign.connect(self.show_dialog)
+        # self.run_main.show_progress_detail_sign.connect(self.show_progress_detail)
+
+        # 程序执行信号
+        # self.run_end_sign.connect(self.deal_run_end_sign)
+
+        # # 改变状态
+        # self.change_state_sign.connect(self.set_state)
+
+        # self.setWindowTitle("详情生成(new)")
+        # 线程锁
+        self.lock = Lock()
+        self.image_dir = ""
+        # 0禁用  1进行中  2已结束 99等待结束
+        self.state = 2
+        self.data_mode_generate_detail = DataModeGenerateDetail()
+        self.init_cutout()
+        self.init_detail()
+        # self.init_schedule()
+        self.set_state(0)
+        # self.show()
+        # 延时检查是否存在匹配图片
+        # QTimer.singleShot(1000, self.check_and_matching_pics)
+
+    # 添加进度页面
+    # def init_schedule(self, *args):
+    #     self.ui.back.clicked.connect(self.back)
+    #     self.ui.stop.clicked.connect(self.to_stop)
+    #     self.ui.progressBar.hide()
+
+    def back(self, *args):
+        self.set_state(0)
+
+    def to_stop(self, *args):
+        self.state = 99
+
+    # 自动检查匹配图片
+    def check_and_matching_pics(self, *args, **kwargs):
+        func = partial(self.run_matching)
+        self.do_thread_run(func=func, call_back=self.run_matching_call, time_out=30)
+
+    # 运行自动匹配
+    def run_matching(self, *args):
+        image_dir = "{}/data".format(os.getcwd())
+        check_path(image_dir)
+        baseDealImage = BaseDealImage(image_dir=image_dir)
+        result = baseDealImage.dealMoveImage(image_dir=image_dir, callback_func=None)
+        # return {'code': 0, 'msg': '处理完成', 'target_path': output_path, 'data': {}}
+        return result
+
+    # 自动匹配的回调函数
+    def run_matching_call(self, return_data):
+
+        print(return_data)
+        code = return_data["code"]
+        if code == 0:
+            output_path = return_data["target_path"]
+            self.image_dir = "{}/{}".format(os.getcwd(), output_path)
+            self.show_img_dir()
+
+    def show_progress_detail(self, text):
+        self.ui.textBrowser_4.append(text)
+
+    # todo ===========主方法入口===========
+    def deal_run_end_sign(self, config_data: dict):
+        if config_data["sign_text"] == "开始抠图":
+            # 先做整体校验
+            func = partial(self.run_main.check_before_cutout, config_data=config_data)
+            self.do_thread_run(
+                func=func,
+                call_back=self.run_main.check_for_cutout_image_first_call_back,
+                time_out=30,
+                is_show_mask=False,
+            )
+
+        print(config_data)
+        if config_data["sign_text"] == "已结束抠图处理":
+            if config_data["detail_is_enable"]:
+                # 先做整体校验
+                # temp_name=self.last_temp, temp_name_list=self.temp_list
+                func = partial(
+                    self.run_main.check_before_detail, config_data=config_data
+                )
+                self.do_thread_run(
+                    func=func,
+                    call_back=self.run_main.check_for_detail_first_call_back,
+                    time_out=30,
+                    is_show_mask=False,
+                )
+            else:
+                self.set_state(state_value=2)
+
+        if config_data["sign_text"] == "已结束详情处理":
+            if config_data["upload_is_enable"]:
+                to_deal_dir = "{}/软件-详情图生成".format(config_data["image_dir"])
+                print("to_deal_dir", to_deal_dir)
+                if os.path.exists(to_deal_dir):
+                    self.upload_pic = UploadPic(
+                        windows=self, to_deal_dir=to_deal_dir, config_data=config_data
+                    )
+                    self.upload_pic.run_end_sign.connect(self.deal_run_end_sign)
+                    self.upload_pic.show_progress_detail_sign.connect(
+                        self.show_progress_detail
+                    )
+                    self.upload_pic.run()
+                    # threading.Thread(target=self.upload_pic.run_by_thread, args=()).start()
+                else:
+                    self.set_state(state_value=2)
+            else:
+                self.set_state(state_value=2)
+
+        if config_data["sign_text"] == "结束":
+            self.set_state(state_value=2)
+
+    def change_temp_mode(self, *args, **kwargs):
+        if self.ui.check_use_excel.isChecked():
+            self.ui.temp_list_get.hide()
+        else:
+            self.ui.temp_list_get.show()
+
+    # 重新载入模块
+    def reload_temp_mode(self, *args, **kwargs):
+        a = QMessageBox.question(
+            self, "确认", "是否重启加载模板", QMessageBox.Yes | QMessageBox.No
+        )
+        if a != QMessageBox.Yes:
+            return
+
+        # 检查资源包
+        if settings.env == "prod":
+            if self.top_windows:
+                self.top_windows.check_resources_download()
+
+        settings.PLUGINS_DICT = LoadAllPlugins(windows=None).load()
+        # 重新载入模板
+        self.load_temp_list()
+
+    #  获取页面配置
+    def get_config(self, *args):
+        if not settings.IsLogin:
+            QMessageBox.question(
+                self, "确认", "请先登录", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        logo_name = self.ui.logo_select_ui.currentText()
+        if logo_name == "请选择":
+            QMessageBox.question(
+                self, "确认", "请先选择logo模板", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        if self.ui.cutout_exquisite.isChecked() and not bool(settings.IsExquisiteMode):
+            QMessageBox.question(
+                self, "确认", "暂无 精细化抠图权限", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        if "失败" in self.image_dir:
+            self.WaringMessage("文件夹路径不能含有 失败 字样")
+            return
+
+        image_dir = self.ui.path_name.text()
+        image_order = self.ui.line_image_pos.text()
+        is_check_number = self.ui.check_image_total.isChecked()
+        resize_image_view = self.ui.need_resize.text()
+        logo_name = self.ui.logo_select_ui.currentText()
+        settings.MATCHING_LAST_LOGO_TEMP = logo_name
+        logo_path = self.logo_path_dict[logo_name]
+
+        cutout_mode = "1"
+        if self.ui.cutout_exquisite.isChecked():
+            cutout_mode = "2"
+        configData = {
+            "imageorder": image_order,
+            "resize_image_view": resize_image_view,
+            "is_check_number": str(is_check_number),
+            "cutout_mode": str(cutout_mode),
+            "matching_mode_last": settings.MATCHING_MODE_LAST,
+            "matching_last_temp": self.last_temp,
+            "matching_last_logo_temp": logo_name,
+        }
+        settings.cutimage_dict["imageorder"] = image_order
+        settings.cutimage_dict["resize_image_view"] = resize_image_view
+        settings.cutimage_dict["is_check_number"] = str(is_check_number)
+        settings.cutimage_dict["cutout_mode"] = str(cutout_mode)
+        settings.set_config(data_dict=configData, section="cutimage")
+        config_data = {}
+        config_data["image_dir"] = image_dir
+        config_data["image_order"] = image_order
+        config_data["is_check_number"] = is_check_number
+        config_data["resize_image_view"] = resize_image_view
+        config_data["cutout_mode"] = cutout_mode
+        config_data["logo_path"] = logo_path
+        config_data["special_goods_art_no_folder_line"] = ""
+
+        # =======================================
+
+        settings.GOODS_DETAIL_IS_USE_EXCEL = self.is_use_excel
+        settings.GOODS_DETAIL_EXCEL_PATH = self.excel_path
+        settings.GOODS_DETAIL_LAST_IMAGE_PATH = self.image_dir
+        settings.GOODS_DETAIL_IS_CHECK_COLOR_IS_ALL = self.is_check_color_is_all
+        settings.GOODS_DETAIL_IS_PASS = self.is_check_is_pass
+        settings.GOODS_DETAIL_LAST_TEMP = self.last_temp
+
+        data_dict = {
+            "is_use_excel": "是" if self.is_use_excel else "否",
+            "excel_path": self.excel_path if self.excel_path else "",
+            "last_image_path": self.image_dir if self.image_dir else "",
+            "is_check_color_is_all": "是" if self.is_check_color_is_all else "否",
+            "last_temp": self.last_temp,
+            "goods_detail_temp_mode_last": settings.GOODS_DETAIL_TEMP_MODE_LAST,
+        }
+        settings.set_config(data_dict=data_dict, section="goods_detail")
+        # 红蜻蜓/惠利玛,且为非表格数据必须需要登录处理
+        assigned_page = self.ui.assigned_page.text()
+        if assigned_page:
+            assigned_page_dict = self.check_assigned_page(assigned_page)
+            if not assigned_page_dict:
+                self.WaringMessage("指定模板填写格式不合规")
+        else:
+            assigned_page_dict = {}
+
+        config_data["is_use_excel"] = self.is_use_excel
+        config_data["excel_path"] = self.excel_path
+        config_data["is_check_color_is_all"] = self.is_check_color_is_all
+        config_data["assigned_page_dict"] = assigned_page_dict
+        config_data["temp_class"] = self.temp_class
+        config_data["temp_name"] = self.last_temp
+        config_data["temp_name_list"] = self.temp_list
+        config_data["target_error_folder"] = self.target_error_folder
+        print("=============config_data=============")
+        print(config_data)
+        return config_data
+
+    # 抠图相关处理
+    def init_cutout(self):
+        # 指定单独处理的颜色文件夹
+        self.special_goods_art_no_folder = []
+        self.show_img_dir()
+        self.ui.select_path.mousePressEvent = self.change_img_dir
+        self.ui.line_image_pos.setText(settings.cutimage_dict["imageorder"])
+        self.ui.need_resize.setText(settings.cutimage_dict["resize_image_view"])
+        self.ui.check_image_total.setChecked(
+            bool(settings.cutimage_dict["is_check_number"])
+        )
+
+        if settings.DEFAULT_CUTOUT_MODE == "普通抠图":
+            self.ui.cutout_nomal.setChecked(True)
+        elif settings.DEFAULT_CUTOUT_MODE == " 精细化抠图":
+            self.ui.cutout_exquisite.setChecked(True)
+
+        if not bool(settings.IsExquisiteMode):
+            self.ui.cutout_exquisite.setEnabled(False)
+
+        self.progress_sign.connect(self.show_progress)
+        self.ui.run.clicked.connect(self.run)  # 抠图与主图加工
+
+        self.set_logo_selected()
+        # 手动匹配图片
+        self.ui.manual_matching_pic.mousePressEvent = (
+            self.manual_matching_pic_mousePressEvent
+        )
+
+    def init_detail(self):
+        # 使用使用表格数据
+        self.is_use_excel = settings.GOODS_DETAIL_IS_USE_EXCEL
+        self.excel_path = settings.GOODS_DETAIL_EXCEL_PATH
+
+        # 目标文件夹
+        self.image_dir = settings.GOODS_DETAIL_LAST_IMAGE_PATH
+        # 对应的错误文件夹目录
+        if self.image_dir and os.path.exists(self.image_dir):
+            self.target_error_folder = "{}/软件-生成详情错误".format(self.image_dir)
+        else:
+            self.target_error_folder = ""
+
+        # 检查颜色是否齐全
+        self.is_check_color_is_all = settings.GOODS_DETAIL_IS_CHECK_COLOR_IS_ALL
+        # 已生成主图的自动过滤
+        self.is_check_is_pass = settings.GOODS_DETAIL_IS_PASS
+
+        # 上次使用的详情模板
+        self.last_temp = settings.GOODS_DETAIL_LAST_TEMP
+
+        # 重新载入模块
+        self.ui.reload_func.mousePressEvent = self.reload_temp_mode
+        # 手工整理数据
+        self.ui.manual_matching_pic.mousePressEvent = self.manual_matching_pic
+
+        # -------初始化页面--------
+        if self.is_use_excel:
+            self.ui.check_use_excel.setChecked(True)
+        else:
+            self.ui.check_use_excel.setChecked(False)
+
+        if self.excel_path:
+            self.ui.excel_path_show.setText(self.excel_path)
+
+        if self.image_dir:
+            self.ui.path_name.setText(self.image_dir)
+
+        if self.is_check_color_is_all:
+            self.ui.check_image_total_2.setChecked(True)
+        else:
+            self.ui.check_image_total_2.setChecked(False)
+
+        # 是否开启使用外部数据
+        self.ui.check_use_excel.toggled.connect(self.check_box_change)
+        self.ui.check_image_total_2.toggled.connect(self.check_box_change)
+        self.check_box_change()
+
+        self.ui.select_path.mousePressEvent = self.change_img_dir
+        self.ui.select_excel.mousePressEvent = self.select_excel_path
+
+        # ====================================
+        self.progress_sign.connect(self.show_progress)
+
+        # 复制Excel模板
+        self.ui.get_excel_temp.mousePressEvent = self.copy_excel
+        self.change_temp_mode()
+
+        # 加载模板
+        QTimer.singleShot(1000, self.load_temp_list)
+
+    def manual_matching_pic_mousePressEvent(self, event):
+        if not self.image_dir:
+            a = QMessageBox.question(
+                self,
+                "确认",
+                "请先选择目标文件夹",
+                QMessageBox.Yes,
+            )
+            return
+        self.manual = MainMatchingWindow(root_path=self.image_dir)
+
+    # 手工整理数据
+    def manual_matching_pic(self, *args, **kwargs):
+        if not self.image_dir:
+            a = QMessageBox.question(
+                self,
+                "确认",
+                "请先选择目标文件夹",
+                QMessageBox.Yes,
+            )
+            return
+        self.manual = MainMatchingWindow(root_path=self.image_dir)
+
+    def load_temp_list(self):
+        # 注册模板类
+        self.temp_class = {}
+        self.temp_list = []
+        plugins_company_name = "企业名称"
+
+        if settings.PROJECT == "红蜻蜓":
+            self.temp_class = {}
+            plugins_company_name = "红蜻蜓"
+
+        elif settings.PROJECT == "惠利玛":
+            if "惠利玛" in settings.Company:
+                self.temp_class = {}
+                plugins_company_name = "惠利玛"
+            if "小苏" in settings.Company:
+                self.temp_class = {}
+                plugins_company_name = "小苏"
+
+        # 通过动态进行加载获取
+        if settings.PLUGINS_DICT:
+            print("554  settings.PLUGINS_DICT")
+            for _key, _value in settings.PLUGINS_DICT["detail_template"].items():
+                print(_key, _value)
+            try:
+                if "全部" in settings.PLUGINS_DICT["detail_template"]:
+                    for i, v in settings.PLUGINS_DICT["detail_template"][
+                        "全部"
+                    ].items():
+                        self.temp_class[i] = v
+                if plugins_company_name in settings.PLUGINS_DICT["detail_template"]:
+                    for i, v in settings.PLUGINS_DICT["detail_template"][
+                        plugins_company_name
+                    ].items():
+                        self.temp_class[i] = v
+            except BaseException as e:
+                print("209  通过动态进行加载详情获取error", e)
+                pass
+
+        # 模板名称列表
+        self.temp_list = [x for x in self.temp_class]
+        print("570 temp_list", self.temp_list)
+
+        # 删除已渲染的模板
+        for i in self.ui.show_temp_list.findChildren(TempItem):
+            i.deleteLater()
+
+        # 模板列表
+        x, y = 10, 10
+        self.ui.show_temp_list.setMinimumSize(self.ui.widget_10.width(), 100)
+        self.ui.show_temp_list.resize(self.ui.widget_10.width(), 100)
+        temp_high = 100
+        for temp_name in self.temp_list:
+            _class = self.temp_class[temp_name]
+            _d = _class.get_temp_pic_info(_class.root)
+            temp_image_path = _d["temp_pic_path"]
+            # print("224 temp_image_path",temp_image_path)
+            # temp_item = TempItem(self.ui.scrollArea, temp_name, temp_image_path)
+            # temp_item.select_sign.connect(self.change_temp_select)
+            # self.ui.horizontalLayout_18.insertWidget(
+            #     self.ui.horizontalLayout_18.count() - 1, temp_item
+            # )
+            temp_item = TempItem(self.ui.show_temp_list, temp_name, temp_image_path)
+            temp_high = temp_item.height()
+            temp_item.select_sign.connect(self.change_temp_select)
+            temp_item.move(x, y)
+            x += temp_item.width()
+            x += 10
+            if x > self.ui.show_temp_list.width() - temp_item.width():
+                y += temp_item.height()
+                y += 10
+                x = 10
+
+        y += 100
+        y += temp_high
+        print(y)
+        self.ui.scrollAreaWidgetContents_3.setMinimumSize(x, self.height() + y)
+        self.ui.show_temp_list.setMinimumSize(x, y)
+        self.ui.show_temp_list.resize(x, y)
+        print(self.ui.show_temp_list.height())
+
+        # 设置默认值
+        if self.last_temp not in self.temp_list:
+            if self.temp_list:
+                self.last_temp = self.temp_list[0]
+
+        # 设置值
+        for temp_item in self.ui.show_temp_list.findChildren(TempItem):
+            temp_item.select()
+            break
+
+    def change_temp_select(self, _id):
+        self.last_temp = _id
+        print("已设置模板的值为:{}".format(self.last_temp))
+        for temp_item in self.ui.show_temp_list.findChildren(TempItem):
+            if temp_item.id != _id:
+                temp_item.cancel_select()
+
+    def copy_excel(self, *args, **kwargs):
+        excel_path = r"{}\resources\init\goods_excel_temp.xlsx".format(os.getcwd())
+        if not os.path.exists(excel_path):
+            return
+
+        self.check_path(r"{}\temp".format(os.getcwd()))
+        self.check_path(r"{}\temp\excel_temp".format(os.getcwd()))
+        new_excel_path = r"{}\temp\excel_temp\goods_excel_temp.xlsx".format(os.getcwd())
+        if not os.path.exists(new_excel_path):
+            shutil.copy(excel_path, new_excel_path)
+        # 打开文件夹
+        os.startfile(r"{}\temp\excel_temp".format(os.getcwd()))
+
+    def check_box_change(self, *args):
+        if self.ui.check_use_excel.isChecked():
+            self.is_use_excel = True
+            self.ui.widget.show()
+            self.ui.temp_list_get.hide()
+        else:
+            self.is_use_excel = False
+            self.ui.widget.hide()
+            self.ui.temp_list_get.show()
+
+        if self.ui.check_image_total_2.isChecked():
+            self.is_check_color_is_all = True
+        else:
+            self.is_check_color_is_all = False
+
+    # 设置选择logo模板
+    def set_logo_selected(self):
+        logo_root_path = ""
+        if settings.PROJECT == "红蜻蜓":
+            logo_root_path = r"{}\resources\LOGO\HQT".format(os.getcwd())
+        elif settings.PROJECT == "惠利玛":
+            if "小苏" in settings.Company:
+                logo_root_path = r"{}\resources\LOGO\xiaosushuoxie".format(os.getcwd())
+            if "惠利玛" in settings.Company:
+                logo_root_path = r"{}\resources\LOGO\HLM".format(os.getcwd())
+
+        self.logo_path_dict = {"请选择": "", "无logo": ""}
+
+        if logo_root_path:
+            if os.path.exists(logo_root_path):
+                image_list = get_images(logo_root_path)
+                for image_data in image_list:
+                    self.logo_path_dict[image_data["file_name"]] = image_data[
+                        "file_path"
+                    ]
+
+        self.ui.logo_select_ui.addItems([x for x in self.logo_path_dict])
+
+        self.ui.logo_select_ui.setCurrentText("请选择")
+        if settings.MATCHING_LAST_LOGO_TEMP:
+            if settings.MATCHING_LAST_LOGO_TEMP in [x for x in self.logo_path_dict]:
+                self.ui.logo_select_ui.setCurrentText(settings.MATCHING_LAST_LOGO_TEMP)
+
+    def select_excel_path(self, *args, **kwargs):
+        folder = QFileDialog.getOpenFileName(self, "选取excel", "./")[0]
+        if "xlsx" in folder:
+            # if len(folder) > 20:
+            #     text = folder[-20:]
+            # else:
+            #     text = folder
+
+            self.ui.excel_path_show.setText(folder)
+            self.excel_path = folder
+            print("folder", folder)
+
+    def change_img_dir(self, *args):
+        folder = QFileDialog.getExistingDirectory(
+            self, "选取文件夹", settings.GOODS_DETAIL_LAST_IMAGE_PATH
+        )
+        if folder:
+            # todo 增加检测是否有正常文件文件夹
+            folder = check_goods_folder(folder)
+            if folder is None:
+                QMessageBox.question(
+                    self,
+                    "确认",
+                    "您已选的文件夹下不存在任何有效数据",
+                    QMessageBox.Yes,
+                )
+                return
+            else:
+                self.image_dir = folder
+                self.target_error_folder = "{}/软件-生成详情错误".format(self.image_dir)
+                self.show_img_dir()
+
+    def change_img_dir_by_text(self, folder):
+        self.image_dir = folder
+        self.target_error_folder = "{}/软件-生成详情错误".format(self.image_dir)
+        self.show_img_dir()
+
+    def show_img_dir(self):
+        self.ui.path_name.setText(self.image_dir)
+
+    def set_state(self, state_value: int):
+        # 0禁用  1进行中  2已结束
+        if state_value not in [0, 1, 2, 99]:
+            return
+        self.state = state_value
+        # if self.state == 0:
+        #     self.ui.stackedWidget.setCurrentIndex(0)
+        # if self.state == 1:
+        #     self.ui.stackedWidget.setCurrentIndex(1)
+        #     self.ui.progressBar.setValue(0)
+        #     self.ui.textBrowser_4.clear()
+        #     self.ui.back.hide()
+        #     self.ui.stop.show()
+
+        # if self.state == 2:
+        #     self.ui.back.show()
+        #     self.ui.stop.hide()
+
+        #     pass
+
+    def check_path(self, _path):
+        if not os.path.exists(_path):
+            os.mkdir(_path)
+        return True
+
+    def show_progress(self, *args, **kwargs):
+        pass
+
+    # todo 主程序处理
+    def run(self):
+        config_data = self.get_config()
+        if not config_data:
+            return
+        # self.queren = QueRen(self)
+        # ret = self.queren.exec()
+        # if ret == QDialog.Accepted:
+        #     print("对话框以接受状态关闭")
+        for key, value in queren_get_config().items():
+            config_data[key] = value
+        config_data["sign_text"] = "开始抠图"
+        for key, value in config_data.items():
+            print("key:{},  value:{}".format(key, value))
+        self.set_state(state_value=1)
+        self.run_end_sign.emit(config_data)
+
+    # 解析指定模板页面数据
+    def check_assigned_page(self, text: str):
+
+        try:
+            # temp_dict 示例{“4”:“xiaosushuoxie-4”}
+            temp_dict = {}
+            for i in self.temp_list:
+                temp_dict[i[-1]] = i
+
+            text = text.replace(" ", "")
+            text = text.replace(",", ",")
+            text = text.replace(":", ":")
+            text = text.replace(";", ",")
+            assigned_page_list = text.split(",")
+            assigned_page_dict = {}
+            for i in assigned_page_list:
+                if not i:
+                    continue
+                _k, _v = i.split(":")
+                _k = temp_dict[_k]
+                if _k not in assigned_page_dict:
+                    assigned_page_dict[_k] = []
+                if _v not in assigned_page_dict[_k]:
+                    assigned_page_dict[_k].append(_v)
+            # assigned_page_dict 数据结构,一个模板中存在多个页面数值。
+            return assigned_page_dict
+        except:
+            return {}
+
+    # 弹出对话框
+    def show_dialog(self, data):
+        windows = data["windows"]
+        windows.dialog_result = ""
+        my_dialog = DialogShow(
+            self,
+            text=data["text"],
+            button_1=data["button_1"] if "button_1" in data else None,
+            button_2=data["button_2"] if "button_2" in data else None,
+            button_3=data["button_3"] if "button_3" in data else None,
+        )
+        ret = my_dialog.exec()
+        print("641  my_dialog.flag_name", my_dialog.flag_name)
+        # 根据用户的选择发出相应的信号
+        windows.dialog_result_signal.emit(my_dialog.flag_name)

+ 8 - 8
python/service/base.py

@@ -1,7 +1,7 @@
 import os
 import copy
 import configparser
-from module.base_mode.module_aes import Aes
+# from module.base_mode.module_aes import Aes
 import exifread
 from natsort import ns, natsorted
 import shutil
@@ -327,13 +327,13 @@ def get_dict_value(_dict, key, default=None):
         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 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):

+ 7 - 7
python/service/base_deal.py

@@ -1,18 +1,18 @@
 import json
-# from module_generate_goods_art_no_table import GenerateGoodsArtNoTable
-
-from grenerate_main_image_test import GeneratePic
+from .module_generate_goods_art_no_table import GenerateGoodsArtNoTable
+from func_timeout import FunctionTimedOut
+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 .remove_bg_ali import RemoveBgALi, Picture
+from .deal_cutout import DealCutout
 
 
 # from data import DataModeAutoDealPics
 import time
-from image_pic_deal import OnePicDeal
+from .image_pic_deal import OnePicDeal
 
 from natsort import natsorted,ns
 import os
@@ -36,7 +36,7 @@ class BaseDealImage(object):
     def __init__(self, image_dir=None):
         self.goods_images_count_dict = defaultdict(int)
         # 数据模型
-        self.data_mode_auto_deal_pics = DataModeAutoDealPics()
+        # self.data_mode_auto_deal_pics = DataModeAutoDealPics()
         self.image_dir = image_dir
         pass
 

+ 400 - 2
python/service/data.py

@@ -1,8 +1,11 @@
 import settings
-from module.data_mode.data_metaclass import DataBaseModel
+from .data_metaclass import DataBaseModel
 from PIL import Image
 from io import BytesIO
-
+import copy
+import pandas as pd
+import settings
+import numpy as np
 
 class DataModeAutoDealPics(DataBaseModel):
     def __init__(self):
@@ -26,3 +29,398 @@ class DataModeAutoDealPics(DataBaseModel):
         # 识别左右脚
         r_data = self.get_online_data.yolo_shoes_category(image_url=image_url)
         return r_data
+
+
+class DataModeGenerateDetail(DataBaseModel):
+    def __init__(self):
+        super().__init__()
+        self.get_online_data.refresh_headers()
+
+    def get_real_color_name(self, color_name: str):
+        _ = ["薄棉", "加厚", "厚棉"]
+        for i in _:
+            color_name = color_name.replace(i, "")
+        return color_name
+
+    def get_goods_art_no_info(self, numbers_list=None, goods_art_list=None):
+        if goods_art_list:
+            data_list = copy.deepcopy(goods_art_list)
+            flag = "Goods_Art"
+        else:
+            data_list = copy.deepcopy(numbers_list)
+            flag = "NUM"
+        _list = []
+        # 单次请求数少于20个
+        goods_dict = {}
+        while data_list:
+            one_data = data_list.pop()
+            _list.append(one_data)
+
+            if len(_list) == 20 or len(data_list) == 0:
+                if flag == "NUM":
+                    # 注意,红蜻蜓和惠利玛函数不同
+                    online_goods_art_data = self.get_online_data.get_goods_art_no_info(
+                        numbers_list=_list
+                    )
+                else:
+                    online_goods_art_data = self.get_online_data.get_goods_art_no_info(
+                        goods_art_list=_list
+                    )
+                if online_goods_art_data:
+                    for key in online_goods_art_data:
+                        # 映射为真实的文件夹名称
+                        goods_dict[key] = online_goods_art_data[key]
+                _list = []
+        return goods_dict
+
+    def get_basic_goods_art_data_by_hqt_and_hlm(self, folder_name_list):
+        """
+        查询红蜻蜓货号的对应数据,输入写入为编码,可能带有@,可能为货号等 NUN为编码
+        """
+        original_folder_name_list = copy.deepcopy(folder_name_list)
+
+        # 编号与文件名映射表
+        return_dict = {}
+        for folder_name in original_folder_name_list:
+            folder_name: str
+            if "NUM" not in folder_name:
+                # 即为货号
+                return_dict[folder_name] = {
+                    "type": "goods_art_no",
+                    "name": folder_name.upper(),
+                    "文件夹名称": folder_name,
+                    "data": {},
+                }
+            else:
+                if "@" not in folder_name:
+                    return_dict[folder_name] = {
+                        "type": "goods_number",
+                        "name": folder_name.upper().replace("NUM", ""),
+                        "文件夹名称": folder_name,
+                        "data": {},
+                    }
+                else:
+                    return_dict[folder_name] = {
+                        "type": "goods_number",
+                        "name": folder_name.upper().replace("NUM", "").split("@")[1],
+                        "文件夹名称": folder_name,
+                        "data": {},
+                    }
+
+        # -----------请求货号数据处理------------
+        goods_art_no_list = [
+            v["name"] for k, v in return_dict.items() if v["type"] == "goods_art_no"
+        ]
+        goods_art_no_all_data = self.get_goods_art_no_info(
+            goods_art_list=goods_art_no_list
+        )
+        for folder_name, value in return_dict.items():
+            if value["type"] == "goods_art_no":
+                if value["name"] in goods_art_no_all_data:
+                    return_dict[folder_name]["data"] = goods_art_no_all_data[
+                        value["name"]
+                    ]
+
+        # ------------请求编码数据----------------------
+        goods_number_list = [
+            v["name"] for k, v in return_dict.items() if v["type"] == "goods_number"
+        ]
+        goods_number_all_data = self.get_goods_art_no_info(
+            numbers_list=goods_number_list
+        )
+        for folder_name, value in return_dict.items():
+            if value["type"] == "goods_number":
+                if value["name"] in goods_number_all_data:
+                    return_dict[folder_name]["data"] = goods_number_all_data[
+                        value["name"]
+                    ]
+
+        # 清空没有值的数据
+        error_key = []
+        for folder_name, value in return_dict.items():
+            if not value["data"]:
+                error_key.append(folder_name)
+
+        if error_key:
+            for folder_name in error_key:
+                return_dict.pop(folder_name)
+
+        return {"code": 0, "message": "", "data": return_dict}
+
+    def get_basic_goods_art_data_form_excel(self, folder_name_list, excel_path, keys):
+
+        # =====创建虚拟表格并进行连表处理
+        need_df = pd.DataFrame(columns=["文件夹名称"])
+        for folder_name in folder_name_list:
+            new_row = {
+                "文件夹名称": str(folder_name),
+            }
+            need_df = need_df._append(new_row, ignore_index=True)
+
+        need_df = need_df.fillna(value="")
+
+        # 打开表格并进行匹配
+        _df = pd.read_excel(excel_path, sheet_name=0, header=0)
+        # 去重数据
+        duplicates = _df.duplicated(subset=["文件夹名称"], keep="first")
+        _df = _df.loc[~duplicates]
+        _df = _df.fillna(value="")
+        _df = _df.astype(str)
+
+        # 数据匹配关联,左关联
+        need_df = pd.merge(
+            need_df,
+            _df,
+            on=["文件夹名称"],
+            how="left",
+            indicator=False,
+        )
+        # 补全字段
+        header_list = need_df.columns.values.tolist()
+        for key in keys:
+            if key not in header_list:
+                need_df[key] = ""
+
+        need_df = need_df.fillna(value="")
+        need_df = need_df.astype(str)
+        # 数据转字典
+        return_dict = {}
+        message = ""
+        for index, row in need_df.iterrows():
+            if settings.PROJECT == "红蜻蜓":
+                if row["商品货号"] and row["款号"] and row["编号"]:
+                    return_dict[row["文件夹名称"]] = {
+                        "type": "goods_art_no",
+                        "name": row["文件夹名称"].upper(),
+                        "文件夹名称": row["文件夹名称"],
+                        "template_name": row["模板名称"],
+                        "data": row.to_dict(),
+                    }
+                else:
+                    message = "商品货号、款号、编号必须有值"
+            else:
+                if row["商品货号"] and row["款号"]:
+                    return_dict[row["文件夹名称"]] = {
+                        "type": "goods_art_no",
+                        "name": row["文件夹名称"].upper(),
+                        "文件夹名称": row["文件夹名称"],
+                        "template_name": row["模板名称"],
+                        "data": row.to_dict(),
+                    }
+
+        print("return_dict", return_dict)
+
+        if not return_dict:
+            message += "\n没有找到任何匹配的数据"
+            return {"code": 99, "message": message, "data": return_dict}
+        else:
+            return {"code": 0, "message": message, "data": return_dict}
+
+    def check_goods_is_not_deficiency(self, goods_no_dict: dict):
+        # 数据返回结果为款号列表
+        error_data = {}
+        for key, value in goods_no_dict.items():
+            number = value["货号资料"][0]["编号"].replace("NUM", "")
+            number_list = [x["编号"].replace("NUM", "") for x in value["货号资料"]]
+            all_color_name = [
+                self.get_real_color_name(x["颜色名称"]) for x in value["货号资料"]
+            ]  # 本地数据
+            goods_no = key
+
+            _t_goods_number, all_goods_number = (
+                self.get_online_data.get_on_goods_all_art(number=number)
+            )
+            # all_goods_number------》 [{'number': '14250232', 'goods_art_no': 'AC52001173', 'color': '杏色'}]
+
+            for i in all_goods_number:
+                if i["number"] not in number_list:
+                    # 检查款号+颜色名称是否已经存在
+                    real_color = self.get_real_color_name(i["color"])
+                    if real_color not in all_color_name:
+                        if goods_no not in error_data:
+                            error_data[goods_no] = {"message": "颜色:"}
+                        error_data[goods_no]["message"] += "{}缺失 ".format(real_color)
+
+        return error_data
+
+    def get_basic_template_information(self, _goods_no_dict, excel_path: str):
+        # 本地的文件夹数据形成一个虚拟表格
+        original_df = pd.DataFrame(columns=["款号"])
+        for goods_no in _goods_no_dict:
+            new_row = {"款号": str(goods_no), "flag": "是"}
+            original_df = original_df._append(new_row, ignore_index=True)
+        original_df = original_df.fillna(value="")
+
+        # EXCEL表格数据
+        local_df = pd.read_excel(excel_path, sheet_name=0, header=0)
+        values = {
+            "模板名称": "任意",
+        }
+        # 确保所有模板都有名称
+        local_df.fillna(value=values, inplace=True)
+        # 分组后取第一个有效值(即不是NaN的值);此处用于获取某个款号的所有基础信息
+        group_local_df = local_df.groupby(["模板名称", "款号"]).agg(
+            lambda x: x.dropna().iloc[0] if not x.dropna().empty else np.nan
+        )
+        group_local_df = group_local_df.reset_index()
+
+        # -----------数据匹配关联,左关联
+        group_local_df = pd.merge(
+            group_local_df,
+            original_df,
+            on=["款号"],
+            how="left",
+            indicator=False,
+        )
+        # -----------只取有标记的数据
+        group_local_df = group_local_df.loc[~group_local_df["flag"].isnull(), :]
+        group_local_df = group_local_df.loc[group_local_df["模板名称"] != "任意", :]
+        # -----------数据规整
+        group_local_df = group_local_df.fillna(value="")
+        group_local_df = group_local_df.astype(str)
+
+        # 组装成字典
+        temp_goods_no_dict = {}
+        for index, row in group_local_df.iterrows():
+            goods_no = row["款号"]
+            if goods_no not in temp_goods_no_dict:
+                temp_goods_no_dict[goods_no] = {}
+            temp_goods_no_dict[goods_no][row["模板名称"]] = row.to_dict()
+
+        return temp_goods_no_dict
+
+    def check_goods_is_not_deficiency_form_excel(
+        self, goods_no_dict: dict, excel_path: str
+    ):
+        # 检查颜色是否齐全,基于Excel处理
+        """
+        步骤:
+        1、goods_no_dict款号生成一个表格(款号+颜色)
+        2、excel表格数据进行款号+颜色去重
+        3、excel表格关联 上述的款号 进行标记,其他删除
+        4、excel表格关联 上述款号与颜色,进行标记
+        5、上述未标记的就是没有的颜色(有对应款号)。
+        6、对对应的款号进行设置为错误数据
+        返回数据结构
+
+        {
+        “A00001--款号”:{“message”:“这个款的错误原因”},
+        “A00002--款号”:{“message”:“这个款的错误原因”},
+        }
+
+        """
+
+        def change_to_int(x):
+            if pd.isna(x):
+                return 0
+            else:
+                try:
+                    x = int(x)
+                    return str(x)
+                except:
+                    return x
+
+        error_data = {}
+        # -----------------goods_no_dict款号生成一个表格(款号+颜色)
+        new_df = pd.DataFrame(columns=["款号", "颜色名称", "款色标记"])
+        for key, value in goods_no_dict.items():
+            goods_no = value["款号"]
+            for color_name in [x["颜色名称"] for x in value["货号资料"]]:
+                new_row = {
+                    "款号": goods_no,
+                    "颜色名称": self.get_real_color_name(color_name),
+                    "款色标记": 1,
+                }
+
+                new_df = new_df._append(new_row, ignore_index=True)
+        new_df.drop_duplicates(subset=["款号", "颜色名称"], keep="first", inplace=True)
+
+        # -------------参考的本地excel表格数据进行款号+颜色去重------------
+        local_df = pd.read_excel(excel_path, sheet_name=0, header=0)
+        local_df.fillna(
+            value={
+                "颜色名称": "",
+            },
+            inplace=True,
+        )
+        local_df = local_df.astype({"颜色名称": str})
+
+        header_list = local_df.columns.values.tolist()
+
+        for key in ["帮高", "鞋宽", "跟高"]:
+            if key in header_list:
+                local_df[key] = local_df[key].apply(change_to_int)
+
+        #     颜色名称规整
+        local_df["颜色名称"] = local_df["颜色名称"].apply(self.get_real_color_name)
+        local_df.drop_duplicates(
+            subset=["款号", "颜色名称"], keep="first", inplace=True
+        )
+
+        # ------------excel表格关联 上述的款号 进行标记,其他删除
+        _df = new_df.drop_duplicates(subset=["款号"], keep="first", inplace=False)
+        _df = _df[["款号"]]
+        _df["对应款号"] = 1
+        local_df = pd.merge(
+            local_df,
+            _df,
+            on=["款号"],
+            how="left",
+            indicator=False,
+        )
+        local_df = local_df.loc[local_df["对应款号"] == 1, :]
+
+        # ------------excel表格关联 上述款号与颜色,进行标记;取没有关联的颜色数据---------
+        local_df = pd.merge(
+            local_df,
+            new_df,
+            on=["款号", "颜色名称"],
+            how="left",
+            indicator=False,
+        )
+        local_df.fillna(
+            value={
+                "款色标记": 0,
+            },
+            inplace=True,
+        )
+        local_df = local_df.loc[local_df["款色标记"] == 0, :]
+        print("local_df")
+        print(local_df)
+
+        # ------------数据分组,对对应的款号进行设置为错误数据---------------
+        grouped_df = (
+            local_df.groupby("款号")["颜色名称"]
+            .apply(lambda x: "、".join(x))
+            .reset_index()
+        )
+        for index, row in grouped_df.iterrows():
+            error_data[row["款号"]] = {
+                "message": "颜色:{}缺失".format(row["颜色名称"])
+            }
+
+        print("======error_data")
+        print(json.dumps(error_data))
+
+        return error_data
+
+
+class DataModeUploadPic(DataBaseModel):
+    def __init__(self):
+        super().__init__()
+        self.get_online_data.refresh_headers()
+        self.is_deal_goods_no = []
+
+    def get_goods_art_no_info(self, numbers_list=None, goods_art_list=None):
+        return self.get_online_data.get_goods_art_no_info(
+            numbers_list=numbers_list, goods_art_list=goods_art_list
+        )
+
+    def upload_pic_list_data(self, data):
+        return self.get_online_data.upload_pic_list_data(data)
+
+    def upload_pic(self, goods_data):
+        return self.get_online_data.upload_pic(goods_data)
+
+    def check_is_uploaded(self, goods_art_no=None):
+        return self.get_online_data.check_detail_image(goods_art_no)

+ 295 - 0
python/service/data_metaclass.py

@@ -0,0 +1,295 @@
+from logger import logger
+from databases import DeviceConfig, PhotoRecord, SqlQuery
+from sqlalchemy.orm import sessionmaker, scoped_session
+import settings
+from .online_request.module_online_data import OnlineDataRequest, GetOnlineDataHLM
+
+import time
+from datetime import datetime, timedelta
+from sqlalchemy.sql import func
+from sqlalchemy.orm import aliased
+import copy
+import threading
+
+
+class DataBaseModel():
+    # 基础数据基类
+    # signal_data = Signal(dict)
+    # add_data_to_online_sign = Signal(dict)
+
+    def __init__(self):
+        # super().__init__(parent=None)
+        self.logger = logger
+        # 创建一个实例化的会话对象 session;该会话会自己处理上下文,线程结束时会自动关闭会话;适合多线程处理
+        self.session = SqlQuery()
+
+        if settings.PROJECT == "红蜻蜓":
+            self.get_online_data = OnlineDataRequest()
+        elif settings.PROJECT == "惠利玛":
+            self.get_online_data = GetOnlineDataHLM()
+
+        # self.add_data_to_online_sign.connect(self.send_record_to_online)
+
+    # 新增数据的记录发送到云端
+    def send_record_to_online(self, data):
+        # _data = {"goods_art_no":goods_art_no,
+        #          "image_index": image_index,
+        #          "photo_create_time": photo_create_time,
+        #          "image_deal_mode": image_deal_mode,
+        #          "take_photo_created_at": date_time.strftime('%Y-%m-%d %H:%M:%S'),
+        #          }
+        data["photo_create_time"] = "{}".format(data["photo_create_time"])
+        data["take_photo_created_at"] = "{}".format(data["take_photo_created_at"])
+        self.get_online_data.add_auto_photo_logs(data)
+        pass
+
+    def send_info(self, _type="", mode="", data=None):
+        self.signal_data.emit({"_type": _type, "plugins_mode": mode, "data": data})
+
+    def send_log(self, text, is_debug=False):
+        if is_debug:
+            self.logger.debug(text)
+        else:
+            self.logger.info(text)
+
+    def insert_data_to_phone_record(self, data: dict):
+        # 异步提交数据到后台
+        photo_create_time = data["photo_create_time"]
+        try:
+            photo_create_time = time.strptime(photo_create_time, "%Y-%m-%d %H:%M:%S")
+        except:
+            photo_create_time = time.strptime(photo_create_time, "%Y:%m:%d %H:%M:%S")
+
+        photo_create_time = time.mktime(photo_create_time)
+        photo_create_time = datetime.fromtimestamp(photo_create_time)
+
+        image_deal_mode = data["image_deal_mode"] if "image_deal_mode" in data else 0
+        image_index = data["image_index"] if "image_index" in data else 0
+        goods_art_no = data["goods_art_no"]
+        d_1 = PhotoRecord(
+            goods_art_no=goods_art_no,
+            image_index=image_index,
+            photo_create_time=photo_create_time,
+            image_deal_mode=image_deal_mode,
+        )
+
+        self.session.add(d_1)
+        self.session.commit()
+        date_time = datetime.fromtimestamp(time.time())
+        _data = {"goods_art_no": goods_art_no,
+                 "image_index": image_index,
+                 "photo_create_time": photo_create_time,
+                 "image_deal_mode": image_deal_mode,
+                 "take_photo_created_at": date_time.strftime('%Y-%m-%d %H:%M:%S'),
+                 }
+        # self.add_data_to_online_sign.emit(_data)
+
+    def get_goods_art_no(self, date_time_original):
+        time_array = time.strptime(date_time_original, "%Y:%m:%d %H:%M:%S")
+        time_array = time.mktime(time_array)
+        datetime_obj = datetime.fromtimestamp(time_array)
+        result = (
+            self.session.query(PhotoRecord)
+            .filter(PhotoRecord.photo_create_time == datetime_obj)
+            .order_by(PhotoRecord.id.desc())
+            .scalar()
+        )
+        if result:
+            return result.goods_art_no, result.image_index, result.image_deal_mode
+        else:
+            return None
+
+    def get_data_from_hqt_with_goods_art_no(self, goods_art_no_list):
+        _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 = self.get_online_data.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 get_data_from_hqt(self, goods_number_list):
+        _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 = self.get_online_data.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 device_config_delete(self, config_id):
+        d_c_1 = self.session.query(DeviceConfig).get(config_id)
+        self.session.delete(d_c_1)
+        self.session.commit()
+
+    def device_config_delete_by_mode_type(self, mode_type: str):
+        all_data = (
+            self.session.query(DeviceConfig)
+            .filter(DeviceConfig.mode_type == mode_type)
+            .all()
+        )
+        [self.session.delete(u) for u in all_data]
+        self.session.commit()
+
+    def device_config_copy_by_id(self, config_id):
+        d_c_1 = self.session.query(DeviceConfig).get(config_id)
+
+        d_c_2 = DeviceConfig(
+            mode_type=d_c_1.mode_type,
+            execution_type=d_c_1.execution_type,
+            action_name=d_c_1.action_name + "_复制",
+            action_index=d_c_1.action_index + 1,
+            picture_index=d_c_1.picture_index,
+            camera_height=d_c_1.camera_height,
+            camera_angle=d_c_1.camera_angle,
+            number_focus=d_c_1.number_focus,
+            take_picture=d_c_1.take_picture,
+            turntable_position=d_c_1.turntable_position,
+            turntable_angle=d_c_1.turntable_angle,
+            shoe_upturn=d_c_1.shoe_upturn,
+            pre_delay=d_c_1.pre_delay,
+            after_delay=d_c_1.after_delay,
+            led_switch=d_c_1.led_switch,
+            is_wait=d_c_1.is_wait,
+        )
+        # 复制一个后,其他的数据的排序进行后移
+        other_d_c = self.session.query(DeviceConfig).filter(
+            DeviceConfig.mode_type == d_c_1.mode_type,
+            DeviceConfig.action_index >= d_c_1.action_index + 1,
+            DeviceConfig.delete_time.is_(None),
+        )
+        other_d_c.update(
+            {
+                DeviceConfig.action_index: DeviceConfig.action_index + 1,
+            }
+        )
+        self.session.add(d_c_2)
+
+        self.session.commit()
+
+    def device_config_add(self, data: dict):
+        f_data = (
+            self.session.query(
+                func.max(DeviceConfig.action_index).label("max_action_index")
+            )
+            .filter(
+                DeviceConfig.mode_type == data["mode_type"],
+                DeviceConfig.delete_time.is_(None),
+            )
+            .scalar()
+        )
+
+        if not f_data:
+            data["action_index"] = 10
+        else:
+            data["action_index"] = f_data + 10
+
+        d_c_1 = DeviceConfig(**data)
+        self.session.add(d_c_1)
+        self.session.commit()
+
+    def device_config_change_action_index(self, up_config_id_1, down_config_id_2):
+        d_c_1 = (
+            self.session.query(DeviceConfig.action_index)
+            .filter(DeviceConfig.id == up_config_id_1)
+            .scalar()
+        )
+        d_c_2 = (
+            self.session.query(DeviceConfig.action_index)
+            .filter(DeviceConfig.id == down_config_id_2)
+            .scalar()
+        )
+
+        if d_c_1 == d_c_2:
+            d_c = self.session.query(DeviceConfig).get(up_config_id_1)
+            d_c.update(
+                {
+                    DeviceConfig.action_index: DeviceConfig.action_index - 1,
+                }
+            )
+        else:
+            d_c_update_1 = self.session.query(DeviceConfig).filter(
+                DeviceConfig.id == up_config_id_1
+            )
+            d_c_update_2 = self.session.query(DeviceConfig).filter(
+                DeviceConfig.id == down_config_id_2
+            )
+            d_c_update_1.update(
+                {
+                    DeviceConfig.action_index: d_c_2,
+                }
+            )
+            d_c_update_2.update(
+                {
+                    DeviceConfig.action_index: d_c_1,
+                }
+            )
+        self.session.commit()
+        pass
+
+    def device_config_update(self, data: dict):
+        d_c = self.session.query(DeviceConfig).filter(DeviceConfig.id == data["id"])
+        data.pop("id")
+        d_c.update(data)
+        self.session.commit()
+
+    def device_config_config_list(self, mode_type, action_name=None):
+        _q = self.session.query(DeviceConfig).filter(
+            DeviceConfig.mode_type == mode_type, DeviceConfig.delete_time.is_(None)
+        )
+        if action_name:
+            _q = _q.filter(DeviceConfig.action_name == action_name)
+        results = _q.order_by(DeviceConfig.action_index.asc()).all()
+        return [self.to_dict(row) for row in results]
+
+    def to_dict(self, row):
+        return {c.name: getattr(row, c.name, None) for c in row.__table__.columns}
+
+    def device_config_detail(self, config_id):
+        results = (
+            self.session.query(DeviceConfig)
+            .filter(DeviceConfig.id == config_id, DeviceConfig.delete_time.is_(None))
+            .one()
+        )
+        return self.to_dict(results)
+
+    def delete3MonthAgoRecord(self):
+        """删除3个月以前得数据"""
+        print("d_c_1")
+        # 计算3个月前的日期
+        three_months_ago = datetime.now() - timedelta(days=90)
+        d_c_1 = (
+            self.session.query(PhotoRecord)
+            .filter(PhotoRecord.create_time < three_months_ago)
+            .delete()
+        )
+        self.session.commit()

+ 6 - 6
python/service/deal_cutout.py

@@ -3,13 +3,13 @@ import time
 from concurrent.futures import ThreadPoolExecutor, wait
 import threading
 
-from deal_one_image import DealOneImage, DealOneImageBeforehand
-from module.log.log import MyLogger
-from module.online_request.module_online_data import GetOnlineDataHLM
+from .deal_one_image import DealOneImage, DealOneImageBeforehand
+from logger import logger
+from .online_request.module_online_data import GetOnlineDataHLM
 
 
-class DealCutout(QThread):
-    signal_data = Signal(dict)
+class DealCutout():
+    # signal_data = Signal(dict)
 
     def __init__(self, windows):
         super().__init__()
@@ -22,7 +22,7 @@ class DealCutout(QThread):
         self.is_deal_num = 0
         # 图片列表
         self.upload_pic_dict = {}
-        self.logger = MyLogger().logger
+        self.logger = logger
 
         self.remaining_times = 0  # 剩余总次数
         self.get_online_data = GetOnlineDataHLM()

+ 48 - 55
python/service/deal_image.py

@@ -1,4 +1,3 @@
-
 import os
 
 from  natsort import natsorted,ns
@@ -6,7 +5,7 @@ import shutil
 import exifread
 import time
 import datetime
-from databases import DeviceConfig,SqlQuery,CRUD
+from databases import PhotoRecord,SqlQuery,CRUD
 import settings
 import copy
 import json
@@ -14,15 +13,14 @@ import requests
 from service.pic_deal import Picture
 import xlsxwriter
 from PIL import Image
-
+from .base_deal import BaseDealImage
 _Type = ['.png', '.PNG', '.jpg', '.JPG', '.gif', '.GIF', ".jpge", ".JPGE"]
 
-class DealImage():
-
-
 
+class DealImage(BaseDealImage):
 
     def __init__(self, image_dir=None):
+        super().__init__()
         self.image_dir = image_dir
         self.header = None
     def check_path(self, image_dir: str):
@@ -41,24 +39,25 @@ class DealImage():
             else:
                 return False
 
-    def get_goods_art_no(self, date_time_original):
-        time_array = time.strptime(date_time_original, "%Y:%m:%d %H:%M:%S")
+    def get_goods_art_no(self, goods_art_no):
+        # time_array = time.strptime(date_time_original, "%Y:%m:%d %H:%M:%S")
 
-        time_array = time.mktime(time_array)
-        datetime_obj = datetime.fromtimestamp(time_array)
+        # time_array = time.mktime(time_array)
+        # datetime_obj = datetime.datetime.fromtimestamp(time_array)
 
         session = SqlQuery()
-        configModel = CRUD(DeviceConfig)
+        configModel = CRUD(PhotoRecord)
         result = configModel.read(
-            session, conditions={"photo_create_time": datetime_obj}, order_by="id", ascending=True
+            session,
+            conditions={"goods_art_no": goods_art_no},
+            order_by="id",
+            ascending=True,
         )
         if result:
             return result.goods_art_no, result.image_index, result.image_deal_mode
         else:
             return None
 
-
-
     def get_data_from_hqt_with_goods_art_no(self, goods_art_no_list):
         _goods_art_no_list = copy.deepcopy(goods_art_no_list)
         _list = []
@@ -93,7 +92,6 @@ class DealImage():
         _s = requests.session().post(url=url, data=data, headers=self.header)
         response_data = _s.json()
 
-
         goods_number_data = {}
         # ["", "", "", "", "", "", "", "", "", "", "", ]
         if "data" not in response_data:
@@ -111,7 +109,6 @@ class DealImage():
 
         return goods_number_data
 
-
     def get_data_from_hqt(self, goods_number_list):
         _goods_number_list = copy.deepcopy(goods_number_list)
         _list = []
@@ -149,8 +146,6 @@ class DealImage():
                 other_path = path + "/" + name
                 check_folder(other_path)
 
-
-
     def move_images(self, goods_art_no, goods_art_no_path, old_image_path):
         """
         步骤:
@@ -167,6 +162,7 @@ class DealImage():
         file = os.path.split(old_image_path)[1]
         # 扩展名
         e = os.path.splitext(file)[1]
+        print("self.goods_images_count_dict", self.goods_images_count_dict)
         # 获取图片序列
         self.goods_images_count_dict[goods_art_no] += 1
         # A9999(1).jpg
@@ -175,12 +171,13 @@ class DealImage():
         # 移动图片
         shutil.move(old_image_path, original_image_path)
 
-    def dealMoveImage(self, image_dir: str, callback_func=None) -> dict:
+    def dealMoveImage(self, image_dir: str, callback_func=None,goods_art_no=None) -> dict:
         if not self.check_path(image_dir=image_dir + "/历史"):
-            return {'code': 1, 'msg': '文件夹创建失败', 'data': {}}
+            return False, "文件夹创建失败"
 
         # 遍历目标文件夹,获取有拍摄信息的图片,并按拍摄时间排序
         files = self.list_dir(image_dir)
+        print("files", files)
         original_photo_list = []  # 原始图片列表
         for file in files:
             # -----图片清洗
@@ -195,7 +192,7 @@ class DealImage():
             date_time_original = self.get_date_time_original(file_path)  # 获取照片拍照时间
             if date_time_original:
                 # 基于照片的时间,与数据库匹配goods_art_no
-                _data = self.get_goods_art_no(date_time_original)
+                _data = self.get_goods_art_no(goods_art_no)
                 if _data:
                     # 能匹配上数据库
                     goods_art_no, image_index, image_deal_mode = _data
@@ -219,34 +216,34 @@ class DealImage():
                 shutil.move(file_path, image_dir + "/历史/" + file)
 
         if not original_photo_list:
-            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.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.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"]]["商品货号"]
+            return False, "没有任何匹配的图片"
+        # 暂不处理红蜻蜓
+        # 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.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.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(
@@ -281,7 +278,6 @@ class DealImage():
             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:
@@ -300,9 +296,7 @@ class DealImage():
 
         # 完成处理
         # self.set_state(state_value=2)
-        return {'code': 0, 'msg': '处理完成', 'target_path': output_path, 'data': {}}
-
-
+        return True, output_path
 
     def deal(cls, dir_path):
 
@@ -429,9 +423,8 @@ class DealImage():
                         },
                         )
 
-
                 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])
+                sheet.write_row("E{}".format(index + 2), [goods_pic_total])

+ 5 - 6
python/service/deal_one_image.py

@@ -1,17 +1,16 @@
 import copy
 import os.path
 # from module.other.module_online_data import GetOnlineData
-from module.online_request.module_online_data import GetOnlineDataHLM
+from .online_request.module_online_data import GetOnlineDataHLM
 
 import time
-from module.log.log import MyLogger
+from logger import logger
 
 import os
 from PIL import Image
 # from module.other.remove_bg_ali import RemoveBgALi
-from module.base_mode.remove_bg_pixian import RemoveBgPiXian
-from module.base_mode.remove_bg_ali import RemoveBgALi
-from module.base_mode.remove_bg_ali import Picture
+from .remove_bg_pixian import RemoveBgPiXian
+from .remove_bg_ali import RemoveBgALi, Picture
 import cv2
 import numpy as np
 import settings
@@ -28,7 +27,7 @@ class Base(object):
         self.file_name = image_data["file_name"]
         self.file = os.path.split(self.file_path)[1]
         self.is_once_data = {}
-        self.logger = MyLogger().logger
+        self.logger = logger
 
     def add_log(self, text, _type="info"):
         self.logger.info("第{}个,图片名称:{},内容:{}".format(self.num, self.file, text))

+ 176 - 0
python/service/detail_func.py

@@ -0,0 +1,176 @@
+import json
+import os
+import settings
+import shutil
+
+
+def get_all_dir_info(image_dir):
+    # 遍历货号获取所有货号--可能为编号
+    folder_name_list = []
+    for folder_name in os.listdir(image_dir):
+        _path = "{}/{}".format(image_dir, folder_name)
+        if not os.path.isdir(_path):
+            continue
+        if "无法对应" in folder_name:
+            continue
+        if "软件" in folder_name:
+            if len(os.listdir(_path)) == 0:
+                # 删除空文件夹
+                os.rmdir(_path)
+            continue
+        folder_name_list.append(folder_name)
+
+    return folder_name_list
+
+
+def get_all_detail_info(image_dir):
+    # 遍历货号获取所有货号--可能为编号
+    _list = []
+    for goods_no in os.listdir(image_dir):
+        goods_art_no_path = "{}/{}".format(image_dir, goods_no)
+        if not os.path.isdir(goods_art_no_path):
+            continue
+        _list.append(goods_no)
+    return _list
+
+
+def get_all_dir_info_and_pic_info(root_path, folder_name_list, need_view_list):
+    # 遍历货号获取所有货号--可能为编号
+    """
+    数据返回为每个文件夹下的文件结构
+    """
+
+    return_data = {}
+    for one_folder in folder_name_list:
+        source_material_path = "{}/{}/阴影图处理".format(root_path, one_folder)
+        data = {"pics": {},
+                "message": "",
+                "800x800": []
+                }
+
+        # source_material_path ===================
+        if not os.path.exists(source_material_path):
+            data["message"] = "缺失 阴影图处理 文件夹"
+        else:
+            if len(os.listdir(source_material_path)) % 2 != 0:
+                data["message"] = "缺失 阴影图处理 图片不为2的倍数"
+            else:
+                # 遍历图片
+                _pic_name_list = []
+                for pic in os.listdir(source_material_path):
+                    _pic = pic.replace(".png", "")
+                    _pic = _pic.split("_")
+                    if len(_pic) == 3:
+                        # 示例:侧视-阴影
+                        data["pics"]["{}-{}".format(_pic[1], _pic[2])] = "{}/{}".format(source_material_path, pic)
+                        _pic_name_list.append(_pic[1])
+
+                # 检查图片是否都有
+                _ = [x for x in need_view_list if x not in _pic_name_list]
+                if _:
+                    _ = ",".join(_)
+                    data["message"] += "\图片{}视角缺失".format(_)
+
+        # 放入商品货号主图 ===================
+        goods_art_800_pic_f = "{}/{}/800x800".format(root_path, one_folder)
+        if not os.path.exists(goods_art_800_pic_f):
+            data["message"] += "\缺失主图文件夹"
+        else:
+            if len(os.listdir(goods_art_800_pic_f)) == 0:
+                data["message"] += "\缺失主图"
+            else:
+                for goods_art_pic in os.listdir(goods_art_800_pic_f):
+                    # 过滤非图片信息
+                    if "jpg" in goods_art_pic or "JPG" in goods_art_pic or "png" in goods_art_pic or "PNG" in goods_art_pic:
+                        data["800x800"].append("{}/{}".format(goods_art_800_pic_f, goods_art_pic))
+
+        # =======================
+        return_data[one_folder] = data
+    print("检查没有问题的文件夹-----------return_data")
+    print(json.dumps(return_data))
+
+    return return_data
+
+
+def merge_local_and_remote_data(all_dir_info_data: dict, remote_data):
+    """
+    all_dir_info_data:文件夹基础数据;文件夹结构数据
+    remote_data:远程数据
+    """
+    print("---all_dir_info_data:")
+    print(json.dumps(all_dir_info_data))
+
+    print("---remote_data:")
+    print(json.dumps(remote_data))
+
+    error_folder_name_list = []
+    for folder_name, value in all_dir_info_data.items():
+        if folder_name not in remote_data:
+            value["message"] = "文件夹没有对应数据信息"
+            error_folder_name_list.append(folder_name)
+        else:
+            value["remote_data"] = remote_data[folder_name]
+
+    # =======开始组装数据
+
+    # 组装数据,按款号进行组装数据
+    goods_no_dict = {}
+    for folder_name, value in all_dir_info_data.items():
+        if "remote_data" not in value:
+            continue
+        if "data" not in value["remote_data"]:
+            continue
+
+        _remote_data = value["remote_data"]["data"]
+        goods_no = _remote_data["款号"]
+        if goods_no not in goods_no_dict:
+            goods_no_dict[goods_no] = {}
+            goods_no_dict[goods_no]["款号"] = goods_no
+            goods_no_dict[goods_no]["货号资料"] = []
+
+        _goods_number = _remote_data["编号"] if "编号" in _remote_data else ""
+        if _goods_number:
+            _goods_number = "NUM{}".format(_goods_number)
+        else:
+            _goods_number = ""
+
+        goods_no_dict[goods_no]["货号资料"].append({"货号": _remote_data["商品货号"],
+                                                "文件夹名称": folder_name,
+                                                "编号": _goods_number,
+                                                "颜色名称": _remote_data["颜色名称"] if "颜色名称" in _remote_data else "",
+                                                "pics": value["pics"],
+                                                "800x800": value["800x800"],
+                                                })
+
+        # 添加款号的基础信息,进行去重处理,此处为动态字段
+        key = ["款号", "商品货号", "编号", "颜色名称", ]
+        for _k, _v in _remote_data.items():
+            if _k not in key:
+                if _k not in goods_no_dict[goods_no]:
+                    goods_no_dict[goods_no][_k] = ""
+                if not goods_no_dict[goods_no][_k]:
+                    if _v:
+                        goods_no_dict[goods_no][_k] = _v
+
+    # ======如果为红蜻蜓,则key需要更改为编号=========
+    # 改款号信息  取任意一个商品的编号作为款号,格式为KNUM00001
+    if settings.PROJECT == "红蜻蜓":
+        _goods_no_dict = {}
+        for key, value in goods_no_dict.items():
+            number = "K{}".format(value["货号资料"][0]["编号"])
+            _goods_no_dict[number] = value
+    else:
+        _goods_no_dict = goods_no_dict
+
+    return _goods_no_dict, error_folder_name_list
+
+
+def move_folders(path_list, target_folder):
+    create_folder(target_folder)
+    for source_folder in path_list:
+        shutil.move(source_folder, target_folder)
+
+
+def create_folder(path):
+    if not os.path.exists(path):
+        os.makedirs(path)

+ 84 - 0
python/service/detail_temp.py

@@ -0,0 +1,84 @@
+# from import_qt_mode import *
+# from UI.auto_deal_pics_ui.temp_item import Ui_Form as UI_temp_item
+import os
+from PIL import Image
+import threading
+from io import BytesIO
+
+
+# 详情itme模板组件
+class TempItem():
+    # select_sign = Signal(str)
+
+    def __init__(self, parent, _id, image_path):
+        super().__init__(parent)
+        # self.ui = UI_temp_item()
+        # self.ui.setupUi(self)
+        self.temp_name = _id
+        self.image_path = image_path
+        self.id = _id
+        self.is_select = False
+        self.init()
+        # self.show()
+
+    def init(self):
+        # self.ui.label.mousePressEvent = self.pic_show
+        # self.ui.radioButton.clicked.connect(self.select)
+        self.set_label_image()
+        # self.label_temp_name = QLabel(self)
+        # self.label_temp_name.setText(str(self.temp_name))
+        # self.label_temp_name.move(5, self.height() - self.label_temp_name.height())
+
+    def cancel_select(self, *args):
+        self.is_select = False
+        # self.ui.radioButton.setChecked(False)
+
+    def select(self, *args):
+        self.is_select = True
+        # self.ui.radioButton.setChecked(True)
+        # self.select_sign.emit(self.id)
+
+    def pic_show(self, *args, **kwargs):
+        if os.path.exists(self.image_path):
+            im = Image.open(self.image_path)
+            threading.Thread(target=im.show, args=()).start()
+
+    def set_label_image(self):
+        if not os.path.exists(self.image_path):
+            return
+        # try:
+        #     bytes_io = BytesIO()
+        #     w, h = self.ui.image_show.width(), self.ui.image_show.height()
+        #     _img_raw = Image.open(self.image_path)
+        #     _img_raw = self.to_resize(_img_raw, width=w * 2)
+        #     _img_raw = _img_raw.crop(box=(0, 0, w * 2, h * 2))
+        #     # _img_raw.thumbnail((w * 2, int(_img_raw.height * w * 2 / _img_raw.width)))
+        #     # img_raw = _img_raw.resize(size=(w, h))
+        #     _img_raw.save(bytes_io, "JPEG")
+        #     icon = QPixmap()
+        #     # icon.loadFromData(img)
+        #     icon.loadFromData(bytes_io.getvalue())
+        #     icon = icon.scaled(
+        #         self.ui.image_show.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
+        #     )
+        #     self.ui.image_show.setPixmap(icon)
+        #     # self.mousePressEvent = lambda x: self.show_one_data(row)
+        # except:
+        pass
+
+    def to_resize(self, _im, width=None, high=None):
+        _im_x, _im_y = _im.size
+        if width and high:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(high)
+            re_x = int(_im_x * re_y / _im_y)
+        _im = _im.resize((re_x, re_y))
+        return _im
+

+ 11 - 11
python/service/excel_base_func.py

@@ -1,4 +1,4 @@
-from PySide6.QtWidgets import QApplication
+# from PySide6.QtWidgets import QApplication
 from PIL import Image
 import settings
 import time
@@ -6,16 +6,16 @@ import os
 
 def get_clip_image(image_path):
     try:
-        cb = QApplication.clipboard()
-        if cb.mimeData().hasImage():
-            qt_img = cb.image()
-            pil_img = Image.fromqimage(qt_img)  # 转换为PIL图像
-            if pil_img.width > 10:
-                pil_img = pil_img.convert("RGB")
-                pil_img.save(image_path)
-                print("图片剪切板保存成功:{}".format(image_path))
-
-            return True
+        # cb = QApplication.clipboard()
+        # if cb.mimeData().hasImage():
+        #     qt_img = cb.image()
+        #     pil_img = Image.fromqimage(qt_img)  # 转换为PIL图像
+        #     if pil_img.width > 10:
+        #         pil_img = pil_img.convert("RGB")
+        #         pil_img.save(image_path)
+        #         print("图片剪切板保存成功:{}".format(image_path))
+
+            # return True
         return False
     except BaseException as e:
         print(e)

+ 313 - 0
python/service/generate_goods_art_no_table/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 ..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():
+    # 货号表生成
+    # 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

+ 422 - 0
python/service/generate_main_image/grenerate_main_image_test.py

@@ -0,0 +1,422 @@
+import os
+import copy
+import time
+from .image_deal_base_func import *
+from PIL import Image, ImageDraw
+from blend_modes import multiply
+import os
+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)
+        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_line = cv2_to_pil(img_with_shifted_line)
+        mask = mask_line.convert('L')  # 转换为灰度图
+        mask = ImageOps.invert(mask)
+        # 蒙版扩边
+        mask = expand_mask(mask, expansion_radius=30, blur_radius=10)
+
+        # ====================生成新的图片
+        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)  # 粘贴有阴影的地方
+
+        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]  # 需要检查的像素行
+
+        _im_shadow = copy.copy(im_shadow)
+        Midtones = 0.7
+        Highlight = 235
+        k = 8
+        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] >= 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)
+
+        # =====抠图,形成新的阴影背景图=====
+        _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

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

@@ -0,0 +1,216 @@
+import cv2
+import numpy as np
+from PIL import Image, ImageEnhance, ImageFilter, ImageOps
+import settings
+
+# 锐化图片
+def sharpen_image(img, factor=1.0):
+    # 创建一个ImageEnhance对象
+    enhancer = ImageEnhance.Sharpness(img)
+    # 应用增强,值为0.0给出模糊图像,1.0给出原始图像,大于1.0给出锐化效果
+    # 调整这个值来增加或减少锐化的程度
+    sharp_img = enhancer.enhance(factor)
+    return sharp_img
+
+
+def to_resize(_im, width=None, high=None) -> Image:
+    _im_x, _im_y = _im.size
+    if width and high:
+        if _im_x >= _im_y:
+            high = None
+        else:
+            width = None
+    if width:
+        re_x = int(width)
+        re_y = int(_im_y * re_x / _im_x)
+    else:
+        re_y = int(high)
+        re_x = int(_im_x * re_y / _im_y)
+    _im = _im.resize((re_x, re_y),resample=settings.RESIZE_IMAGE_MODE)
+    return _im
+
+
+def pil_to_cv2(pil_image):
+    # 将 PIL 图像转换为 RGB 或 RGBA 格式
+    if pil_image.mode != 'RGBA':
+        pil_image = pil_image.convert('RGBA')
+    # 将 PIL 图像转换为 numpy 数组
+    cv2_image = np.array(pil_image)
+    # 由于 PIL 的颜色顺序是 RGB,而 OpenCV 的颜色顺序是 BGR,因此需要交换颜色通道
+    cv2_image = cv2.cvtColor(cv2_image, cv2.COLOR_RGBA2BGRA)
+    return cv2_image
+
+
+def cv2_to_pil(cv_img):
+    return Image.fromarray(cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB))
+
+
+def get_mini_crop_img(img):
+    old_x, old_y = img.size
+    x1, y1, x2, y2 = img.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
+    img = img.crop((new_x1, new_y1, new_x2, new_y2))  # 切图
+    box = (new_x1, new_y1, new_x2, new_y2)
+    return img, box
+
+
+def expand_mask(mask, expansion_radius=5, blur_radius=0):
+    # 对蒙版进行膨胀处理
+    mask = mask.filter(ImageFilter.MaxFilter(expansion_radius * 2 + 1))
+    # 应用高斯模糊滤镜
+    if blur_radius > 0:
+        mask = mask.filter(ImageFilter.GaussianBlur(blur_radius))
+
+    return mask
+
+
+def find_lowest_non_transparent_points(cv2_png):
+    # cv2_png 为cv2格式的带有alpha通道的图片
+
+    alpha_channel = cv2_png[:, :, 3]
+    """使用Numpy快速查找每列的最低非透明点"""
+    h, w = alpha_channel.shape
+    # 创建一个掩码,其中非透明像素为True
+    mask = alpha_channel > 0
+    # 使用np.argmax找到每列的第一个非透明像素的位置
+    # 因为是从底部向上找,所以需要先翻转图像
+    flipped_mask = np.flip(mask, axis=0)
+    min_y_values = h - np.argmax(flipped_mask, axis=0) - 1
+    # 将全透明列的值设置为-1
+    min_y_values[~mask.any(axis=0)] = -1
+    return min_y_values
+
+
+def draw_shifted_line(image, min_y_values, shift_amount=15,
+                      one_line_pos=(0, 100),
+                      line_color=(0, 0, 0),
+                      line_thickness=20):
+    """
+    image:jpg cv2格式的原始图
+    min_y_values 透明图中,不透明区域的最低那条线
+    shift_amount:向下偏移值
+    line_color:线颜色
+    line_thickness:线宽
+    """
+    # 将最低Y值向下迁移20个像素,但确保不超过图片的高度
+    # 创建空白图片
+    image = np.ones((image.shape[0], image.shape[1], 3), dtype=np.uint8) * 255
+
+    # 对线条取转成图片
+    shifted_min_y_values = np.clip(min_y_values + shift_amount, 0, image.shape[0] - 1)
+
+    # 使用Numpy索引批量绘制直线
+    min_y_threshold = 50  # Y轴像素小于50的不处理
+    valid_x = (shifted_min_y_values >= min_y_threshold) & (shifted_min_y_values != -1)
+
+    # 对曲线取平均值
+    # # 对曲线取平均值
+    # min_y = np.max(min_y_values)
+    # min_y_values_2 = min_y_values + min_y
+    # min_y_values_2 = min_y_values_2 / 2
+    # min_y_values_2 = min_y_values_2.astype(int)
+    # shifted_min_y_values = np.clip(min_y_values_2 + shift_amount, 0, image.shape[0] - 1)
+
+    x_coords = np.arange(image.shape[1])[valid_x]
+    y_start = shifted_min_y_values[valid_x]
+    y_end = y_start + line_thickness
+
+    # 使用Numpy广播机制创建线条区域的索引
+    for x, start, end in zip(x_coords, y_start, y_end):
+        image[start:end, x, :3] = line_color  # 只修改RGB通道
+
+    # 计算整个图像的最低非透明点
+    lowest_y = np.max(min_y_values[min_y_values != -1]) if np.any(min_y_values != -1) else -1
+    # 绘制原最低非透明点处的线
+    cv2.line(image, (one_line_pos[0], lowest_y + 5), (one_line_pos[1], lowest_y + 5), line_color,
+             thickness=line_thickness)
+
+    _y = lowest_y + 18
+    if _y > image.shape[0]:  # 超过图片尺寸
+        _y = image.shape[0] - 5
+    return image, _y
+
+
+def clean_colors(img):
+    # 转成灰度图
+    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
+    return img
+
+
+def calculated_shadow_brightness(img: Image):
+    # 打开图片并转换为灰度模式
+    image = img.convert('L')
+    # 将图片数据转为numpy数组
+    image_data = np.array(image)
+    # 创建布尔掩码以识别非白色区域
+    non_white_mask = image_data < 252
+
+    # 使用掩码提取非白色像素的亮度值
+    non_white_values = image_data[non_white_mask]
+
+    # print(len(non_white_values),len(image_data))
+    # 如果存在非白色像素,则计算平均亮度;否则返回0
+    if len(non_white_values) > 0:
+        average_brightness = np.mean(non_white_values)
+    else:
+        average_brightness = 0  # 没有非白色像素时的情况
+
+    return average_brightness
+
+
+def levels_adjust(img, Shadow, Midtones, Highlight, OutShadow, OutHighlight, Dim):
+    # 色阶处理
+    # img 为cv2格式
+
+    # dim = 3的时候调节RGB三个分量, 0调节B,1调节G,2调节R
+    if Dim == 3:
+        mask_shadow = img < Shadow
+        img[mask_shadow] = Shadow
+        mask_Highlight = img > Highlight
+        img[mask_Highlight] = Highlight
+    else:
+        mask_shadow = img[..., Dim] < Shadow
+        img[mask_shadow] = Shadow
+        mask_Highlight = img[..., Dim] > Highlight
+        img[mask_Highlight] = Highlight
+
+    if Dim == 3:
+        Diff = Highlight - Shadow
+        rgbDiff = img - Shadow
+        clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
+        outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
+        data = np.array(outClRgb * 255, dtype='uint8')
+        img = data
+    else:
+        Diff = Highlight - Shadow
+        rgbDiff = img[..., Dim] - Shadow
+        clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
+        outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
+        data = np.array(outClRgb * 255, dtype='uint8')
+        img[..., Dim] = data
+    return img
+
+
+def calculate_average_brightness_opencv(img_gray, rows_to_check):
+    # 二值化的图片 CV对象
+    # 计算图片亮度
+    height, width = img_gray.shape
+
+    brightness_list = []
+    for row in rows_to_check:
+        if 0 <= row < height:
+            # 直接计算该行的平均亮度
+            row_data = img_gray[row, :]
+            average_brightness = np.mean(row_data)
+            brightness_list.append(average_brightness)
+        else:
+            print(f"警告:行号{row}超出图片范围,已跳过。")
+
+    return brightness_list

+ 481 - 0
python/service/generate_main_image/module_generate_main_image.py

@@ -0,0 +1,481 @@
+"""
+步骤:
+1、抠图
+2、色阶处理,保留阴影
+3、定位商品中心,并进行抠图
+4、缩放定位,并粘贴到中心位置
+5、粘贴水印
+"""
+import copy
+import time
+import cv2
+import numpy as np
+from PIL import Image, ImageEnhance
+import os
+from module.log.log import MyLogger
+
+if __name__ != '__main__':
+    import settings
+
+
+class GeneratePic(object):
+    def __init__(self):
+        self.logger = MyLogger()
+        pass
+
+    def to_resize(self, _im, width=None, high=None):
+        _im_x, _im_y = _im.size
+        if width and high:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(high)
+            re_x = int(_im_x * re_y / _im_y)
+        _im = _im.resize((re_x, re_y))
+        return _im
+
+    def clean_colors(self, img):
+        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
+        return img
+
+    def levels_adjust(self, img, Shadow, Midtones, Highlight, OutShadow, OutHighlight, Dim):
+        # print(img)
+        # dim = 3的时候调节RGB三个分量, 0调节B,1调节G,2调节R
+        if Dim == 3:
+            mask_shadow = img < Shadow
+            img[mask_shadow] = Shadow
+            mask_Highlight = img > Highlight
+            img[mask_Highlight] = Highlight
+        else:
+            mask_shadow = img[..., Dim] < Shadow
+            img[mask_shadow] = Shadow
+            mask_Highlight = img[..., Dim] > Highlight
+            img[mask_Highlight] = Highlight
+
+        if Dim == 3:
+            Diff = Highlight - Shadow
+            rgbDiff = img - Shadow
+            clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
+            outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
+            data = np.array(outClRgb * 255, dtype='uint8')
+            img = data
+        else:
+            Diff = Highlight - Shadow
+            rgbDiff = img[..., Dim] - Shadow
+            clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
+            outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
+            data = np.array(outClRgb * 255, dtype='uint8')
+            img[..., Dim] = data
+        return img
+
+    def calculate_average_brightness_opencv(self, img_gray, rows_to_check):
+        # 二值化的图片 CV对象
+        height, width = img_gray.shape
+
+        brightness_list = []
+        for row in rows_to_check:
+            if 0 <= row < height:
+                # 直接计算该行的平均亮度
+                row_data = img_gray[row, :]
+                average_brightness = np.mean(row_data)
+                brightness_list.append(average_brightness)
+            else:
+                print(f"警告:行号{row}超出图片范围,已跳过。")
+
+        return brightness_list
+
+    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, **kwargs):  # im 为cv对象
+        """
+        image_path:原始图
+        cut_image_path:抠图结果
+        out_path:输出文件夹
+        image_deal_mode:图片处理模式,1表示需要镜像处理
+        """
+
+        # ==========先进行剪切原图
+        im = Image.open(image_path)
+        old_x, old_y = im.size
+
+        cut_image = Image.open(cut_image_path)
+        try:
+            x1, y1, x2, y2 = cut_image.getbbox()
+            # im_shadow.crop((x1, y1, x2, y2)).show()# 切图
+        except BaseException as e:
+            print("抠图失败")
+            return False
+
+        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))  # 切图
+        cut_image = cut_image.crop((new_x1, new_y1, new_x2, new_y2))  # 切图
+
+        new_x, new_y = im.size
+        # ================自动色阶处理
+
+        _im = cv2.cvtColor(np.asarray(im), cv2.COLOR_RGB2BGR)
+        # 背景阴影
+        im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
+
+        s = time.time()
+        rows = [int(new_y * 19.8 / 20)]
+
+        # draw = ImageDraw.Draw(im)
+        # for row in rows:
+        #     draw.line((0, row, new_x, row), fill=128, width=3)
+        # im.show()
+
+        _im_shadow = copy.copy(im_shadow)
+        Midtones = 0.52
+        Highlight = 235
+        k = 20
+        while k:
+            k -= 1
+            Midtones += 0.1
+            Highlight -= 5
+            _im_shadow = self.levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight,
+                                            OutShadow=0,
+                                            OutHighlight=255, Dim=3)
+            brightness_list = self.calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows)
+            print(brightness_list)
+            if brightness_list[0] >= 254:
+                break
+
+        print(time.time() - s)
+
+        im_shadow = Image.fromarray(cv2.cvtColor(_im_shadow, cv2.COLOR_BGR2RGB))
+        im_shadow.paste(cut_image, (0, 0), cut_image)  # 把原图粘贴回去,避免色差
+
+        # 保存带有阴影的底图,没有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)
+
+        # im_shadow.show()
+        # 主图物体的缩放依据大小
+        if resize_mode is None:
+            im_shadow = self.to_resize(_im=im_shadow, width=1400, high=1400)
+            cut_image = self.to_resize(_im=cut_image, width=1400, high=1400)
+
+        elif resize_mode == 1:
+            im_shadow = self.to_resize(_im=im_shadow, width=1400, high=1400)
+            cut_image = self.to_resize(_im=cut_image, width=1400, high=1400)
+
+        elif resize_mode == 2:
+            if im_shadow.height <= im_shadow.width * 1.2:
+                im_shadow = self.to_resize(_im=im_shadow, width=650)
+                cut_image = self.to_resize(_im=cut_image, width=650)
+            else:
+                im_shadow = self.to_resize(_im=im_shadow, high=1400)
+                cut_image = self.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 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)
+
+        # image_bg.show()
+        image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG")
+        return True
+
+
+class GeneratePicPiJu(object):
+    def __init__(self):
+        pass
+
+    def calculate_average_brightness_opencv(self, img_gray, rows_to_check):
+        # 二值化的图片 CV对象
+        height, width = img_gray.shape
+
+        brightness_list = []
+        for row in rows_to_check:
+            if 0 <= row < height:
+                # 直接计算该行的平均亮度
+                row_data = img_gray[row, :]
+                average_brightness = np.mean(row_data)
+                brightness_list.append(average_brightness)
+            else:
+                print(f"警告:行号{row}超出图片范围,已跳过。")
+
+        return brightness_list
+
+    def to_resize(self, _im, width=None, high=None):
+        _im_x, _im_y = _im.size
+        if width and high:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(high)
+            re_x = int(_im_x * re_y / _im_y)
+        _im = _im.resize((re_x, re_y))
+        return _im
+
+    def run(self, image_path, cut_image_path, out_path, image_deal_mode=0, image_index=99,
+            out_pic_size=800, is_logo=True, out_process_path_1=None, out_process_path_2=None, max_box=None):  # im 为cv对象
+        """
+        image_path:原始图
+        cut_image_path:抠图结果
+        out_path:输出文件夹
+        image_deal_mode:图片处理模式,1表示需要镜像处理
+        """
+
+        # ==========先进行剪切原图
+        im = Image.open(image_path)
+        old_x, old_y = im.size
+
+        cut_image = Image.open(cut_image_path)
+        try:
+            x1, y1, x2, y2 = cut_image.getbbox()
+            # im_shadow.crop((x1, y1, x2, y2)).show()# 切图
+        except BaseException as e:
+            print("抠图失败")
+            return False
+
+        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))  # 切图
+        cut_image = cut_image.crop((new_x1, new_y1, new_x2, new_y2))  # 切图
+
+        new_x, new_y = im.size
+        # ================自动色阶处理
+
+        _im = cv2.cvtColor(np.asarray(im), cv2.COLOR_RGB2BGR)
+        # 背景阴影
+        im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
+
+        s = time.time()
+        rows = [int(new_y * 19.8 / 20)]
+
+        # draw = ImageDraw.Draw(im)
+        # for row in rows:
+        #     draw.line((0, row, new_x, row), fill=128, width=3)
+        # im.show()
+
+        _im_shadow = copy.copy(im_shadow)
+        Midtones = 0.52
+        Highlight = 235
+        k = 20
+        while k:
+            k -= 1
+            Midtones += 0.1
+            Highlight -= 5
+            _im_shadow = self.levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight,
+                                            OutShadow=0,
+                                            OutHighlight=255, Dim=3)
+            brightness_list = self.calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows)
+            print(brightness_list)
+            if brightness_list[0] >= 254:
+                break
+
+        print(time.time() - s)
+
+        im_shadow = Image.fromarray(cv2.cvtColor(_im_shadow, cv2.COLOR_BGR2RGB))
+        im_shadow.paste(cut_image, (0, 0), cut_image)  # 把原图粘贴回去,避免色差
+
+        # 保存带有阴影的底图,没有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)
+
+        # im_shadow.show()
+        if max_box:
+            im_shadow = self.to_resize(_im=im_shadow, width=max_box[0], high=max_box[1])
+            cut_image = self.to_resize(_im=cut_image, width=max_box[0], high=max_box[1])
+        else:
+            if image_index != 3:
+                im_shadow = self.to_resize(_im=im_shadow, width=1000, high=1000)
+                cut_image = self.to_resize(_im=cut_image, width=1000, high=1000)
+            else:
+                im_shadow = self.to_resize(_im=im_shadow, width=650)
+                cut_image = self.to_resize(_im=cut_image, width=650)
+
+        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 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)
+
+        # image_bg.show()
+        image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG")
+        return True
+
+    def increase_brightness(self, im, brightness_factor):
+        # 打开图片
+        # 将图片转换为RGB模式
+        # image = image.convert("RGBA")
+        # 获取图片的亮度值
+        enhancer = ImageEnhance.Brightness(im)
+        brightness = enhancer.enhance(brightness_factor)
+        return brightness
+
+    def clean_colors(self, img):
+        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
+        return img
+
+    def levels_adjust(self, img, Shadow, Midtones, Highlight, OutShadow, OutHighlight, Dim):
+        # print(img)
+        # dim = 3的时候调节RGB三个分量, 0调节B,1调节G,2调节R
+        if Dim == 3:
+            mask_shadow = img < Shadow
+            img[mask_shadow] = Shadow
+            mask_Highlight = img > Highlight
+            img[mask_Highlight] = Highlight
+        else:
+            mask_shadow = img[..., Dim] < Shadow
+            img[mask_shadow] = Shadow
+            mask_Highlight = img[..., Dim] > Highlight
+            img[mask_Highlight] = Highlight
+
+        if Dim == 3:
+            Diff = Highlight - Shadow
+            rgbDiff = img - Shadow
+            clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
+            outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
+            data = np.array(outClRgb * 255, dtype='uint8')
+            img = data
+        else:
+            Diff = Highlight - Shadow
+            rgbDiff = img[..., Dim] - Shadow
+            clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
+            outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
+            data = np.array(outClRgb * 255, dtype='uint8')
+            img[..., Dim] = data
+        return img
+
+
+if __name__ == '__main__':
+    # 测试皮具主图生成
+    for jpg_name in os.listdir("image"):
+        if "png" in jpg_name:
+            continue
+        file_name = os.path.splitext(jpg_name)[0]
+        original_image_path = r"image\{}.jpg".format(file_name)
+        original_move_bg_image_path = r"image\{}.png".format(file_name)
+        out_path = r"out\{}.jpg".format(file_name)
+        is_image_deal_mode = 1
+        resize_mode = 1
+
+        print(original_image_path)
+        GeneratePicPiJu().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=1600,
+                              is_logo=False,
+                              max_box=None
+                              )

+ 1 - 1
python/service/grenerate_main_image_test.py

@@ -1,7 +1,7 @@
 import os
 import copy
 import time
-from image_deal_base_func import *
+from .image_deal_base_func import *
 from PIL import Image, ImageDraw
 from blend_modes import multiply
 import os

+ 2 - 1
python/service/image_pic_deal.py

@@ -1,10 +1,11 @@
 """
 """
 
+
 import cv2
 import numpy as np
 from PIL import Image, ImageDraw, ImageFont
-from module.view_control.auto_deal_pics.data import DataModeAutoDealPics
+from .data import DataModeAutoDealPics
 
 
 class OnePicDeal(object):

+ 123 - 0
python/service/load_plugins.py

@@ -0,0 +1,123 @@
+import os
+import importlib.util
+import sys
+
+
+# import custom_plugins
+
+
+class LoadAllPlugins(object):
+    def __init__(self, windows):
+        self.windows = windows
+        self.ignore_files = ["get_remote_has256.py", "__init__.py"]
+        self.ignore_dirs = ["other_micropython_code", "__pycache__"]
+
+    def send_log(self, text):
+        if self.windows:
+            self.windows.send_log(text)
+
+    def load_plugins(self, plugin_dir):
+        """
+        递归加载插件目录下的所有包和模块。
+        """
+        plugin_modules = {}
+        # 获取插件目录的绝对路径
+        plugin_dir_abs = os.path.abspath(plugin_dir)
+        print("22 plugin_dir_abs", plugin_dir_abs)
+        # # 如果插件目录不在sys.path中,则添加它
+        if plugin_dir_abs not in sys.path:
+            sys.path.append(plugin_dir_abs)
+
+        def load_module_from_file(module_name, file_path):
+            try:
+                spec = importlib.util.spec_from_file_location(module_name, file_path)
+                module = importlib.util.module_from_spec(spec)
+                spec.loader.exec_module(module)
+                print(f"Loaded module Successful: {module_name}")
+                self.send_log(f"Loaded module Successful: {module_name}")
+                return module
+            except Exception as e:
+                print(f"Failed to load module {module_name}: {e}")
+                self.send_log(f"Failed to load module {module_name}: {e}")
+                return None
+
+        def traverse_directory(directory):
+            for root, dirs, files in os.walk(directory):
+                f = True
+                for i in self.ignore_dirs:
+                    if i in root:
+                        f = False
+                        break
+
+                if f is False:
+                    continue
+                # 确保当前目录被视为Python包
+                init_file = os.path.join(root, '__init__.py')
+                if not os.path.isfile(init_file):
+                    open(init_file, 'a').close()  # 创建空的 __init__.py 文件
+
+                # 加载当前目录下的所有 .py 文件(不包括 __init__.py)
+                for file in files:
+                    if file in self.ignore_files:
+                        continue
+                    if file.endswith('.py'):
+                        module_name = \
+                            os.path.relpath(os.path.join(root, file), plugin_dir).replace(os.sep, '.').rsplit('.', 1)[0]
+                        module = load_module_from_file(module_name, os.path.join(root, file))
+                        if module is not None:
+                            plugin_modules[module_name] = module
+
+        # 遍历插件目录,寻找所有的包和模块
+        traverse_directory(plugin_dir_abs)
+
+        return plugin_modules
+
+    def do_load(self, root):
+        plugin_b_dir = r"{}/custom_plugins".format(os.getcwd())
+        if plugin_b_dir not in sys.path:
+            sys.path.insert(0, plugin_b_dir)
+        plugins_dict = {"detail_template": {},
+                        "ampy_scribe": {},
+                        "generate_scribe": {},
+                        }
+
+        self.load_plugins(plugin_dir="{}\custom_plugins/plugins_mode".format(root))
+        plugins = self.load_plugins(plugin_dir="{}\custom_plugins/plugins".format(root))
+        for i, py_file in plugins.items():
+            print(i)
+            if hasattr(py_file, 'plugins_name'):
+                if py_file.plugins_name == "详情模板":
+                    company_name_list = py_file.company_name_list
+                    template_name = py_file.template_name
+                    # 一个模板文件可能适合多个不同的企业
+                    if "全部" in company_name_list:
+                        company_name_list = ["全部"]
+
+                    for company_name in company_name_list:
+                        if company_name not in plugins_dict["detail_template"]:
+                            plugins_dict["detail_template"][company_name] = {}
+                        plugins_dict["detail_template"][company_name][template_name] = py_file.DetailPicGet
+
+                if py_file.plugins_name == "AMPY插件":
+                    plugins_dict["ampy_scribe"]["AMPY插件"] = py_file.RemoteUpdate
+                # if py_file.plugins_name == "主图生成与上传":
+                #     plugins_dict["generate_scribe"]["主图生成与上传"] = py_file.AutoDealPics
+
+        return plugins_dict
+
+    def load(self):
+        print("Loading plugins...")
+        plugins_dict = {}
+        try:
+            plugins_dict = self.do_load(root=os.getcwd())
+        except BaseException as e:
+            print(e)
+            self.send_log("Loading plugins. error {}".format(e))
+        # print(plugins_dict)
+        # _class = plugins_dict["detail_template"]["小苏"]["xiaosushuoxie-1"]
+        # _class()
+        return plugins_dict
+
+
+if __name__ == '__main__':
+    LoadAllPlugins(None).load()

+ 512 - 0
python/service/manual_image_matching/goods_art_image_list.py

@@ -0,0 +1,512 @@
+import os.path
+import random
+import shutil
+import time
+
+from utils.utils_func import get_images, move_elements_inplace, check_path, get_cutout_image_info
+
+
+class GoodsArtImageList():
+    # select_clearn_all_sign = Signal(str)
+
+    def __init__(self, goods_art_no_folder_path, parent=None, windows=None,
+                 label_color="green", is_goods_art_no_folder=True):
+        super().__init__(parent)
+        # self.ui = Ui_Form()
+        # self.ui.setupUi(self)
+        self.windows = windows
+
+        # parent: QWidget
+        self.parent = parent
+        self.has_selected = False
+        # self.ui.label_5.setStyleSheet("background-color: {};".format(label_color))
+
+        self.goods_art_no_folder_path = goods_art_no_folder_path
+        self.folder_name = os.path.split(goods_art_no_folder_path)[1]
+        self.is_goods_art_no_folder = is_goods_art_no_folder
+        if is_goods_art_no_folder:
+            self.original_path = "{}/原始图".format(goods_art_no_folder_path)
+            self.cut_out_path = "{}/已扣图".format(goods_art_no_folder_path)
+        else:
+            self.original_path = goods_art_no_folder_path
+            self.cut_out_path = ""
+            # self.ui.del_folder.hide()
+
+        self.drag_start_position = None
+        # self.ui.widget.setMinimumSize(300, 200)
+        self.image_data_list = []
+        self.init_data()
+        # self.show_list()
+        # self.init()
+        # self.show()
+
+    def init(self):
+        self.setAcceptDrops(True)
+        self.ui.widget.setAcceptDrops(True)
+        self.ui.widget.dropEvent = self.list_dropEvent
+        self.ui.widget.dragEnterEvent = self.list_dragEnterEvent
+        self.ui.widget.dragMoveEvent = self.list_dragMoveEvent
+        self.ui.del_folder.mousePressEvent = self.del_folder
+        self.ui.open_folder.mousePressEvent = self.open_folder
+
+    # 删除文件夹
+    # def del_folder(self, *args):
+    #     if not self.is_goods_art_no_folder:
+    #         return
+    #     a = QMessageBox.question(self, '确认', '是否删除该文件夹',
+    #                              QMessageBox.Yes | QMessageBox.No)
+    #     if a != QMessageBox.Yes:
+    #         return
+
+    #     try:
+    #         shutil.rmtree(self.goods_art_no_folder_path)
+    #     except BaseException as e:
+    #         print("删除文件夹失败", e)
+    #     self.hide()
+    #     self.deleteLater()
+
+    # 打开本地文件夹
+    def open_folder(self, *args):
+        if os.path.exists(self.original_path):
+            os.startfile(self.original_path)
+
+    def set_new_parent(self, parent):
+        self.parent = parent
+        self.setParent(parent)
+
+    # def resizeEvent(self, event: QResizeEvent):
+    #     # print(self.size())
+    #     self.show_list()
+    #     # 当窗口大小发生变化时,更新标签文本
+    #     # 调用父类的resizeEvent以确保其他必要的调整也被执行
+    #     super().resizeEvent(event)
+
+    # 刷新展示列表
+    # def show_list(self):
+    #     self.ui.label.setText("文件夹:{}".format(self.folder_name))
+    #     if not self.image_data_list:
+    #         self.ui.widget.setMinimumHeight(20)
+    #         self.ui.widget.setMaximumHeight(20)
+    #         return
+    #     x, y = 0, 5
+    #     total_len = len(self.image_data_list)
+    #     for index, image_label in enumerate(self.image_data_list):
+    #         image_label: ImageLabel
+    #         image_label.image_index = index
+    #         image_label.move(x, y)
+    #         image_label.show_text()
+    #         image_label.show()
+    #         x += image_label.width() + 5
+    #         if x + image_label.width() > self.ui.widget.width():
+    #             x = 0
+    #             if total_len != index + 1:
+    #                 y += image_label.height() + 5
+    #     self.ui.widget.setMaximumHeight(y + image_label.height())
+    #     self.ui.widget.setMinimumHeight(y + image_label.height())
+    #     self.setMaximumHeight(y + image_label.height() + self.ui.label.height())
+
+    #     # 刷新界面命令
+    #     QApplication.processEvents()
+
+    # 初始化数据
+    def init_data(self):
+        # for index, image_data in enumerate(get_images(self.original_path)):
+        #     image_label = ImageLabel(text="",
+        #                              image_index=index,
+        #                              image_path=image_data["file_path"],
+        #                              image_name=image_data["file_name"],
+        #                              root_path=self.original_path,
+        #                              is_goods_list=True,
+        #                              parent=self.ui.widget,
+        #                              windows=self,
+        #                              top_windows=self.windows
+        #                              )
+        #     self.reconnect_sign(image_label, self)
+        #     self.image_data_list.append(image_label)
+        pass
+            # # 刷新界面命令
+            # QApplication.processEvents()
+
+    # 重新与当前父组件绑定信号
+    def reconnect_sign(self, image_label, windows):
+        try:
+            image_label.select_clearn_all_sign.disconnect()
+            image_label.selected_sign.disconnect()
+            image_label.drop_sign.disconnect()
+            image_label.drop_from_sign.disconnect()
+            image_label.delete_image_sign.disconnect()
+        except:
+            pass  # 如果没有连接则忽略错误
+
+        image_label.select_clearn_all_sign.connect(windows.clearn_all_label_select)
+        image_label.selected_sign.connect(windows.set_selected)
+        image_label.drop_sign.connect(windows.deal_image_label_to)
+        image_label.drop_from_sign.connect(windows.deal_image_label_from)
+        image_label.delete_image_sign.connect(windows.delete_image_items)
+
+    def set_image_label_parent(self, image_label):
+        image_label.set_new_parent(self.ui.widget)
+
+    # 批量删除图片
+    def delete_image_items(self, image_index_list: list):
+        image_index_list.sort(reverse=True)
+        # for image_index in image_index_list:
+        #     image_label: ImageLabel
+        #     image_label = self.image_data_list.pop(image_index)
+        #     image_label.del_image()
+        #     image_label.deleteLater()
+        # self.show_list()
+
+    # 设置有选择
+    def set_selected(self):
+        self.has_selected = True
+        self.select_clearn_all_sign.emit(self.original_path)
+
+    # 清除所有已选
+    def clearn_all_label_select(self, flag=False):
+        _f = False
+        if flag:
+            _f = True
+        else:
+            if self.has_selected:
+                _f = True
+
+        if _f:
+            for image_label in self.image_data_list:
+                # image_label: ImageLabel
+                # if image_label.selected:
+                image_label.set_selected(False)
+
+    # 手动排序
+    def re_sort_by_manual(self):
+        # 重新排序
+        pass
+
+    # 粘贴到事件;处理同文件夹内容
+    def deal_image_label_to(self, data, is_real_same_folder=False):
+        is_same_folder = data["is_same_folder"]
+        print(data)
+        # 来自同文件夹
+        if is_same_folder:
+            target_index = data["to_image_index"]
+            indices = data["from_image_index_list"]
+            if target_index in indices:
+                return
+            move_elements_inplace(lst=self.image_data_list, indices=indices, target_index=target_index,
+                                  is_real_same_folder=is_real_same_folder)
+            # 刷新文件名称
+            self.rename_image(target=self)
+            self.show_list()
+
+    # 处理不同文件夹事件
+    def deal_image_label_from(self, data):
+        # 如果是来源于不同的文件夹,则需要处理这个。
+        """
+        从原列表进行剔除
+        添加到新列表,并插入到指定位置
+        """
+        is_same_folder = data["is_same_folder"]
+        if is_same_folder is False:
+            from_image_index_list = data["from_image_index_list"]
+            from_image_index_list: list
+            from_image_index_list.sort(reverse=True)
+            to_source = data["to_source"]
+            from_source = data["from_source"]
+            to_source_len_image = len(to_source.image_data_list) - 1
+            to_image_index = data["to_image_index"]
+            indices = []
+            # 从末尾进行剔除,并添加到另外一个列表
+            temp = []
+            for from_index in from_image_index_list:
+                image_label = from_source.image_data_list.pop(from_index)
+                temp.append(image_label)
+
+            # 倒序处理
+            for image_label in temp[::-1]:
+                to_source_len_image += 1
+                to_source.image_data_list.append(image_label)
+                to_source.set_image_label_parent(image_label)
+                to_source.set_image_label_windows(image_label, to_source)
+                to_source.reconnect_sign(image_label, to_source)  # 重新绑定信号
+
+                # 设定父类
+                image_label.windows = to_source
+                indices.append(to_source_len_image)
+            # 目标文件夹处理
+            move_elements_inplace(lst=to_source.image_data_list, indices=indices, target_index=to_image_index)
+            self.dealMoveImage(to_source=to_source)
+
+            to_source.show_list()
+            # 刷新来源文件夹
+            from_source.show_list()
+
+            # 全部重命名
+            self.rename_image(target=to_source)
+
+            # 取消已选数据
+            to_source.has_selected = True
+            from_source.has_selected = True
+
+            to_source.clearn_all_label_select(flag=True)
+            from_source.clearn_all_label_select(flag=True)
+
+    def set_image_label_windows(self, image_label, windows):
+        image_label.set_image_label_windows(windows)
+
+    def dealMoveImage_beifen(self, to_source):
+        '''
+        从其他文件夹复制移动图片
+         参数例 sources = ['D:\\phpstudy_pro\\WWW\\auto_photo\\output\\2024-11-18/2222222/原始图/abc.jpg', D:\\phpstudy_pro\\WWW\\auto_photo\\output\\2024-11-18/2222222/原始图/ddd.jpg]
+            target = 'D:\\phpstudy_pro\\WWW\\auto_photo\\output\\2024-11-18/2222222/原始图'
+        '''
+        target = to_source.original_path
+        targetParent, _ = os.path.split(target)
+        for imageLabel in to_source.image_data_list:
+            if imageLabel.root_path == target:
+                continue
+            path, sourceFile = os.path.split(imageLabel.image_path)
+            sourceFileName, file_extension = os.path.splitext(sourceFile)
+            parentPath, _ = os.path.split(path)
+            sourceCutPath = '{}/原始图_已抠图/{}.png'.format(parentPath, sourceFileName)
+            newFileName = str(time.time()) + str(random.randint(1000000, 9999999))
+            targetName = target + '/' + newFileName + file_extension
+
+            if os.path.exists(imageLabel.image_path):
+                shutil.move(imageLabel.image_path, targetName)
+                self.upImageLabel(item=imageLabel, image_name=newFileName, image_path=targetName)
+                if os.path.exists(sourceCutPath) and os.path.exists(
+                        "{}/原始图_已抠图".format(targetParent)) and os.path.isdir("{}/原始图_已抠图".format(targetParent)):
+                    cutTargetPath = '{}/原始图_已抠图/{}.png'.format(targetParent, newFileName)
+                    shutil.move(sourceCutPath, cutTargetPath)
+
+        self.rename_image(to_source)
+
+    def dealMoveImage(self, to_source):
+        '''
+        从其他文件夹复制移动图片
+         参数例 sources = ['D:\\phpstudy_pro\\WWW\\auto_photo\\output\\2024-11-18/2222222/原始图/abc.jpg', D:\\phpstudy_pro\\WWW\\auto_photo\\output\\2024-11-18/2222222/原始图/ddd.jpg]
+            target = 'D:\\phpstudy_pro\\WWW\\auto_photo\\output\\2024-11-18/2222222/原始图'
+        '''
+        """
+        步骤:
+        1、获取来源的图片与已扣图图片
+        2、随机进行命名,(包括已扣图)
+        3、移动
+        以上过程需要考虑是否存在对应路径或图片
+        """
+        # original_path 为原始图路径
+        target_original_path = to_source.original_path
+        # target_original_root_path 为目标的原始图路径
+        target_goods_art_no_root_path, _ = os.path.split(target_original_path)
+
+        # target_cutout_root_path 为目标的原始图_已扣图路径
+        target_cutout_root_path = None
+        if "原始图" == target_original_path[-3:]:
+            target_cutout_root_path = target_original_path[:-3] + "原始图_已抠图"
+            if not os.path.exists(target_cutout_root_path):
+                # 创建一个已扣图路径
+                check_path(target_cutout_root_path)
+
+        # 轮询移动每个文件
+        for imageLabel in to_source.image_data_list:
+            # 来源为当前原图片文件夹,则忽略
+            if imageLabel.root_path == target_original_path:
+                continue
+
+            image_info_data = get_cutout_image_info(imageLabel.image_path)
+            if image_info_data is None:
+                continue
+
+            # 创建一个随机名称
+            new_file_name = str(int(time.time()) + random.randint(10000, 59999))
+            target_image_path = "{}/{}{}".format(target_original_path, new_file_name,
+                                                 image_info_data["source_file_extension"])
+            # 移动原始图
+            shutil.move(image_info_data["source_image_path"], target_image_path)
+            self.upImageLabel(item=imageLabel, image_name=new_file_name, image_path=target_image_path)
+
+            # 移动已扣图图片
+            print(image_info_data)
+            if target_cutout_root_path is None:
+                # 如果不存在已扣图文件夹,但是图片又存在,则需要进行文件删除
+                if image_info_data["cutout_image_path"]:
+                    if os.path.exists(image_info_data["cutout_image_path"]):
+                        os.remove(image_info_data["cutout_image_path"])
+                continue
+
+            if image_info_data["cutout_image_path"] is not None:
+                # 移动原始图
+                target_cutout_image_path = "{}/{}{}".format(target_cutout_root_path, new_file_name,
+                                                            image_info_data["cutout_image_file_extension"])
+                print("target_cutout_image_path", target_cutout_image_path)
+
+                shutil.move(image_info_data["cutout_image_path"], target_cutout_image_path)
+
+        # self.rename_image(to_source)
+
+    def rename_image_beifen(self, target):
+        """
+         重新排序,重新命名图片
+        """
+        target: GoodsArtImageList
+
+        path, _ = os.path.split(target.original_path)
+        _, artno = os.path.split(path)
+
+        imageNames = []
+        for index, item in enumerate(target.image_data_list):
+            imageNames.append(item.image_name)
+            _, image_extend = os.path.splitext(item.image_path)
+            targetPath = "{}/copy_tmp({}){}".format(item.root_path, index, image_extend)
+            if os.path.exists(item.image_path):
+                shutil.move(item.image_path, targetPath)
+                imageName = 'copy_tmp({})'.format(index)
+                self.upImageLabel(item=item, image_name=imageName, image_path=targetPath)
+
+        for index, item in enumerate(target.image_data_list):
+            _, image_extend = os.path.splitext(item.image_path)
+            imageName = '{}({})'.format(artno, index + 1)
+            targetPath = "{}/{}{}".format(item.root_path, imageName, image_extend)
+            if os.path.exists(item.image_path):
+                shutil.move(item.image_path, targetPath)
+                self.upImageLabel(item=item, image_name=imageName, image_path=targetPath)
+
+        self.dealCutImage(target, imageNames)
+
+    def rename_image_by_prefix(self, target, new_file_prefix):
+        pass
+        # for image_label in target.image_data_list:
+        #     image_label: ImageLabel
+        #     if not os.path.exists(image_label.image_path):
+        #         continue
+
+        #     if os.path.isdir(image_label.image_path):
+        #         continue
+
+        #     # 原始图信息
+        #     original_path, original_file = os.path.split(image_label.image_path)
+        #     original_file_name, original_file_extension = os.path.splitext(original_file)
+        #     new_file_name = "{}({})".format(new_file_prefix, image_label.image_index + 11)
+        #     new_original_file_name = "{}/{}{}".format(original_path,
+        #                                               new_file_name,
+        #                                               original_file_extension
+        #                                               )
+        #     # 重命名
+        #     os.rename(image_label.image_path, new_original_file_name)
+        #     self.upImageLabel(item=image_label, image_name=new_file_name, image_path=new_original_file_name)
+
+        #     # 处理已扣图的重命名
+        #     if original_path[-3:] != "原始图":
+        #         continue
+        #     cutout_original_path = original_path[:-3] + "原始图_已抠图"
+        #     cutout_image_path = "{}/{}.png".format(cutout_original_path, original_file_name)
+        #     if not os.path.exists(cutout_image_path):
+        #         continue
+
+        #     new_cutout_file_name = "{}/{}.png".format(cutout_original_path,
+        #                                               new_file_name,
+        #                                               )
+        #     # 重命名
+        #     os.rename(cutout_image_path, new_cutout_file_name)
+
+    def rename_image(self, target):
+        """
+         重新排序,重新命名图片
+        """
+        """
+        步骤:
+        1、先统一修改一个随机名称str(random.randint(60000, 99999))
+        2、再还原为指定名称
+        以上已扣图需要同步修改
+        """
+
+        target: GoodsArtImageList
+        # original_path 为原始图路径
+        # print("398            rename_image          ")
+        new_file_prefix = str(random.randint(60000, 99999))
+        self.rename_image_by_prefix(target, new_file_prefix)
+        self.rename_image_by_prefix(target, target.folder_name)
+
+    def dealCutImage(self, target, imageNames):
+        path, _ = os.path.split(target.original_path)
+        _, artno = os.path.split(path)
+        cutout_path = '{}/原始图_已抠图'.format(path)
+        if not os.path.exists(cutout_path):
+            return None
+
+        cutImages = self.getDirImageList(cutout_path)
+        for index, item in enumerate(cutImages):
+            if item['image_name'] not in imageNames:
+                shutil.move(item['image_path'], '{}/原始图_已抠图/remove{}.png'.format(path, index))
+
+        for index, item in enumerate(imageNames):
+            if os.path.exists('{}/原始图_已抠图/{}.png'.format(path, item)):
+                shutil.move('{}/原始图_已抠图/{}.png'.format(path, item),
+                            '{}/原始图_已抠图/copy_tmp({}).png'.format(path, index))
+                imageNames[index] = 'copy_tmp({})'.format(index)
+
+        for index, item in enumerate(imageNames):
+            if os.path.exists('{}/原始图_已抠图/{}.png'.format(path, item)):
+                shutil.move('{}/原始图_已抠图/{}.png'.format(path, item),
+                            '{}/原始图_已抠图/{}({}).png'.format(path, artno, index + 1))
+
+    def upImageLabel(self, item, image_name, image_path):
+        item.text = image_name
+        item.image_name = image_name
+        item.image_path = image_path
+        item.root_path, _ = os.path.split(image_path)
+        item.show_text()
+
+    def getDirImageList(self, path):
+        list = os.listdir(path)
+        result = []
+        for val in list:
+            name, _ = os.path.splitext(val)
+            result.append({
+                'image_path': '{}/{}'.format(path, val),
+                'image_name': name
+            })
+        return result
+        # print(item.image_path)
+
+    # 有且,只有目标粘贴位置才会触发该方法;粘贴到空白位置
+    def list_dropEvent(self, event):
+        # mime = event.mimeData()
+        pass
+        # if mime.hasFormat('application/x-draggable-labels'):
+        #     return_data = {"is_same_folder": True,
+        #                    "from_image_index_list": [],
+        #                    "from_source": "",
+        #                    "to_image_index": 0,
+        #                    "to_source": "",
+        #                    }
+        #     byte_array = mime.data('application/x-draggable-labels')
+        #     stream = QDataStream(byte_array, QIODevice.ReadOnly)
+        #     while not stream.atEnd():
+        #         image_index = stream.readInt32()
+        #         return_data["from_image_index_list"].append(image_index)
+
+        #     from_source = event.source()
+        #     return_data["from_source"] = from_source.windows
+        #     return_data["to_source"] = self
+        #     return_data["to_image_index"] = len(self.image_data_list)  # 添加到末尾
+
+        #     if from_source.windows == self:
+        #         return_data["is_same_folder"] = True
+        #         print("粘贴到末尾,同文件夹")
+        #     else:
+        #         return_data["is_same_folder"] = False
+        #         print("粘贴到末尾,不同文件夹")
+
+        #     if return_data["is_same_folder"] is False:
+        #         # 不同文件夹处理,处理发送到来源文件夹的操作
+        #         self.deal_image_label_from(return_data)
+        #     else:
+        #         # 同文件夹处理,发送粘贴的信号
+        #         self.deal_image_label_to(return_data, is_real_same_folder=True)
+
+    def list_dragEnterEvent(self, event):
+        event.acceptProposedAction()
+
+    def list_dragMoveEvent(self, event):
+        event.acceptProposedAction()

+ 332 - 0
python/service/manual_image_matching/image_label.py

@@ -0,0 +1,332 @@
+import json
+import os.path
+
+from import_qt_mode import *
+from UI.manual_image_matching.image_items import Ui_Form as Ui_Image_Items
+from PIL import Image
+import rawpy, io
+
+
+class ImageLabel(QLabel, Ui_Image_Items):
+    drop_sign = Signal(dict)  # 粘贴的位置
+    drop_from_sign = Signal(dict)  # 粘贴来源的位置
+    select_clearn_all_sign = Signal()
+    selected_sign = Signal()  # 有选中
+    delete_image_sign = Signal(list) # 删除通知
+
+    def __init__(self, text, image_index, image_path, image_name, root_path, is_goods_list, parent=None, windows=None,
+                 top_windows=None):
+        super().__init__(parent)
+        self.setupUi(self)
+        # self.setFixedSize(150, 160)
+        # self.ui_image.setFixedSize(150, 150)
+        self.windows = windows
+        self.top_windows = top_windows
+        self.parent = parent
+        self.text = image_name
+        self.image_index = image_index
+        self.image_path = image_path
+        self.image_name = image_name
+        self.root_path = root_path
+        self.ui_text.setText(self.text)
+
+        # 是图片列表还是普通列表
+        self.is_goods_list = is_goods_list
+        self.icon = None
+        self.set_label_image()
+        self.setMouseTracking(True)
+        self.drag_start_position = None
+        self.selected = False
+        self.setAcceptDrops(True)
+
+    def set_image_label_windows(self,windows):
+        self.windows = windows
+
+    def show_text(self):
+        self.ui_text.setText(self.text)
+
+    def crop_to_square(self, img):
+        # 打开图片
+        width, height = img.size  # 获取原始图片的宽度和高度
+        if width == height:
+            return img
+        if width > height:
+            _x = int((width - height) / 2)
+            img = img.crop((_x, 0, width - _x, height))
+        else:
+            _y = int((height - width) / 2)
+            img = img.crop((0, _y, width, height - _y))
+        return img
+
+    def set_new_parent(self, parent):
+        self.parent = parent
+        self.setParent(parent)
+
+    def show_big_image(self):
+        if self.top_windows is not None:
+            if self.icon:
+                self.top_windows.show_big_image(self.icon)
+
+    def set_label_image2(self):
+        try:
+            w, h = self.ui_image.width() * 2, self.ui_image.height() * 2
+            if "CR2" == self.image_path[-3:]:
+                _img_raw = Image.open(self.image_path)
+            else:
+                if "jpg" == self.image_path[-3:] or "png" == self.image_path[-3:]:
+                    _img_raw = Image.open(self.image_path)
+                else:
+                    with rawpy.imread(self.image_path) as raw:
+                        raw_image = raw.postprocess()
+                    _img_raw = Image.fromarray(raw_image)
+
+            size = (w, int(_img_raw.height * w / _img_raw.width))
+            _img_raw = _img_raw.resize(size)
+            _img_raw = _img_raw.convert("RGB")
+
+            bytes_io = io.BytesIO()
+            _img_raw.save(bytes_io, "JPEG")
+
+            icon = QPixmap()
+            icon.loadFromData(bytes_io.getvalue())
+            self.icon = icon
+            icon = icon.scaled(
+                self.ui_image.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
+            )
+            self.ui_image.setPixmap(icon)
+        except:
+            pass
+
+    def set_label_image(self):
+        try:
+            w, h = self.ui_image.width() * 2, self.ui_image.height() * 2
+            if "CR2" == self.image_path[-3:]:
+                _img_raw = Image.open(self.image_path)
+            else:
+                if "jpg" == self.image_path[-3:] or "png" == self.image_path[-3:]:
+                    _img_raw = Image.open(self.image_path)
+                else:
+                    with rawpy.imread(self.image_path) as raw:
+                        raw_image = raw.postprocess()
+                    _img_raw = Image.fromarray(raw_image)
+
+            # 取缩略图
+            _img_raw.thumbnail((w, int(_img_raw.height * w / _img_raw.width)))
+
+            size = (w, int(_img_raw.height * w / _img_raw.width))
+            _img_raw = _img_raw.resize(size)
+            _img_raw = _img_raw.convert("RGB")
+
+            bytes_io = io.BytesIO()
+            _img_raw.save(bytes_io, "JPEG")
+
+            icon = QPixmap()
+            icon.loadFromData(bytes_io.getvalue())
+            self.icon = icon
+            icon = icon.scaled(
+                self.ui_image.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
+            )
+            self.ui_image.setPixmap(icon)
+        except:
+            pass
+
+    def get_image_orientation(self, img):
+        # 获取EXIF数据
+        exif = img._getexif()
+        if exif is not None:
+            # EXIF标签274对应的是Orientation
+            orientation = exif.get(0x0112)
+            if orientation == 2:
+                # 水平翻转
+                img = img.transpose(Image.FLIP_LEFT_RIGHT)
+            elif orientation == 3:
+                # 旋转180度
+                img = img.rotate(180, expand=True)
+            elif orientation == 4:
+                # 垂直翻转
+                img = img.transpose(Image.FLIP_TOP_BOTTOM)
+            elif orientation == 5:
+                # 水平翻转后顺时针旋转90度
+                img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_270)
+            elif orientation == 6:
+                # 顺时针旋转90度
+                img = img.transpose(Image.ROTATE_270)
+            elif orientation == 7:
+                # 水平翻转后逆时针旋转90度
+                img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_90)
+            elif orientation == 8:
+                # 逆时针旋转90度
+                img = img.transpose(Image.ROTATE_90)
+            else:
+                pass
+
+        return img
+
+    def set_selected(self, selected):
+        self.selected = selected
+        self.setStyleSheet("background-color: yellow;" if selected else "background-color: white;")
+
+    def mouseMoveEvent(self, event):
+        if not (event.buttons() & Qt.LeftButton) or self.drag_start_position is None:
+            return
+
+        if (event.pos() - self.drag_start_position).manhattanLength() < QApplication.startDragDistance():
+            return
+
+        print("Starting drag...{}".format(self.image_name))  # 调试信息
+
+        # 创建拖拽任务
+        drag = QDrag(self)
+        mime_data = QMimeData()
+
+        # 收集所有选中的控件的数据
+        data = QByteArray()
+        stream = QDataStream(data, QIODevice.WriteOnly)
+
+        # 写入图片索引
+        for widget in self.parent.findChildren(ImageLabel):
+            if widget.selected:
+                if widget != self:
+                    stream.writeInt32(widget.image_index)
+
+        # 设置当前为选中
+        self.set_selected(True)
+
+        stream.writeInt32(self.image_index)
+
+        mime_data.setData('application/x-draggable-labels', data)
+        drag.setMimeData(mime_data)
+
+        # 可选:设置一个图标或快照来表示正在拖拽的对象
+        pixmap = QPixmap(self.size())
+        self.render(pixmap)
+        drag.setPixmap(pixmap)
+
+        # 设置热点为鼠标点击位置相对窗口的位置
+        hot_spot = event.position().toPoint()
+        drag.setHotSpot(hot_spot)
+
+        # 开始拖拽操作
+        result = drag.exec(Qt.MoveAction)
+        if result == Qt.MoveAction:
+            self.setParent(None)  # 如果成功移动,则从原位置移除
+            self.hide()
+
+        self.drag_start_position = None
+        print("end drag...{}".format(self.image_name))  # 调试信息
+
+    def mouseReleaseEvent(self, event):
+        super().mouseReleaseEvent(event)
+        # print("Mouse released.")  # 调试信息
+        self.drag_start_position = None
+
+    # 清除所有已选
+    def clear_all(self):
+        self.select_clearn_all_sign.emit()
+
+    def mousePressEvent(self, event):
+        if event.button() == Qt.LeftButton:
+            self.show_big_image()
+            self.drag_start_position = event.pos()
+            self.selected_sign.emit()  # 发送单击事件给父窗口
+            if event.modifiers() & Qt.ControlModifier:
+                # 如果按下了 Ctrl 键,则进行复选
+                self.set_selected(not self.selected)
+            else:
+                self.clear_all()
+                self.set_selected(not self.selected)
+
+    def dragEnterEvent(self, event):
+        event.acceptProposedAction()
+
+    def dragMoveEvent(self, event):
+        event.acceptProposedAction()
+
+    # 粘贴来源的位置,发送信号
+    def drop_from_event(self, return_data):
+        self.drop_from_sign.emit(return_data)
+
+    def dropEvent(self, event):
+        mime = event.mimeData()
+        if mime.hasFormat('application/x-draggable-labels'):
+
+            return_data = {"is_same_folder": True,
+                           "from_image_index_list": [],
+                           "from_source": "",
+                           "to_image_index": 0,
+                           "to_source": "",
+                           }
+            byte_array = mime.data('application/x-draggable-labels')
+            stream = QDataStream(byte_array, QIODevice.ReadOnly)
+            while not stream.atEnd():
+                image_index = stream.readInt32()
+                return_data["from_image_index_list"].append(image_index)
+
+            from_source = event.source()
+            return_data["from_source"] = from_source.windows
+            return_data["to_source"] = self.windows
+            return_data["to_image_index"] = self.image_index
+
+            if from_source.windows == self.windows:
+                return_data["is_same_folder"] = True
+                print("同文件夹")
+            else:
+                return_data["is_same_folder"] = False
+                print("不同文件夹")
+
+            if return_data["is_same_folder"] is False:
+                # 不同文件夹处理,处理发送到来源文件夹的操作
+                from_source.drop_from_event(return_data)
+            else:
+                # 同文件夹处理,发送粘贴的信号
+                self.drop_sign.emit(return_data)
+
+    def contextMenuEvent(self, event):
+        # 创建一个QMenu对象
+        menu = QMenu(self)
+
+        # 添加菜单项
+        delete_action = menu.addAction("删除图片")
+
+        # 连接菜单项到槽函数
+        action = menu.exec(self.mapToGlobal(event.pos()))
+        if action == delete_action:
+            self.delete_selected_item()
+
+    def delete_selected_item(self):
+        # 获取当前选中的项并删除它
+        print("delete_selected_item")
+        image_index_list = [self.image_index]
+        # 写入图片索引
+        for widget in self.parent.findChildren(ImageLabel):
+            if widget.selected:
+                if widget != self:
+                    image_index_list.append(widget.image_index)
+
+        if image_index_list:
+            self.delete_image_sign.emit(image_index_list)
+
+    def del_image(self):
+        """
+        删除图片
+        """
+        parentPath, _ = os.path.split(self.root_path)
+        cutImagePath = "{}/原始图_已抠图/{}.png".format(parentPath, self.image_name)
+        f = False
+        try:
+            if os.path.exists(self.image_path):
+                os.remove(self.image_path)
+                f = True
+
+            if os.path.exists(cutImagePath):
+                os.remove(cutImagePath)
+                f = True
+
+        except FileNotFoundError:
+            print(f"删除文件失败.")
+        except Exception as e:
+            print(f"发生错误: {e}")
+
+        if f:
+            self.hide()
+            del self

+ 153 - 0
python/service/manual_image_matching/m_image_matching_cotrol.py

@@ -0,0 +1,153 @@
+import os
+from import_qt_mode import *
+from UI.manual_image_matching.manual_image_matching_main import Ui_Form
+from module.view_control.manual_image_matching.m_image_matching_data import MatchingData
+from module.view_control.manual_image_matching.goods_art_image_list import GoodsArtImageList
+from module.view_control.MineQWidget import MineQWidget, LoadingMask
+
+
+class MainMatchingWindow(MineQWidget, QWidget):
+    mask_load_sign = Signal(bool)
+
+    def __init__(self, root_path=None):
+        super().__init__()
+        self.ui = Ui_Form()
+        self.ui.setupUi(self)
+        # 安装事件过滤器
+        self.installEventFilter(self)
+        self.ui.splitter.setSizes([900, 450])
+
+        # 移除 Qt.WindowStaysOnTopHint 标志以取消置顶
+        self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
+        # 默认最大化显示窗口
+        self.showMaximized()
+
+        self.matching_data = MatchingData()
+        if not root_path:
+            # root_path = r"D:\phpstudy_pro\WWW\auto_photo\output\2024-11-18"
+            root_path = r"D:\MyDocuments\PythonCode\MyPython\red_dragonfly\deal_pics\auto_capture_V2\auto_photo\output\2024-12-20-2 - 副本"
+
+        self.root_path = root_path
+        print(self.size())
+        # 加载图片
+        self.state = 0
+        self.__mask = LoadingMask(self)
+        self.mask_load_sign.connect(self.mask_load)
+        self.installEventFilter(self.__mask)
+        self.__mask.is_end_sign.connect(self.hide_mask)
+
+        QTimer.singleShot(1000, self.load_images)
+        self.ui.select_path.clicked.connect(self.selectPath)
+        self.show()
+
+    def mask_load(self, flag):
+        print("mask_load")
+        if flag:
+            self.__mask.show()
+        else:
+            self.__mask.close()
+
+    def load_images(self):
+        self.show_all_goods_list(self.root_path)
+
+    def show_all_goods_list(self, root_path):
+        # for i in self.ui.action_widget_list.findChildren(GoodsArtImageList):
+        #     i.deleteLater()
+
+
+
+        self.mask_load_sign.emit(True)
+        for data in self.matching_data.get_all_folder(root_path):
+            # folder_name = data["folder_name"]
+            # original_path = data["original_path"]
+            # cut_out_path = data["cut_out_path"]
+            goods_art_image_list = GoodsArtImageList(goods_art_no_folder_path=data["goods_art_no_folder_path"],
+                                                     parent=self.ui.action_widget_list,
+                                                     windows=self,
+                                                     label_color = data["label_color"],
+                                                     )
+            self.ui.verticalLayout_4.insertWidget(self.ui.verticalLayout_4.count() - 1, goods_art_image_list)
+            goods_art_image_list.select_clearn_all_sign.connect(self.clearn_all_label_select)
+
+        self.mask_load_sign.emit(False)
+
+    # 清除所有已选
+    def clearn_all_label_select(self, original_path):
+        for goods_art_image_list_item in self.ui.action_widget_list.findChildren(GoodsArtImageList):
+            goods_art_image_list_item: GoodsArtImageList
+            if goods_art_image_list_item.original_path != original_path:
+                if goods_art_image_list_item.has_selected:
+                    goods_art_image_list_item.clearn_all_label_select()
+                    goods_art_image_list_item.has_selected = False
+
+        for goods_art_image_list_item in self.ui.action_widget_list_2.findChildren(GoodsArtImageList):
+            goods_art_image_list_item: GoodsArtImageList
+            if goods_art_image_list_item.original_path != original_path:
+                if goods_art_image_list_item.has_selected:
+                    goods_art_image_list_item.clearn_all_label_select()
+                    goods_art_image_list_item.has_selected = False
+
+    def selectPath(self):
+        # a = QMessageBox.question(
+        #     self,
+        #     "继续",
+        #     "如选择的文件夹图片很多,则此处会非常耗时",
+        #     QMessageBox.Yes | QMessageBox.No,
+        # )
+        # if a != QMessageBox.Yes:
+        #     return
+
+        path = QFileDialog.getExistingDirectory(self, "选择文件夹", "")
+        if not path:
+            return
+        if os.path.isfile(path):
+            return
+
+        _, folder_name = os.path.split(path)
+
+        for widget in self.ui.action_widget_list_2.findChildren(GoodsArtImageList):
+            widget.deleteLater()
+        self.mask_load_sign.emit(True)
+        goods_art_image_list = GoodsArtImageList(goods_art_no_folder_path=path,
+                                                 parent=self.ui.action_widget_list_2,
+                                                 is_goods_art_no_folder=False,
+                                                 )
+        goods_art_image_list.select_clearn_all_sign.connect(self.clearn_all_label_select)
+        self.ui.verticalLayout_5.insertWidget(self.ui.verticalLayout_5.count() - 1, goods_art_image_list)
+        self.mask_load_sign.emit(False)
+
+    def show_big_image(self, icon):
+        icon = icon.scaled(self.ui.show_big_image.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
+        self.ui.show_big_image.setPixmap(icon)
+
+    def eventFilter(self, obj, event):
+        if obj == self and event.type() == QEvent.WindowStateChange:
+            if self.isMaximized():
+                print("检测到窗口最大化")
+                QTimer.singleShot(1000, self.show_again)
+        return super().eventFilter(obj, event)
+
+    def show_again(self, *args):
+        print("检测到窗口最大化2")
+        print(self.size())
+        self.updateGeometry()
+        # self.resizeEvent(QResizeEvent(self.size(), QSize()))
+        # self.adjustSize()
+        self.ui.action_widget_list.updateGeometry()
+        print(self.size())
+
+        for i in self.ui.action_widget_list.findChildren(GoodsArtImageList):
+            i.repaint()
+        for i in self.ui.action_widget_list_2.findChildren(GoodsArtImageList):
+            i.repaint()
+
+    def resizeEvent(self, event):
+        super().resizeEvent(event)
+
+
+if __name__ == '__main__':
+    app = QApplication([])
+    window = MainMatchingWindow()
+    window.resize(1000, 800)
+    window.show()
+    app.exec()

+ 42 - 0
python/service/manual_image_matching/m_image_matching_data.py

@@ -0,0 +1,42 @@
+import os
+
+from utils.utils_func import list_dir
+
+
+class MatchingData(object):
+
+    def __init__(self):
+        pass
+
+    def get_all_folder(self, root_path):
+        folder_list = []
+        last_goods_no = ""
+        last_label_color = "green"
+        if os.path.exists(root_path):
+            all_folder = list_dir(root_path)
+            for folder_name in all_folder:
+                # original_path = "{}/{}/800x800".format(root_path, folder_name)
+                original_path = "{}/{}/原始图".format(root_path, folder_name)
+                cut_out_path = "{}/{}/原始图_已抠图".format(root_path, folder_name)
+                if os.path.exists(original_path) and os.path.exists(cut_out_path):
+                    if folder_name[:-1] == last_goods_no:
+                        label_color = last_label_color
+                    else:
+                        if last_label_color == "green":
+                            last_label_color = "blue"
+                        else:
+                            last_label_color = "green"
+                        last_goods_no = folder_name[:-1]
+                        label_color = last_label_color
+
+                    folder_list.append(
+                        {"root_path": root_path,
+                         "goods_art_no_folder_path": "{}/{}".format(root_path, folder_name),
+                         "original_path": original_path,
+                         "folder_name": folder_name,
+                         "cut_out_path": cut_out_path,
+                         "label_color": label_color,
+                         }
+                    )
+
+        return folder_list

+ 1046 - 0
python/service/match_and_cutout_mode_control/base_deal_image_v2.py

@@ -0,0 +1,1046 @@
+import hashlib
+import json
+import os
+import shutil
+import time
+
+# from multiprocessing import Process, Queue
+import exifread
+import requests
+
+import settings
+from ..image_pic_deal import OnePicDeal
+from ..generate_goods_art_no_table.module_generate_goods_art_no_table import GenerateGoodsArtNoTable
+# from module.view_control.generate_main_image.module_generate_main_image import GeneratePicPiJu, GeneratePic
+from ..generate_main_image.grenerate_main_image_test import GeneratePic
+from ..matching_photos.data import DataModeMatchPhoto
+from collections import defaultdict
+from threading import Lock
+from ..remove_bg_ali import RemoveBgALi
+from PIL import Image
+from ..pic_deal import Picture
+from natsort import ns, natsorted
+from ..deal_cutout import DealCutout
+from ..data import DataModeAutoDealPics
+
+# from import_qt_mode import *
+
+_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):
+        # 对所有缺失已抠图的进行抠图处理
+        print("1windows.state", windows.state)
+        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,
+                              )
+        print("2windows.state", windows.state)
+        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:
+                    print("54  windows.state   不为1,", windows.state)
+                    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
+                )
+
+            # todo 主图生成处理,使用进程处理耗性能操作
+            # 创建输入和输出队列
+
+            # input_queue = Queue()
+            # output_queue = Queue()
+
+            # kwargs = {"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,
+            #           "input_queue": input_queue,
+            #           "output_queue": output_queue,
+            #           }
+            #
+            # p = Process(target=generate_pic.run, kwargs=kwargs)
+            # # 启动子进程
+            # p.start()
+            # # 等待子进程结束
+            # p.join()
+            #
+            # if output_queue.empty():
+            #     is_successful = False
+            # else:
+            #     print("output_queue.get():", output_queue.get())
+
+            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 calculate_image_md5(self, filepath):
+        # image = Image.open(filepath)
+        # image_data = image.getdata()
+        # md5hash = hashlib.md5()
+        # md5hash.update(image_data)
+        # md5_value = md5hash.hexdigest()
+        # image.close()
+        # return md5_value
+        md5_hash = hashlib.md5()
+        with Image.open(filepath) as image:
+            image_data = image.tobytes()
+            md5_hash.update(image_data)
+        return md5_hash.hexdigest()
+
+    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()
+                            im = remove_pic_ins.get_image_cut(file_path=original_image_path,
+                                                              out_file_path=original_move_bg_image_path)
+                            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 + "/历史"):
+            callback_func('文件夹创建失败')
+            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("与数据库匹配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))
+                    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("没有任何匹配的图片~")
+            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)
+
+            # 重命名并进行移动
+            self.move_images(goods_art_no, goods_art_no_path, original_image_path)  # 货号、货号文件路径、原始图路径
+
+            # self.progress_sign.emit({"type": "移动原始图片", "progress_bar_value": int(n / total_num * 100)})
+            # self.show_progress_detail("货号{} 相关文件夹创建完成,已移动原图~".format(goods_art_no))
+            callback_func("货号{} 相关文件夹创建完成,已移动原图~".format(goods_art_no))
+
+        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)

+ 1219 - 0
python/service/match_and_cutout_mode_control/module_matching_photos_v2.py

@@ -0,0 +1,1219 @@
+# from UI.matching_photos.matching_photos import Ui_Form as matching_photos_Ui_Form
+import json
+import os
+
+from UI.matching_photos.matching_and_cutout_photos import (
+    Ui_Form as matching_photos_Ui_Form,
+)
+
+from import_qt_mode import *
+
+import settings
+from collections import defaultdict
+
+from module.view_control.match_and_cutout_mode_control.base_deal_image_v2 import BaseDealImage
+from module.view_control.generate_goods_art_no_table.module_generate_goods_art_no_table import GenerateGoodsArtNoTable
+from module.view_control.matching_photos.data import DataModeMatchPhoto
+from module.view_control.manual_image_matching.m_image_matching_cotrol import MainMatchingWindow
+
+from PySide6.QtGui import QPixmap
+from PIL import Image
+import io
+from functools import partial
+import threading
+import time
+from module.base_mode.pic_deal import PicDeal
+from module.base_mode.base import *
+from module.view_control.MineQWidget import MineQWidget, DialogShow, WorkerOneThread
+from threading import Event
+from UI.matching_photos.temp_item import Ui_Form as UI_temp_item
+from concurrent.futures import ThreadPoolExecutor
+import concurrent.futures
+
+"""
+照片自动货号匹配 将图片放置在指定文件夹下,并自动对应不同的货号进行整理
+"""
+
+
+class Communicate(QObject):
+    # 定义一个信号用于请求显示对话框
+    show_dialog_signal = Signal()
+    # 定义一个信号用于返回对话框的结果
+    dialog_result_signal = Signal(str)
+
+
+class MatchingPhotos(MineQWidget):
+    progress_sign = Signal(dict)
+    info_sign = Signal(str)
+    text_show = Signal(str)
+    show_dialog_sign = Signal(dict)
+    change_state_sign = Signal(int)
+
+    def __init__(self):
+        super().__init__()
+        self.image_dir = settings.PhotoImageDir
+        self.ui = matching_photos_Ui_Form()
+        self.ui.setupUi(self)
+        # 0禁用  1进行中  2已结束
+        self.state = 2
+        self.event = Event()
+        self.goods_images_count_dict = defaultdict(int)
+        self.data_match_photo = DataModeMatchPhoto()
+        self.excel_path = ""
+        # 上次使用的详情模板
+        self.last_temp = settings.MATCHING_LAST_TEMP
+        # 指定单独处理的颜色文件夹
+        self.special_goods_art_no_folder = []
+
+        self.communicator = Communicate()
+        self.dialog_result = ""
+        self.communicator.dialog_result_signal.connect(self.on_dialog_result)
+        self.event_dialog = Event()
+        self.show_dialog_sign.connect(self.show_dialog)
+
+        # 改变状态
+        self.change_state_sign.connect(self.set_state)
+
+        self.init()
+        self.show()
+
+    def init(self):
+        # 文本
+        self.ui.textBrowser.clear()
+        text = """请将capture One 加工后的图片放在指定目录下(目录为当前文件夹下的{}/data文件夹),务必确保导出的图片有拍摄时间信息。""".format(os.getcwd())
+        self.ui.textBrowser.append(text)
+        self.ui.tabWidget.setCurrentIndex(0)
+        # 页面视图1
+        self.ui.stackedWidget.setCurrentIndex(0)
+        self.text_show.connect(self.append_text_to_browser)
+        self.show_img_dir()
+        self.ui.select_path.mousePressEvent = self.change_img_dir
+        self.ui.progressBar.setValue(0)
+        self.ui.progressBar.setMaximum(100)
+        self.ui.progressBar.setValue(0)
+        self.ui.progressBar.hide()
+        self.ui.line_image_pos.setText(settings.cutimage_dict['imageorder'])
+        self.ui.need_resize.setText(settings.cutimage_dict['resize_image_view'])
+
+        self.ui.check_image_total.setChecked(bool(settings.cutimage_dict['is_check_number']))
+        self.ui.check_is_pass.setChecked(bool(settings.cutimage_dict['is_filter']))
+        print(settings.cutimage_dict)
+        if settings.DEFAULT_CUTOUT_MODE == '普通抠图':
+            self.ui.cutout_nomal.setChecked(True)
+        elif settings.DEFAULT_CUTOUT_MODE == ' 精细化抠图':
+            self.ui.cutout_exquisite.setChecked(True)
+
+        if not bool(settings.IsExquisiteMode):
+            self.ui.cutout_exquisite.setEnabled(False)
+
+        # 展示是否指定内容抠图
+        self.ui.is_do_other.toggled.connect(self.change_do_special_treatment)
+        self.change_do_special_treatment()
+
+        self.progress_sign.connect(self.show_progress)
+        self.ui.run.clicked.connect(self.run_image_cut)  # 抠图与主图加工
+        self.ui.run_2.clicked.connect(self.run_matching)  # 匹配图片
+        self.ui.back.clicked.connect(self.back)
+        self.ui.stop.clicked.connect(self.to_stop)
+
+        # 页面视图2
+        self.ui.back.hide()
+        self.set_logo_selected()
+
+        # 手动匹配图片
+        self.ui.manual_matching_pic.mousePressEvent = self.manual_matching_pic_mousePressEvent
+
+    # 暂停
+    def to_stop(self, *args):
+        if self.state == 1:
+            self.set_state(state_value=99)
+
+    # 切换是否特殊处理的开关
+    def change_do_special_treatment(self, *args):
+        if self.ui.is_do_other.isChecked():
+            self.ui.show_deal_condition.show()
+        else:
+            self.ui.show_deal_condition.hide()
+
+    # 设置选择logo模板
+    def set_logo_selected(self):
+        logo_root_path = ""
+        if settings.PROJECT == "红蜻蜓":
+            logo_root_path = r"{}\resources\LOGO\HQT".format(os.getcwd())
+        elif settings.PROJECT == "惠利玛":
+            if "小苏" in settings.Company:
+                logo_root_path = r"{}\resources\LOGO\xiaosushuoxie".format(os.getcwd())
+            if "惠利玛" in settings.Company:
+                logo_root_path = r"{}\resources\LOGO\HLM".format(os.getcwd())
+
+        self.logo_path_dict = {"请选择": "", "无logo": ""}
+
+        if logo_root_path:
+            if os.path.exists(logo_root_path):
+                image_list = get_images(logo_root_path)
+                for image_data in image_list:
+                    self.logo_path_dict[image_data["file_name"]] = image_data["file_path"]
+
+        self.ui.logo_select_ui.addItems([x for x in self.logo_path_dict])
+
+        self.ui.logo_select_ui.setCurrentText("请选择")
+        if settings.MATCHING_LAST_LOGO_TEMP:
+            if settings.MATCHING_LAST_LOGO_TEMP in [x for x in self.logo_path_dict]:
+                self.ui.logo_select_ui.setCurrentText(settings.MATCHING_LAST_LOGO_TEMP)
+
+    def manual_matching_pic_mousePressEvent(self, event):
+        if not self.image_dir:
+            a = QMessageBox.question(
+                self,
+                "确认",
+                "请先选择目标文件夹",
+                QMessageBox.Yes,
+            )
+            return
+        self.manual = MainMatchingWindow(root_path=self.image_dir)
+
+    def get_config(self, *args):
+        if not settings.IsLogin:
+            QMessageBox.question(
+                self, "确认", "请先登录", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        logo_name = self.ui.logo_select_ui.currentText()
+        if logo_name == "请选择":
+            QMessageBox.question(
+                self, "确认", "请先选择logo模板", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        if self.ui.cutout_exquisite.isChecked() and not bool(settings.IsExquisiteMode):
+            QMessageBox.question(
+                self, "确认", "暂无 精细化抠图权限", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        image_dir = self.ui.path_name.text()
+        image_order = self.ui.line_image_pos.text()
+        is_check_number = self.ui.check_image_total.isChecked()
+        is_filter = self.ui.check_is_pass.isChecked()
+        resize_image_view = self.ui.need_resize.text()
+        logo_name = self.ui.logo_select_ui.currentText()
+        settings.MATCHING_LAST_LOGO_TEMP = logo_name
+
+        logo_path = self.logo_path_dict[logo_name]
+
+        cutout_mode = '1'
+        if self.ui.cutout_exquisite.isChecked():
+            cutout_mode = '2'
+
+        configData = {
+            'imageorder': image_order,
+            'resize_image_view': resize_image_view,
+            'is_check_number': str(is_check_number),
+            'is_filter': str(is_filter),
+            'cutout_mode': str(cutout_mode),
+            'matching_mode_last': settings.MATCHING_MODE_LAST,
+            "matching_last_temp": self.last_temp,
+            "matching_last_logo_temp": logo_name,
+        }
+
+        settings.cutimage_dict['imageorder'] = image_order
+        settings.cutimage_dict['resize_image_view'] = resize_image_view
+        settings.cutimage_dict['is_check_number'] = str(is_check_number)
+        settings.cutimage_dict['is_filter'] = str(is_filter)
+        settings.cutimage_dict['cutout_mode'] = str(cutout_mode)
+        settings.set_config(data_dict=configData, section='cutimage')
+
+        config_data = {}
+        config_data["image_dir"] = image_dir
+        config_data["image_order"] = image_order
+        config_data["is_check_number"] = is_check_number
+        config_data["is_filter"] = is_filter
+        config_data["resize_image_view"] = resize_image_view
+        config_data["cutout_mode"] = cutout_mode
+        config_data["logo_path"] = logo_path
+        return config_data
+
+    @Slot()
+    def on_stop_clicked(self):
+        self.event.set()
+        pass
+
+    def change_img_dir(self, *args):
+        folder = QFileDialog.getExistingDirectory(self, "选取文件夹", "./")
+        print(folder)
+        settings.set_config(data_dict={"photo_image_dir": folder})
+
+        # 清空已特殊选择等处理
+        self.ui.is_do_other.setChecked(False)
+
+        self.show_img_dir(image_dir=folder)
+
+    def show_img_dir(self, image_dir=None):
+        if image_dir:
+            self.image_dir = image_dir
+        else:
+            self.image_dir = settings.PhotoImageDir
+
+        if self.image_dir:
+            self.ui.path_name.show()
+            self.ui.path_name.setText(self.image_dir)
+        else:
+            self.ui.path_name.hide()
+
+    def show_progress(self, data):
+        progress_bar_value = data["progress_bar_value"]
+        self.ui.progressBar.setValue(progress_bar_value)
+
+    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, 99]:
+            return
+        self.state = state_value
+
+        if self.state == 0:
+            self.ui.stackedWidget.setCurrentIndex(0)
+        if self.state == 1:
+            self.ui.stackedWidget.setCurrentIndex(1)
+            self.ui.back.hide()
+            self.ui.stop.show()
+            self.ui.stop.setEnabled(True)
+            self.ui.textBrowser_4.clear()
+            self.ui.textBrowser_4.show()
+            self.event.clear()
+
+        if self.state == 2:
+            self.ui.stackedWidget.setCurrentIndex(1)
+            self.ui.back.show()
+            self.ui.stop.hide()
+
+        if self.state == 99:
+            self.ui.stop.setEnabled(False)
+        print("设置state:{}".format(state_value))
+
+    def back(self):
+        self.ui.stackedWidget.setCurrentIndex(0)
+        self.ui.back.hide()
+        self.ui.run.show()
+        self.ui.stop.hide()
+        self.ui.textBrowser_4.clear()
+        self.ui.textBrowser_4.hide()
+
+    def run_matching(self, *args):
+        if settings.PROJECT == "红蜻蜓":
+            if not settings.IsLogin:
+                a = QMessageBox.question(
+                    self,
+                    "确认",
+                    "请先进行登录红蜻蜓账号",
+                    QMessageBox.Yes | QMessageBox.No,
+                )
+                return
+
+        self.set_state(state_value=1)
+        self.goods_images_count_dict = {}
+        self.goods_images_count_dict = defaultdict(int)
+        self.do_thread_run(func=self.run_by_thread, call_back=self.matchingCallBack, time_out=60)
+
+        # self.t = threading.Thread(target=self.run_by_thread, args=())
+        # self.t.start()
+
+    def matchingCallBack(self, data: dict):
+        self.set_state(2)
+        if data['code'] == 0:
+            self.show_progress_detail('处理完成')
+            button_1, button_2, button_3 = None, None, None
+            button_1 = "去抠图"
+            text = '移动完成'
+            my_dialog = DialogShow(self, text=text, button_1=button_1, button_2=button_2)
+            ret = my_dialog.exec()
+            if "去抠图" in my_dialog.flag_name:
+                self.back()
+                self.ui.tabWidget.setCurrentIndex(1)
+                path = os.getcwd() + "/" + data['target_path']
+                self.ui.path_name.setText(path)
+        else:
+            self.show_progress_detail(data['msg'])
+            text = data['msg']
+            my_dialog = DialogShow(self, text=text)
+            ret = my_dialog.exec()
+
+    def run_image_cut(self, *args):
+        if not settings.IsLogin:
+            QMessageBox.question(
+                self, "确认", "请先登录", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        logo_name = self.ui.logo_select_ui.currentText()
+        if logo_name == "请选择":
+            QMessageBox.question(
+                self, "确认", "请先选择logo模板", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        if self.ui.cutout_exquisite.isChecked() and not bool(settings.IsExquisiteMode):
+            QMessageBox.question(
+                self, "确认", "暂无 精细化抠图权限", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        # 先做整体校验
+        self.do_thread_run(func=self.check_for_cutout_image_first,
+                           call_back=self.check_for_cutout_image_first_call_back, time_out=30)
+
+    # 抠图前先做数据规整处理;类似详情图生成逻辑
+    def check_for_cutout_image_first(self, ):
+        return_data = {
+            "code": 99,
+            "message": "",
+            "data": {
+                "all_goods_art_no_folder_data": [],
+            },
+        }
+        image_dir = self.ui.path_name.text()
+        image_order = self.ui.line_image_pos.text()
+        is_check_number = self.ui.check_image_total.isChecked()
+        is_filter = self.ui.check_is_pass.isChecked()
+        resize_image_view = self.ui.need_resize.text()
+        logo_name = self.ui.logo_select_ui.currentText()
+        settings.MATCHING_LAST_LOGO_TEMP = logo_name
+        logo_path = self.logo_path_dict[logo_name]
+
+        # 是否精细化抠图,默认为普通抠图
+        cutout_mode = '1'
+        if self.ui.cutout_exquisite.isChecked():
+            cutout_mode = '2'
+
+        configData = {
+            'imageorder': image_order,
+            'resize_image_view': resize_image_view,
+            'is_check_number': str(is_check_number),
+            'is_filter': str(is_filter),
+            'cutout_mode': str(cutout_mode),
+            'matching_mode_last': settings.MATCHING_MODE_LAST,
+            "matching_last_temp": self.last_temp,
+            "matching_last_logo_temp": logo_name,
+        }
+
+        settings.cutimage_dict['imageorder'] = image_order
+        settings.cutimage_dict['resize_image_view'] = resize_image_view
+        settings.cutimage_dict['is_check_number'] = str(is_check_number)
+        settings.cutimage_dict['is_filter'] = str(is_filter)
+        settings.cutimage_dict['cutout_mode'] = str(cutout_mode)
+        settings.set_config(data_dict=configData, section='cutimage')
+
+        # =================整理要处理的货号文件夹数据=================
+        # all_goods_art_no_folder_data 为数据列表,格式如下:
+        # {"folder_path": file_path,
+        #                  "folder_name": _file,
+        #                  "root_path": path,
+        #                  "to_do": True,  # 是否需要继续处理;默认为需要处理
+        #                  }
+        # 自动处理红蜻蜓货号,进行重命名
+        if settings.PROJECT == "红蜻蜓":
+            # 规整红蜻蜓货号图
+            all_goods_art_no_folder_data = get_all_goods_art_no_folder(path=image_dir)
+            BaseDealImage().rename_folder_for_hqt(all_goods_art_no_folder_data=all_goods_art_no_folder_data)
+
+        # 重新获取文件夹信息
+        all_goods_art_no_folder_data = get_all_goods_art_no_folder(path=image_dir)
+
+        f = True
+        if self.ui.is_do_other.isChecked():
+            for i in all_goods_art_no_folder_data:
+                i["label"] = "不处理"
+            is_filter = False
+            specified_goods_art_no_folder = self.ui.special_goods_art_no_folder_line.text()
+            specified_goods_art_no_folder = specified_goods_art_no_folder.strip()
+            specified_goods_art_no_folder = specified_goods_art_no_folder.replace(",", ",")
+            specified_goods_art_no_folder_list = specified_goods_art_no_folder.split(",")
+            specified_goods_art_no_folder_list = [x for x in specified_goods_art_no_folder_list if x]
+            if not specified_goods_art_no_folder_list:
+                return_data["message"] += '请手动输入文件夹名称(多选),或关闭指定文件夹模式\n'
+            else:
+                for i in all_goods_art_no_folder_data:
+                    if i["folder_path"] in specified_goods_art_no_folder_list:
+                        i["label"] = "待处理"
+            # 哪些数据不合规
+            all_folder_name_list = [x["folder_name"] for x in all_goods_art_no_folder_data]
+            for goods_art_no_folder_name in specified_goods_art_no_folder_list:
+                if goods_art_no_folder_name not in all_folder_name_list:
+                    return_data["message"] += '文件夹:{},在您选的目录下不存在\n'.format(goods_art_no_folder_name)
+                    f = False
+
+        if not f:
+            self.set_state(state_value=2)
+            return
+
+        # 清空指定文件夹的已抠图文件
+        if self.ui.is_do_other.isChecked():
+            if self.ui.is_clean_cutout_image.isChecked():
+                for folder_data in all_goods_art_no_folder_data:
+                    goods_art_no_folder_path = "{}/原始图_已抠图".format(folder_data["folder_path"])
+                    if os.path.exists(goods_art_no_folder_path):
+                        remove_all_file(goods_art_no_folder_path)
+
+        return_data["data"]["succeed_folder_list"] = 1
+
+        # ==================检查填写的图片视角是否符合要求
+        res = BaseDealImage().getImageOrder(image_order=image_order, resize_image_view=resize_image_view)
+        if res['code'] != 0:
+            return_data["message"] += "{}\n".format(res['msg'])
+            return return_data
+        else:
+            # 图片命名顺序
+            image_order_list = res['imageOrderList']
+            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_data["image_order_list"] = image_order_list
+
+        # ================是否过滤已有生成的文件夹
+        if is_filter:
+            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"]
+                _p = "{}/800x800".format(folder_path)
+                if os.path.exists(_p):
+                    if len(os.listdir(_p)):
+                        goods_art_no_folder_data["label"] = "不处理"
+
+        # ================检查每个货号文件夹图片数量是否符合要求
+        all_goods_art_no_folder_data, message = BaseDealImage().check_folders_image_amount(all_goods_art_no_folder_data,
+                                                                                           image_order_list)
+        if message:
+            return_data["message"] += "{}\n".format(message)
+        return_data["code"] = 0
+        return_data["data"]["all_goods_art_no_folder_data"] = all_goods_art_no_folder_data
+        return_data["data"]["image_dir"] = image_dir
+        return_data["data"]["resize_image_view"] = resize_image_view
+        return_data["data"]["cutout_mode"] = cutout_mode
+        return_data["data"]["image_order_list"] = image_order_list
+        return_data["data"]["logo_path"] = logo_path
+        return return_data
+
+    # 抠图校验后的回调函数处理
+    def check_for_cutout_image_first_call_back(self, return_data):
+        # return_data = {
+        #     "code": 99,
+        #     "message": "",
+        #     "data": {
+        #         "all_goods_art_no_folder_data": [],
+        #     },
+        # }
+        code = return_data["code"]
+        print("===================return_data=================")
+        print(json.dumps(return_data))
+        if code != 0:
+            # self.show_message(return_data["message"])
+            _dialog_dict = {"text": return_data["message"],
+                            "windows": self,
+                            }
+            self.show_dialog_sign.emit(_dialog_dict)
+            self.event_dialog.wait()
+            return
+
+        do_next = False
+        text = "check_for_cutout_image_first_call_back\n"
+        all_goods_art_no_folder_data = return_data["data"]["all_goods_art_no_folder_data"]
+        button_1, button_2, button_3 = None, None, None
+        text += return_data["message"]
+        # 存在错误文件夹
+        error_folder = [x for x in all_goods_art_no_folder_data if x["label"] == "错误"]
+        todo_folder = [x for x in all_goods_art_no_folder_data if x["label"] == "待处理"]
+        if error_folder:
+            button_2 = "移除错误文件"
+        if error_folder and todo_folder:
+            button_1 = "移除并继续"
+            button_3 = "继续(忽略其他)"
+        if not error_folder and todo_folder:
+            button_1 = "继续"
+            # do_next = True
+        text += "\n==================\n错误数据:{}个,校验无误数据:{}个".format(len(error_folder), len(todo_folder))
+        # my_dialog = DialogShow(
+        #     self, text=text, button_1=button_1, button_2=button_2, button_3=button_3
+        # )
+        # ret = my_dialog.exec()
+
+        _dialog_dict = {"text": text,
+                        "button_1": button_1,
+                        "button_2": button_2,
+                        "button_3": button_3,
+                        "windows": self,
+                        }
+        self.show_dialog_sign.emit(_dialog_dict)
+        # self.exec_()
+        # 等待事件被设置
+        self.event_dialog.wait()
+        print("self.dialog_result", self.dialog_result)
+
+        print("520  ===============my_dialog.flag_name===============")
+
+        if "移除" in self.dialog_result:
+            for error_folder_data in [x for x in all_goods_art_no_folder_data if x["label"] == "错误"]:
+                self.move_error_folders(
+                    one_path=error_folder_data["folder_path"],
+                    target_folder="{}/软件-处理失败".format(self.image_dir),
+                )
+        if "继续" in self.dialog_result:
+            do_next = True
+
+        if do_next:
+            all_goods_art_no_folder_data = [x for x in all_goods_art_no_folder_data if x["label"] == "待处理"]
+            # self.set_state(state_value=1)
+            self.change_state_sign.emit(1)
+            time.sleep(1.5)
+            # 强制刷新界面命令
+            # QApplication.processEvents()
+            kwargs = {"all_goods_art_no_folder_data": all_goods_art_no_folder_data,
+                      "callback_func": self.show_progress_detail,
+                      "image_order_list": return_data["data"]["image_order_list"],
+                      "cutout_mode": return_data["data"]["cutout_mode"],
+                      "resize_image_view": return_data["data"]["resize_image_view"],
+                      "windows": self,
+                      "logo_path": return_data["data"]["logo_path"], }
+            # self.t = threading.Thread(target=self.do_run2,
+            #                           kwargs={"all_goods_art_no_folder_data": all_goods_art_no_folder_data,
+            #                                   "callback_func": self.show_progress_detail,
+            #                                   "image_order_list": return_data["data"]["image_order_list"],
+            #                                   "cutout_mode": return_data["data"]["cutout_mode"],
+            #                                   "resize_image_view": return_data["data"]["resize_image_view"],
+            #                                   "windows": self,
+            #                                   "logo_path": return_data["data"]["logo_path"],
+            #                                   })
+            # self.t.start()
+            # new_func = partial(self.do_run2,
+            #                    all_goods_art_no_folder_data=kwargs["all_goods_art_no_folder_data"],
+            #                    callback_func=kwargs["callback_func"],
+            #                    image_order_list=kwargs["image_order_list"],
+            #                    cutout_mode=kwargs["cutout_mode"],
+            #                    resize_image_view=kwargs["resize_image_view"],
+            #                    windows=kwargs["windows"],
+            #                    logo_path=kwargs["logo_path"],
+            #                    )
+            # self._w_3 = WorkerOneThread(func=new_func, name="_w_3")
+            # self._w_3.start()
+            self.do_run2(all_goods_art_no_folder_data=kwargs["all_goods_art_no_folder_data"],
+                         callback_func=kwargs["callback_func"],
+                         image_order_list=kwargs["image_order_list"],
+                         cutout_mode=kwargs["cutout_mode"],
+                         resize_image_view=kwargs["resize_image_view"],
+                         windows=kwargs["windows"],
+                         logo_path=kwargs["logo_path"], )
+
+    def do_run2(self, all_goods_art_no_folder_data,
+                callback_func,
+                image_order_list,
+                cutout_mode,
+                resize_image_view,
+                windows,
+                logo_path):
+        try:
+            print("610 BaseDealImage")
+            print(all_goods_art_no_folder_data)
+        except BaseException as e:
+            print("615", e)
+
+        BaseDealImage().run_main(
+            all_goods_art_no_folder_data=all_goods_art_no_folder_data,
+            callback_func=callback_func,
+            image_order_list=image_order_list,
+            cutout_mode=cutout_mode,
+            resize_image_view=resize_image_view,
+            windows=windows,
+            logo_path=logo_path)
+
+        # max_workers = 1
+        # with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
+        #     futures = []
+        #     futures.append(executor.submit(
+        #         BaseDealImage().run_main,
+        #         all_goods_art_no_folder_data=all_goods_art_no_folder_data,
+        #         callback_func=callback_func,
+        #         image_order_list=image_order_list,
+        #         cutout_mode=cutout_mode,
+        #         resize_image_view=resize_image_view,
+        #         windows=windows,
+        #         logo_path=logo_path,
+        #     ))
+        #
+        #     # 使用 wait 方法等待所有任务完成
+        #     done, not_done = concurrent.futures.wait(futures)
+        #     # 处理完成的任务
+        #     for future in done:
+        #         if settings.IS_TEST:
+        #             result = future.result()
+
+        # ==============完成处理==============
+        self.change_state_sign.emit(2)
+        # self.set_state(state_value=2)
+        callback_func("已结束")
+
+    def move_error_folders(self, one_path, target_folder, message=""):
+        if os.path.exists(one_path):
+            check_path(target_folder)
+            move_folders(path_list=[one_path], target_folder=target_folder)
+
+    def threadImageCut(self, ):
+        image_dir = self.ui.path_name.text()
+        baseDealImage = BaseDealImage(image_dir=image_dir)
+        image_order = self.ui.line_image_pos.text()
+        is_check_number = self.ui.check_image_total.isChecked()
+        is_filter = self.ui.check_is_pass.isChecked()
+        resize_image_view = self.ui.need_resize.text()
+        logo_name = self.ui.logo_select_ui.currentText()
+        settings.MATCHING_LAST_LOGO_TEMP = logo_name
+
+        logo_path = self.logo_path_dict[logo_name]
+
+        cutout_mode = '1'
+        if self.ui.cutout_exquisite.isChecked():
+            cutout_mode = '2'
+
+        configData = {
+            'imageorder': image_order,
+            'resize_image_view': resize_image_view,
+            'is_check_number': str(is_check_number),
+            'is_filter': str(is_filter),
+            'cutout_mode': str(cutout_mode),
+            'matching_mode_last': settings.MATCHING_MODE_LAST,
+            "matching_last_temp": self.last_temp,
+            "matching_last_logo_temp": logo_name,
+        }
+
+        settings.cutimage_dict['imageorder'] = image_order
+        settings.cutimage_dict['resize_image_view'] = resize_image_view
+        settings.cutimage_dict['is_check_number'] = str(is_check_number)
+        settings.cutimage_dict['is_filter'] = str(is_filter)
+        settings.cutimage_dict['cutout_mode'] = str(cutout_mode)
+        settings.set_config(data_dict=configData, section='cutimage')
+
+        # =================整理要处理的货号文件夹数据=================
+        all_goods_art_no_folder_data = get_all_goods_art_no_folder(path=image_dir)
+        f = True
+        if self.ui.is_do_other.isChecked():
+            for i in all_goods_art_no_folder_data:
+                i["todo"] = False
+
+            is_filter = False
+            specified_goods_art_no_folder = self.ui.special_goods_art_no_folder_line.text()
+            specified_goods_art_no_folder = specified_goods_art_no_folder.strip()
+            specified_goods_art_no_folder = specified_goods_art_no_folder.replace(",", ",")
+            specified_goods_art_no_folder_list = specified_goods_art_no_folder.split(",")
+            specified_goods_art_no_folder_list = [x for x in specified_goods_art_no_folder_list if x]
+            if not specified_goods_art_no_folder_list:
+                self.show_progress_detail('请手动输入文件夹名称(多选),或关闭指定文件夹模式')
+            else:
+                for i in all_goods_art_no_folder_data:
+                    if i["folder_path"] in specified_goods_art_no_folder_list:
+                        i["todo"] = True
+            # 哪些数据不合规
+            all_folder_name_list = [x["folder_name"] for x in all_goods_art_no_folder_data]
+            for goods_art_no_folder_name in specified_goods_art_no_folder_list:
+                if goods_art_no_folder_name not in all_folder_name_list:
+                    self.show_progress_detail('文件夹:{},在您选的目录下不存在'.format(goods_art_no_folder_name))
+                    f = False
+
+        if not f:
+            self.set_state(state_value=2)
+            return
+
+        # 清空指定文件夹的已抠图文件
+        # print("todo_goods_art_no_folder_name_list",todo_goods_art_no_folder_name_list)
+        if self.ui.is_do_other.isChecked():
+            if self.ui.is_clean_cutout_image.isChecked():
+                for folder_data in all_goods_art_no_folder_data:
+                    goods_art_no_folder_path = "{}/原始图_已抠图".format(folder_data["folder_path"])
+                    if os.path.exists(goods_art_no_folder_path):
+                        remove_all_file(goods_art_no_folder_path)
+
+        if settings.IS_TEST:
+            result = baseDealImage.cutoutImage(
+                image_dir=image_dir,
+                all_goods_art_no_folder_data=all_goods_art_no_folder_data,
+                image_order=image_order,
+                is_check_number=is_check_number,
+                is_filter=is_filter,
+                resize_image_view=resize_image_view,
+                callback_func=self.show_progress_detail,
+                event=self.event,
+                cutout_mode=cutout_mode,
+                logo_path=logo_path,
+            )
+            if "code" in result and result['code'] != 0:
+                self.show_progress_detail(result['msg'])
+            else:
+                self.show_progress_detail('处理完成')
+        else:
+            try:
+                result = baseDealImage.cutoutImage(
+                    image_dir=image_dir,
+                    image_order=image_order,
+                    all_goods_art_no_folder_data=all_goods_art_no_folder_data,
+                    is_check_number=is_check_number,
+                    is_filter=is_filter,
+                    resize_image_view=resize_image_view,
+                    callback_func=self.show_progress_detail,
+                    event=self.event,
+                    cutout_mode=cutout_mode,
+                    logo_path=logo_path,
+                )
+
+                if "code" in result and result['code'] != 0:
+                    self.show_progress_detail(result['msg'])
+                else:
+                    self.show_progress_detail('处理完成')
+
+            except BaseException as e:
+                print(e)
+                self.show_progress_detail('程序异常')
+
+        self.set_state(state_value=2)
+
+    def show_progress_detail(self, text):
+        self.text_show.emit(text)
+
+    def append_text_to_browser(self, text):
+        self.ui.textBrowser_4.append(text)
+
+    def run_by_thread(self):
+        image_dir = "{}/data".format(os.getcwd())
+        baseDealImage = BaseDealImage(image_dir=image_dir)
+        # result = baseDealImage.dealMoveImage(
+        #     image_dir=settings.PhotoOutputDir, callback_func=self.show_progress_detail
+        # )
+        result = baseDealImage.dealMoveImage(
+            image_dir=image_dir, callback_func=self.show_progress_detail
+        )
+        return result
+
+    def run_by_thread_backup(self):
+        if not self.check():
+            return
+
+        # 遍历目标文件夹,获取有拍摄信息的图片,并按拍摄时间排序
+        files = os.listdir(self.image_dir)
+        _Type = [".png", ".PNG", ".jpg", ".JPG", ".gif", ".GIF", ".jpge", ".JPGE"]
+        original_photo_list = []  # 原始图片列表
+        for file in files:
+            # -----图片清洗
+            file_path = self.image_dir + "/" + file
+            if os.path.isdir(file_path):  # 忽略文件夹
+                continue
+            file_name, e = os.path.splitext(file)
+            if e not in _Type:  # 非图片进行移除
+                shutil.move(file_path, self.image_dir + "/历史/" + file)
+                continue
+
+            date_time_original = self.get_date_time_original(
+                file_path
+            )  # 获取照片拍照时间
+            if date_time_original:
+                # 基于照片的时间,与数据库匹配goods_art_no
+                _data = self.data_match_photo.get_goods_art_no(date_time_original)
+
+                if _data:
+                    # 能匹配上数据库
+                    goods_art_no, image_index, image_deal_mode = _data
+                    print(
+                        "与数据库匹配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)
+                    )
+                    # shutil.move(photo_dict["file_path"], self.image_dir + "/历史/" + photo_dict["file"])
+                    continue
+            else:
+                shutil.move(file_path, self.image_dir + "/历史/" + file)
+
+        if not original_photo_list:
+            self.show_progress_detail("没有任何匹配的图片~")
+            self.set_state(state_value=2)
+            return
+
+        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.data_match_photo.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.data_match_photo.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)
+
+            # 重命名并进行移动
+            self.move_images(
+                goods_art_no, goods_art_no_path, original_image_path
+            )  # 货号、货号文件路径、原始图路径
+
+            self.progress_sign.emit(
+                {"type": "移动原始图片", "progress_bar_value": int(n / total_num * 100)}
+            )
+            self.show_progress_detail(
+                "货号{} 相关文件夹创建完成,已移动原图~".format(goods_art_no)
+            )
+
+        if n != 0:
+            # if settings.MattingPics:
+            #     # 检查所有未处理的货号文件夹,查看是否有完成图片加工处理
+            #     self.deal_images()
+
+            # 自动生成一个货号表
+            print("output_path", output_path)
+            GenerateGoodsArtNoTable.deal(output_path)
+
+        # 完成处理
+        self.set_state(state_value=2)
+
+    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 deal_images(self):
+        """
+        2、压缩并上传平台获取抠图
+        3、抠图处理成白底图
+        4、做成800*800/200*200
+        :return:
+        """
+        output_path = "output"
+        """
+        扫描文档,检查有哪些需要进行抠图等处理
+        """
+        to_do_images_total = 0
+        for _date_folder in os.listdir(output_path):
+            if not os.path.isdir("{}/{}".format(output_path, _date_folder)):
+                continue
+            for goods_art_no_folder in os.listdir(
+                    "{}/{}".format(output_path, _date_folder)
+            ):
+                # print(goods_art_no_folder)
+                all_original_images = os.listdir(
+                    "{}/{}/{}/原始图".format(
+                        output_path, _date_folder, goods_art_no_folder
+                    )
+                )
+                all_moved_images = [
+                    os.path.splitext(x)[0]
+                    for x in os.listdir(
+                        "{}/{}/{}/原始图_已抠图".format(
+                            output_path, _date_folder, goods_art_no_folder
+                        )
+                    )
+                ]
+                for file in all_original_images:
+                    if os.path.splitext(file)[0] not in all_moved_images:
+                        print("----------》", goods_art_no_folder, file)
+                        to_do_images_total += 1
+
+        if to_do_images_total > 0:
+            self.progress_sign.emit(
+                {"type": "处理图片 抠图、加工等", "progress_bar_value": 0}
+            )
+        else:
+            return True
+
+        """开始处理抠图等处理"""
+        n = 0
+        for _date_folder in os.listdir(output_path):
+            # 过滤非文件夹的文件
+            if not os.path.isdir("{}/{}".format(output_path, _date_folder)):
+                continue
+
+            # 扫描每个货号
+            for goods_art_no_folder in os.listdir(
+                    "{}/{}".format(output_path, _date_folder)
+            ):
+                all_original_images = os.listdir(
+                    "{}/{}/{}/原始图".format(
+                        output_path, _date_folder, goods_art_no_folder
+                    )
+                )
+                all_moved_images = [
+                    os.path.splitext(x)[0]
+                    for x in os.listdir(
+                        "{}/{}/{}/原始图_已抠图".format(
+                            output_path, _date_folder, goods_art_no_folder
+                        )
+                    )
+                ]
+                for file in all_original_images:
+                    original_image_path = "{}/{}/{}/原始图/{}".format(
+                        output_path, _date_folder, goods_art_no_folder, file
+                    )
+                    file_name = os.path.splitext(file)[0]
+                    if file_name not in all_moved_images:
+                        print("上传平台获取抠图结果----------》", file)
+                        goods_art_no_folder_path = "{}/{}/{}".format(
+                            output_path, _date_folder, goods_art_no_folder
+                        )
+                        pic_deal = PicDeal()
+                        # 上传平台获取抠图结果
+                        original_move_bg_image_path = "{}/原始图_已抠图/{}{}".format(
+                            goods_art_no_folder_path, file_name, ".png"
+                        )
+                        if pic_deal.remove_bg(
+                                in_path=original_image_path,
+                                out_path=original_move_bg_image_path,
+                        ):
+                            self.show_progress_detail(
+                                "货号图{} 已完成抠图处理~".format(file_name)
+                            )
+                            # 抠图处理成白底图 800*800
+                            out_800_path = "{}/800x800/{}{}".format(
+                                goods_art_no_folder_path, file_name, ".jpg"
+                            )
+                            pic_deal.create_800image(
+                                image_path=original_move_bg_image_path,
+                                out_path=out_800_path,
+                            )
+                            # 抠图处理成白底图 200*200
+                            out_200_path = "{}/200images/{}{}".format(
+                                goods_art_no_folder_path, file_name, ".jpg"
+                            )
+                            pic_deal.resize_and_save(
+                                image_path=out_800_path,
+                                out_path=out_200_path,
+                                width=200,
+                            )
+                            self.show_progress_detail(
+                                "货号图{} 已完成800*800图片和200*200图片制作~".format(
+                                    file_name
+                                )
+                            )
+                        else:
+                            self.show_progress_detail(
+                                "货号图{} 抠图处理失败~".format(file_name)
+                            )
+                        # 完成处理的图片进度
+                        n += 1
+                        self.progress_sign.emit(
+                            {
+                                "type": "处理图片 抠图、加工等",
+                                "progress_bar_value": int(n / to_do_images_total * 100),
+                            }
+                        )
+
+    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 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 on_dialog_result(self, result):
+        """处理对话框结果"""
+        self.dialog_result = result
+        print("1077  处理对话框结果:{}".format(result))
+        # self.quit()  # 结束事件循
+        self.event_dialog.set()
+
+    # 弹出对话框
+    def show_dialog(self, data):
+        windows = data["windows"]
+        windows.dialog_result = ""
+
+        my_dialog = DialogShow(self, text=data["text"],
+                               button_1=data["button_1"] if "button_1" in data else None,
+                               button_2=data["button_2"] if "button_2" in data else None,
+                               button_3=data["button_3"] if "button_3" in data else None, )
+        ret = my_dialog.exec()
+        print("632  my_dialog.flag_name", my_dialog.flag_name)
+        # 根据用户的选择发出相应的信号
+        windows.communicator.dialog_result_signal.emit(my_dialog.flag_name)
+
+
+class TempItem(QWidget):
+    select_sign = Signal(str)
+
+    def __init__(self, parent, _id, image_path):
+        super().__init__(parent)
+        self.ui = UI_temp_item()
+        self.ui.setupUi(self)
+        self.image_path = image_path
+        self.id = _id
+        self.is_select = False
+        self.init()
+
+    def init(self):
+        self.ui.label.mousePressEvent = self.pic_show
+        self.ui.radioButton.clicked.connect(self.select)
+        self.set_label_image()
+
+    def cancel_select(self, *args):
+        self.is_select = False
+        self.ui.radioButton.setChecked(False)
+
+    def select(self, *args):
+        self.is_select = True
+        self.ui.radioButton.setChecked(True)
+        self.select_sign.emit(self.id)
+
+    def pic_show(self, *args, **kwargs):
+        if os.path.exists(self.image_path):
+            im = Image.open(self.image_path)
+            threading.Thread(target=im.show, args=()).start()
+
+    def set_label_image(self):
+        if not os.path.exists(self.image_path):
+            return
+        try:
+            bytes_io = io.BytesIO()
+            w, h = self.ui.image_show.width(), self.ui.image_show.height()
+            _img_raw = Image.open(self.image_path)
+            _img_raw = self.to_resize(_img_raw, width=w * 2)
+            _img_raw = _img_raw.crop(box=(0, 0, w * 2, h * 2))
+            # _img_raw.thumbnail((w * 2, int(_img_raw.height * w * 2 / _img_raw.width)))
+            # img_raw = _img_raw.resize(size=(w, h))
+            _img_raw.save(bytes_io, "JPEG")
+            icon = QPixmap()
+            # icon.loadFromData(img)
+            icon.loadFromData(bytes_io.getvalue())
+            icon = icon.scaled(
+                self.ui.image_show.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
+            )
+            self.ui.image_show.setPixmap(icon)
+            # self.mousePressEvent = lambda x: self.show_one_data(row)
+        except:
+            pass
+
+    def to_resize(self, _im, width=None, high=None):
+        _im_x, _im_y = _im.size
+        if width and high:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(high)
+            re_x = int(_im_x * re_y / _im_y)
+        _im = _im.resize((re_x, re_y))
+        return _im

+ 8 - 0
python/service/matching_photos/data.py

@@ -0,0 +1,8 @@
+from ..data_metaclass import DataBaseModel
+
+
+class DataModeMatchPhoto(DataBaseModel):
+    def __init__(self):
+        super().__init__()
+
+

+ 847 - 0
python/service/matching_photos/module_matching_photos.py

@@ -0,0 +1,847 @@
+# from UI.matching_photos.matching_photos import Ui_Form as matching_photos_Ui_Form
+import os
+
+from UI.matching_photos.matching_and_cutout_photos import (
+    Ui_Form as matching_photos_Ui_Form,
+)
+from import_qt_mode import *
+
+import settings
+from collections import defaultdict
+
+from module.service.base_deal_image import BaseDealImage
+from module.view_control.generate_goods_art_no_table.module_generate_goods_art_no_table import GenerateGoodsArtNoTable
+from module.view_control.matching_photos.data import DataModeMatchPhoto
+from module.view_control.manual_image_matching.m_image_matching_cotrol import MainMatchingWindow
+
+from PySide6.QtGui import QPixmap
+from PIL import Image
+import io
+
+import threading
+import shutil
+import time
+from module.base_mode.pic_deal import PicDeal
+# import exifread
+from module.base_mode.base import *
+from module.view_control.MineQWidget import MineQWidget, DialogShow
+from threading import Event
+from functools import partial
+from UI.matching_photos.temp_item import Ui_Form as UI_temp_item
+
+"""
+照片自动货号匹配 将图片放置在指定文件夹下,并自动对应不同的货号进行整理
+"""
+
+
+class MatchingPhotos(MineQWidget):
+    progress_sign = Signal(dict)
+    info_sign = Signal(str)
+    text_show = Signal(str)
+
+    def __init__(self):
+        super().__init__()
+        self.image_dir = settings.PhotoImageDir
+        self.ui = matching_photos_Ui_Form()
+        self.ui.setupUi(self)
+        # 0禁用  1进行中  2已结束
+        self.state = 2
+        self.event = Event()
+        self.goods_images_count_dict = defaultdict(int)
+        self.data_match_photo = DataModeMatchPhoto()
+        self.excel_path = ""
+        # 上次使用的详情模板
+        self.last_temp = settings.MATCHING_LAST_TEMP
+        # 指定单独处理的颜色文件夹
+        self.special_goods_art_no_folder = []
+
+        self.init()
+        self.show()
+
+    def init(self):
+        # 文本
+        self.ui.textBrowser.clear()
+        text = """请将capture One 加工后的图片放在指定目录下(目录为当前文件夹下的{}/data文件夹),务必确保导出的图片有拍摄时间信息。""".format(os.getcwd())
+        self.ui.textBrowser.append(text)
+        self.ui.tabWidget.setCurrentIndex(0)
+        # 页面视图1
+        self.ui.stackedWidget.setCurrentIndex(0)
+        self.text_show.connect(self.append_text_to_browser)
+        self.show_img_dir()
+        self.ui.select_path.mousePressEvent = self.change_img_dir
+        self.ui.progressBar.setValue(0)
+        self.ui.progressBar.setMaximum(100)
+        self.ui.progressBar.setValue(0)
+        self.ui.progressBar.hide()
+        self.ui.line_image_pos.setText(settings.cutimage_dict['imageorder'])
+        self.ui.need_resize.setText(settings.cutimage_dict['resize_image_view'])
+
+        self.ui.check_image_total.setChecked(bool(settings.cutimage_dict['is_check_number']))
+        self.ui.check_is_pass.setChecked(bool(settings.cutimage_dict['is_filter']))
+        print(settings.cutimage_dict)
+        if settings.DEFAULT_CUTOUT_MODE == '普通抠图':
+            self.ui.cutout_nomal.setChecked(True)
+        elif settings.DEFAULT_CUTOUT_MODE == ' 精细化抠图':
+            self.ui.cutout_exquisite.setChecked(True)
+
+        if not bool(settings.IsExquisiteMode):
+            self.ui.cutout_exquisite.setEnabled(False)
+
+        # 展示是否指定内容抠图
+        self.ui.is_do_other.toggled.connect(self.change_do_special_treatment)
+        self.change_do_special_treatment()
+
+        self.progress_sign.connect(self.show_progress)
+        self.ui.run.clicked.connect(self.run_image_cut)  # 抠图与主图加工
+        self.ui.run_2.clicked.connect(self.run_matching)  # 匹配图片
+        self.ui.back.clicked.connect(self.back)
+        # 页面视图2
+        self.ui.back.hide()
+        self.set_logo_selected()
+
+        # 手动匹配图片
+        self.ui.manual_matching_pic.mousePressEvent = self.manual_matching_pic_mousePressEvent
+
+    # 切换是否特殊处理的开关
+    def change_do_special_treatment(self, *args):
+        if self.ui.is_do_other.isChecked():
+            self.ui.show_deal_condition.show()
+        else:
+            self.ui.show_deal_condition.hide()
+
+    # 设置选择logo模板
+    def set_logo_selected(self):
+        logo_root_path = ""
+        if settings.PROJECT == "红蜻蜓":
+            logo_root_path = r"{}\resources\LOGO\HQT".format(os.getcwd())
+        elif settings.PROJECT == "惠利玛":
+            if "小苏" in settings.Company:
+                logo_root_path = r"{}\resources\LOGO\xiaosushuoxie".format(os.getcwd())
+            if "惠利玛" in settings.Company:
+                logo_root_path = r"{}\resources\LOGO\HLM".format(os.getcwd())
+
+        self.logo_path_dict = {"请选择": "", "无logo": ""}
+
+        if logo_root_path:
+            if os.path.exists(logo_root_path):
+                image_list = get_images(logo_root_path)
+                print("111111111          image_list", image_list)
+                for image_data in image_list:
+                    self.logo_path_dict[image_data["file_name"]] = image_data["file_path"]
+
+        self.ui.logo_select_ui.addItems([x for x in self.logo_path_dict])
+
+        self.ui.logo_select_ui.setCurrentText("请选择")
+        if settings.MATCHING_LAST_LOGO_TEMP:
+            if settings.MATCHING_LAST_LOGO_TEMP in [x for x in self.logo_path_dict]:
+                self.ui.logo_select_ui.setCurrentText(settings.MATCHING_LAST_LOGO_TEMP)
+
+    def manual_matching_pic_mousePressEvent(self, event):
+        if not self.image_dir:
+            a = QMessageBox.question(
+                self,
+                "确认",
+                "请先选择目标文件夹",
+                QMessageBox.Yes,
+            )
+            return
+        self.manual = MainMatchingWindow(root_path=self.image_dir)
+
+    @Slot()
+    def on_stop_clicked(self):
+        self.event.set()
+        pass
+
+    def change_img_dir(self, *args):
+        folder = QFileDialog.getExistingDirectory(self, "选取文件夹", "./")
+        print(folder)
+        settings.set_config(data_dict={"photo_image_dir": folder})
+
+        # 清空已特殊选择等处理
+        self.ui.is_do_other.setChecked(False)
+
+        self.show_img_dir(image_dir=folder)
+
+    def show_img_dir(self, image_dir=None):
+        if image_dir:
+            self.image_dir = image_dir
+        else:
+            self.image_dir = settings.PhotoImageDir
+
+        if self.image_dir:
+            self.ui.path_name.show()
+            self.ui.path_name.setText(self.image_dir)
+        else:
+            self.ui.path_name.hide()
+
+    def show_progress(self, data):
+        progress_bar_value = data["progress_bar_value"]
+        self.ui.progressBar.setValue(progress_bar_value)
+
+    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.ui.stackedWidget.setCurrentIndex(0)
+        if self.state == 1:
+            self.ui.stackedWidget.setCurrentIndex(1)
+            self.ui.back.hide()
+            self.ui.stop.show()
+            self.ui.textBrowser_4.clear()
+            self.ui.textBrowser_4.show()
+            self.event.clear()
+
+        if self.state == 2:
+            self.ui.stackedWidget.setCurrentIndex(1)
+            self.ui.back.show()
+            self.ui.stop.hide()
+
+    def back(self):
+        self.ui.stackedWidget.setCurrentIndex(0)
+        self.ui.back.hide()
+        self.ui.run.show()
+        self.ui.stop.hide()
+        self.ui.textBrowser_4.clear()
+        self.ui.textBrowser_4.hide()
+
+    def check_first_call_back(self, data):
+        # 首次数据校验的信息返回
+        # self.show_message(text="22222222222222222222222")
+        # QMessageBox.critical(self, "警告", "1111111", QMessageBox.Ok)
+        code = data["code"]
+        if data["message"]:
+            button_1, button_2, button_3 = None, None, None
+            button_1 = "移除"
+            button_2 = "继续"
+            text = data["message"]
+            my_dialog = DialogShow(self, text=text, button_1=button_1, button_2=button_2)
+            ret = my_dialog.exec()
+            print(my_dialog.flag_name)
+            if "移除" in my_dialog.flag_name:
+                pass
+            if "继续" in my_dialog.flag_name:
+                pass
+
+    def check_test(self, test):
+        print(test)
+        time.sleep(3)
+        return_data = {"code": 99,
+                       "message": "messagemessagemessagemessagemessagemessagemessage",
+                       "data": {"error_folder_list": [],
+                                "goods_no_dict": {},
+                                "succeed_folder_list": [],
+                                }}
+
+        return return_data
+
+    def run_matching11111(self, *args):
+        func = partial(self.check_test, test=1111111)
+        self.do_thread_run(func=func, call_back=self.check_first_call_back, time_out=30)
+
+    def run_matching(self, *args):
+        if settings.PROJECT == "红蜻蜓":
+            if not settings.IsLogin:
+                a = QMessageBox.question(
+                    self,
+                    "确认",
+                    "请先进行登录红蜻蜓账号",
+                    QMessageBox.Yes | QMessageBox.No,
+                )
+                return
+
+        self.set_state(state_value=1)
+        self.goods_images_count_dict = {}
+        self.goods_images_count_dict = defaultdict(int)
+        self.do_thread_run(func=self.run_by_thread, call_back=self.matchingCallBack, time_out=60)
+
+        # self.t = threading.Thread(target=self.run_by_thread, args=())
+        # self.t.start()
+
+    def matchingCallBack(self, data: dict):
+        self.set_state(2)
+        if data['code'] == 0:
+            self.show_progress_detail('处理完成')
+            button_1, button_2, button_3 = None, None, None
+            button_1 = "去抠图"
+            text = '移动完成'
+            my_dialog = DialogShow(self, text=text, button_1=button_1, button_2=button_2)
+            ret = my_dialog.exec()
+            if "去抠图" in my_dialog.flag_name:
+                self.back()
+                self.ui.tabWidget.setCurrentIndex(1)
+                path = os.getcwd() + "/" + data['target_path']
+                self.ui.path_name.setText(path)
+        else:
+            self.show_progress_detail(data['msg'])
+            text = data['msg']
+            my_dialog = DialogShow(self, text=text)
+            ret = my_dialog.exec()
+
+    def run_image_cut(self, *args):
+        if not settings.IsLogin:
+            QMessageBox.question(
+                self, "确认", "请先登录", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        logo_name = self.ui.logo_select_ui.currentText()
+        if logo_name == "请选择":
+            QMessageBox.question(
+                self, "确认", "请先选择logo模板", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        if self.ui.cutout_exquisite.isChecked() and not bool(settings.IsExquisiteMode):
+            QMessageBox.question(
+                self, "确认", "暂无 精细化抠图权限", QMessageBox.Yes | QMessageBox.No
+            )
+            return
+
+        self.set_state(state_value=1)
+        self.t = threading.Thread(target=self.threadImageCut, args=())
+        self.t.start()
+
+    def threadImageCut(
+            self,
+    ):
+        image_dir = self.ui.path_name.text()
+        baseDealImage = BaseDealImage(image_dir=image_dir)
+        image_order = self.ui.line_image_pos.text()
+        is_check_number = self.ui.check_image_total.isChecked()
+        is_filter = self.ui.check_is_pass.isChecked()
+        resize_image_view = self.ui.need_resize.text()
+        logo_name = self.ui.logo_select_ui.currentText()
+        settings.MATCHING_LAST_LOGO_TEMP = logo_name
+
+        logo_path = self.logo_path_dict[logo_name]
+
+        cutout_mode = '1'
+        if self.ui.cutout_exquisite.isChecked():
+            cutout_mode = '2'
+
+        configData = {
+            'imageorder': image_order,
+            'resize_image_view': resize_image_view,
+            'is_check_number': str(is_check_number),
+            'is_filter': str(is_filter),
+            'cutout_mode': str(cutout_mode),
+            'matching_mode_last': settings.MATCHING_MODE_LAST,
+            "matching_last_temp": self.last_temp,
+            "matching_last_logo_temp": logo_name,
+        }
+
+        settings.cutimage_dict['imageorder'] = image_order
+        settings.cutimage_dict['resize_image_view'] = resize_image_view
+        settings.cutimage_dict['is_check_number'] = str(is_check_number)
+        settings.cutimage_dict['is_filter'] = str(is_filter)
+        settings.cutimage_dict['cutout_mode'] = str(cutout_mode)
+        settings.set_config(data_dict=configData, section='cutimage')
+
+        # =================整理要处理的货号文件夹数据=================
+        is_ok = True
+        all_goods_art_no_folder_data = get_all_goods_art_no_folder(path=image_dir)
+        todo_goods_art_no_folder_name_list = []
+        if not self.ui.is_do_other.isChecked():
+            todo_goods_art_no_folder_name_list = [x["folder_name"] for x in all_goods_art_no_folder_data]
+        else:
+            is_filter = False
+            specified_goods_art_no_folder = self.ui.special_goods_art_no_folder_line.text()
+            specified_goods_art_no_folder = specified_goods_art_no_folder.strip()
+            specified_goods_art_no_folder = specified_goods_art_no_folder.replace(",", ",")
+            if not specified_goods_art_no_folder:
+                self.show_progress_detail('请手动输入文件夹名称(多选),或关闭指定文件夹模式')
+                is_ok = False
+            else:
+                specified_goods_art_no_folder_list = specified_goods_art_no_folder.split(",")
+                all_folder_name_list = [x["folder_name"] for x in all_goods_art_no_folder_data]
+                for goods_art_no_folder_name in specified_goods_art_no_folder_list:
+                    if goods_art_no_folder_name:
+                        if goods_art_no_folder_name not in all_folder_name_list:
+                            self.show_progress_detail('文件夹:{},在您选的目录下不存在'.format(goods_art_no_folder_name))
+                            is_ok = False
+                        else:
+                            todo_goods_art_no_folder_name_list.append(goods_art_no_folder_name)
+
+        if not is_ok:
+            self.set_state(state_value=2)
+            return
+
+        # 清空指定文件夹的已抠图文件
+        print("todo_goods_art_no_folder_name_list",todo_goods_art_no_folder_name_list)
+        if self.ui.is_do_other.isChecked():
+            if self.ui.is_clean_cutout_image.isChecked():
+                for goods_art_no_folder_name in todo_goods_art_no_folder_name_list:
+                    goods_art_no_folder_path = "{}/{}/原始图_已抠图".format(image_dir, goods_art_no_folder_name)
+                    print("goods_art_no_folder_path",goods_art_no_folder_path)
+                    if os.path.exists(goods_art_no_folder_path):
+                        remove_all_file(goods_art_no_folder_path)
+
+        if settings.IS_TEST:
+            result = baseDealImage.cutoutImage(
+                image_dir=image_dir,
+                todo_goods_art_no_folder_name_list=todo_goods_art_no_folder_name_list,
+                image_order=image_order,
+                is_check_number=is_check_number,
+                is_filter=is_filter,
+                resize_image_view=resize_image_view,
+                callback_func=self.show_progress_detail,
+                event=self.event,
+                cutout_mode=cutout_mode,
+                logo_path=logo_path,
+            )
+            if "code" in result and result['code'] != 0:
+                self.show_progress_detail(result['msg'])
+            else:
+                self.show_progress_detail('处理完成')
+        else:
+            try:
+                result = baseDealImage.cutoutImage(
+                    image_dir=image_dir,
+                    image_order=image_order,
+                    todo_goods_art_no_folder_name_list=todo_goods_art_no_folder_name_list,
+                    is_check_number=is_check_number,
+                    is_filter=is_filter,
+                    resize_image_view=resize_image_view,
+                    callback_func=self.show_progress_detail,
+                    event=self.event,
+                    cutout_mode=cutout_mode,
+                    logo_path=logo_path,
+                )
+
+                if "code" in result and result['code'] != 0:
+                    self.show_progress_detail(result['msg'])
+                else:
+                    self.show_progress_detail('处理完成')
+
+            except BaseException as e:
+                print(e)
+                self.show_progress_detail('程序异常')
+
+        self.set_state(state_value=2)
+
+    def show_progress_detail(self, text):
+        self.text_show.emit(text)
+
+    def append_text_to_browser(self, text):
+        self.ui.textBrowser_4.append(text)
+
+    def run_by_thread(self):
+        image_dir = "{}/data".format(os.getcwd())
+        baseDealImage = BaseDealImage(image_dir=image_dir)
+        # result = baseDealImage.dealMoveImage(
+        #     image_dir=settings.PhotoOutputDir, callback_func=self.show_progress_detail
+        # )
+        result = baseDealImage.dealMoveImage(
+            image_dir=image_dir, callback_func=self.show_progress_detail
+        )
+        return result
+
+    def run_by_thread_backup(self):
+        if not self.check():
+            return
+
+        # 遍历目标文件夹,获取有拍摄信息的图片,并按拍摄时间排序
+        files = os.listdir(self.image_dir)
+        _Type = [".png", ".PNG", ".jpg", ".JPG", ".gif", ".GIF", ".jpge", ".JPGE"]
+        original_photo_list = []  # 原始图片列表
+        for file in files:
+            # -----图片清洗
+            file_path = self.image_dir + "/" + file
+            if os.path.isdir(file_path):  # 忽略文件夹
+                continue
+            file_name, e = os.path.splitext(file)
+            if e not in _Type:  # 非图片进行移除
+                shutil.move(file_path, self.image_dir + "/历史/" + file)
+                continue
+
+            date_time_original = self.get_date_time_original(
+                file_path
+            )  # 获取照片拍照时间
+            if date_time_original:
+                # 基于照片的时间,与数据库匹配goods_art_no
+                _data = self.data_match_photo.get_goods_art_no(date_time_original)
+
+                if _data:
+                    # 能匹配上数据库
+                    goods_art_no, image_index, image_deal_mode = _data
+                    print(
+                        "与数据库匹配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)
+                    )
+                    # shutil.move(photo_dict["file_path"], self.image_dir + "/历史/" + photo_dict["file"])
+                    continue
+            else:
+                shutil.move(file_path, self.image_dir + "/历史/" + file)
+
+        if not original_photo_list:
+            self.show_progress_detail("没有任何匹配的图片~")
+            self.set_state(state_value=2)
+            return
+
+        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.data_match_photo.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.data_match_photo.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)
+
+            # 重命名并进行移动
+            self.move_images(
+                goods_art_no, goods_art_no_path, original_image_path
+            )  # 货号、货号文件路径、原始图路径
+
+            self.progress_sign.emit(
+                {"type": "移动原始图片", "progress_bar_value": int(n / total_num * 100)}
+            )
+            self.show_progress_detail(
+                "货号{} 相关文件夹创建完成,已移动原图~".format(goods_art_no)
+            )
+
+        if n != 0:
+            # if settings.MattingPics:
+            #     # 检查所有未处理的货号文件夹,查看是否有完成图片加工处理
+            #     self.deal_images()
+
+            # 自动生成一个货号表
+            print("output_path", output_path)
+            GenerateGoodsArtNoTable.deal(output_path)
+
+        # 完成处理
+        self.set_state(state_value=2)
+
+    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 deal_images(self):
+        """
+        2、压缩并上传平台获取抠图
+        3、抠图处理成白底图
+        4、做成800*800/200*200
+        :return:
+        """
+        output_path = "output"
+        """
+        扫描文档,检查有哪些需要进行抠图等处理
+        """
+        to_do_images_total = 0
+        for _date_folder in os.listdir(output_path):
+            if not os.path.isdir("{}/{}".format(output_path, _date_folder)):
+                continue
+            for goods_art_no_folder in os.listdir(
+                    "{}/{}".format(output_path, _date_folder)
+            ):
+                # print(goods_art_no_folder)
+                all_original_images = os.listdir(
+                    "{}/{}/{}/原始图".format(
+                        output_path, _date_folder, goods_art_no_folder
+                    )
+                )
+                all_moved_images = [
+                    os.path.splitext(x)[0]
+                    for x in os.listdir(
+                        "{}/{}/{}/原始图_已抠图".format(
+                            output_path, _date_folder, goods_art_no_folder
+                        )
+                    )
+                ]
+                for file in all_original_images:
+                    if os.path.splitext(file)[0] not in all_moved_images:
+                        print("----------》", goods_art_no_folder, file)
+                        to_do_images_total += 1
+
+        if to_do_images_total > 0:
+            self.progress_sign.emit(
+                {"type": "处理图片 抠图、加工等", "progress_bar_value": 0}
+            )
+        else:
+            return True
+
+        """开始处理抠图等处理"""
+        n = 0
+        for _date_folder in os.listdir(output_path):
+            # 过滤非文件夹的文件
+            if not os.path.isdir("{}/{}".format(output_path, _date_folder)):
+                continue
+
+            # 扫描每个货号
+            for goods_art_no_folder in os.listdir(
+                    "{}/{}".format(output_path, _date_folder)
+            ):
+                all_original_images = os.listdir(
+                    "{}/{}/{}/原始图".format(
+                        output_path, _date_folder, goods_art_no_folder
+                    )
+                )
+                all_moved_images = [
+                    os.path.splitext(x)[0]
+                    for x in os.listdir(
+                        "{}/{}/{}/原始图_已抠图".format(
+                            output_path, _date_folder, goods_art_no_folder
+                        )
+                    )
+                ]
+                for file in all_original_images:
+                    original_image_path = "{}/{}/{}/原始图/{}".format(
+                        output_path, _date_folder, goods_art_no_folder, file
+                    )
+                    file_name = os.path.splitext(file)[0]
+                    if file_name not in all_moved_images:
+                        print("上传平台获取抠图结果----------》", file)
+                        goods_art_no_folder_path = "{}/{}/{}".format(
+                            output_path, _date_folder, goods_art_no_folder
+                        )
+                        pic_deal = PicDeal()
+                        # 上传平台获取抠图结果
+                        original_move_bg_image_path = "{}/原始图_已抠图/{}{}".format(
+                            goods_art_no_folder_path, file_name, ".png"
+                        )
+                        if pic_deal.remove_bg(
+                                in_path=original_image_path,
+                                out_path=original_move_bg_image_path,
+                        ):
+                            self.show_progress_detail(
+                                "货号图{} 已完成抠图处理~".format(file_name)
+                            )
+                            # 抠图处理成白底图 800*800
+                            out_800_path = "{}/800x800/{}{}".format(
+                                goods_art_no_folder_path, file_name, ".jpg"
+                            )
+                            pic_deal.create_800image(
+                                image_path=original_move_bg_image_path,
+                                out_path=out_800_path,
+                            )
+                            # 抠图处理成白底图 200*200
+                            out_200_path = "{}/200images/{}{}".format(
+                                goods_art_no_folder_path, file_name, ".jpg"
+                            )
+                            pic_deal.resize_and_save(
+                                image_path=out_800_path,
+                                out_path=out_200_path,
+                                width=200,
+                            )
+                            self.show_progress_detail(
+                                "货号图{} 已完成800*800图片和200*200图片制作~".format(
+                                    file_name
+                                )
+                            )
+                        else:
+                            self.show_progress_detail(
+                                "货号图{} 抠图处理失败~".format(file_name)
+                            )
+                        # 完成处理的图片进度
+                        n += 1
+                        self.progress_sign.emit(
+                            {
+                                "type": "处理图片 抠图、加工等",
+                                "progress_bar_value": int(n / to_do_images_total * 100),
+                            }
+                        )
+
+    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 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
+
+
+class TempItem(QWidget):
+    select_sign = Signal(str)
+
+    def __init__(self, parent, _id, image_path):
+        super().__init__(parent)
+        self.ui = UI_temp_item()
+        self.ui.setupUi(self)
+        self.image_path = image_path
+        self.id = _id
+        self.is_select = False
+        self.init()
+
+    def init(self):
+        self.ui.label.mousePressEvent = self.pic_show
+        self.ui.radioButton.clicked.connect(self.select)
+        self.set_label_image()
+
+    def cancel_select(self, *args):
+        self.is_select = False
+        self.ui.radioButton.setChecked(False)
+
+    def select(self, *args):
+        self.is_select = True
+        self.ui.radioButton.setChecked(True)
+        self.select_sign.emit(self.id)
+
+    def pic_show(self, *args, **kwargs):
+        if os.path.exists(self.image_path):
+            im = Image.open(self.image_path)
+            threading.Thread(target=im.show, args=()).start()
+
+    def set_label_image(self):
+        if not os.path.exists(self.image_path):
+            return
+        try:
+            bytes_io = io.BytesIO()
+            w, h = self.ui.image_show.width(), self.ui.image_show.height()
+            _img_raw = Image.open(self.image_path)
+            _img_raw = self.to_resize(_img_raw, width=w * 2)
+            _img_raw = _img_raw.crop(box=(0, 0, w * 2, h * 2))
+            # _img_raw.thumbnail((w * 2, int(_img_raw.height * w * 2 / _img_raw.width)))
+            # img_raw = _img_raw.resize(size=(w, h))
+            _img_raw.save(bytes_io, "JPEG")
+            icon = QPixmap()
+            # icon.loadFromData(img)
+            icon.loadFromData(bytes_io.getvalue())
+            icon = icon.scaled(
+                self.ui.image_show.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
+            )
+            self.ui.image_show.setPixmap(icon)
+            # self.mousePressEvent = lambda x: self.show_one_data(row)
+        except:
+            pass
+
+    def to_resize(self, _im, width=None, high=None):
+        _im_x, _im_y = _im.size
+        if width and high:
+            if _im_x >= _im_y:
+                high = None
+            else:
+                width = None
+        if width:
+            re_x = int(width)
+            re_y = int(_im_y * re_x / _im_x)
+        else:
+            re_y = int(high)
+            re_x = int(_im_x * re_y / _im_y)
+        _im = _im.resize((re_x, re_y))
+        return _im

+ 2 - 2
python/service/module_generate_goods_art_no_table.py

@@ -2,10 +2,10 @@
 import threading
 import xlsxwriter
 import shutil
-from pic_deal import Picture
+from .pic_deal import Picture
 
 
-from excel_base_func import *
+from .excel_base_func import *
 
 
 class GenerateGoodsArtNoTable():

+ 423 - 0
python/service/online_request/module_online_data.py

@@ -0,0 +1,423 @@
+import requests
+import settings
+import json
+import numpy as np
+
+
+class JsonEncoder(json.JSONEncoder):
+    """Convert numpy classes to JSON serializable objects."""
+
+    def default(self, obj):
+        if isinstance(obj, (np.integer, np.floating, np.bool_)):
+            return obj.item()
+        elif isinstance(obj, np.ndarray):
+            return obj.tolist()
+        else:
+            return super(JsonEncoder, self).default(obj)
+
+
+class OnlineDataRequest(object):
+    def __init__(self):
+        self.s = requests.session()
+        self.post_headers = {"Authorization": settings.Headers["Authorization"],
+                             "Origin": settings.Headers["Origin"],
+                             "Host": settings.Headers["Host"],
+                             "Content-Length": "0",
+                             "Content-Type": "application/json",
+                             "Accept": "application/json"}
+        print("28   Authorization:", self.post_headers["Authorization"])
+
+    def refresh_headers(self):
+        self.post_headers = {"Authorization": settings.Headers["Authorization"],
+                             "Origin": settings.Headers["Origin"],
+                             "Host": settings.Headers["Host"],
+                             "Content-Length": "0",
+                             "Content-Type": "application/json",
+                             "Accept": "application/json"}
+
+    def auth_user(self):
+        # 用户登录
+        url = "{domain}/api/auth/user".format(
+            domain=settings.DOMAIN
+        )
+        s = requests.session()
+        _s = s.get(url=url, headers=settings.Headers)
+        response_data = _s.json()
+        return response_data
+
+    def logout(self):
+        url = "{domain}/api/auth/logout".format(
+            domain=settings.DOMAIN
+        )
+        s = requests.session()
+        _s = s.post(url=url, headers=settings.Headers)
+
+    def get_change_bar_code(self, code):
+        url = "{domain}/api/hct/open/sting_search_goods?string={code}".format(domain=settings.DOMAIN, code=code)
+        try:
+            s = requests.get(url)
+            goods_art_no = s.json()["data"]["goods_art_no"]
+            return goods_art_no
+        except BaseException as e:
+            print(e)
+            return
+
+    def get_goods_art_no_info(self, numbers_list=None, goods_art_list=None):
+        # 获取商品基础信息,入参为商品的编号
+        url = "{domain}/api/backend/produce/goods/info".format(
+            domain=settings.DOMAIN
+        )
+        if numbers_list:
+            data = {
+                'numbers': numbers_list
+            }
+            print("请求编码:", numbers_list)
+        else:
+            data = {
+                'goods_art_nos': goods_art_list
+            }
+            print("请求货号:", goods_art_list)
+
+        post_headers = {"Authorization": settings.Headers["Authorization"],
+                        "Origin": settings.Headers["Origin"],
+                        "Host": settings.Headers["Host"],
+                        "Content-Length": "",
+                        "Content-Type": "application/json",
+                        "Accept": "application/json"}
+        data = json.dumps(data)
+        post_headers["Content-Length"] = str(len(data))
+        _s = self.s.post(url=url, data=data, headers=post_headers)
+        # _s = self.s.get(url=url, params=params, headers=settings.Headers)
+        response_data = _s.json()
+        # print(response_data)
+        # print("\n")
+
+        goods_number_data = {}
+        # ["", "", "", "", "", "", "", "", "", "", "", ]
+        if "data" not in response_data:
+            return {}
+
+        for data in response_data["data"]:
+            if numbers_list:
+                number = data["number"]
+            else:
+                number = data["goods_art_no"].upper()
+
+            goods_number_data[number] = {}
+            goods_number_data[number]["商品面料"] = data["fabric"]
+            goods_number_data[number]["商品内里"] = data["lining"]
+            goods_number_data[number]["商品鞋底"] = data["sole"]
+            goods_number_data[number]["后帮高"] = data["back_height"]
+            goods_number_data[number]["前掌宽"] = data["forefoot_width"]
+            goods_number_data[number]["鞋跟高"] = data["heel_height"]
+            goods_number_data[number]["FAB介绍"] = data["fab_info"]
+            goods_number_data[number]["编号"] = data["number"]
+            goods_number_data[number]["商品货号"] = data["goods_art_no"].upper()
+            goods_number_data[number]["款号"] = data["goods_number"].upper()
+            goods_number_data[number]["颜色名称"] = data["color"]
+            goods_number_data[number]["所属企划"] = data["projects"][0]
+            goods_number_data[number]["设计方名称"] = data["purchasing_unit"]
+            goods_number_data[number]["供应商"] = data["supplier_name"]
+            goods_number_data[number]["供应商编码"] = data["supplier_code"].lstrip('0')
+            goods_number_data[number]["供应商货号"] = data["supplier_goods_artno"]
+            goods_number_data[number]["销售工厂"] = data["sales_factory_name"]
+            goods_number_data[number]["销售组织"] = data["man_org_name"]
+            goods_number_data[number]["是否SAP"] = data["source"]
+            goods_number_data[number]["OEM报价"] = data["oem_price"]
+            goods_number_data[number]["出厂价"] = data["ex_factory_price"]
+            goods_number_data[number]["首单货期"] = data["earliest_delivery_date"]
+            goods_number_data[number]["包装"] = data["package_specification"]
+            goods_number_data[number]["创建日期"] = data["created_at"]
+            goods_number_data[number]["货号图"] = data["image"]
+        return goods_number_data
+
+    def get_on_goods_all_art(self, number):
+        # 获取商品基础信息,入参为商品的编号
+        url = "{domain}/api/backend/produce/goods/query/numbers?number={number}".format(
+            domain=settings.DOMAIN,
+            number=number
+        )
+        _s = self.s.get(url=url, headers=settings.Headers)
+        response_data = _s.json()
+        print(number, response_data)
+        """
+        14250230 {'data': {'goods_number': 'AC5200117', 'brother_goods_arts': [{'number': '14250232', 'goods_art_no': 'AC52001173', 'color': '杏色'}, {'number': '14250231', 'goods_art_no': 'AC52001172', 'color': '灰色'}, {'number': '14250230', 'goods_art_no': 'AC52001171', 'color': '黑色'}]}, 'code': 0, 'message': 'success'}
+        """
+        return response_data["data"]["goods_number"], response_data["data"]["brother_goods_arts"], \
+               response_data["data"]["goods_art_no"]
+
+    def get_views(self, image_url):
+
+        url = "http://{}/shoes_category".format(settings.VIEW_DEAL_DOMAIN)
+        data = {"train_path": "./datasets/Shoes_Dataset/Train/angle",
+                "model_filename": "./models/0320/output0320.pth",
+                "validate_path": image_url, }
+        _s = requests.post(url=url, data=json.dumps(data), )
+        response_data = _s.json()
+        return response_data["classify_result"]
+
+    def uploadImage(self, local_path: str) -> str:
+        post_headers = {"Authorization": settings.Headers["Authorization"]}
+        url = settings.DOMAIN + '/api/upload'
+        resultData = self.s.post(url, files={'file': open(local_path, 'rb')}, headers=post_headers).json()
+        return resultData['data']['url']
+
+    def get_current_menu(self):
+        def get_menu(_menu_dict, _data):
+            for menu in _data:
+                _menu_dict[menu["key"]] = {}
+                for mods in menu["mods_arr"]:
+                    _menu_dict[menu["key"]][mods["key"]] = mods["name"]
+                if "_child" in menu:
+                    get_menu(_menu_dict, menu["_child"])
+            return _menu_dict
+
+        url = "{domain}/api/backend/basic/get_current_menu".format(
+            domain=settings.DOMAIN,
+        )
+        _s = self.s.get(url=url, headers=settings.Headers)
+        response_data = _s.json()
+        try:
+            menu_data = response_data["data"]["pc_menu"]
+            menu_dict = {}
+            menu_dict = get_menu(menu_dict, menu_data)
+        except:
+            menu_dict = {}
+        # print(json.dumps(menu_dict,ensure_ascii=False))
+        # raise 1
+        return menu_dict
+
+    # 获取所有资源的配置
+    def get_resource_config(self):
+        url = "{domain}/api/openai/query_client_addons".format(domain=settings.DOMAIN)
+        _s = self.s.get(url=url, headers=settings.Headers, params={"type": "client_camera"})
+        response_data = _s.json()
+        return response_data
+
+    # 拍照日志上报
+    def add_auto_photo_logs(self, data):
+        url = "{domain}/api/openai/add_auto_photo_logs".format(domain=settings.DOMAIN)
+        post_data = {"goods_no": data["goods_art_no"],
+                     "take_photo_created_at": data["take_photo_created_at"],
+                     "photo_created_at": data["photo_create_time"],
+                     "image_dispose_mode": data["image_deal_mode"],
+                     "photo_serial_number": data["image_index"], }
+
+        post_data = json.dumps(post_data)
+
+        _s = self.s.post(url=url, headers=self.post_headers, data=post_data)
+        response_data = _s.json()
+        if settings.IS_TEST:
+            print("209-----拍照日志上报   add_auto_photo_logs")
+            print(response_data)
+        return response_data
+
+    def upload_pic_list_data(self, data):
+        url = "{domain}/api/backend/goods/save/images".format(
+            domain=settings.DOMAIN
+        )
+        data = json.dumps(data)
+        self.post_headers["Content-Length"] = str(len(data))
+        _s = self.s.post(url=url, data=data, headers=self.post_headers)
+        response_data = _s.json()
+        try:
+            if response_data["code"] == 0 and response_data["message"] == "success":
+                return True
+            else:
+                print(data)
+                print(response_data)
+                return False
+        except BaseException as e:
+            print(data)
+            print(e)
+            print(response_data)
+            return False
+
+    def upload_pic(self, goods_data):
+        # 检查货号图是否存在
+        url = "{domain}/api/backend/upload".format(
+            domain=settings.DOMAIN
+        )
+        # print(url)
+        headers = {'Authorization': settings.Headers["Authorization"],
+                   'User-Agent': settings.Headers["User-Agent"],
+                   'Origin': settings.Headers["Origin"],
+                   'Host': settings.Headers["Host"], }
+
+        files = [
+            ('file',
+             (goods_data["file_path"], goods_data["image_io"], 'image/{}'.format(goods_data["e"])))
+        ]
+
+        _s = requests.post(url=url, headers=headers, files=files)
+        response_data = _s.json()
+
+        return response_data["data"]["url"]
+
+    # 查询是否已有详情图
+    def check_detail_image(self, goods_art_no):
+        url = "{domain}/api/backend/goods/check_detail_image?number={number}".format(domain=settings.DOMAIN,
+                                                                                     number=goods_art_no)
+        _s = self.s.get(url=url, headers=settings.Headers)
+        response_data = _s.json()
+        # print(response_data)
+        return response_data["data"]["hasDetailImage"]
+
+    # 调用API识别是否是拖鞋
+    def yolo_shoes_category(self, image_url):
+        url = "{domain}/api/ai_image/main/yolo_shoes_category".format(domain=settings.DOMAIN)
+        post_data = {"image_url": image_url,
+                     }
+
+        post_data = json.dumps(post_data)
+
+        _s = self.s.post(url=url, headers=self.post_headers, data=post_data)
+        response_data = _s.json()
+        if settings.IS_TEST:
+            print("278-----yolo_shoes_category")
+            print(response_data)
+
+        r_data = None
+        try:
+            r_data = response_data["data"]["category"]
+        except BaseException as e:
+            print("285", e)
+        return r_data
+
+    # 图片上传by IO
+    def upload_image_by_io(self, image_io) -> str:
+        post_headers = {"Authorization": settings.Authorization}
+        url = settings.DOMAIN + '/api/upload'
+        resultData = self.s.post(url, files={'file': image_io}, headers=post_headers).json()
+        return resultData['data']['url']
+
+
+class GetOnlineDataHLM(OnlineDataRequest):
+
+    def upload_pic(self, goods_data):
+        # 检查货号图是否存在
+        url = "{domain}/api/backend/upload".format(
+            domain=settings.DOMAIN
+        )
+        # print(url)
+        headers = {'Authorization': settings.Headers["Authorization"],
+                   'User-Agent': settings.Headers["User-Agent"],
+                   'Origin': settings.Headers["Origin"],
+                   'Host': settings.Headers["Host"], }
+
+        files = [
+            ('file',
+             (goods_data["file_path"], goods_data["image_io"], 'image/{}'.format(goods_data["e"])))
+        ]
+
+        _s = requests.post(url=url, headers=headers, files=files)
+        response_data = _s.json()
+
+        return response_data["data"]["url"]
+
+    def upload_pic_list_data(self, data):
+        url = "{domain}/api/backend/goods/save/images".format(
+            domain=settings.DOMAIN
+        )
+        data = json.dumps(data)
+        self.post_headers["Content-Length"] = str(len(data))
+        _s = self.s.post(url=url, data=data, headers=self.post_headers)
+        response_data = _s.json()
+        try:
+            if response_data["code"] == 0 and response_data["message"] == "success":
+                return True
+            else:
+                print(response_data)
+                return False
+        except BaseException as e:
+            print(e)
+            print(response_data)
+            return False
+
+    def get_goods_art_no_info(self, numbers_list=None, goods_art_list=None):
+        # 获取商品基础信息,入参为商品的编号
+        url = "{domain}/api/backend/goods_client/goods_query".format(
+            domain=settings.DOMAIN
+        )
+        data = {
+            'goods_art_list': goods_art_list
+        }
+        print("url:", url)
+        print("请求货号:", goods_art_list)
+
+        post_headers = {"Authorization": settings.Headers["Authorization"],
+                        "Origin": settings.Headers["Origin"],
+                        "Host": settings.Headers["Host"],
+                        "Content-Length": "",
+                        "Content-Type": "application/json",
+                        "Accept": "application/json"}
+        data = json.dumps(data)
+        print(post_headers)
+        print(data)
+        # post_headers["Content-Length"] = str(len(data))
+        _s = self.s.post(url=url, data=data, headers=post_headers)
+        # _s = self.s.get(url=url, params=params, headers=settings.Headers)
+        response_data = _s.json()
+        print(response_data)
+        print("\n")
+
+        goods_number_data = {}
+        # ["", "", "", "", "", "", "", "", "", "", "", ]
+        if "data" not in response_data:
+            return {}
+
+        for data in response_data["data"]:
+            goods_number_data[data["goods_art_no"]] = {}
+            goods_number_data[data["goods_art_no"]]["商品货号"] = data["goods_art_no"].upper()
+            goods_number_data[data["goods_art_no"]]["款号"] = data["goods_number"].upper()
+            goods_number_data[data["goods_art_no"]]["商品面料"] = data["fabric"]
+            goods_number_data[data["goods_art_no"]]["商品内里"] = data["lining"]
+            goods_number_data[data["goods_art_no"]]["商品鞋底"] = data["sole"]
+            goods_number_data[data["goods_art_no"]]["鞋垫"] = data["insole"]
+            goods_number_data[data["goods_art_no"]]["颜色名称"] = data["color"]
+
+        return goods_number_data
+
+    def uploadImage(self, local_path: str) -> str:
+        post_headers = {"Authorization": settings.Authorization}
+        url = settings.DOMAIN + '/api/upload'
+        resultData = self.s.post(url, files={'file': open(local_path, 'rb')}, headers=post_headers).json()
+        return resultData['data']['url']
+
+    # ============pixian抠图处理==========================
+    def dispose_point(self, _type):
+        # 扣分 sub;add为增加分数,每次操作一分
+        url = "{domain}/api/ai_image/client/dispose_point".format(
+            domain=settings.DOMAIN)
+        data = {"type": _type}
+        _s = self.s.post(url=url, headers=self.post_headers, data=json.dumps(data), timeout=10)
+        response_data = _s.json()
+        return response_data
+
+    def send_message(self, text):
+        # 发送钉钉消息
+        url = "{domain}/api/ai_image/client/send_message".format(
+            domain=settings.DOMAIN)
+        data = {"message": text}
+        _s = self.s.post(url=url, headers=self.post_headers, data=json.dumps(data), timeout=10)
+        response_data = _s.json()
+        return response_data
+
+    def get_cutout_image_times(self):
+        # 获取抠图剩余次数
+        url = "{domain}/api/ai_image/client/search_company_balance".format(
+            domain=settings.DOMAIN)
+        _s = self.s.post(url=url, headers=self.post_headers, timeout=10)
+        response_data = _s.json()
+        if "data" not in response_data:
+            return False
+        else:
+            return response_data["data"]
+
+    def get_key_secret(self):
+        # 获取抠图剩余次数
+        url = "{domain}/api/ai_image/client/get_key_serect".format(
+            domain=settings.DOMAIN)
+        _s = self.s.post(url=url, headers=self.post_headers, timeout=10)
+        response_data = _s.json()
+        return response_data["data"]

+ 230 - 0
python/service/remove_bg_pixian.py

@@ -0,0 +1,230 @@
+import copy
+import os
+from PIL import Image
+from .remove_bg_ali import RemoveBgALi
+import requests
+from io import BytesIO
+
+
+class Segment(object):
+    def __init__(self):
+        self.k = "pxnib99dbchtmdm"
+        self.s = "ub9uj5678gs4m2bnrass1t3tn6ughlk065ianosk06akagolcr2u"
+
+    def get_no_bg_goods2(self, file_path=None, _im=None):
+        im = _im
+        img = BytesIO()
+        try:
+            im.save(img, format='JPEG')  # format: PNG or JPEG
+        except:
+            im.save(img, format='PNG')  # format: PNG or JPEG
+        img.seek(0)  # rewind to the start
+
+        # img = "https://ossimg.valimart.net/uploads/vali_ai/20241011/172864207036098.png"
+
+        response = requests.post(
+            'http://47.76.110.118:27777/api/v2/remove-background',
+            files={'image.url': img},
+            data={
+                # Add more upload options here
+            },
+            auth=(self.k, self.s)
+        )
+        # print(response.content)
+        if response.status_code == requests.codes.ok:
+            return response.content, ""
+        else:
+            return None, response.content
+
+    def get_no_bg_goods_by_url(self, url):
+        response = requests.post(
+            'https://api.pixian.ai/api/v2/remove-background',
+            data={
+                'image.url': url
+            },
+            auth=(self.k, self.s)
+        )
+
+        if response.status_code == requests.codes.ok:
+            return response.content, ""
+        else:
+            print("response.status_code:", response.status_code)
+            return None, response.content
+
+    def get_no_bg_goods(self, file_path=None, _im=None, key=None):
+        im = _im
+        img = BytesIO()
+        try:
+            im.save(img, format='JPEG')  # format: PNG or JPEG
+        except:
+            im.save(img, format='PNG')  # format: PNG or JPEG
+        img.seek(0)  # rewind to the start
+
+        if key:
+            # 切换key
+            auth = key
+            # auth = (self.k, self.s)
+        else:
+            auth = (self.k, self.s)
+
+
+        try:
+            response = requests.post(
+                'https://api.pixian.ai/api/v2/remove-background',
+                files={'image': img},
+                data={
+                    # Add more upload options here
+                },
+                auth=auth,
+                timeout=40
+            )
+        except BaseException as e:
+            data = {"im": None,
+                    "status_code": "time_out",
+                    "message":"{}".format(e)
+                    }
+            return data
+
+        # print(response.content)
+
+        data = {"im": None,
+                "status_code": response.status_code, }
+
+        if response.status_code == requests.codes.ok:
+            data["im"] = Image.open(BytesIO(response.content))
+
+        return data
+
+
+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 RemoveBgPiXian(object):
+    def __init__(self):
+        self.segment = Segment()
+        self.r = RemoveBgALi()
+
+    def direct_matting_image(self, image):
+        #不能超过32,000,000尺寸的数据
+        x, y = image.size
+        f = False
+        if x * y > 32000000:
+            r = 32000000 / x * y
+            image = image.resize(size=(int(x * r), int(y * r)))
+            f = True
+        pic, _ = self.segment.get_no_bg_goods(file_path=None, _im=image)
+        if not pic:
+            return None, _
+        _img_im = Image.open(BytesIO(pic))  # 阿里返回的抠图结果 已转PIL对象
+        if f:
+            _img_im = _img_im.resize(size=(x, y))
+        return _img_im, ""
+
+    def run_by_image_url(self, url):
+        pic, _ = self.segment.get_no_bg_goods_by_url(url)
+        if pic is not None:
+            _img_im = Image.open(BytesIO(pic))
+            return _img_im, None
+        else:
+            return None, _
+
+    def run_by_image_im(self, im, key):
+        return self.segment.get_no_bg_goods(_im=im, key=key)
+
+    def get_image_cut(self, file_path, out_file_path=None, original_im=None, image_preprocessing=False, is_test=False):
+        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 False, {"data": "抠图图片不能是PNG"}
+
+        if is_test:
+            cut_image = self.r.get_image_cut(file_path=None, out_file_path=None, original_im=original_pic.im)
+            if out_file_path:
+                cut_image.save(out_file_path)
+            return True, {}
+
+        if image_preprocessing:
+            cut_image = self.r.get_image_cut(file_path=None, out_file_path=None, original_im=original_pic.im)
+            image_deal_info = {}
+            x1, y1, x2, y2 = cut_image.getbbox()
+            image_deal_info["鞋子原始位置"] = (x1, y1, x2, y2)
+            o_w, o_h = cut_image.size
+            image_deal_info["鞋子原始抠图后大小"] = (o_w, o_h)
+            # 扩边处理
+            _w, _h = x2 - x1, y2 - y1
+            out_px = 0.06
+            _w, _h = int(out_px * _w), int(out_px * _h)
+            n_x1, n_y1, n_x2, n_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h
+            if n_x1 < 0:
+                n_x1 = 0
+            if n_y1 < 0:
+                n_y1 = 0
+            if n_x2 > o_w:
+                n_x2 = o_w
+            if n_y2 > o_h:
+                n_y2 = o_h
+
+            image_deal_info["抠图扩边后位置"] = (n_x1, n_y1, n_x2, n_y2)
+            cut_image = original_pic.im.crop(image_deal_info["抠图扩边后位置"])
+            image_deal_info["抠图扩边后图片大小"] = cut_image.size
+            image_deal_info["原始图片大小"] = (original_pic.x, original_pic.y)
+
+            # 使用pixian进行抠图
+            second_cut_image, _ = self.direct_matting_image(image=cut_image)
+            if not second_cut_image:
+                return False, {"data": _}
+
+            if second_cut_image.size != image_deal_info["抠图扩边后图片大小"]:
+                print("图片尺寸还原")
+                second_cut_image = second_cut_image.resize(image_deal_info["抠图扩边后图片大小"])
+
+            # 创建空白图片并粘贴回去
+            _img_im = Image.new(mode="RGBA", size=image_deal_info["原始图片大小"], color=(0, 0, 0, 0))
+            _img_im.paste(second_cut_image, box=(image_deal_info["抠图扩边后位置"][0], image_deal_info["抠图扩边后位置"][1]))
+        else:
+            _img_im = self.direct_matting_image(image=original_pic.im)
+            pass
+
+        if out_file_path:
+            _img_im.save(out_file_path)
+        return True, {}
+
+    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 = RemoveBgPiXian()
+    path = r"C:\Users\gymmc\Desktop\白底部分品类45度\测试\eva_keai_1.png"
+    out_path = "{}._no_bg-out.png".format(path)
+    r.get_image_cut(path, out_file_path=out_path)

+ 22 - 23
python/service/run_main.py

@@ -1,15 +1,13 @@
 import settings
-from module.view_control.generate_goods_no_detail_pic import detail_func
+from .detail_func import *
 import json
-from base import *
-from import_qt_mode import *
-from module.view_control.match_and_cutout_mode_control.base_deal_image_v2 import BaseDealImage
-from module.view_control.MineQWidget import DialogShow, WorkerOneThread
+from .base import *
+from .match_and_cutout_mode_control.base_deal_image_v2 import BaseDealImage
+# from module.view_control.MineQWidget import DialogShow, WorkerOneThread
 import threading
 from concurrent.futures import ThreadPoolExecutor
 import concurrent.futures
-from module.view_control.generate_goods_no_detail_pic.data import DataModeGenerateDetail, DataModeUploadPic
-from module.view_control.generate_goods_no_detail_pic.detail_func import create_folder
+from .data import DataModeGenerateDetail
 import time
 from PIL import Image
 from io import BytesIO
@@ -17,27 +15,27 @@ import os, re
 from functools import partial
 # from multiprocessing import Process, Queue
 import pickle
-from  base_deal import BaseDealImage
+from  .base_deal import BaseDealImage
 
 
-class RunMain(QThread):
-    run_end_sign = Signal(dict)
-    show_dialog_sign = Signal(dict)
-    show_progress_detail_sign = Signal(str)
+class RunMain():
+    # run_end_sign = Signal(dict)
+    # show_dialog_sign = Signal(dict)
+    # show_progress_detail_sign = Signal(str)
 
-    # 定义一个信号用于请求显示对话框
-    show_dialog_signal = Signal()
-    # 定义一个信号用于返回对话框的结果
-    dialog_result_signal = Signal(str)
+    # # 定义一个信号用于请求显示对话框
+    # show_dialog_signal = Signal()
+    # # 定义一个信号用于返回对话框的结果
+    # dialog_result_signal = Signal(str)
 
     # dialog_result_signal = Signal(str)
 
-    def __init__(self, windows):
+    def __init__(self):
         super().__init__()
-        self.windows = windows
+        # self.windows = windows
         self.dialog_result = ""
         self.data_mode_generate_detail = DataModeGenerateDetail()
-        self.dialog_result_signal.connect(self.on_dialog_result)
+        # self.dialog_result_signal.connect(self.on_dialog_result)
         self.event = threading.Event()
 
     def set_state(self, state_value: int):
@@ -68,10 +66,10 @@ class RunMain(QThread):
         special_goods_art_no_folder_line = config_data["special_goods_art_no_folder_line"]
 
         # 自动处理红蜻蜓货号,进行重命名
-        if settings.PROJECT == "红蜻蜓":
-            # 规整红蜻蜓货号图
-            all_goods_art_no_folder_data = get_all_goods_art_no_folder(path=image_dir)
-            BaseDealImage().rename_folder_for_hqt(all_goods_art_no_folder_data=all_goods_art_no_folder_data)
+        # if settings.PROJECT == "红蜻蜓":
+        #     # 规整红蜻蜓货号图
+        #     all_goods_art_no_folder_data = get_all_goods_art_no_folder(path=image_dir)
+        #     BaseDealImage().rename_folder_for_hqt(all_goods_art_no_folder_data=all_goods_art_no_folder_data)
 
         # 重新获取文件夹信息
         all_goods_art_no_folder_data = get_all_goods_art_no_folder(path=image_dir)
@@ -924,6 +922,7 @@ class RunMain(QThread):
     def show_progress_detail(self, text):
         # self.show_progress_detail_sign.emit(text)
         # self.windows.show_progress_detail(text)
+        pass
 
     def detail_deal_one_data(self, goods_no, value, out_put_dir, temp_name, assigned_page_list):
         if self.windows.state == 99:

+ 11 - 10
python/service/upload_pic.py

@@ -1,6 +1,5 @@
-from import_qt_mode import *
-from module.view_control.generate_goods_no_detail_pic.data import DataModeUploadPic
-from module.view_control.MineQWidget import DialogShow, WorkerOneThread
+from .data import DataModeUploadPic
+# from module.view_control.MineQWidget import DialogShow, WorkerOneThread
 import os
 import threading
 import time
@@ -11,10 +10,10 @@ from io import BytesIO
 
 
 # 详情图上传
-class UploadPic(QObject):
-    signal_data = Signal(dict)
-    run_end_sign = Signal(dict)
-    show_progress_detail_sign = Signal(str)
+class UploadPic():
+    # signal_data = Signal(dict)
+    # run_end_sign = Signal(dict)
+    # show_progress_detail_sign = Signal(str)
 
     def __init__(self, windows, to_deal_dir, config_data):
         super().__init__()
@@ -36,11 +35,13 @@ class UploadPic(QObject):
     def run_by_thread(self):
         self.run_by_thread_real()
         self.config_data["sign_text"] = "结束"
-        self.run_end_sign.emit(self.config_data)
+        # self.run_end_sign.emit(self.config_data)
+        self.config_data
 
     def run(self):
-        self._w_5 = WorkerOneThread(func=self.run_by_thread, name="_w_5")
-        self._w_5.start()
+        pass
+        # self._w_5 = WorkerOneThread(func=self.run_by_thread, name="_w_5")
+        # self._w_5.start()
 
     def run_by_thread_real(self, *args):
         self.show_info("====开始处理====")

+ 102 - 0
python/utils/utils_func.py

@@ -137,3 +137,105 @@ def httpPosthandler(url, data=None, json=None, headers=None):
     except requests.exceptions.RequestException as e:
         print(f"POST请求失败: {e}")
         return None
+
+
+# 指定一个列表,指定一个需要移动的数据列表,移动到指定列表中的位置
+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 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