Przeglądaj źródła

Merge remote-tracking branch 'origin/master'

panqiuyao 7 miesięcy temu
rodzic
commit
840f7c341d

+ 1 - 1
electron/config/config.default.js

@@ -64,7 +64,7 @@ module.exports = (appInfo) => {
    * 远程模式-web地址
    */
   config.remoteUrl = {
-    enable: true,
+    enable: false,
     url: 'http://localhost:3000/#/home'
   };
 

+ 8 - 1
electron/config/config.prod.js

@@ -9,7 +9,7 @@ module.exports = (appInfo) => {
   /**
    * 开发者工具
    */
-  config.openDevTools = true;
+  config.openDevTools = false;
 
   /**
    * 应用程序顶部菜单
@@ -22,6 +22,13 @@ module.exports = (appInfo) => {
   config.jobs = {
     messageLog: false
   };
+  /**
+   * 远程模式-web地址
+   */
+  config.remoteUrl = {
+    enable: false,
+    url: 'http://localhost:3000/#/home'
+  };
 
   return {
     ...config

+ 1 - 1
electron/controller/utils.js

@@ -62,7 +62,7 @@ class UtilsController extends Controller {
     win.loadURL(config.url); // 设置窗口的 URL
     // 监听窗口关闭事件
 
-    win.webContents.openDevTools(config.openDevTools);
+   // win.webContents.openDevTools(config.openDevTools);
     win.on('close', () => {
       delete this.app.electron[config.id]; // 删除窗口引用
     });

+ 5 - 145
python/action.json

@@ -1,6 +1,6 @@
 [
     {
-        "mode_type": "执行左脚程序",
+        "tab_id": 1,
         "execution_type": "程序1",
         "action_name": "俯视",
         "action_index": 10,
@@ -20,7 +20,7 @@
         "is_need_confirm": false
     },
     {
-        "mode_type": "执行左脚程序",
+        "tab_id": 1,
         "execution_type": "程序1",
         "action_name": "侧视",
         "action_index": 20,
@@ -40,7 +40,7 @@
         "is_need_confirm": false
     },
     {
-        "mode_type": "执行左脚程序",
+        "tab_id": 1,
         "execution_type": "程序1",
         "action_name": "后跟",
         "action_index": 30,
@@ -60,7 +60,7 @@
         "is_need_confirm": false
     },
     {
-        "mode_type": "执行左脚程序",
+        "tab_id": 1,
         "execution_type": "程序2",
         "action_name": "鞋底",
         "action_index": 40,
@@ -80,7 +80,7 @@
         "is_need_confirm": false
     },
     {
-        "mode_type": "执行左脚程序",
+        "tab_id": 1,
         "execution_type": "程序2",
         "action_name": "内里",
         "action_index": 50,
@@ -98,145 +98,5 @@
         "led_switch": false,
         "is_wait": false,
         "is_need_confirm": false
-    },
-    {
-        "mode_type": "执行左脚程序",
-        "execution_type": "程序2",
-        "action_name": "初始化位置",
-        "action_index": 60,
-        "picture_index": 99,
-        "camera_height": 200,
-        "camera_angle": 12.0,
-        "is_system": false,
-        "number_focus": 1,
-        "take_picture": false,
-        "turntable_position": 300.0,
-        "turntable_angle": 0.0,
-        "shoe_upturn": false,
-        "pre_delay": 0.0,
-        "after_delay": 0.0,
-        "led_switch": true,
-        "is_wait": false,
-        "is_need_confirm": false
-    },
-    {
-        "mode_type": "执行右脚程序",
-        "execution_type": "程序2",
-        "action_name": "俯视",
-        "action_index": 10,
-        "picture_index": 99,
-        "camera_height": 200,
-        "camera_angle": 14.0,
-        "is_system": true,
-        "number_focus": 2,
-        "take_picture": true,
-        "turntable_position": 300.0,
-        "turntable_angle": -32.0,
-        "shoe_upturn": false,
-        "pre_delay": 0.0,
-        "after_delay": 0.0,
-        "led_switch": false,
-        "is_wait": false,
-        "is_need_confirm": false
-    },
-    {
-        "mode_type": "执行右脚程序",
-        "execution_type": "程序2",
-        "action_name": "侧视",
-        "action_index": 20,
-        "picture_index": 99,
-        "camera_height": 0,
-        "camera_angle": 3.0,
-        "is_system": true,
-        "number_focus": 0,
-        "take_picture": true,
-        "turntable_position": 300.0,
-        "turntable_angle": 0.0,
-        "shoe_upturn": false,
-        "pre_delay": 0.0,
-        "after_delay": 0.0,
-        "led_switch": false,
-        "is_wait": false,
-        "is_need_confirm": false
-    },
-    {
-        "mode_type": "执行右脚程序",
-        "execution_type": "程序2",
-        "action_name": "后跟",
-        "action_index": 30,
-        "picture_index": 99,
-        "is_system": true,
-        "camera_height": 0,
-        "camera_angle": 3.0,
-        "number_focus": 0,
-        "take_picture": true,
-        "turntable_position": 450.0,
-        "turntable_angle": 70.0,
-        "shoe_upturn": false,
-        "pre_delay": 0.0,
-        "after_delay": 0.0,
-        "led_switch": false,
-        "is_wait": false,
-        "is_need_confirm": false
-    },
-    {
-        "mode_type": "执行右脚程序",
-        "execution_type": "程序2",
-        "action_name": "鞋底",
-        "action_index": 40,
-        "picture_index": 99,
-        "is_system": true,
-        "camera_height": 0,
-        "camera_angle": 3.0,
-        "number_focus": 0,
-        "take_picture": true,
-        "turntable_position": 100.0,
-        "turntable_angle": 0.0,
-        "shoe_upturn": true,
-        "pre_delay": 0.0,
-        "after_delay": 0.0,
-        "led_switch": false,
-        "is_wait": false,
-        "is_need_confirm": false
-    },
-    {
-        "mode_type": "执行右脚程序",
-        "execution_type": "程序2",
-        "action_name": "内里",
-        "action_index": 50,
-        "picture_index": 99,
-        "is_system": true,
-        "camera_height": 0,
-        "camera_angle": 3.0,
-        "number_focus": 0,
-        "take_picture": true,
-        "turntable_position": 500.0,
-        "turntable_angle": 180.0,
-        "shoe_upturn": false,
-        "pre_delay": 0.0,
-        "after_delay": 0.0,
-        "led_switch": false,
-        "is_wait": false,
-        "is_need_confirm": false
-    },
-    {
-        "mode_type": "执行右脚程序",
-        "execution_type": "程序2",
-        "action_name": "初始化位置",
-        "action_index": 60,
-        "picture_index": 99,
-        "camera_height": 200,
-        "is_system": false,
-        "camera_angle": 12.0,
-        "number_focus": 1,
-        "take_picture": false,
-        "turntable_position": 300.0,
-        "turntable_angle": 0.0,
-        "shoe_upturn": false,
-        "pre_delay": 0.0,
-        "after_delay": 0.0,
-        "led_switch": true,
-        "is_wait": false,
-        "is_need_confirm": false
     }
 ]

+ 50 - 0
python/action_tabs.json

@@ -0,0 +1,50 @@
+[
+    {
+        "mode_type": 0,
+        "mode_name": "执行左脚程序"
+    },
+    {
+        "mode_type": 0,
+        "mode_name": "自定义配置1"
+    },
+    {
+        "mode_type": 0,
+        "mode_name": "自定义配置2"
+    },
+    {
+        "mode_type": 0,
+        "mode_name": "自定义配置3"
+    },
+    {
+        "mode_type": 0,
+        "mode_name": "自定义配置4"
+    },
+    {
+        "mode_type": 0,
+        "mode_name": "自定义配置5"
+    },
+    {
+        "mode_type": 1,
+        "mode_name": "执行右脚程序"
+    },
+    {
+        "mode_type": 1,
+        "mode_name": "自定义配置1"
+    },
+    {
+        "mode_type": 1,
+        "mode_name": "自定义配置2"
+    },
+    {
+        "mode_type": 1,
+        "mode_name": "自定义配置3"
+    },
+    {
+        "mode_type": 1,
+        "mode_name": "自定义配置4"
+    },
+    {
+        "mode_type": 1,
+        "mode_name": "自定义配置5"
+    }
+]

+ 141 - 68
python/api.py

@@ -16,7 +16,7 @@ import time, shutil, os
 from sqlalchemy import and_, asc, desc
 from functools import partial
 from service.deal_image import DealImage
-from databases import DeviceConfig, SysConfigs, SqlQuery, CRUD, select
+from databases import DeviceConfig, SysConfigs, SqlQuery, CRUD, select,DeviceConfigTabs
 from service.run_main import RunMain
 import importlib
 from service.auto_deal_pics.upload_pic import UploadPic
@@ -49,13 +49,6 @@ async def index():
     return {"message": "Hello World"}
 
 
-@app.get("/send_test")
-async def index():
-    data = {"data1": 1, "data2": 2, "data3": 3, "data4": 4}
-    await socket_manager.send_message(msg="测试", data=data)
-    return {"message": "Hello World"}
-
-
 @app.get("/scan_serials", description="扫描可用的设备端口")
 async def scanSerials():
     """扫描串口"""
@@ -64,44 +57,6 @@ async def scanSerials():
     return {"message": "Hello World"}
 
 
-@app.get("/test_conndevice")
-def test_conndevice():
-    device_control = DeviceControl()
-    p_list = []
-    temp_ports_dict = {}
-    # while 1:
-    time.sleep(1)
-    ports_dict = device_control.scan_serial_port()
-    temp_ports_dict = ports_dict
-
-    if not ports_dict:
-        # 全部清空 移除所有串口
-        if p_list:
-            _p = p_list.pop()
-            device_control.remove_port(_p)
-        # continue
-
-    if ports_dict:
-        # print(plist)
-        for index, _i in enumerate(p_list):
-            if _i not in ports_dict:
-                _p = p_list.pop(index)
-                device_control.remove_port(_p)
-
-        for _port_name, _port_value in ports_dict.items():
-            if _port_name not in p_list:
-                try:
-                    p_list.append(_port_name)
-                    device_control.add_port_by_linkage(_port_name)
-                except BaseException as e:
-                    print(e.__traceback__.tb_frame.f_globals["__file__"])  # 发生异常所在的文件
-                    print(e.__traceback__.tb_lineno)  # 发生异常所在的行数
-                    print("串口不存在{} {}".format(_port_name, e))
-
-            # threading.Thread(target=self.add_port, args=(_port_name, _port_value)).start()
-            # self.add_port(_p)
-
-
 @app.api_route("/forward_request", methods=["GET", "POST"], description="代理转发hlm项目得请求")
 async def forwardRequest(request: HlmForwardRequest):
     """
@@ -155,6 +110,10 @@ def fromExcelHandler(params: HandlerDetail):
             image_dir = "{}/data/".format(os.getcwd()).replace("\\", "/")
             check_path(image_dir)
             for itemImg in images:
+                if itemImg.image_path == "" or itemImg.image_path == None:
+                    raise UnicornException(
+                        f"货号【{goods_art_no}】存在没有拍摄完成的图片,请重拍或删除后重试"
+                    )
                 if not os.path.exists(image_dir + "/" + os.path.basename(itemImg.image_path)):
                     shutil.copy(itemImg.image_path, image_dir)
             dealImage = DealImage(image_dir)
@@ -181,12 +140,14 @@ def fromExcelHandler(params: HandlerDetail):
                 "cutout_mode": settings.CUTOUT_MODE,
                 "logo_path": params.logo_path,
                 "special_goods_art_no_folder_line": "",
-                "is_use_excel": (False if params.excel_path == "" else True),  # 是否使用excel
+                "is_use_excel": (
+                    False if params.excel_path == "" else True
+                ),  # 是否使用excel
                 "excel_path": params.excel_path,  # excel路径
                 "is_check_color_is_all": False,
-                "cutout_is_pass": True,
+                "cutout_is_pass": False,
                 "assigned_page_dict": {},
-                "detail_is_pass": True,
+                "detail_is_pass": False,
                 "upload_is_pass": False,
                 "upload_is_enable": False,
                 "is_filter": False,
@@ -258,6 +219,7 @@ def fromExcelHandler(params: HandlerDetail):
 @app.post("/handle_detail")
 async def handle_detail(request: Request, params: HandlerDetail):
     goods_art_no_array = params.goods_art_no
+    is_only_cutout = params.is_only_cutout
     handler_result = []
     handler_result_folder = ""
     if params.excel_path != "" and params.excel_path != None:
@@ -272,9 +234,19 @@ async def handle_detail(request: Request, params: HandlerDetail):
             images = pr.read_all(session, conditions={"goods_art_no": goods_art_no})
             if not images:
                 raise UnicornException("没有可用货号数据")
+            detail_counts = len(params.template_image_order.split(","))
+            image_counts = len(images)
+            if image_counts < detail_counts:
+                raise UnicornException(
+                    f"货号:[{goods_art_no}],实际照片数量:{image_counts}张,小于详情图要求数量:{detail_counts}张"
+                )
             image_dir = "{}/data/".format(os.getcwd()).replace("\\", "/")
             check_path(image_dir)
             for itemImg in images:
+                if itemImg.image_path == "" or itemImg.image_path == None:
+                    raise UnicornException(
+                        f"货号【{goods_art_no}】存在没有拍摄完成的图片,请重拍或删除后重试"
+                    )
                 if not os.path.exists(image_dir + "/" + os.path.basename(itemImg.image_path)):
                     shutil.copy(itemImg.image_path, image_dir)
             dealImage = DealImage(image_dir)
@@ -297,12 +269,14 @@ async def handle_detail(request: Request, params: HandlerDetail):
                 "cutout_mode": settings.CUTOUT_MODE,
                 "logo_path": params.logo_path,
                 "special_goods_art_no_folder_line": "",
-                "is_use_excel": (False if params.excel_path == "" else True),  # 是否使用excel
+                "is_use_excel": (
+                    False if params.excel_path == "" else True
+                ),  # 是否使用excel
                 "excel_path": params.excel_path,  # excel路径
                 "is_check_color_is_all": False,
-                "cutout_is_pass": True,
+                "cutout_is_pass": False,
                 "assigned_page_dict": {},
-                "detail_is_pass": True,
+                "detail_is_pass": False,
                 "upload_is_pass": False,
                 "upload_is_enable": False,
                 "is_filter": False,
@@ -325,6 +299,18 @@ async def handle_detail(request: Request, params: HandlerDetail):
             return_data = run_main.check_before_cutout(config_data)
             cutout_res = run_main.check_for_cutout_image_first_call_back(return_data)
             check_for_detail_first_res = None
+            if is_only_cutout == 1 and cutout_res == True:
+                out_put_dir = return_data["data"]["image_dir"] + "/" + goods_art_no
+                out_put_dir_path = "{}/{}".format(os.getcwd(), out_put_dir).replace(
+                    "\\", "/"
+                )
+                # print("out_put_dir_path", out_put_dir_path)
+                handler_result_folder = out_put_dir_path
+                handler_result.append(
+                    {"goods_art_no": goods_art_no, "success": True, "info": "处理成功"}
+                )
+                # print("检测到需要裁剪图片,开始裁剪图片", return_data)
+                continue
             if cutout_res == True:
                 return_data_check_before_detail = run_main.check_before_detail(config_data)
                 print(
@@ -370,14 +356,49 @@ async def handle_detail(request: Request, params: HandlerDetail):
     }
 
 
+@app.get("/get_device_tabs", description="获取可执行程序命令列表")
+def get_device_tabs(type:int):
+    session = SqlQuery()
+    statement = (
+        select(DeviceConfigTabs)
+        .where(DeviceConfigTabs.mode_type == type).order_by(asc("id"))
+    )
+    result = session.exec(statement).all()
+    session.close()
+    sys = CRUD(SysConfigs)
+    action_configs = sys.read(session, conditions={"key": "action_configs"})
+    session.close()
+    return {
+        "code": 0,
+        "msg": "",
+        "data": {"tabs": result, "select_configs": json.loads(action_configs.value)},
+    }
+
+
+@app.post("/update_tab_name", description="更改tab名称")
+def update_tab_name(params: DeviceConfigTabsReq):
+    if params.mode_name == "":
+        return {"code": 1, "msg": "名称不能为空", "data": {}}
+    session = SqlQuery()
+    tabModel = CRUD(DeviceConfigTabs)
+    kwargs = {"mode_name": params.mode_name}
+    tabModel.updateConditions(session, conditions={"id": params.id}, **kwargs)
+    session.close()
+    return {
+        "code": 0,
+        "msg": "",
+        "data": None,
+    }
+
+
 @app.post("/get_device_configs", description="获取可执行程序命令列表")
 def get_device_configs(params: ModelGetDeviceConfig):
-    mode_type = params.mode_type
+    tab_id = params.tab_id
     session = SqlQuery()
     configModel = CRUD(DeviceConfig)
     configList = configModel.read_all(
         session,
-        conditions={"mode_type": mode_type},
+        conditions={"tab_id": tab_id},
         order_by="action_index",
         ascending=True,
     )
@@ -400,14 +421,22 @@ def get_device_configs(params: ModelGetDeviceConfigDetail):
 
 
 @app.post("/device_config_detail_query", description="通过条件获取可执行程序详情")
-def get_device_configs(params: ModelGetDeviceConfigDetailQuery):
-    mode_type = params.mode_type
-    action_name = params.action_name
+def device_config_detail_query():
+    # tab_id = params.tab_id
+    # action_name = params.action_name
     session = SqlQuery()
+    sys = CRUD(SysConfigs)
+    action_configs = sys.read(session, conditions={"key": "action_configs"})
+    action_configs_value = json.loads(action_configs.value)
+    left_config = action_configs_value.get("left")
     configModel = CRUD(DeviceConfig)
-    model = configModel.read(session, conditions={"mode_type": mode_type, "action_name": action_name})
+    model = configModel.read(
+        session, conditions={"tab_id": left_config, "action_name": "侧视"}
+    )
     if model == None:
-        return {"code": 1, "msg": "数据不存在", "data": None}
+        model = configModel.read(
+            session, conditions={"tab_id": left_config}
+        )
     return {"code": 0, "msg": "", "data": model}
 
 
@@ -421,6 +450,9 @@ def get_device_configs(params: ModelGetDeviceConfigDetail):
         return {"code": 1, "msg": "数据不存在", "data": None}
     if model.is_system == True:
         return {"code": 1, "msg": "系统配置不允许删除", "data": None}
+    configArray = configModel.read_all(session, conditions={"tab_id": model.tab_id})
+    if len(configArray) == 1:
+        return {"code": 1, "msg": "请至少保留一个配置", "data": None}
     configModel.delete(session, obj_id=action_id)
     return {"code": 0, "msg": "删除成功", "data": None}
 
@@ -446,20 +478,23 @@ def save_device_config(params: SaveDeviceConfig):
 
 @app.post("/reset_config", description="创建或修改一条可执行命令")
 def reset_config(params: ModelGetDeviceConfig):
-    mode_type = params.mode_type
-    if mode_type == None or mode_type == "":
+    tab_id = params.tab_id
+    if tab_id == None or tab_id == "":
         return {"code": 1, "msg": "参数错误", "data": None}
     session = SqlQuery()
     deviceConfig = CRUD(DeviceConfig)
-    res = deviceConfig.deleteConditions(session, conditions={"mode_type": mode_type})
+    first_config = deviceConfig.read(session, conditions={"tab_id": tab_id})
+    res = deviceConfig.deleteConditions(session, conditions={"tab_id": tab_id})
     if res is False:
         return {"code": 1, "msg": "操作失败", "data": None}
     actions = json.load(open("action.json", encoding="utf-8"))
-    act = []
-    for item in actions:
-        if item.get("mode_type") == mode_type:
-            act.append(item)
-    batch_insert_device_configs(session, act)
+    for data in actions:
+        data["tab_id"] = tab_id
+        data["is_system"] = first_config.is_system
+        device_config = DeviceConfig(**data)
+        session.add(device_config)
+    session.commit()
+    session.close()
     return {"code": 0, "msg": "操作成功", "data": None}
 
 
@@ -479,8 +514,27 @@ def get_photo_records(page: int = 1, size: int = 5):
     list = []
     result = session.exec(statement).all()
     print("group 完成 ", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+    join_conditions = [
+        {
+            "model": DeviceConfig,
+            "on": PhotoRecord.action_id == DeviceConfig.id,
+            "is_outer": False,  # 可选,默认False,设为True则为LEFT JOIN
+        }
+    ]
     for item in result:
-        list_item = photos.read_all(session, conditions={"goods_art_no": item.goods_art_no})
+        print(item)
+        # list_item = photos.read_all(
+        #     session,
+        #     conditions={"goods_art_no": item.goods_art_no},
+        #     join_conditions=join_conditions,
+        # )
+        query = (
+            select(PhotoRecord, DeviceConfig.action_name)
+            .where(PhotoRecord.goods_art_no == item.goods_art_no)
+            .join(DeviceConfig, PhotoRecord.action_id == DeviceConfig.id)
+        )
+        list_item = session.exec(query).mappings().all()
+        session.close()
         list.append(
             {
                 "goods_art_no": item.goods_art_no,
@@ -556,6 +610,25 @@ def get_sys_config(key: str = None):
     }
 
 
+@app.post("/update_left_right_config", description="更新左右脚配置")
+def update_left_right_config(params: LeftRightParams):
+    session = SqlQuery()
+    sysConfig = CRUD(SysConfigs)
+    model = sysConfig.read(session, conditions={"key": "action_configs"})
+    if model == None:
+        return {"code": 1, "msg": "配置不存在", "data": None}
+    config_value = json.loads(model.value)
+    config_value[params.type] = params.id
+    update_value = json.dumps(config_value)
+    # 走编辑逻辑
+    kwargs = {"key": "action_configs", "value": update_value}
+    save_device_config = sysConfig.updateConditions(
+        session, conditions={"key": "action_configs"}, **kwargs
+    )
+    # return {"code": 0, "msg": "操作成功", "data": save_device_config}
+    return {"code": 0, "msg": "操作成功", "data": None}
+
+
 @app.post("/update_sys_configs", description="创建或修改系统配置")
 def save_sys_configs(params: SysConfigParams):
     session = SqlQuery()

+ 40 - 14
python/databases.py

@@ -7,11 +7,11 @@ import json
 from sqlalchemy import and_, desc, asc
 
 from sqlalchemy.dialects import sqlite
-from model import DeviceConfig, PhotoRecord, SysConfigs
+from model import DeviceConfig, PhotoRecord, SysConfigs, DeviceConfigTabs
 
 
 # 创建SQLite数据库引擎
-sqlite_file_name = "database.db"
+sqlite_file_name = "C:/Users/Default/AppData/Roaming/Zhihuiyin/database.db"
 sqlite_url = f"sqlite:///{sqlite_file_name}"
 engine = create_engine(
     sqlite_url,
@@ -37,11 +37,22 @@ def __get_session():
             session.close()
 
 
-def batch_insert_device_configs(session: Session, data_list: list):
+def batch_insert_device_configs(session: Session, action_tabs: list, data_list: list):
     """批量插入数据到设备配置表"""
-    for data in data_list:
-        device_config = DeviceConfig(**data)
-        session.add(device_config)
+    for idx, tab in enumerate(action_tabs):
+        crud = CRUD(DeviceConfigTabs)
+        device_tab = DeviceConfigTabs(
+            mode_type=tab.get("mode_type"),
+            mode_name=tab.get("mode_name"),
+        )
+        create_obj = crud.create(session, obj_in=device_tab)
+        for data in data_list:
+            data["tab_id"] = create_obj.id
+            data["is_system"] = False
+            if idx  in [0, 6]:
+                data["is_system"] = True
+            device_config = DeviceConfig(**data)
+            session.add(device_config)
     session.commit()
     session.close()
 
@@ -119,17 +130,32 @@ class CRUD:
         conditions: Optional[Dict] = None,
         order_by: Optional[str] = None,
         ascending: bool = True,
+        join_conditions: Optional[list] = None,  # 新增:支持多个JOIN
     ):
         query = select(self.model)
+        # 处理JOIN逻辑
+        if join_conditions:
+            for join_info in join_conditions:
+                joined_model = join_info.get("model")
+                on_clause = join_info.get("on")
+                is_outer = join_info.get("is_outer", False)
+
+                if not joined_model or not on_clause:
+                    continue
+
+                if is_outer:
+                    query = query.outerjoin(joined_model, on_clause)
+                else:
+                    query = query.join(joined_model, on_clause)
         if conditions:
-            query = query.where(
-                and_(
-                    *(
-                        getattr(self.model, key) == value
-                        for key, value in conditions.items()
-                    )
-                )
-            )
+            for key, value in conditions.items():
+                column = getattr(self.model, key)
+                if isinstance(value, list):
+                    # 如果值是列表,使用 IN 查询
+                    query = query.where(column.in_(value))
+                else:
+                    # 否则使用等于条件
+                    query = query.where(column == value)
         if order_by:
             if ascending:
                 query = query.order_by(asc(getattr(self.model, order_by)))

+ 33 - 0
python/docs/socket命令.md

@@ -89,6 +89,7 @@ _(该命令用于单独自定义配置中某一项的单独调整测试,不进
 * data参数释义:
     * image_counts : 执行拍照任务的数量,可理解为照片张数
     * goods_art_no : 货号
+    * action_names : 执行的动作名称
     * current_time : 当前时间
 * msg 消息提示
 #### 响应示例
@@ -98,6 +99,13 @@ _(该命令用于单独自定义配置中某一项的单独调整测试,不进
     "data": {
         "image_counts": 4,
         "goods_art_no": "1234556",
+        "action_names": [
+            "俯视",
+            "侧视",
+            "后跟",
+            "鞋底",
+            "内里"
+        ],
         "current_time": "年月日时分秒"
     },
     "msg_type":"image_process"
@@ -520,4 +528,29 @@ _(该命令用于单独自定义配置中某一项的单独调整测试,不进
     "type": "re_take_picture"
 }
 ```
+#### 停止拍摄
+<mark>以下操作需要连接设备且初始化<mark>
+* data:null
+* type:
+    * 当该字段为stop_action时,代表停止拍摄
+##### 请求示例
+```python
+{
+    "data":null,
+    "type": "stop_action"
+}
+```
+<mark>当通过物理遥控器按键停止拍摄时,会收到以下消息,前端需要把上方的【停止拍摄】消息转发给后端即可<mark>
+```
+{
+    "code": 0,
+    "msg": "停止执行组合动作",
+    "status": -1,
+    "data": {
+        "type": "stop_action",
+        "data": null
+    },
+    "msg_type": "stop_action"
+}
+```
 ##### 未完待续.....

+ 28 - 3
python/mcu/DeviceControl.py

@@ -15,6 +15,7 @@ from databases import insert_photo_records
 from .McuDeviationSet import McuDeviationSet
 from .OtherSet import OtherSet
 from .DebugUart import DebugUart
+import copy
 
 # mcu命令
 class DeviceControl(BaseClass, metaclass=SingletonType):
@@ -48,6 +49,8 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         self.p_list = []
         self.temp_ports_dict = {}
         self.is_running = False
+        self.is_runn_action = False
+        self.is_stop_action = False
         self.connect_state = False
         self.device_name_dict = {
             "camera_steering": 0,
@@ -132,8 +135,6 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
         )
         self.msg_type = "mcu"
 
-    
-
     def setOtherMaxMinValue(self,item, value):
         value = int(value)
         parameter_limits = {
@@ -1370,10 +1371,26 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
             return
         image_counts = 0
         if config_list:
+            action_names = []
+            if len(config_list) > 1:
+                if config_list[-1]["take_picture"] is True:
+                    new_init_config = copy.copy(config_list[0])
+                    new_init_config["action_name"] = "移动到初始位"
+                    new_init_config["number_focus"] = 0
+                    new_init_config["take_picture"] = False
+                    new_init_config["shoe_upturn"] = False
+                    new_init_config["pre_delay"] = 0.0
+                    new_init_config["after_delay"] = 0.0
+                    new_init_config["led_switch"] = True
+                    new_init_config["turntable_angle"] = 0.0
+                    new_init_config["is_wait"] = False
+                    new_init_config["is_need_confirm"] = False
+                    config_list.append(new_init_config)
             for idx, item in enumerate(config_list):
                 is_take_picture = item["take_picture"]
                 action_id = item["id"]
                 if is_take_picture:
+                    action_names.append(item["action_name"])
                     image_counts += 1
                     # 批量插入
                     image_deal_mode = 0 if action_info == "执行左脚程序" else 1
@@ -1393,13 +1410,20 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                 data={
                     "goods_art_no": goods_art_no,
                     "image_counts": image_counts,
-                    "current_time":datetime.datetime.now(settings.TIME_ZONE).strftime("%Y-%m-%d %H:%M:%S"),
+                    "action_names": action_names,
+                    "current_time": datetime.datetime.now(settings.TIME_ZONE).strftime(
+                        "%Y-%m-%d %H:%M:%S"
+                    ),
                 },
             )
             self.controlDevice("laser_position", 0)
             self.msg_type = "mcu"
+            self.is_runn_action = True
             for index, action in enumerate(config_list):
                 await asyncio.sleep(0.1)
+                if self.is_stop_action == True:
+                    self.is_stop_action = False
+                    break
                 action_is_take_picture = action["take_picture"]
                 image_index = -1
                 if action_is_take_picture:
@@ -1449,6 +1473,7 @@ class DeviceControl(BaseClass, metaclass=SingletonType):
                     pass
                 # self.action_state = 2
             self.action_state = 2
+            self.is_runn_action = False
             self.msg_type = "photo_take_finish"
             self.sendSocketMessage(
                 code=0,

+ 9 - 0
python/mcu/RemoteControlV2.py

@@ -253,12 +253,14 @@ class RemoteControlV2(BaseClass):
         else:
             print("开始单拍1")
             if record.image_index == 19:
+                self.msg_type = "photo_take"
                 self.sendSocketMessage(
                     code=1,
                     msg="单拍失败,单个货号最多允许拍摄20张产品图",
                     data=None,
                     device_status=2,
                 )
+                self.msg_type = "blue_tooth"
                 return
             image_index = record.image_index + 1
             self.photo_take_state = 1
@@ -356,6 +358,13 @@ class RemoteControlV2(BaseClass):
                 _data = {"type": self.msg_type,"data":None}
                 self.sendSocketMessage(0, "处理单拍消息", data=_data, device_status=-1)
                 self.msg_type = "blue_tooth"
+            if button_value in [9]:
+                # 处理停止
+                self.msg_type = "stop_action"
+                # 0 闲置;1进行中;2已完成;
+                _data = {"type": self.msg_type, "data": None}
+                self.sendSocketMessage(0, "停止执行组合动作", data=_data, device_status=-1)
+                self.msg_type = "blue_tooth"
             self.sendSocketMessage(code=0, msg="", data=message, device_status=2)
             if settings.IS_DEBUG == "true":
                 print("收到按键", button_value)

+ 1 - 0
python/model/__init__.py

@@ -1,3 +1,4 @@
 from .device_config import DeviceConfig
 from .photo_record import PhotoRecord
 from .sys_configs import SysConfigs
+from .device_config_tabs import DeviceConfigTabs

+ 1 - 1
python/model/device_config.py

@@ -6,7 +6,7 @@ from sqlmodel import Field, SQLModel
 class DeviceConfig(SQLModel, table=True):
     __tablename__ = "device_config"
     id: Optional[int] = Field(default=None, primary_key=True)
-    mode_type: Optional[str] = Field(default="执行左脚程序", index=True, max_length=128)
+    tab_id: Optional[int] = Field(default=None, index=True, max_length=11)
     action_name: Optional[str] = Field(
         default=None, index=True, max_length=128, description="动作名称"
     )

+ 12 - 0
python/model/device_config_tabs.py

@@ -0,0 +1,12 @@
+from typing import Optional
+from datetime import datetime
+from sqlmodel import Field, SQLModel
+# 定义DeviceConfig模型类
+
+class DeviceConfigTabs(SQLModel, table=True):
+    __tablename__ = "device_config_tabs"
+    id: Optional[int] = Field(default=None, primary_key=True)
+    mode_name: Optional[str] = Field(default="执行左脚程序", index=True, max_length=128)
+    mode_type: Optional[int] = Field(
+        default=0, index=True, max_length=128, description="动作类型;0左脚;1右脚"
+    )

+ 19 - 7
python/models.py

@@ -10,8 +10,8 @@ class HlmForwardRequest(BaseModel):
 
 class ModelGetDeviceConfig(BaseModel):
     """获取可执行程序命令列表"""
-    mode_type: Optional[str] =Field(
-        default="执行左脚程序", description="类型,【执行左脚程序】或者【执行右脚程序】"
+    tab_id: int =Field(
+        default=1, description="类型,【执行左脚程序】或者【执行右脚程序】"
     )
 
 
@@ -26,7 +26,7 @@ class ModelGetDeviceConfigDetail(BaseModel):
 class ModelGetDeviceConfigDetailQuery(BaseModel):
     """获取可执行程序命令列表"""
 
-    mode_type: Optional[str] = Field(default="执行左脚程序", index=True, max_length=128)
+    tab_id: int = Field(default=1, index=True, max_length=128)
     action_name: Optional[str] = Field(default="", index=True, max_length=128)
 
 
@@ -34,7 +34,7 @@ class SaveDeviceConfig(BaseModel):
     """获取可执行程序命令列表"""
 
     id: Optional[int] = Field(default=None, primary_key=True)
-    mode_type: Optional[str] = Field(default="", index=True, max_length=128)
+    tab_id: Optional[int] = Field(default=0, index=True)
     action_name: Optional[str] = Field(default="", index=True, max_length=128)
     action_index: Optional[int] = Field(default=None)
     action_status: Optional[bool] = Field(default=None)
@@ -65,6 +65,13 @@ class SysConfigParams(BaseModel):
     value: str = Field(default=None, description="json数据")
 
 
+class LeftRightParams(BaseModel):
+    """系统配置"""
+
+    id: int = Field(default=0, description="配置id")
+    type: str = Field(default="left", description="类型,left左;right:右边")
+
+
 class TemplateItem(BaseModel):
     """模板项"""
 
@@ -88,13 +95,18 @@ class HandlerDetail(BaseModel):
     temp_name: str = Field(default="", description="选中的模板名称")
     temp_list: list[TemplateItem] = Field(default=None, description="所有模板列表")
     logo_path: Optional[str] = Field(default="", description="logo地址路径")
-
+    is_only_cutout: Optional[int] = Field(default=0, description="是否仅抠图;0否;1是")
 
 class LogoParams(BaseModel):
     """logo参数"""
 
     logo_path: str = Field(default="", description="logo地址路径")
-    
+
 class LogoParamsupdate(BaseModel):
     """系统配置"""
-    path: str = Field(default=None, description="要删除得文件路径")
+    path: str = Field(default=None, description="要删除得文件路径")
+
+
+class DeviceConfigTabsReq(BaseModel):
+    id: int = Field(default=None, primary_key=True)
+    mode_name: str = Field(default="", description="Tab名称")

+ 9 - 5
python/service/base_deal.py

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

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

@@ -1,12 +1,16 @@
 import os
 import copy
 import time
-from .image_deal_base_func import *
+from service.generate_main_image.image_deal_base_func import *
 from PIL import Image, ImageDraw
 from blend_modes import multiply
 import os
 import settings
 from functools import wraps
+from service.multi_threaded_image_saving import ImageSaver
+from service.get_mask_by_green import GetMask
+
+#
 
 
 def time_it(func):
@@ -15,7 +19,9 @@ def time_it(func):
         start_time = time.time()  # 记录开始时间
         result = func(*args, **kwargs)  # 调用原始函数
         end_time = time.time()  # 记录结束时间
-        print(f"Executing {func.__name__} took {end_time - start_time:.4f} seconds.")  # 打印耗时
+        print(
+            f"Executing {func.__name__} took {end_time - start_time:.4f} seconds."
+        )  # 打印耗时
         return result
 
     return wrapper
@@ -25,10 +31,11 @@ class GeneratePic(object):
     def __init__(self, is_test=False):
         # self.logger = MyLogger()
         self.is_test = is_test
+        self.saver = ImageSaver()
         pass
 
     @time_it
-    def get_mask_and_config(self, im_jpg: Image, im_png: Image):
+    def get_mask_and_config(self, im_jpg: Image, im_png: Image, curve_mask: bool):
         """
         步骤:
         1、尺寸进行对应缩小
@@ -48,27 +55,67 @@ class GeneratePic(object):
         # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
         image_high = im_jpg.height
         print("图片高度:", image_high)
+        # TODO 待移除
+        if settings.app:
+            settings.app.processEvents()
         cv2_jpg = pil_to_cv2(im_jpg)
-        # 返回线条图片,以及最低位置
-        img_with_shifted_line, lowest_y = draw_shifted_line(image=cv2_jpg,
-                                                            min_y_values=min_y_values,
-                                                            shift_amount=15,
-                                                            one_line_pos=(x1, x2),
-                                                            line_color=(0, 0, 0),
-                                                            line_thickness=20)
 
+        # 返回线条图片,以及最低位置
+        print("返回线条图片,以及最低位置")
+        # crop_image_box=(x1, y1, x2, y2),
+        if curve_mask:
+            crop_image_box = None
+        else:
+            # 不需要曲线部分的蒙版
+            crop_image_box = (x1, y1, x2, y2)
+        img_with_shifted_line, lowest_y = draw_shifted_line(
+            image=cv2_jpg,
+            min_y_values=min_y_values,
+            shift_amount=15,
+            one_line_pos=(x1, x2),
+            line_color=(0, 0, 0),
+            line_thickness=20,
+            app=settings.app,
+            crop_image_box=crop_image_box,
+        )
+        # TODO 待移除
+        if settings.app:
+            settings.app.processEvents()
+        print("66  制作蒙版")
         # 制作蒙版
         mask_line = cv2_to_pil(img_with_shifted_line)
-        mask = mask_line.convert('L')  # 转换为灰度图
+        mask = mask_line.convert("L")  # 转换为灰度图
         mask = ImageOps.invert(mask)
         # 蒙版扩边
-        mask = expand_mask(mask, expansion_radius=30, blur_radius=10)
-
+        print("72  蒙版扩边")
+        # 默认expansion_radius 65 blur_radius 45
+        mask = expand_or_shrink_mask(
+            pil_image=mask, expansion_radius=50, blur_radius=35
+        )
+
+        # =============使用绿色蒙版进行处理
+        if settings.IS_GET_GREEN_MASK:
+            print("============使用绿色蒙版进行处理")
+            mask = mask.convert("RGB")
+            white_bg = Image.new(mode="RGB", size=im_png.size, color=(0, 0, 0))
+            green_areas_mask_pil = GetMask().find_green_areas(cv2_jpg)
+            green_areas_mask_pil = expand_or_shrink_mask(
+                pil_image=green_areas_mask_pil, expansion_radius=15, blur_radius=5
+            )
+            mask.paste(white_bg, mask=green_areas_mask_pil.convert("L"))
+            mask = mask.convert("L")
+
+        # TODO 待移除
+        if settings.app:
+            settings.app.processEvents()
         # ====================生成新的图片
+        print("84  生成新的图片")
         bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255))
         bg.paste(im_png, mask=im_png)
         bg.paste(im_jpg, mask=mask)  # 粘贴有阴影的地方
-
+        # TODO 待移除
+        if settings.app:
+            settings.app.processEvents()
         if self.is_test:
             _bg = bg.copy()
             draw = ImageDraw.Draw(_bg)
@@ -77,16 +124,12 @@ class GeneratePic(object):
             end_point = (_bg.width, lowest_y)  # 直线的结束点
             # 定义直线的颜色(R, G, B)
             line_color = (255, 0, 0)  # 红色
+            _r = Image.new(mode="RGBA", size=im_png.size, color=(246, 147, 100, 255))
+            # mask_line = mask_line.convert('L')  # 转换为灰度图
+            # mask_line = ImageOps.invert(mask_line)
+            # _bg.paste(_r, mask=mask)
             # 绘制直线
             draw.line([start_point, end_point], fill=line_color, width=1)
-            # mask.show()
-            # bg = pil_to_cv2(bg)
-            # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2)
-            # bg = cv2_to_pil(bg)
-            _r = Image.new(mode="RGBA", size=im_png.size, color=(246, 147, 100, 255))
-            mask_line = mask_line.convert('L')  # 转换为灰度图
-            mask_line = ImageOps.invert(mask_line)
-            _bg.paste(_r, mask=mask)
             _bg.show()
 
         # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png")
@@ -103,25 +146,42 @@ class GeneratePic(object):
         print("image_high lowest_y", image_high, lowest_y)
         rows = [lowest_y]  # 需要检查的像素行
 
+        print("copy.copy(im_shadow)")
         _im_shadow = copy.copy(im_shadow)
         Midtones = 0.7
         Highlight = 235
-        k = 8
+        k = copy.copy(settings.COLOR_GRADATION_CYCLES)
+        print("循环识别")
+        xunhuan = 0
         while k:
+            xunhuan += 1
+            if settings.app:
+                settings.app.processEvents()
             k -= 1
-            Midtones += 0.1
-            if Midtones > 1:
-                Midtones = 1
+            Midtones += 0.035
+            if Midtones > 1.7:
+                Midtones = 1.7
             Highlight -= 3
-            _im_shadow = levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight,
-                                       OutShadow=0,
-                                       OutHighlight=255, Dim=3)
-            brightness_list = calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows)
-            print(brightness_list)
 
