Переглянути джерело

feat(image): 添加图像羽化滤镜功能并优化主图生成效果

- 在主图生成流程中新增 filter_2 方法实现图像羽化处理
- 对阴影图像应用羽化效果,提升视觉柔和度
- 优化图像处理逻辑,解决纯黑区域统计问题
- 临时注释 MCU 运动检测异常处理的 socket 消息发送
- 整理代码格式,优化导入语句和空行布局
- 添加新的相机属性控制功能模块(temp2.py)
rambo 1 день тому
батько
коміт
c93aae1498

+ 1 - 1
python/mcu/ProgramItem.py

@@ -173,7 +173,7 @@ class ProgramItem(BaseClass):
                 self.error_info_text = "MCU检测运动未停止,自动退出"
                 self.set_state(state_value=99)  # 标记异常
                 print("MCU检测运动未停止,自动退出")
-                self.sendSocketMessage(msg=self.error_info_text, device_status=-1)
+                # self.sendSocketMessage(msg=self.error_info_text, device_status=-1)
                 print("\033[1;31m执行结束\033[0m", "MCU检测运动未停止,自动退出")
                 return False
                 # return True

+ 22 - 0
python/service/grenerate_main_image_test.py

@@ -819,6 +819,7 @@ class GeneratePic(object):
                 backdrop_prepped, source_prepped, opacity=opacity_params / 100
             )
             im_shadow = Image.fromarray(np.uint8(blended_np)).convert("RGB")
+        im_shadow = self.filter_2(im_shadow=im_shadow, feather=100)
         # 把原图粘贴回去,避免色差
         im_shadow.paste(cut_image, (0, 0), mask=cut_image)
         # _new_im_shadow.show()
@@ -1034,3 +1035,24 @@ class GeneratePic(object):
         else:
             scale = box_height / height
         return scale
+
+    def filter_2(self, im_shadow, feather=100):
+        # feather 羽化宽度,400 图建议 40~120,越大越柔和
+        background = Image.new(mode="RGB", size=im_shadow.size, color=(255, 255, 255))
+        # 遮罩边缘做羽化处理,设计一个蒙版
+        w, h = im_shadow.size
+        mask = Image.new(mode="L", size=im_shadow.size, color=0)
+        draw = ImageDraw.Draw(mask)
+
+        # x0, y0:矩形左上角坐标
+        # x1, y1:矩形右下角坐标
+        draw.rectangle([feather, feather, w - feather, h - feather], fill=255)
+
+        # 高斯模糊,让边缘从白到黑渐变
+        mask = mask.filter(ImageFilter.GaussianBlur(feather / 2))
+        # if self.is_test:
+        #     mask.show()
+        background.paste(im=im_shadow, mask=mask)
+        im_shadow = background
+
+        return im_shadow

+ 4 - 0
python/service/image_deal_base_func.py

@@ -496,5 +496,9 @@ def get_png_brightness(img_gray, mask):
     H, S, V = cv2.split(hsv)
     # print(V[0])
     v = V[V != 0]  # 亮度非零的值
+    if len(v) == 0:
+        # mask 全黑(无背景/阴影区域)或该区域全为纯黑,没有可统计的像素
+        # 返回 255.0 视同已调亮到位,直接结束循环(纯黑区域 levels_adjust 也调不亮)
+        return 255.0
     average_v = sum(v) / len(v)
     return average_v

+ 38 - 22
python/settings.py

@@ -2,12 +2,13 @@ from wsgiref import headers
 from dotenv import load_dotenv, find_dotenv
 from pathlib import Path  # Python 3.6+ only
 import configparser, json, pytz
-import requests,os
+import requests, os
 import pillow_avif
 from utils.common import message_queue
 import hashlib
 from pathlib import Path