+            _im_shadow = levels_adjust(
+                img=im_shadow,
+                Shadow=0,
+                Midtones=Midtones,
+                Highlight=Highlight,
+                OutShadow=0,
+                OutHighlight=255,
+                Dim=3,
+            )
+            brightness_list = calculate_average_brightness_opencv(
+                img_gray=_im_shadow, rows_to_check=rows
+            )
+            print(
+                "循环识别:{},Midtones:{},Highlight:{},brightness_list:{}".format(
+                    xunhuan, Midtones, Highlight, brightness_list
+                )
+            )
             if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
                 break
-        print("Midtones,Highlight:", Midtones, Highlight)
 
         im_shadow = cv2_to_pil(_im_shadow)
 
@@ -130,8 +190,12 @@ class GeneratePic(object):
 
         # 1、图片预处理,只保留阴影
         only_shadow_img = im_shadow.copy()
-        only_shadow_img.paste(Image.new(mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)),
-                              mask=im_png)
+        only_shadow_img.paste(
+            Image.new(
+                mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)
+            ),
+            mask=im_png,
+        )
         average_brightness = calculated_shadow_brightness(only_shadow_img)
         print("average_brightness:", average_brightness)
 
@@ -143,7 +207,7 @@ class GeneratePic(object):
 
         return mask, config
 