-import os,stat
+import os, stat
+
 TIME_ZONE = pytz.timezone("Asia/Shanghai")
 from numpy import true_divide
 from databases import (
@@ -20,6 +21,7 @@ from databases import (
     batch_insert_device_configs,
     batch_insert_device_configsNew,
 )
+
 USER_TOKEN = None  # 可以通过外部设置或从配置文件中加载
 USER_ENV = None  # 可以通过外部设置或从配置文件中加载
 USER_CAMERAS = False  # 可以通过外部设置或从配置文件中加载
@@ -52,6 +54,8 @@ else:
             session.add(config)
             session.commit()  # 合并事务提交
 session.close()
+
+
 # 初始化数据表---结束
 
 
@@ -99,6 +103,8 @@ def getSysConfigs(key, item, default=None):
         if default_format == "" or default_format == None:
             return "png"
     return config.get(item, default)
+
+
 def updateSysConfigs(params):
     session = SqlQuery()
     sysConfig = CRUD(SysConfigs)
@@ -110,16 +116,19 @@ def updateSysConfigs(params):
     sysConfig.updateConditions(session, conditions={"key": params["key"]}, **params)
     session.close()
 
+
 def get_dict_value(_dict, key, default=None):
     if key in _dict:
         return _dict[key]
     else:
         return default
+
+
 # 鞋设备为1  服装设备为5
 MCU_CODE = 1
 MACHINE_LEVEL = (
     "二档"
-    if getSysConfigs("other_configs", "device_speed", "二档") == ""
+    if not getSysConfigs("other_configs", "device_speed", "二档")
     else getSysConfigs("other_configs", "device_speed", "二档")
 )
 IS_TEST = False
@@ -127,11 +136,12 @@ IS_MCU = True
 IS_LIN_SHI_TEST = False
 PhotographSeconds = float(
     0.5
-    if getSysConfigs("take_photo_configs", "camera_delay", "0.5") == ""
+    if not getSysConfigs("take_photo_configs", "camera_delay", "0.5")
     else getSysConfigs("take_photo_configs", "camera_delay", "0.5")
 )  # 拍照停留时间
 MAX_PIXIAN_SIZE = 12000000
 
+
 def moveSpeed(level: str = None):
     config = {
         "一档": {
@@ -209,7 +219,6 @@ HLM_HOST = config.get("log", "hlm_host")
 
 PROJECT = config.get("app", "project")
 
-
 # ----------------------------------
 mcu_config_dict = config.items("mcu_config")
 _mcu_config_dict = {}
@@ -232,7 +241,6 @@ MOVE_UP = _mcu_config_dict["move_up"]
 MOVE_DOWN = _mcu_config_dict["move_down"]
 STOP = _mcu_config_dict["stop"]
 
-
 # camera_config_dict = config.items("camera_config")
 # _camera_config_dict = {}
 # for i, k in mcu_config_dict:
@@ -246,14 +254,16 @@ DOMAIN = (
     if config.get("app", "env") != "dev"
     else "https://dev2.pubdata.cn"
 )
+
+
 def getDoman(env):
     return "https://dev2.valimart.net" if env != "dev" else "https://dev2.pubdata.cn"
 
+
 Company = "惠利玛"
 
 is_test_plugins = true_divide
 
-
 OUT_PIC_MODE = "." + getSysConfigs("basic_configs", "image_out_format", "png")  # ".png"
 
 OUT_PIC_SIZE = (
@@ -262,7 +272,6 @@ OUT_PIC_SIZE = (
     else getSysConfigs("basic_configs", "main_image_size", [1600])
 )  # 主图大小
 
-
 Mode = getSysConfigs("other_configs", "product_type", "鞋类")  # 程序执行类
 OUT_PIC_FACTOR = float(
     1
@@ -270,17 +279,14 @@ OUT_PIC_FACTOR = float(
     else getSysConfigs("basic_configs", "image_sharpening", "1")
 )  # 图片锐化
 
-
 RESIZE_IMAGE_MODE = 1
 
 GRENERATE_MAIN_PIC_BRIGHTNESS = 254  # 色阶是否调整到位判断
 
 RUNNING_MODE = getSysConfigs("other_configs", "running_mode", "普通模式")
 
-
 DEFAULT_CUTOUT_MODE = getSysConfigs("other_configs", "cutout_mode", "普通抠图")
 
-
 CUTOUT_MODE = (
     0 if getSysConfigs("other_configs", "cutout_mode", "普通抠图") == "普通抠图" else 1
 )
@@ -308,6 +314,8 @@ IMAGE_SAVE_MAX_WORKERS = int(
 COLOR_GRADATION_CYCLES = int(
     getSysConfigs("other_configs", "color_gradation_cycles", 23)
 )  # 色阶处理循环次数
+
+
 # grenerate_main_pic_brightness 亮度范围 默认值254 范围 200 -255 步长 1
 # opacity 透明度 默认值 0.5 范围 0.5-1 步长 0.1
 # IMAGE_MASK_CONFIG = int(
@@ -336,7 +344,9 @@ def recordDataPoint(token=None, page="", uuid=None, data=""):
     return requests.post(
         headers=headers, data=json.dumps(params), url=DOMAIN + "/api/record/point"
     )
-def syncPhotoRecord(data,action_type=0):
+
+
+def syncPhotoRecord(data, action_type=0):
     """同步图片记录"""
     headers = {
         "Authorization": f"Bearer {USER_TOKEN}",
@@ -344,9 +354,9 @@ def syncPhotoRecord(data,action_type=0):
     }
     machine_type = MACHINE_TYPE
     url = getDoman(USER_ENV) + f"/api/ai_image/camera_machine/photo_records_handler"
-    postData = {"data":data,"machine_type":machine_type,'action_type':action_type}
-    response = requests.post(url=url,data=json.dumps(postData, ensure_ascii=False, default=str), headers=headers)
-    print("用户token",response.content)
+    postData = {"data": data, "machine_type": machine_type, 'action_type': action_type}
+    response = requests.post(url=url, data=json.dumps(postData, ensure_ascii=False, default=str), headers=headers)
+    print("用户token", response.content)
 
 
 def syncBatchPhotoRecordDelete():
@@ -376,12 +386,13 @@ def checkRecordSyncStatus():
     url = getDoman(USER_ENV) + f"/api/ai_image/camera_machine/photo_records_handler?machine_type={machine_type}"
     try:
         response = requests.get(url=url, headers=headers).json()
-        return response.get("data").get('status',True)
+        return response.get("data").get('status', True)
     except Exception as e:
         return True
 
+
 async def sendSocketMessage(
-    code=0, msg="", data=None, device_status=2, msg_type=""
+        code=0, msg="", data=None, device_status=2, msg_type=""
 ):
     data = {
         "code": code,
@@ -403,14 +414,19 @@ def calculate_md5(filepath):
             md5hash.update(chunk)
         # 返回MD5哈希的十六进制表示
         return md5hash.hexdigest()
+
+
 __output_dir = config.get("output_config", "output_dir")
 path_obj = Path(os.path.abspath(__output_dir))
 OUTPUT_DIR = path_obj.absolute()
-print("OUTPUT_DIR",__output_dir,OUTPUT_DIR)
+print("OUTPUT_DIR", __output_dir, OUTPUT_DIR)
+
+
 def handle_remove_readonly(func, path, exc):
     os.chmod(path, stat.S_IWRITE)
     func(path)
 
+
 try:
     __scan_dir = config.get("scan_config", "scan_dir")
     if __scan_dir == "" or __scan_dir is None:
@@ -424,7 +440,7 @@ try:
 except:
     SCAN_DIR = None
     print('扫码配置不存在')
-print("SCAN_DIR",SCAN_DIR)
+print("SCAN_DIR", SCAN_DIR)
 CUSTOMER_TEMPLATE_URL = config.get("customer_template", "template_url")
 
 
@@ -436,11 +452,11 @@ def hex_to_rgb(hex_color):
     """
     # 移除可能存在的 # 符号
     hex_color = hex_color.lstrip('#')
-    
+
     # 确保是有效的十六进制字符串
     if len(hex_color) != 6:
         raise ValueError("十六进制颜色值格式不正确,应为6位十六进制数")
-    
+
     # 将十六进制字符串转换为RGB值
     try:
         r = int(hex_color[0:2], 16)
@@ -462,5 +478,5 @@ def rgb_to_hex(r, g, b):
     # 验证RGB值在有效范围内
     if not all(0 <= val <= 255 for val in [r, g, b]):
         return "#FFFFFF"
-    
+
     return "#{:02X}{:02X}{:02X}".format(r, g, b)

+ 41 - 0
python/temp2.py

@@ -0,0 +1,41 @@
+import asyncio
+
+from mcu.capture.smart_shooter_class import SmartShooter
+
+smart = SmartShooter(None)
+
+
+async def parserProperty(CameraPropertyType=None):
+    status, camera_property = await smart.GetCameraProperty()
+    if CameraPropertyType:
+        camera_property_obj = None
+        for item in camera_property:
+            if item['CameraPropertyType'] == CameraPropertyType:
+                camera_property_obj = item
+                break
+        return camera_property_obj if camera_property_obj else camera_property
+    return camera_property
+
+
+async def setCameraProperty(property="Aperture", value=""):
+    await smart.setCameraProperty(property=property, value=value)
+
+
+async def main():
+    '''
+    Aperture:光圈
+    ShutterSpeed:快门速度
+    ISO:ISO设置
+    Exposure:曝光
+    "CameraPropertyType": 参数类型,
+    "CameraPropertyValue": 当前值,
+    "CameraPropertyIsWriteable": 是否可写,
+    "CameraPropertyRange" 参数范围
+    '''
+    # camera_property = await parserProperty("Aperture")
+    # print('property', camera_property)
+    await setCameraProperty('Exposure', '5.0')
+
+
+if __name__ == "__main__":
+    asyncio.run(main())