-    def get_mask_and_config_beifen(self, im_jpg: Image, im_png: Image, out_image_path=None):
+    def get_mask_and_config_1_2025_05_18(self, im_jpg: Image, im_png: Image):
         """
         步骤:
         1、尺寸进行对应缩小
@@ -151,9 +215,7 @@ class GeneratePic(object):
         3、自动色阶检查亮度
         4、输出自动色阶参数、以及放大的尺寸蒙版
         """
-        # ===================尺寸进行对应缩小
-        orign_x, orign_y = im_jpg.size
-
+        # ===================尺寸进行对应缩小(提升处理速度)
         im_jpg = to_resize(im_jpg, width=800)
         im_png = to_resize(im_png, width=800)
         x1, y1, x2, y2 = im_png.getbbox()
@@ -163,31 +225,75 @@ class GeneratePic(object):
         # 查找每列的最低非透明点
         min_y_values = find_lowest_non_transparent_points(cv2_png)
         # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
+        image_high = im_jpg.height
+        print("图片高度:", image_high)
+        # TODO 待移除
+        if settings.app:
+            settings.app.processEvents()
         cv2_jpg = pil_to_cv2(im_jpg)
         # 返回线条图片,以及最低位置
-        img_with_shifted_line, lowest_y = draw_shifted_line(image=cv2_jpg,
-                                                            min_y_values=min_y_values,
-                                                            shift_amount=15,
-                                                            one_line_pos=(x1, x2),
-                                                            line_color=(0, 0, 0),
-                                                            line_thickness=20)
-
+        print("返回线条图片,以及最低位置")
+        img_with_shifted_line, lowest_y = draw_shifted_line(
+            image=cv2_jpg,
+            min_y_values=min_y_values,
+            shift_amount=15,
+            one_line_pos=(x1, x2),
+            line_color=(0, 0, 0),
+            line_thickness=20,
+            app=settings.app,
+            crop_image_box=(x1, y1, x2, y2),
+        )
+        # TODO 待移除
+        if settings.app:
+            settings.app.processEvents()
+        print("66  制作蒙版")
         # 制作蒙版
-        mask = cv2_to_pil(img_with_shifted_line)
-        mask = mask.convert('L')  # 转换为灰度图
+        mask_line = cv2_to_pil(img_with_shifted_line)
+        mask = mask_line.convert("L")  # 转换为灰度图
         mask = ImageOps.invert(mask)
         # 蒙版扩边
-        mask = expand_mask(mask, expansion_radius=30, blur_radius=10)
-
+        print("72  蒙版扩边")
+        # 默认expansion_radius 65 blur_radius 45
+        mask = expand_or_shrink_mask(
+            pil_image=mask, expansion_radius=50, blur_radius=35
+        )
+
+        # mask1 = expand_mask(mask, expansion_radius=30, blur_radius=10)
+        # mask1.save("mask1.png")
+        # mask2 = expand_or_shrink_mask(pil_image=mask, expansion_radius=60, blur_radius=30)
+        # mask2.save("mask2.png")
+        # raise 11
+
+        # TODO 待移除
+        if settings.app:
+            settings.app.processEvents()
         # ====================生成新的图片
+        print("84  生成新的图片")
         bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255))
         bg.paste(im_png, mask=im_png)
         bg.paste(im_jpg, mask=mask)  # 粘贴有阴影的地方
-
-        # bg = pil_to_cv2(bg)
-        # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2)
-        # bg = cv2_to_pil(bg)
-        # bg.show()
+        # TODO 待移除
+        if settings.app:
+            settings.app.processEvents()
+        if self.is_test:
+            _bg = bg.copy()
+            draw = ImageDraw.Draw(_bg)
+            # 定义直线的起点和终点坐标
+            start_point = (0, lowest_y)  # 直线的起始点
+            end_point = (_bg.width, lowest_y)  # 直线的结束点
+            # 定义直线的颜色(R, G, B)
+            line_color = (255, 0, 0)  # 红色
+            # 绘制直线
+            draw.line([start_point, end_point], fill=line_color, width=1)
+            # mask.show()
+            # bg = pil_to_cv2(bg)
+            # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2)
+            # bg = cv2_to_pil(bg)
+            _r = Image.new(mode="RGBA", size=im_png.size, color=(246, 147, 100, 255))
+            mask_line = mask_line.convert("L")  # 转换为灰度图
+            mask_line = ImageOps.invert(mask_line)
+            _bg.paste(_r, mask=mask)
+            _bg.show()
 
         # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png")
         # bg.show()
@@ -197,35 +303,69 @@ class GeneratePic(object):
         _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR)
         # 背景阴影
         im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
-
+        print("image_high lowest_y", image_high, lowest_y)
+        if lowest_y < 0 or lowest_y >= image_high:
+            lowest_y = image_high - 1
+        print("image_high lowest_y", image_high, lowest_y)
         rows = [lowest_y]  # 需要检查的像素行
+
+        print("copy.copy(im_shadow)")
         _im_shadow = copy.copy(im_shadow)
-        Midtones = 0.62
+        Midtones = 0.7
         Highlight = 235
-        k = 10
+        k = 12
+        print("循环识别")
         while k:
+            print("循环识别:{}".format(k))
+            if settings.app:
+                settings.app.processEvents()
             k -= 1
             Midtones += 0.1
             if Midtones > 1:
                 Midtones = 1
             Highlight -= 3
-            _im_shadow = levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight,
-                                       OutShadow=0,
-                                       OutHighlight=255, Dim=3)
-            brightness_list = calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows)
+            _im_shadow = levels_adjust(
+                img=im_shadow,
+                Shadow=0,
+                Midtones=Midtones,
+                Highlight=Highlight,
+                OutShadow=0,
+                OutHighlight=255,
+                Dim=3,
+            )
+            brightness_list = calculate_average_brightness_opencv(
+                img_gray=_im_shadow, rows_to_check=rows
+            )
             print(brightness_list)
-            if brightness_list[0] >= 254:
+            if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
                 break
         print("Midtones,Highlight:", Midtones, Highlight)
-        config = (Midtones, Highlight)
+
         im_shadow = cv2_to_pil(_im_shadow)
-        im_shadow.paste(im_png, (0, 0), im_png)  # 把原图粘贴回去,避免色差
-        if out_image_path:
-            im_shadow.save(out_image_path)
+
+        # ========================================================
+        # 计算阴影的亮度,用于确保阴影不要太黑
+
+        # 1、图片预处理,只保留阴影
+        only_shadow_img = im_shadow.copy()
+        only_shadow_img.paste(
+            Image.new(
+                mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)
+            ),
+            mask=im_png,
+        )
+        average_brightness = calculated_shadow_brightness(only_shadow_img)
+        print("average_brightness:", average_brightness)
+
+        config = {
+            "Midtones": Midtones,
+            "Highlight": Highlight,
+            "average_brightness": average_brightness,
+        }
 
         return mask, config
 
-    def my_test(self,**kwargs):
+    def my_test(self, **kwargs):
         if "output_queue" in kwargs:
             output_queue = kwargs["output_queue"]
         else:
@@ -235,9 +375,23 @@ class GeneratePic(object):
             output_queue.put(True)
 
     @time_it
-    def run(self, image_path, cut_image_path, out_path, image_deal_mode=0, image_index=99,
-            out_pic_size=1024, is_logo=True, out_process_path_1=None, out_process_path_2=None,
-            resize_mode=None, max_box=None, logo_path="", **kwargs):  # im 为cv对象
+    def run(
+        self,
+        image_path,
+        cut_image_path,
+        out_path,
+        image_deal_mode=0,
+        image_index=99,
+        out_pic_size=1024,
+        is_logo=True,
+        out_process_path_1=None,
+        out_process_path_2=None,
+        resize_mode=None,
+        max_box=None,
+        logo_path="",
+        curve_mask=False,
+        **kwargs,
+    ):  # im 为cv对象
         """
         image_path:原始图
         cut_image_path:抠图结果 与原始图尺寸相同
@@ -249,6 +403,7 @@ class GeneratePic(object):
         out_process_path_1=None, 有阴影的图片,白底非透明
         out_process_path_2=None, 已抠图的图片
         resize_mode=0,1,2 主体缩小尺寸
+        curve_mask 为True时,表示为对鞋曲线部分的mask,不做剪裁
         """
         if "output_queue" in kwargs:
             output_queue = kwargs["output_queue"]
@@ -268,24 +423,33 @@ class GeneratePic(object):
 
         # ================自动色阶处理
         _s = time.time()
-        shadow_mask, config = self.get_mask_and_config(im_jpg=im_shadow, im_png=cut_image)
+        shadow_mask, config = self.get_mask_and_config(
+            im_jpg=im_shadow, im_png=cut_image, curve_mask=curve_mask
+        )
         print("242  need_time_2:{}".format(time.time() - _s))
 
         shadow_mask = shadow_mask.resize(im_shadow.size)
 
         # =====抠图,形成新的阴影背景图=====
-        _new_im_shadow = Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255))
+        # TODO 待移除
+        # settings.app.processEvents()
+
+        _new_im_shadow = Image.new(
+            mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255)
+        )
         _new_im_shadow.paste(im_shadow, mask=shadow_mask)  # 粘贴有阴影的地方
         # _new_im_shadow.show()
-
         _new_im_shadow = pil_to_cv2(_new_im_shadow)
         _new_im_shadow = cv2.cvtColor(_new_im_shadow, cv2.COLOR_BGR2GRAY)
-        _new_im_shadow = levels_adjust(img=_new_im_shadow,
-                                       Shadow=0,
-                                       Midtones=config["Midtones"],
-                                       Highlight=config["Highlight"],
-                                       OutShadow=0,
-                                       OutHighlight=255, Dim=3)
+        _new_im_shadow = levels_adjust(
+            img=_new_im_shadow,
+            Shadow=0,
+            Midtones=config["Midtones"],
+            Highlight=config["Highlight"],
+            OutShadow=0,
+            OutHighlight=255,
+            Dim=3,
+        )
 
         im_shadow = cv2_to_pil(_new_im_shadow)
 
@@ -293,7 +457,9 @@ class GeneratePic(object):
         average_brightness = config["average_brightness"]
         if config["average_brightness"] < 180:
             # 调整阴影亮度
-            backdrop_prepped = np.asfarray(Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255)))
+            backdrop_prepped = np.asfarray(
+                Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255))
+            )
             im_shadow = im_shadow.convert("RGBA")
             source_prepped = np.asfarray(im_shadow)
             # im_shadow.show()
@@ -302,8 +468,10 @@ class GeneratePic(object):
             opacity = max(0.5, min(opacity, 1))
 
             print("阴影透明度:{}%".format(int(opacity * 100)))
-            blended_np = multiply(backdrop_prepped, source_prepped, opacity=int(opacity * 100) / 100)
-            im_shadow = Image.fromarray(np.uint8(blended_np)).convert('RGB')
+            blended_np = multiply(
+                backdrop_prepped, source_prepped, opacity=int(opacity * 100) / 100
+            )
+            im_shadow = Image.fromarray(np.uint8(blended_np)).convert("RGB")
             # im_shadow.show()
 
         # 把原图粘贴回去,避免色差
@@ -317,14 +485,24 @@ class GeneratePic(object):
             out_image_1 = im_shadow.copy()
             if image_deal_mode == 1:
                 out_image_1 = out_image_1.transpose(Image.FLIP_LEFT_RIGHT)
-            out_image_1.save(out_process_path_1)
+
+            self.saver.save_image(
+                image=out_image_1, file_path=out_process_path_1, save_mode="png"
+            )
+            # save_image_by_thread(image=out_image_1, out_path=out_process_path_1)
+            # out_image_1.save(out_process_path_1)
 
         # 保存抠图结果,没有底图,没有logo
         if out_process_path_2:
             out_image_2 = cut_image.copy()
             if image_deal_mode == 1:
                 out_image_2 = out_image_2.transpose(Image.FLIP_LEFT_RIGHT)
-            out_image_2.save(out_process_path_2)
+
+            self.saver.save_image(
+                image=out_image_2, file_path=out_process_path_2, save_mode="png"
+            )
+            # save_image_by_thread(image=out_image_2, out_path=out_process_path_2, save_mode="png")
+            # out_image_2.save(out_process_path_2)
 
         # 不生成主图时直接退出
         if not out_path:
@@ -409,13 +587,36 @@ class GeneratePic(object):
             image_bg = sharpen_image(image_bg, factor=settings.OUT_PIC_FACTOR)
 
         if out_pic_size < 1600:
-            image_bg = image_bg.resize((out_pic_size, out_pic_size), resample=settings.RESIZE_IMAGE_MODE)
+            image_bg = image_bg.resize(
+                (out_pic_size, out_pic_size), resample=settings.RESIZE_IMAGE_MODE
+            )
 
         if settings.OUT_PIC_MODE == ".jpg":
-            image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG")
+            if settings.OUT_PIC_QUALITY == "普通":
+                self.saver.save_image(
+                    image=image_bg,
+                    file_path=out_path,
+                    save_mode="jpg",
+                    quality=None,
+                    dpi=None,
+                    _format="JPEG",
+                )
+                # save_image_by_thread(image_bg, out_path, save_mode="jpg", quality=None, dpi=None, _format="JPEG")
+                # image_bg.save(out_path, format="JPEG")
+            else:
+                self.saver.save_image(
+                    image=image_bg,
+                    file_path=out_path,
+                    save_mode="jpg",
+                    quality=100,
+                    dpi=(300, 300),
+                    _format="JPEG",
+                )
+                # save_image_by_thread(image_bg, out_path, save_mode="jpg", quality=100, dpi=(300, 300), _format="JPEG")
+                # image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG")
         else:
-            # quality=quality
-            image_bg.save(out_path, quality=100)
+            self.saver.save_image(image=image_bg, file_path=out_path, save_mode="png")
+            # image_bg.save(out_path)
 
         if output_queue is not None:
             output_queue.put(True)

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

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

+ 43 - 0
python/service/get_mask_by_green.py

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

+ 250 - 103
python/service/grenerate_main_image_test.py

@@ -7,7 +7,8 @@ from blend_modes import multiply
 import os
 import settings
 from functools import wraps
-
+from .multi_threaded_image_saving import ImageSaver
+from .get_mask_by_green import GetMask
 
 def time_it(func):
     @wraps(func)  # 使用wraps来保留原始函数的元数据信息
@@ -15,7 +16,9 @@ def time_it(func):
         start_time = time.time()  # 记录开始时间
         result = func(*args, **kwargs)  # 调用原始函数
         end_time = time.time()  # 记录结束时间
-        print(f"Executing {func.__name__} took {end_time - start_time:.4f} seconds.")  # 打印耗时
+        print(
+            f"Executing {func.__name__} took {end_time - start_time:.4f} seconds."
+        )  # 打印耗时
         return result
 
     return wrapper
@@ -25,10 +28,11 @@ class GeneratePic(object):
     def __init__(self, is_test=False):
         # self.logger = MyLogger()
         self.is_test = is_test
+        self.saver = ImageSaver()
         pass
 
     @time_it
-    def get_mask_and_config(self, im_jpg: Image, im_png: Image):
+    def get_mask_and_config(self, im_jpg: Image, im_png: Image, curve_mask: bool):
         """
         步骤:
         1、尺寸进行对应缩小
@@ -48,44 +52,55 @@ class GeneratePic(object):
         # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
         image_high = im_jpg.height
         print("图片高度:", image_high)
-        # TODO 待移除
-        # settings.app.processEvents()
         cv2_jpg = pil_to_cv2(im_jpg)
+
         # 返回线条图片,以及最低位置
         print("返回线条图片,以及最低位置")
-        img_with_shifted_line, lowest_y = draw_shifted_line(image=cv2_jpg,
-                                                            min_y_values=min_y_values,
-                                                            shift_amount=15,
-                                                            one_line_pos=(x1, x2),
-                                                            line_color=(0, 0, 0),
-                                                            line_thickness=20,
-                                                            app=None)
-        # TODO 待移除
-        # settings.app.processEvents()
+        # crop_image_box=(x1, y1, x2, y2),
+        if curve_mask:
+            crop_image_box = None
+        else:
+            # 不需要曲线部分的蒙版
+            crop_image_box = (x1, y1, x2, y2)
+        img_with_shifted_line, lowest_y = draw_shifted_line(
+            image=cv2_jpg,
+            min_y_values=min_y_values,
+            shift_amount=15,
+            one_line_pos=(x1, x2),
+            line_color=(0, 0, 0),
+            line_thickness=20,
+            app=settings.app,
+            crop_image_box=crop_image_box,
+        )
         print("66  制作蒙版")
         # 制作蒙版
         mask_line = cv2_to_pil(img_with_shifted_line)
-        mask = mask_line.convert('L')  # 转换为灰度图
+        mask = mask_line.convert("L")  # 转换为灰度图
         mask = ImageOps.invert(mask)
         # 蒙版扩边
         print("72  蒙版扩边")
-        mask = expand_or_shrink_mask(pil_image=mask, expansion_radius=65, blur_radius=35)
+        # 默认expansion_radius 65 blur_radius 45
+        mask = expand_or_shrink_mask(
+            pil_image=mask, expansion_radius=50, blur_radius=35
+        )
+
+        # =============使用绿色蒙版进行处理
+        if settings.IS_GET_GREEN_MASK:
+            print("============使用绿色蒙版进行处理")
+            mask = mask.convert("RGB")
+            white_bg = Image.new(mode="RGB", size=im_png.size, color=(0, 0, 0))
+            green_areas_mask_pil = GetMask().find_green_areas(cv2_jpg)
+            green_areas_mask_pil = expand_or_shrink_mask(
+                pil_image=green_areas_mask_pil, expansion_radius=15, blur_radius=5
+            )
+            mask.paste(white_bg, mask=green_areas_mask_pil.convert("L"))
+            mask = mask.convert("L")
 
-        # mask1 = expand_mask(mask, expansion_radius=30, blur_radius=10)
-        # mask1.save("mask1.png")
-        # mask2 = expand_or_shrink_mask(pil_image=mask, expansion_radius=60, blur_radius=30)
-        # mask2.save("mask2.png")
-        # raise 11
-
-        # TODO 待移除
-        # settings.app.processEvents()
         # ====================生成新的图片
         print("84  生成新的图片")
         bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255))
         bg.paste(im_png, mask=im_png)
         bg.paste(im_jpg, mask=mask)  # 粘贴有阴影的地方
-        # TODO 待移除
-        # settings.app.processEvents()
         if self.is_test:
             _bg = bg.copy()
             draw = ImageDraw.Draw(_bg)
@@ -94,16 +109,12 @@ class GeneratePic(object):
             end_point = (_bg.width, lowest_y)  # 直线的结束点
             # 定义直线的颜色(R, G, B)
             line_color = (255, 0, 0)  # 红色
+            _r = Image.new(mode="RGBA", size=im_png.size, color=(246, 147, 100, 255))
+            # mask_line = mask_line.convert('L')  # 转换为灰度图
+            # mask_line = ImageOps.invert(mask_line)
+            # _bg.paste(_r, mask=mask)
             # 绘制直线
             draw.line([start_point, end_point], fill=line_color, width=1)
-            # mask.show()
-            # bg = pil_to_cv2(bg)
-            # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2)
-            # bg = cv2_to_pil(bg)
-            _r = Image.new(mode="RGBA", size=im_png.size, color=(246, 147, 100, 255))
-            mask_line = mask_line.convert('L')  # 转换为灰度图
-            mask_line = ImageOps.invert(mask_line)
-            _bg.paste(_r, mask=mask)
             _bg.show()
 
         # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png")
@@ -124,26 +135,38 @@ class GeneratePic(object):
         _im_shadow = copy.copy(im_shadow)
         Midtones = 0.7
         Highlight = 235
-        k = 8
+        k = copy.copy(settings.COLOR_GRADATION_CYCLES)
         print("循环识别")
+        xunhuan = 0
         while k:
-            # TODO 待移除
-            print("循环识别:{}".format(k))
-            # settings.app.processEvents()
+            xunhuan += 1
+            if settings.app:
+                settings.app.processEvents()
             k -= 1
-            Midtones += 0.1
-            if Midtones > 1:
-                Midtones = 1
+            Midtones += 0.035
+            if Midtones > 1.7:
+                Midtones = 1.7
             Highlight -= 3
-            _im_shadow = levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight,
-                                       OutShadow=0,
-                                       OutHighlight=255, Dim=3)
-            brightness_list = calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows)
-            print(brightness_list)
 
+            _im_shadow = levels_adjust(
+                img=im_shadow,
+                Shadow=0,
+                Midtones=Midtones,
+                Highlight=Highlight,
+                OutShadow=0,
+                OutHighlight=255,
+                Dim=3,
+            )
+            brightness_list = calculate_average_brightness_opencv(
+                img_gray=_im_shadow, rows_to_check=rows
+            )
+            print(
+                "循环识别:{},Midtones:{},Highlight:{},brightness_list:{}".format(
+                    xunhuan, Midtones, Highlight, brightness_list
+                )
+            )
             if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
                 break
-        print("Midtones,Highlight:", Midtones, Highlight)
 
         im_shadow = cv2_to_pil(_im_shadow)
 
@@ -152,8 +175,12 @@ class GeneratePic(object):
 
         # 1、图片预处理,只保留阴影
         only_shadow_img = im_shadow.copy()
-        only_shadow_img.paste(Image.new(mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)),
-                              mask=im_png)
+        only_shadow_img.paste(
+            Image.new(
+                mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)
+            ),
+            mask=im_png,
+        )
         average_brightness = calculated_shadow_brightness(only_shadow_img)
         print("average_brightness:", average_brightness)
 
@@ -165,7 +192,7 @@ class GeneratePic(object):
 
         return mask, config
 
-    def get_mask_and_config_beifen(self, im_jpg: Image, im_png: Image, out_image_path=None):
+    def get_mask_and_config_1_2025_05_18(self, im_jpg: Image, im_png: Image):
         """
         步骤:
         1、尺寸进行对应缩小
@@ -173,9 +200,7 @@ class GeneratePic(object):
         3、自动色阶检查亮度
         4、输出自动色阶参数、以及放大的尺寸蒙版
         """
-        # ===================尺寸进行对应缩小
-        orign_x, orign_y = im_jpg.size
-
+        # ===================尺寸进行对应缩小(提升处理速度)
         im_jpg = to_resize(im_jpg, width=800)
         im_png = to_resize(im_png, width=800)
         x1, y1, x2, y2 = im_png.getbbox()
@@ -185,31 +210,63 @@ class GeneratePic(object):
         # 查找每列的最低非透明点
         min_y_values = find_lowest_non_transparent_points(cv2_png)
         # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
+        image_high = im_jpg.height
+        print("图片高度:", image_high)
         cv2_jpg = pil_to_cv2(im_jpg)
         # 返回线条图片,以及最低位置
-        img_with_shifted_line, lowest_y = draw_shifted_line(image=cv2_jpg,
-                                                            min_y_values=min_y_values,
-                                                            shift_amount=15,
-                                                            one_line_pos=(x1, x2),
-                                                            line_color=(0, 0, 0),
-                                                            line_thickness=20)
-
+        print("返回线条图片,以及最低位置")
+        img_with_shifted_line, lowest_y = draw_shifted_line(
+            image=cv2_jpg,
+            min_y_values=min_y_values,
+            shift_amount=15,
+            one_line_pos=(x1, x2),
+            line_color=(0, 0, 0),
+            line_thickness=20,
+            app=settings.app,
+            crop_image_box=(x1, y1, x2, y2),
+        )
+        print("66  制作蒙版")
         # 制作蒙版
-        mask = cv2_to_pil(img_with_shifted_line)
-        mask = mask.convert('L')  # 转换为灰度图
+        mask_line = cv2_to_pil(img_with_shifted_line)
+        mask = mask_line.convert("L")  # 转换为灰度图
         mask = ImageOps.invert(mask)
         # 蒙版扩边
-        mask = expand_mask(mask, expansion_radius=30, blur_radius=10)
+        print("72  蒙版扩边")
+        # 默认expansion_radius 65 blur_radius 45
+        mask = expand_or_shrink_mask(
+            pil_image=mask, expansion_radius=50, blur_radius=35
+        )
+
+        # mask1 = expand_mask(mask, expansion_radius=30, blur_radius=10)
+        # mask1.save("mask1.png")
+        # mask2 = expand_or_shrink_mask(pil_image=mask, expansion_radius=60, blur_radius=30)
+        # mask2.save("mask2.png")
+        # raise 11
 
         # ====================生成新的图片
+        print("84  生成新的图片")
         bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255))
         bg.paste(im_png, mask=im_png)
         bg.paste(im_jpg, mask=mask)  # 粘贴有阴影的地方
-
-        # bg = pil_to_cv2(bg)
-        # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2)
-        # bg = cv2_to_pil(bg)
-        # bg.show()
+        if self.is_test:
+            _bg = bg.copy()
+            draw = ImageDraw.Draw(_bg)
+            # 定义直线的起点和终点坐标
+            start_point = (0, lowest_y)  # 直线的起始点
+            end_point = (_bg.width, lowest_y)  # 直线的结束点
+            # 定义直线的颜色(R, G, B)
+            line_color = (255, 0, 0)  # 红色
+            # 绘制直线
+            draw.line([start_point, end_point], fill=line_color, width=1)
+            # mask.show()
+            # bg = pil_to_cv2(bg)
+            # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2)
+            # bg = cv2_to_pil(bg)
+            _r = Image.new(mode="RGBA", size=im_png.size, color=(246, 147, 100, 255))
+            mask_line = mask_line.convert("L")  # 转换为灰度图
+            mask_line = ImageOps.invert(mask_line)
+            _bg.paste(_r, mask=mask)
+            _bg.show()
 
         # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png")
         # bg.show()
@@ -219,31 +276,65 @@ class GeneratePic(object):
         _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR)
         # 背景阴影
         im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
-
+        print("image_high lowest_y", image_high, lowest_y)
+        if lowest_y < 0 or lowest_y >= image_high:
+            lowest_y = image_high - 1
+        print("image_high lowest_y", image_high, lowest_y)
         rows = [lowest_y]  # 需要检查的像素行
+
+        print("copy.copy(im_shadow)")
         _im_shadow = copy.copy(im_shadow)
-        Midtones = 0.62
+        Midtones = 0.7
         Highlight = 235
-        k = 10
+        k = 12
+        print("循环识别")
         while k:
+            print("循环识别:{}".format(k))
+            if settings.app:
+                settings.app.processEvents()
             k -= 1
             Midtones += 0.1
             if Midtones > 1:
                 Midtones = 1
             Highlight -= 3
-            _im_shadow = levels_adjust(img=im_shadow, Shadow=0, Midtones=Midtones, Highlight=Highlight,
-                                       OutShadow=0,
-                                       OutHighlight=255, Dim=3)
-            brightness_list = calculate_average_brightness_opencv(img_gray=_im_shadow, rows_to_check=rows)
+            _im_shadow = levels_adjust(
+                img=im_shadow,
+                Shadow=0,
+                Midtones=Midtones,
+                Highlight=Highlight,
+                OutShadow=0,
+                OutHighlight=255,
+                Dim=3,
+            )
+            brightness_list = calculate_average_brightness_opencv(
+                img_gray=_im_shadow, rows_to_check=rows
+            )
             print(brightness_list)
-            if brightness_list[0] >= 254:
+            if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
                 break
         print("Midtones,Highlight:", Midtones, Highlight)
-        config = (Midtones, Highlight)
+
         im_shadow = cv2_to_pil(_im_shadow)
-        im_shadow.paste(im_png, (0, 0), im_png)  # 把原图粘贴回去,避免色差
-        if out_image_path:
-            im_shadow.save(out_image_path)
+
+        # ========================================================
+        # 计算阴影的亮度,用于确保阴影不要太黑
+
+        # 1、图片预处理,只保留阴影
+        only_shadow_img = im_shadow.copy()
+        only_shadow_img.paste(
+            Image.new(
+                mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)
+            ),
+            mask=im_png,
+        )
+        average_brightness = calculated_shadow_brightness(only_shadow_img)
+        print("average_brightness:", average_brightness)
+
+        config = {
+            "Midtones": Midtones,
+            "Highlight": Highlight,
+            "average_brightness": average_brightness,
+        }
 
         return mask, config
 
@@ -257,10 +348,23 @@ class GeneratePic(object):
             output_queue.put(True)
 
     @time_it
-    def run(self, image_path, cut_image_path, out_path, image_deal_mode=0, image_index=99,
-            out_pic_size=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对象
-        print("****************************处理图像核心********************************\n")
+    def run(
+        self,
+        image_path,
+        cut_image_path,
+        out_path,
+        image_deal_mode=0,
+        image_index=99,
+        out_pic_size=1024,
+        is_logo=True,
+        out_process_path_1=None,
+        out_process_path_2=None,
+        resize_mode=None,
+        max_box=None,
+        logo_path="",
+        curve_mask=False,
+        **kwargs,
+    ):  # im 为cv对象
         """
         image_path:原始图
         cut_image_path:抠图结果 与原始图尺寸相同
@@ -272,6 +376,7 @@ class GeneratePic(object):
         out_process_path_1=None, 有阴影的图片,白底非透明
         out_process_path_2=None, 已抠图的图片
         resize_mode=0,1,2 主体缩小尺寸
+        curve_mask 为True时,表示为对鞋曲线部分的mask,不做剪裁
         """
         if "output_queue" in kwargs:
             output_queue = kwargs["output_queue"]
@@ -291,26 +396,31 @@ class GeneratePic(object):
 
         # ================自动色阶处理
         _s = time.time()
-        shadow_mask, config = self.get_mask_and_config(im_jpg=im_shadow, im_png=cut_image)
+        shadow_mask, config = self.get_mask_and_config(
+            im_jpg=im_shadow, im_png=cut_image, curve_mask=curve_mask
+        )
         print("242  need_time_2:{}".format(time.time() - _s))
 
         shadow_mask = shadow_mask.resize(im_shadow.size)
 
         # =====抠图,形成新的阴影背景图=====
-        # TODO 待移除
-        # settings.app.processEvents()
 
-        _new_im_shadow = Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255))
+        _new_im_shadow = Image.new(
+            mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255)
+        )
         _new_im_shadow.paste(im_shadow, mask=shadow_mask)  # 粘贴有阴影的地方
         # _new_im_shadow.show()
         _new_im_shadow = pil_to_cv2(_new_im_shadow)
         _new_im_shadow = cv2.cvtColor(_new_im_shadow, cv2.COLOR_BGR2GRAY)
-        _new_im_shadow = levels_adjust(img=_new_im_shadow,
-                                       Shadow=0,
-                                       Midtones=config["Midtones"],
-                                       Highlight=config["Highlight"],
-                                       OutShadow=0,
-                                       OutHighlight=255, Dim=3)
+        _new_im_shadow = levels_adjust(
+            img=_new_im_shadow,
+            Shadow=0,
+            Midtones=config["Midtones"],
+            Highlight=config["Highlight"],
+            OutShadow=0,
+            OutHighlight=255,
+            Dim=3,
+        )
 
         im_shadow = cv2_to_pil(_new_im_shadow)
 
@@ -318,7 +428,9 @@ class GeneratePic(object):
         average_brightness = config["average_brightness"]
         if config["average_brightness"] < 180:
             # 调整阴影亮度
-            backdrop_prepped = np.asfarray(Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255)))
+            backdrop_prepped = np.asfarray(
+                Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255))
+            )
             im_shadow = im_shadow.convert("RGBA")
             source_prepped = np.asfarray(im_shadow)
             # im_shadow.show()
@@ -327,8 +439,10 @@ class GeneratePic(object):
             opacity = max(0.5, min(opacity, 1))
 
             print("阴影透明度:{}%".format(int(opacity * 100)))
-            blended_np = multiply(backdrop_prepped, source_prepped, opacity=int(opacity * 100) / 100)
-            im_shadow = Image.fromarray(np.uint8(blended_np)).convert('RGB')
+            blended_np = multiply(
+                backdrop_prepped, source_prepped, opacity=int(opacity * 100) / 100
+            )
+            im_shadow = Image.fromarray(np.uint8(blended_np)).convert("RGB")
             # im_shadow.show()
 
         # 把原图粘贴回去,避免色差
@@ -342,14 +456,24 @@ class GeneratePic(object):
             out_image_1 = im_shadow.copy()
             if image_deal_mode == 1:
                 out_image_1 = out_image_1.transpose(Image.FLIP_LEFT_RIGHT)
-            out_image_1.save(out_process_path_1)
+
+            self.saver.save_image(
+                image=out_image_1, file_path=out_process_path_1, save_mode="png"
+            )
+            # save_image_by_thread(image=out_image_1, out_path=out_process_path_1)
+            # out_image_1.save(out_process_path_1)
 
         # 保存抠图结果,没有底图,没有logo
         if out_process_path_2:
             out_image_2 = cut_image.copy()
             if image_deal_mode == 1:
                 out_image_2 = out_image_2.transpose(Image.FLIP_LEFT_RIGHT)
-            out_image_2.save(out_process_path_2)
+
+            self.saver.save_image(
+                image=out_image_2, file_path=out_process_path_2, save_mode="png"
+            )
+            # save_image_by_thread(image=out_image_2, out_path=out_process_path_2, save_mode="png")
+            # out_image_2.save(out_process_path_2)
 
         # 不生成主图时直接退出
         if not out_path:
@@ -434,13 +558,36 @@ class GeneratePic(object):
             image_bg = sharpen_image(image_bg, factor=settings.OUT_PIC_FACTOR)
 
         if out_pic_size < 1600:
-            image_bg = image_bg.resize((out_pic_size, out_pic_size), resample=settings.RESIZE_IMAGE_MODE)
+            image_bg = image_bg.resize(
+                (out_pic_size, out_pic_size), resample=settings.RESIZE_IMAGE_MODE
+            )
 
         if settings.OUT_PIC_MODE == ".jpg":
-            image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG")
+            if settings.OUT_PIC_QUALITY == "普通":
+                self.saver.save_image(
+                    image=image_bg,
+                    file_path=out_path,
+                    save_mode="jpg",
+                    quality=None,
+                    dpi=None,
+                    _format="JPEG",
+                )
+                # save_image_by_thread(image_bg, out_path, save_mode="jpg", quality=None, dpi=None, _format="JPEG")
+                # image_bg.save(out_path, format="JPEG")
+            else:
+                self.saver.save_image(
+                    image=image_bg,
+                    file_path=out_path,
+                    save_mode="jpg",
+                    quality=100,
+                    dpi=(300, 300),
+                    _format="JPEG",
+                )
+                # save_image_by_thread(image_bg, out_path, save_mode="jpg", quality=100, dpi=(300, 300), _format="JPEG")
+                # image_bg.save(out_path, quality=100, dpi=(300, 300), format="JPEG")
         else:
-            # quality=quality
-            image_bg.save(out_path, quality=100)
+            self.saver.save_image(image=image_bg, file_path=out_path, save_mode="png")
+            # image_bg.save(out_path)
 
         if output_queue is not None:
             output_queue.put(True)

+ 101 - 0
python/service/multi_threaded_image_saving.py

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

+ 7 - 2
python/service/remove_bg_ali.py

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

+ 31 - 5
python/settings.py

@@ -20,8 +20,9 @@ device_config_crud = CRUD(DeviceConfig)
 all_devices = device_config_crud.read_all(session)
 if len(all_devices) == 0:
     # 如果配置表中一条数据都没有,就将初始化数据全部插入到数据表中
+    action_tabs = json.load(open("action_tabs.json", encoding="utf-8"))
     actions = json.load(open("action.json", encoding="utf-8"))
-    batch_insert_device_configs(session, actions)
+    batch_insert_device_configs(session, action_tabs, actions)
 sys_config_crud = CRUD(SysConfigs)
 all_sys_configs = sys_config_crud.read_all(session)
 if len(all_sys_configs) == 0:
@@ -179,10 +180,10 @@ is_test_plugins = true_divide
 
 OUT_PIC_MODE = "."+getSysConfigs("basic_configs", "image_out_format", "png")  # ".png"
 
-OUT_PIC_SIZE = int(
-    800
-    if getSysConfigs("basic_configs", "main_image_size", "800") == ""
-    else getSysConfigs("basic_configs", "main_image_size", "800")
+OUT_PIC_SIZE = (
+    [1600]
+    if getSysConfigs("basic_configs", "main_image_size", [1600]) == ""
+    else getSysConfigs("basic_configs", "main_image_size", [1600])
 )  # 主图大小
 
 
@@ -230,3 +231,28 @@ import importlib
 
 
 # loaded_plugins = [load_plugin(p.lstrip(".")) for p in plugins]
+
+
+GRENERATE_MAIN_PIC_BRIGHTNESS = int(
+    getSysConfigs("other_configs", "grenerate_main_pic_brightness", 254)
+)  # 色阶是否调整到位判断
+SHADOW_PROCESSING = int(
+    getSysConfigs("other_configs", "shadow_processing", 0)
+)  # 0表示要直线和曲线,1 表示只要直线
+LOWER_Y = int(
+    getSysConfigs("other_configs", "lower_y", 4)
+)  # 鞋底以下多少距离作为阴影蒙版
+CHECK_LOWER_Y = int(
+    getSysConfigs("other_configs", "check_lower_y", 4)
+)  # 检测亮度区域,倒数第几行
+IS_GET_GREEN_MASK = (
+    True
+    if getSysConfigs("other_configs", "is_get_green_mask", "否") == "是"
+    else False
+)  # 是否进行绿幕抠图
+IMAGE_SAVE_MAX_WORKERS = int(
+    getSysConfigs("other_configs", "image_save_max_workers", 4)
+)  # 批量保存的线程大小
+COLOR_GRADATION_CYCLES = int(
+    getSysConfigs("other_configs", "color_gradation_cycles", 22)
+)  # 色阶处理循环次数

+ 1 - 0
python/setup.py

@@ -15,6 +15,7 @@ options = {
         "include_files": [
             "config.ini",
             "action.json",
+            "action_tabs.json",
             "sys_configs.json",
             # "database.db",
             # "custom_plugins/",

+ 18 - 19
python/sockets/message_handler.py

@@ -3,7 +3,7 @@ from models import WebSocket
 import json, asyncio
 from mcu.DeviceControl import DeviceControl, checkMcuConnection
 from mcu.BlueToothMode import BlueToothMode
-from databases import DeviceConfig,SqlQuery,CRUD,PhotoRecord
+from databases import DeviceConfig, SqlQuery, CRUD, PhotoRecord, SysConfigs
 from mcu.capture.module_digicam import DigiCam
 
 # socket消息发送逻辑处理方法
@@ -63,25 +63,15 @@ async def handlerSend(
                 return
             device_ctrl = DeviceControl(websocket_manager=manager)
             device_ctrl.controlDevice(device_name, value)
+        case "stop_action":
+            device_ctrl = DeviceControl(websocket_manager=manager)
+            if device_ctrl.is_runn_action == True:
+                print("动作执行中,停止")
+                device_ctrl.is_stop_action = True
+            else:
+                print("动作没有执行,略过")
         case "run_mcu":
             msg_type = "run_mcu"
-            # try:
-            #     # 判断拍照软件是否初始化
-            #     digicam = DigiCam()
-            #     camera_is_connect = digicam.checkCameraConnect()
-            #     if camera_is_connect is not True:
-            #         data = manager.jsonMessage(
-            #             code=1, msg="相机未连接,请检查", msg_type=msg_type
-            #         )
-            #         await manager.send_personal_message(data, websocket)
-            #         return
-            #     digicam.getCaptureFolderPath()
-            # except:
-            #     data = manager.jsonMessage(
-            #         code=1, msg="digicam未初始化,请检查", msg_type=msg_type
-            #     )
-            #     await manager.send_personal_message(data, websocket)
-            #     return
             action_info = data.get("action", "执行左脚程序")
             goods_art_no = data.get("goods_art_no", None)
             if goods_art_no == None or goods_art_no =="":
@@ -92,6 +82,15 @@ async def handlerSend(
                 await manager.send_personal_message(data, websocket)
                 return
             session = SqlQuery()
+            sys_configs = CRUD(SysConfigs)
+            action_configs = sys_configs.read(
+                session, conditions={"key": "action_configs"}
+            )
+            action_configs_json = json.loads(action_configs.value)
+            action_flag = "left"
+            if "右" in action_info:
+                action_flag = "right"
+            tab_id = action_configs_json.get(action_flag)
             photoRecord = CRUD(PhotoRecord)
             goods_art_record = photoRecord.read(session,conditions={"goods_art_no": goods_art_no})
             if goods_art_record != None:
@@ -103,7 +102,7 @@ async def handlerSend(
                 await manager.send_personal_message(data, websocket)
                 return
             crud = CRUD(DeviceConfig)
-            condtions = {"mode_type": action_info, "action_status": True}
+            condtions = {"tab_id": tab_id}
             all_devices = crud.read_all(
                 session, conditions=condtions, order_by="action_index", ascending=True
             )

+ 4 - 4
python/sys_configs.json

@@ -1,7 +1,7 @@
 [
     {
         "key": "basic_configs",
-        "value": "{\"main_image_size\":1600,\"image_out_format\":\"png\",\"image_sharpening\":1.0}"
+        "value": "{\"main_image_size\":[1600],\"image_out_format\":\"png\",\"image_sharpening\":1.0}"
     },
     {
         "key": "take_photo_configs",
@@ -9,10 +9,10 @@
     },
     {
         "key": "other_configs",
-        "value": "{\"product_type\":\"鞋类\",\"cutout_mode\":\"普通抠图\",\"device_speed\":\"二档\",\"running_mode\":\"普通模式\"}"
+        "value": "{\"product_type\":\"鞋类\",\"cutout_mode\":\"普通抠图\",\"device_speed\":\"二档\",\"running_mode\":\"普通模式\",\"grenerate_main_pic_brightness\":254,\"shadow_processing\":0,\"lower_y\":4,\"check_lower_y\":4,\"is_get_green_mask\":\"否\",\"image_save_max_workers\":4,\"color_gradation_cycles\":22}"
     },
     {
-        "key": "logo_configs",
-        "value": "[]"
+        "key": "action_configs",
+        "value": "{\"left\":1,\"right\":7}"
     }
 ]

+ 9 - 19
python/temp.py

@@ -1,21 +1,11 @@
-# from databases import CRUD, SqlQuery, PhotoRecord, insert_photo_records
-# from datetime import datetime
+from databases import CRUD, SqlQuery, PhotoRecord, insert_photo_records, DeviceConfig,DeviceConfigTabs
+from datetime import datetime
 
-# session = SqlQuery()
-# crud = CRUD(PhotoRecord)
-# record = crud.read(session=session, order_by="id", ascending=False)
-# if record == None:
-#     # 发送失败消息
-#     pass
-# else:
-#     insert_photo_records(
-#         record.image_deal_mode, record.goods_art_no, record.image_index+1
-#     )
-import time, shutil
-from utils.utils_func import check_path
-dst_path = "C:/Development/project/python/CameraMachine/build/extraResources/py/data/"
-check_path(dst_path)
-shutil.copy(
-    "C:\Development\project\python\auto_photo\data\DSC_0438.jpg",
-    dst_path,
+session = SqlQuery()
+crud = CRUD(DeviceConfigTabs)
+device_tab = DeviceConfigTabs(
+    mode_type=0,
+    mode_name='test',
 )
+create_obj = crud.create(session, obj_in=device_tab)
+print(create_obj.id)