commit 5ca747feed7d34db0a10fb007a41445750329a4d Author: leon Date: Tue Feb 25 10:15:21 2025 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8333713 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +result/ +.idea/ +.vscode/ \ No newline at end of file diff --git a/1_roi_image.png b/1_roi_image.png new file mode 100644 index 0000000..f86f11f Binary files /dev/null and b/1_roi_image.png differ diff --git a/2_roi_image.png b/2_roi_image.png new file mode 100644 index 0000000..e16a1e9 Binary files /dev/null and b/2_roi_image.png differ diff --git a/Open3D.dll b/Open3D.dll new file mode 100644 index 0000000..d5d29a4 Binary files /dev/null and b/Open3D.dll differ diff --git a/app.log b/app.log new file mode 100644 index 0000000..8d4b8f4 --- /dev/null +++ b/app.log @@ -0,0 +1,5 @@ +2025-02-20 17:05:14 - clog.py:53 - DEBUG:my_logger:This is a debug message +2025-02-20 17:05:14 - clog.py:54 - INFO:my_logger:This is an info message +2025-02-20 17:05:14 - clog.py:55 - WARNING:my_logger:This is a warning message +2025-02-20 17:05:14 - clog.py:56 - ERROR:my_logger:This is an error message +2025-02-20 17:05:14 - clog.py:57 - CRITICAL:my_logger:This is a critical message diff --git a/bm_struct_array.json b/bm_struct_array.json new file mode 100644 index 0000000..ea7614a --- /dev/null +++ b/bm_struct_array.json @@ -0,0 +1,272 @@ +[ + { + "code": "1", + "type": "250x1450", + "x": "196.0", + "y": 0, + "center": "351", + "w": "1450", + "h": "250", + "angle": "0\u00b0" + }, + { + "code": "2", + "type": "300x300", + "x": "174.0", + "y": 0, + "center": "754", + "w": "300", + "h": "300", + "angle": "0\u00b0" + }, + { + "code": "3", + "type": "501x500", + "x": "889.5", + "y": 0, + "center": "1054.5", + "w": "500", + "h": "501", + "angle": "0\u00b0" + }, + { + "code": "4", + "type": "250x648", + "x": "-32.0", + "y": 0, + "center": "1149", + "w": "648", + "h": "250", + "angle": "0\u00b0" + }, + { + "code": "5", + "type": "250x250", + "x": "588.0", + "y": 0, + "center": "1543", + "w": "250", + "h": "250", + "angle": "0\u00b0" + }, + { + "code": "6", + "type": "249x249", + "x": "1031.5", + "y": 0, + "center": "1750.5", + "w": "249", + "h": "249", + "angle": "0\u00b0" + }, + { + "code": "7", + "type": "400x399", + "x": "-199.5", + "y": 0, + "center": "1955", + "w": "399", + "h": "400", + "angle": "0\u00b0" + }, + { + "code": "8", + "type": "300x302", + "x": "394.0", + "y": 0, + "center": "2159", + "w": "302", + "h": "300", + "angle": "0\u00b0" + }, + { + "code": "9", + "type": "300x302", + "x": "996.0", + "y": 0, + "center": "2157", + "w": "302", + "h": "300", + "angle": "0\u00b0" + }, + { + "code": "10", + "type": "248x1447", + "x": "2190.0", + "y": 0, + "center": "345", + "w": "1447", + "h": "248", + "angle": "0\u00b0" + }, + { + "code": "11", + "type": "298x297", + "x": "2198.5", + "y": 0, + "center": "747", + "w": "297", + "h": "298", + "angle": "0\u00b0" + }, + { + "code": "12", + "type": "499x499", + "x": "2891.0", + "y": 0, + "center": "1040.5", + "w": "499", + "h": "499", + "angle": "0\u00b0" + }, + { + "code": "13", + "type": "245x648", + "x": "1994.5", + "y": 0, + "center": "1146.5", + "w": "648", + "h": "245", + "angle": "0\u00b0" + }, + { + "code": "14", + "type": "247x247", + "x": "2589.5", + "y": 0, + "center": "1550.5", + "w": "247", + "h": "247", + "angle": "0\u00b0" + }, + { + "code": "15", + "type": "246x247", + "x": "2998.5", + "y": 0, + "center": "1757", + "w": "247", + "h": "246", + "angle": "0\u00b0" + }, + { + "code": "16", + "type": "398x392", + "x": "1799.5", + "y": 0, + "center": "1955", + "w": "392", + "h": "398", + "angle": "0\u00b0" + }, + { + "code": "17", + "type": "300x293", + "x": "2394.5", + "y": 0, + "center": "2147", + "w": "293", + "h": "300", + "angle": "0\u00b0" + }, + { + "code": "18", + "type": "298x297", + "x": "3001.5", + "y": 0, + "center": "2144", + "w": "297", + "h": "298", + "angle": "0\u00b0" + }, + { + "code": "19", + "type": "245x1447", + "x": "4191.5", + "y": 0, + "center": "344.5", + "w": "1447", + "h": "245", + "angle": "0\u00b0" + }, + { + "code": "20", + "type": "295x300", + "x": "4175.0", + "y": 0, + "center": "743.5", + "w": "300", + "h": "295", + "angle": "0\u00b0" + }, + { + "code": "21", + "type": "498x495", + "x": "4897.5", + "y": 0, + "center": "1030", + "w": "495", + "h": "498", + "angle": "0\u00b0" + }, + { + "code": "22", + "type": "245x648", + "x": "3977.0", + "y": 0, + "center": "1150.5", + "w": "648", + "h": "245", + "angle": "0\u00b0" + }, + { + "code": "23", + "type": "245x246", + "x": "4547.5", + "y": 0, + "center": "1541.5", + "w": "246", + "h": "245", + "angle": "0\u00b0" + }, + { + "code": "24", + "type": "245x245", + "x": "4992.5", + "y": 0, + "center": "1735.5", + "w": "245", + "h": "245", + "angle": "0\u00b0" + }, + { + "code": "25", + "type": "398x392", + "x": "3796.5", + "y": 0, + "center": "1955", + "w": "392", + "h": "398", + "angle": "0\u00b0" + }, + { + "code": "26", + "type": "298x296", + "x": "4392.5", + "y": 0, + "center": "2142", + "w": "296", + "h": "298", + "angle": "0\u00b0" + }, + { + "code": "27", + "type": "297x295", + "x": "4991.5", + "y": 0, + "center": "2146.5", + "w": "295", + "h": "297", + "angle": "0\u00b0" + } +] \ No newline at end of file diff --git a/capture_img_ply.py b/capture_img_ply.py new file mode 100644 index 0000000..96251ed --- /dev/null +++ b/capture_img_ply.py @@ -0,0 +1,167 @@ +# -- coding: utf-8 -- +import time +import os +import paramiko +from scp import SCPClient +import threading +from os import abort +from pathlib import Path +from clog import logger + +HOST = "192.168.100.254" +PORT = 22 +USERNAME = "root" +PASSWORD = "ebaina" + +CLOUD_COUNTS = [400000, 500000, 600000] +DEFAULT_COUNT = 300000 +EXPS = [200 * 1000, 300 * 1000, 400 * 1000] +DEFAULT_EXP = 100 * 1000 +PARENT_DIR_NAME = "test_2" +SAVE_DIR_NAME = "" + + +def ssh_execute_command(host, port, username, password, command): + ret = False + # 创建SSH客户端 + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + client.connect(host, port, username, password) + + # 执行命令 + stdin, stdout, stderr = client.exec_command(command) + # 读取输出结果 + output = stdout.read().decode() + error = stderr.read().decode() + + if output: + logger.info(f"{output}") + if error: + logger.error(f"{error}") + + # 关闭连接 + client.close() + return True + + +def scp_get_file(host, port, username, password, remote_path, local_path): + ret = False + # 创建SSH客户端 + client = paramiko.Transport((host, port)) + client.connect(username=username, password=password) + + # 创建SCP客户端 + scp_client = SCPClient(client) + + # 拉取文件 + try: + scp_client.get(remote_path, local_path) + except Exception as e: + print(e) + return False + + # 关闭连接 + scp_client.close() + client.close() + + return True + + +def cameraCap(exp): + start_time = time.time() + ret = ssh_execute_command( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD, + command="cd /root/app/detect-gui/ \n" + + f"/root/miniconda3/bin/python cap.py --exp={exp}\n" + ) + if not ret: + return False + end_time = time.time() # 记录结束时间 + execution_time = end_time - start_time # 计算执行时间 + print(f"ssh_execute_command 程序执行时间:{execution_time}秒") + time.sleep(1) # 等待操作系统保存图像完毕 + ret = scp_get_file( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD, + remote_path="/root/app/detect-gui/output.jpg", + local_path=Path(__file__).parent / "result" / PARENT_DIR_NAME / SAVE_DIR_NAME + ) + if not ret: + return False + end_time = time.time() # 记录结束时间 + execution_time = end_time - start_time # 计算执行时间 + print(f"cameraCap 程序执行时间:{execution_time}秒") + + +def lidarCap(cloud_count): + start_time = time.time() + print(f"./lidar_driver {cloud_count}\n") + ret = ssh_execute_command( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD, + command="cd /root/calibration_tools \n" + + f"./lidar_driver {cloud_count}\n" + ) + ret = scp_get_file( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD, + remote_path="/root/calibration_tools/output.ply", + local_path=Path(__file__).parent / "result" / PARENT_DIR_NAME / SAVE_DIR_NAME + ) + ret = ssh_execute_command( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD, + command="cd /root/calibration_tools \n " + + "rm output.ply \n" + ) + end_time = time.time() # 记录结束时间 + execution_time = end_time - start_time # 计算执行时间 + print(f"lidarCap 程序执行时间:{execution_time}秒") + + +def cap(exp=DEFAULT_EXP, cloud_count=DEFAULT_COUNT): + global SAVE_DIR_NAME + print(f"曝光时间{exp}") + print(f"点云数量:{cloud_count}") + SAVE_DIR_NAME = f"exp_{exp}_cn_{cloud_count}" + # 如果文件夹不存在则创建 + if not os.path.exists(Path(__file__).parent / "result" / PARENT_DIR_NAME / SAVE_DIR_NAME): + os.makedirs(Path(__file__).parent / "result" / PARENT_DIR_NAME / SAVE_DIR_NAME) + # 雷达线程 + t_lidar = threading.Thread(target=lidarCap, args=(cloud_count,)) + t_lidar.start() + # 相机 + cameraCap(exp) + + # 等待雷达线程结束 + t_lidar.join() + + +if __name__ == '__main__': + # 加载驱动 + ssh_execute_command( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD, + command="/opt/HuarayTech/MVviewer/module/loadAllDrv.sh \n" + ) + for cloud_count in CLOUD_COUNTS: + logger.info("开始采集") + cap(cloud_count = cloud_count) + logger.info("采集完毕===========") + for exp in EXPS: + logger.info("开始采集") + cap(exp = exp) + logger.info("采集完毕===========") diff --git a/capture_img_ply_txt.py b/capture_img_ply_txt.py new file mode 100644 index 0000000..6c72286 --- /dev/null +++ b/capture_img_ply_txt.py @@ -0,0 +1,258 @@ +# -- coding: utf-8 -- +import sys +import time +import os +import paramiko +from scp import SCPClient +import threading +from os import abort +from pathlib import Path +from clog import logger, set_logger_file_handler + +HOST = "192.168.100.254" +PORT = 22 +USERNAME = "root" +PASSWORD = "ebaina" + +# CLOUD_COUNTS = [600000,800000,1000000,1500000,2000000] +CLOUD_COUNTS = [1500000] +DEFAULT_COUNT = 1500000 +EXPS = [2*1000,3*1000] +# EXPS = [100 * 1000] +DEFAULT_EXP = int(3 * 1000) +PARENT_DIR_NAME = "拼接测试_位置3_1平角度_天气_晴天大太阳下午_driverV2_om3" +# PARENT_DIR_NAME = "test_1" +SAVE_DIR_NAME = "" +TEST_CLOUD = False +TEST_EXP = True + + +def ssh_execute_command(host, port, username, password, command): + ret = False + # 创建SSH客户端 + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + client.connect(host, port, username, password) + + # 执行命令 + stdin, stdout, stderr = client.exec_command(command) + # 实时读取标准输出 + for line in iter(stdout.readline, ""): + print(line, end="") # 实时打印 + sys.stdout.flush() # 刷新缓冲区,确保立即输出到终端 + + # 实时读取标准错误(如果有) + for line in iter(stderr.readline, ""): + print(f"ERROR: {line}", end="") + sys.stdout.flush() + + # 关闭连接 + client.close() + return True + + +def scp_get_file(host, port, username, password, remote_path, local_path): + ret = False + # 创建SSH客户端 + client = paramiko.Transport((host, port)) + client.connect(username=username, password=password) + + # 创建SCP客户端 + scp_client = SCPClient(client) + + # 拉取文件 + try: + scp_client.get(remote_path, local_path) + except Exception as e: + print(e) + return False + + # 关闭连接 + scp_client.close() + client.close() + + return True + + +def scp_get_multi_file(host, port, username, password, remote_path, local_path): + ret = False + # 创建SSH客户端 + client = paramiko.Transport((host, port)) + client.connect(username=username, password=password) + + # 创建SCP客户端 + scp_client = SCPClient(client) + + # 拉取文件 + try: + for rp in remote_path: + scp_client.get(rp, local_path) + except Exception as e: + logger.error(f"拉取文件失败:{e}") + return False + + # 关闭连接 + scp_client.close() + client.close() + + return True + + +def cap(exp=DEFAULT_EXP, cloud_count=DEFAULT_COUNT): + global SAVE_DIR_NAME + logger.debug(f"===================开处理============================") + # 先删除旧数据 + logger.debug("删除旧数据") + ssh_execute_command( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD, + command="cd /root/app/detect-gui/ \n " + + "rm output.jpg \n" + + "rm output.ply \n" + + "rm roi_conners.txt \n" + ) + logger.debug("删除旧数据完成") + + logger.debug(f"曝光时间设置为:{exp}") + logger.debug(f"点云数量设置为:{cloud_count}") + SAVE_DIR_NAME = f"exp_{exp}_cn_{cloud_count}" + logger.debug(f"保存路径为:{SAVE_DIR_NAME}") + + # 如果文件夹不存在则创建 + if not os.path.exists(Path(__file__).parent / "result" / PARENT_DIR_NAME / SAVE_DIR_NAME): + os.makedirs(Path(__file__).parent / "result" / PARENT_DIR_NAME / SAVE_DIR_NAME) + + # ssh调用远程 borad_image_framework_tmp.py 进行拍摄图片,雷达点云数据采集,角点ROI数据生成 + start_time = time.time() + command = "export LD_LIBRARY_PATH=/root/app/ss928_codec/mpp/out/lib:/root/app/detect-gui/vendors/image-framework/linux_arm64:/root/app/detect-gui/vendors/image-framework/linux_arm64/lib:/root/app/detect-gui/vendors/image-framework/linux_arm64/lib/npu:/root/app/detect-gui/vendors/image-framework/linux_arm64/lib/svp_npu:$LD_LIBRARY_PATH \n" + \ + "cd /root/app/detect-gui/ \n" + \ + f"/root/miniconda3/bin/python borad_image_framework_tmp.py --exp={exp} --count={cloud_count}\n" + logger.debug(f"ssh_execute_command 命令:{command}") + ssh_execute_command( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD, + command=command, + ) + end_time = time.time() # 记录结束时间 + execution_time = end_time - start_time # 计算执行时间 + logger.debug(f"borad_image_framework_tmp.py 程序执行时间:{execution_time}秒") + time.sleep(1) # 等待操作系统保存数据完毕 + + logger.debug("开始保存文件到本地") + start_time2 = time.time() + local_path = Path(__file__).parent / "result" / PARENT_DIR_NAME / SAVE_DIR_NAME + logger.debug(f"本地文件保存路径为:{local_path}") + scp_get_multi_file( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD, + remote_path=[ + "/root/app/detect-gui/output.jpg", + "/root/app/detect-gui/output.ply", + "/root/app/detect-gui/roi_conners.txt", + ], + local_path=local_path + ) + end_time2 = time.time() # 记录结束时间 + execution_time2 = end_time2 - start_time2 + logger.debug("文件到本地结束") + logger.debug(f"scp_get_file获取文件到本地 程序执行时间:{execution_time2}秒") + logger.debug(f"===================开处理结束============================") + + +def cap_wide(): + logger.debug(f"===================广角开始处理============================") + + # 先删除旧数据 + logger.debug("删除旧数据") + ssh_execute_command( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD, + command="cd /root/app/detect-gui/ \n " + + "rm wide_image_processed.png \n" + + "rm wide_image.png \n" + + "rm wide_image_roi.json \n" + ) + logger.debug("删除旧数据完成") + + start_time = time.time() + command = "export LD_LIBRARY_PATH=/root/app/ss928_codec/mpp/out/lib:/root/app/detect-gui/vendors/image-framework/linux_arm64:/root/app/detect-gui/vendors/image-framework/linux_arm64/lib:/root/app/detect-gui/vendors/image-framework/linux_arm64/lib/npu:/root/app/detect-gui/vendors/image-framework/linux_arm64/lib/svp_npu:$LD_LIBRARY_PATH \n" + \ + "cd /root/app/detect-gui/ \n" + \ + f"/root/miniconda3/bin/python cap_wide_camera.py \n" + logger.debug(f"ssh_execute_command 命令:{command}") + ssh_execute_command( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD, + command=command, + ) + end_time = time.time() # 记录结束时间 + execution_time = end_time - start_time # 计算执行时间 + logger.debug(f"cap_wide_camera.py 程序执行时间:{execution_time}秒") + time.sleep(1) # 等待操作系统保存数据完毕 + + logger.debug("开始保存文件到本地") + start_time2 = time.time() + local_path = Path(__file__).parent / "result" / PARENT_DIR_NAME + logger.debug(f"本地文件保存路径为:{local_path}") + scp_get_multi_file( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD, + remote_path=[ + "/root/app/detect-gui/wide_image_processed.png", + "/root/app/detect-gui/wide_image.png", + "/root/app/detect-gui/wide_image_roi.json", + ], + local_path=local_path + ) + end_time2 = time.time() # 记录结束时间 + execution_time2 = end_time2 - start_time2 + logger.debug("文件到本地结束") + logger.debug(f"scp_get_file获取文件到本地 程序执行时间:{execution_time2}秒") + logger.debug(f"===================广角开处理结束============================") + + +if __name__ == '__main__': + + + # 如果路径不存在则创建 + if not os.path.exists(Path(__file__).parent / "result" / PARENT_DIR_NAME): + os.makedirs(Path(__file__).parent / "result" / PARENT_DIR_NAME) + # 设置日志保存路径 + set_logger_file_handler(Path(__file__).parent / "result" / PARENT_DIR_NAME) + # 加载驱动 + ssh_execute_command( + host=HOST, + port=PORT, + username=USERNAME, + password=PASSWORD, + command="/opt/HuarayTech/MVviewer/module/loadAllDrv.sh \n" + ) + + if (TEST_CLOUD): + for cloud_count in CLOUD_COUNTS: + logger.debug("开始采集") + start_time = time.time() # 记录开始时间 + cap(cloud_count=cloud_count) + end_time = time.time() # 记录结束时间 + elapsed_time = end_time - start_time # 计算总时间 + logger.success(f"采集完毕=========== 耗时: {elapsed_time:.2f} 秒") + if (TEST_EXP): + for exp in EXPS: + logger.debug("开始采集") + start_time = time.time() # 记录开始时间 + cap(exp=exp) + end_time = time.time() # 记录结束时间 + elapsed_time = end_time - start_time # 计算总时间 + logger.success(f"采集完毕=========== 耗时: {elapsed_time:.2f} 秒") + cap_wide() diff --git a/clog.py b/clog.py new file mode 100644 index 0000000..9ec861f --- /dev/null +++ b/clog.py @@ -0,0 +1,92 @@ +import logging +import sys +from fileinput import filename + +from colorlog import ColoredFormatter + +# 创建一个 logger +logger = logging.getLogger('my_logger') +logger.setLevel(logging.DEBUG) + +# 创建一个控制台处理器 +console_handler = logging.StreamHandler() + +# 定义 SUCCESS 级别的数值 (INFO=20, WARNING=30,所以 SUCCESS 可以设为 25) +SUCCESS = 25 +logging.addLevelName(SUCCESS, "SUCCESS") + +# 定义一个 success 方法 +def success(self, message, *args, **kwargs): + if self.isEnabledFor(SUCCESS): + self._log(SUCCESS, message, args, **kwargs) + +# 绑定到 Logger 类上 +logging.Logger.success = success + + +# 创建带颜色的格式化器(用于控制台输出) +console_formatter = ColoredFormatter( + "%(log_color)s%(asctime)s - %(filename)s:%(lineno)d - [%(levelname)s]:%(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + reset=True, + log_colors={ + 'SUCCESS': 'green', # 这里定义 SUCCESS 级别的颜色 + 'DEBUG': 'cyan', + 'INFO': 'white', + 'WARNING': 'yellow', + 'ERROR': 'red', + 'CRITICAL': 'red,bg_black', + }, + secondary_log_colors={ + 'message': { + 'SUCCESS': 'green', # 这里定义 SUCCESS 级别的消息颜色 + 'DEBUG': 'cyan', + 'INFO': 'white', + 'WARNING': 'yellow', + 'ERROR': 'red', + 'CRITICAL': 'red,bg_black', + } + }, + style='%' +) + +# 创建普通格式的格式化器(用于日志文件) +file_formatter = logging.Formatter( + "%(asctime)s - %(filename)s:%(lineno)d - %(levelname)s:%(name)s:%(message)s", + datefmt="%Y-%m-%d %H:%M:%S" +) + +# 设置格式化器 +console_handler.setFormatter(console_formatter) + +# 将处理器添加到 logger +logger.addHandler(console_handler) + +def set_logger_file_handler(p): + # 创建一个文件处理器,保存日志到本地文件 + file_handler = logging.FileHandler(f'{p}/run.log', encoding='utf-8') + file_handler.setFormatter(file_formatter) + logger.addHandler(file_handler) + + # 重定向 stdout 和 stderr 到日志系统 + class LoggerWriter: + def __init__(self, level): + self.level = level + + def write(self, message): + if message.strip(): # 过滤掉空行 + self.level(message.strip()) # 去掉首尾空白字符 + + def flush(self): + pass + + sys.stdout = LoggerWriter(logger.info) + sys.stderr = LoggerWriter(logger.error) + +# 测试日志 +# logger.success("This is a debug message") +# logger.debug("This is a debug message") +# logger.info("This is an info message") +# logger.warning("This is a warning message") +# logger.error("This is an error message") +# logger.critical("This is a critical message") diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..0f6b3d3 --- /dev/null +++ b/config.ini @@ -0,0 +1,48 @@ +[2D] +w = 9344 +h = 7000 +fx = 11550.4192 +fy = 11546.8773 +cx = 4672.28001 +cy = 3499.29103 +k1 = -0.0198130409 +k2 = 0.0446498961 +p1 = 0.000332909840 +p2 = -0.000424586368 +k3 = 0.371045956 +yolo_label = 0 +yolo_prob = 0.601 +yolo_modol_path = ./best_ep23_small_lab.om +save_image = true +roi_y_offset = 20 +edge_min_line_len = 400 +edge_max_line_gap = 120 +edge_min_angle_th = 33 +kk = 0.0 + +[3D] +r = 1.572895519320505 +p = -1.5570521101629837 +y = 0.024731696602587224 +tx = 0.109253 +ty = 0.0710213 +tz = 0.0175978 +dminx = -2.5 +dmaxx = 2.5 +dminy = -2.5 +dmaxy = 2.5 +dminz = 0.0 +dmaxz = 3.6 +kk = 0.0 +cloud_need_points_size = 600000 +save_cload = true + +[sys] +fake = false +camera_cap_fake = true +lidar_cap_fake = true +npu_fake = true +conners_detect_fake = false +fake_image_fpath = ./result/test_1/ep_100000_count_600000/output.jpg +fake_lidar_fpath = ./result/test_1/ep_100000_count_600000/output.ply + diff --git a/detect.py b/detect.py new file mode 100644 index 0000000..1782f18 --- /dev/null +++ b/detect.py @@ -0,0 +1,271 @@ +import os +import shutil +import sys +import threading +import time +from ctypes import * +import logging +from logging import makeLogRecord +from clog import logger +import cv2 +import numpy as np + +from imgProcess import detectionResProcessing +from util import change_config_ini + +image_framework_sdk = None +data_callback = None +SYS_OK = 0 +bStop = False + +# 全局 msg 缓存 +g_msg_cache = [] + +# 全局 frame 缓存 +g_frame_cache = [] + +# 运行模式 +RUNNING_MODE_NONE = -1 # 停止状态 +RUNNING_MODE_LOCATION = 0 # 广角相机运行模式 +RUNNING_MODE_DETECTION = 1 # 高清检测运行模式 +g_running_mode = RUNNING_MODE_NONE # 默认是广角相机运行模式 + +TO_CLIENT_NOTIFY_BASE = 1000 + +# emit的结构体 +MSG_ONLY_RECORD = -1 +MSG_LOCATION_RECORD = 0 +MSG_DETECTION_RECORD = 1 + + +# 获取消息 +def _get_msg_info(code) -> str: + global MSG_LIST + for msg in MSG_LIST: + for key, value in msg.items(): + if code == key: + return value.encode("utf-8") + return "未知错误".encode("utf-8") + + +class Msg: + def __init__(self, msg_type, record, im2D): + self.msg_type = msg_type # 0:刷新广角图片, 1:刷新高清, -1:不刷新图片,纯消息 + self.record = record + self.im2D = im2D + + +class _SubRoiData_(Structure): + _fields_ = [ + ('mPparentId', c_long), + ('mId', c_long), + ('mInfo', c_char * 256), + ('isGood', c_bool), + ('vpOffset', c_float * 2), + ('mRoiPoints', c_float * 8), # 4 * 2 + ('mInnerConners', c_float * 8), # 4 * 2 + ('mInnerConners3D', c_float * 12), # 4 * 3 + ('edges', c_float * 4), + ('center', c_float * 2), + ('cloud_size', c_int), + ('mVc2D', POINTER(c_float)), + ('mVc3D', POINTER(c_float)), + ('H', c_float * 9), + ] + + +SubRoiData = _SubRoiData_ + + +class _CallbackInfo_(Structure): + _fields_ = [ + ('code', c_int), + ('errorInfo', c_char * 256), + ('bGetData', c_bool), + ('mId', c_long), + ('mInfo', c_char * 256), + ('mImPath', c_char * 256), + ('mIm2D3DPtr', c_char * 256), + ('roi_size', c_int), + ('subRois', SubRoiData * 16), + ] + + +CallbackInfo = _CallbackInfo_ +# 回调函数类型定义 +CallbackType = CFUNCTYPE(None, POINTER(CallbackInfo)) + + +def _copy_record(record): + new_record = CallbackInfo() + new_record.code = record.code + new_record.errorInfo = record.errorInfo + new_record.bGetData = record.bGetData + new_record.roi_size = record.roi_size + for i in range(record.roi_size): + if i > 15: # 超过16个ROI,返回 + break + subRois = record.subRois[i] + newSubRois = new_record.subRois[i] + for j in range(8): + newSubRois.mRoiPoints[j] = subRois.mRoiPoints[j] + newSubRois.mInnerConners[j] = subRois.mInnerConners[j] + for j in range(12): + newSubRois.mInnerConners3D[j] = subRois.mInnerConners3D[j] + + return new_record + + +def init_callback(): + global data_callback, bStop + + def _callback(data): + print("_callback回调执行") + global g_msg_cache, g_frame_cache, g_running_mode, bStop + record = CallbackInfo(code=SYS_OK, errorInfo=b"", bGetData=False) + frame = None + ret = SYS_OK + msg = None + + # 获取数据 + data_type = CallbackInfo + data_ptr = cast(data, POINTER(data_type)) + ckinfoCache = data_ptr.contents + record = _copy_record(ckinfoCache) + print(str(g_running_mode) + " : " + str(record.code) + " : " + bytes.decode(record.errorInfo)) + # 当定位模式下,接收到【定位完成】消息 + if g_running_mode == RUNNING_MODE_LOCATION and record.code == (6 + TO_CLIENT_NOTIFY_BASE): + if len(g_frame_cache) == 0: + print("当前没有广角数据") + return + frame = g_frame_cache.pop() # 从图像缓存中去除图像 + g_frame_cache = [] # 清空缓存,避免无限增长 + msg = Msg(MSG_LOCATION_RECORD, _copy_record(record), frame) + # 当检测模式下,接收到【拍照完成】消息 + elif record.code == (8 + TO_CLIENT_NOTIFY_BASE): + bStop = True + time.sleep(1) + elif record.code == (12 + TO_CLIENT_NOTIFY_BASE): + msg = Msg(MSG_DETECTION_RECORD, _copy_record(record), None) + detectionResProcessing(msg,data_img_path,data_detect_res_save_path) + bStop = True + time.sleep(1) + # # frame = np.zeros((7000, 9344, 3), np.uint8) + # # # frame.zeros(9344, 7000, cv2.CV_8UC3) + # # # src_frame_ptr = c_char_p(frame.data.tobytes()) + # # mv = memoryview(frame.data) + # # buffer_pointer = cast(mv.obj.ctypes.data, c_void_p) + # # ret = image_framework_sdk.LibapiGetHQImage( + # # buffer_pointer, frame.shape[1], frame.shape[0], 3, c_char_p(b"PythonProcessing")) + # if ret != SYS_OK: + # print("ret != SYS_OK:" + str(ret)) + # record = CallbackInfo(code=ret, errorInfo=_get_msg_info(ret), bGetData=False) + # msg = Msg(MSG_DETECTION_RECORD, record, None) + # g_msg_cache.append(msg) + # # emit msg + # return + # msg = Msg(MSG_DETECTION_RECORD, _copy_record(record), frame) + # elif g_running_mode == RUNNING_MODE_LOCATION: + # msg = Msg(MSG_LOCATION_RECORD, _copy_record(record), None) + # elif g_running_mode == RUNNING_MODE_DETECTION: + # msg = Msg(MSG_DETECTION_RECORD, _copy_record(record), None) + # else: + # print("未知回调") + # return + + data_callback = CallbackType(_callback) + + +def init_image_framework_sdk(): + global image_framework_sdk + try: + # 加载C++库 + current_file_dir = os.path.dirname(os.path.abspath(__file__)) + image_framework_sdk = CDLL(os.path.join(current_file_dir, f'./image_framework.dll')) + print("Load Image framework sdk success") + except Exception as e: + print(f"Load Image framework sdk failed: {str(e)}") + + +def adjust(): + global image_framework_sdk + record = CallbackInfo(code=SYS_OK, errorInfo=b"", bGetData=False) + ret = image_framework_sdk.LibapiStartDetection() + if ret != SYS_OK: + print(f"image_framework_sdk.LibapiStartDetection():执行失败 :{ret}") + return record + + +def check(): + print("check====================") + global image_framework_sdk + + record = CallbackInfo(code=SYS_OK, errorInfo=b"", bGetData=False) + ret = image_framework_sdk.LibapiContinuetDetection("{}") + if ret != SYS_OK: + print("image_framework_sdk.LibapiContinuetDetection:执行失败") + return record + + +def start(): + global bStop + # 开始检测 + logger.debug("开始adjust") + adjust() + while not bStop: + logger.debug("adjust等待回调") + time.sleep(1) + logger.debug("开始check") + check() + bStop = False + while not bStop: + logger.debug("check等待回调") + time.sleep(1) + + +if __name__ == '__main__': + # 加载驱动 + init_image_framework_sdk() + if image_framework_sdk is None: + raise RuntimeError("Image framework SDK未正确加载!") + + # 设置回调函数 + init_callback() + ret = image_framework_sdk.LibapiInit(1, "", data_callback) + if ret != 0: + raise RuntimeError(f"设置回调函数始设置失败, 错误码: {ret}") + + + dataPath = "./result/hy_1" # 数据路径 + data_img_path = None # 检测的图片路径 + data_detect_res_save_path = None # 检测结果的保存路径 + # 遍历目录 + for root, dirs, files in os.walk(dataPath): + for dir in dirs: + logger.debug(f"开始处理子目录:{dir}") + data_detect_res_save_path = os.path.join(root, dir) + # 目录下的所有文件路径 + for file in os.listdir(os.path.join(root, dir)): + file_path = os.path.join(root, dir, file) + # 将反斜杠替换为正斜杠 + file_path = file_path.replace('\\', '/') + logger.debug(f"文件路径:{file_path}") + # 如果是jpg图片 + if file.endswith("output.jpg"): + # 将文件路径修改到ini配置中 + data_img_path = file_path + change_config_ini("sys", "fake_image_fpath", file_path) + if file.endswith(".ply"): + # 将文件路径修改到ini配置中 + change_config_ini("sys", "fake_lidar_fpath", file_path) + if file.endswith(".txt"): + # txt roi坐标复制到更目录,曹总懒得修改c代码,只能从根目录读取。 + shutil.copy(file_path, os.path.join("./", file)) + logger.info(f"roi坐标文件已复制到更目录") + time.sleep(1) + # ... + # 开始检测 + bStop = False + logger.debug("开始检测") + start() + exit() diff --git a/draw_ymj.py b/draw_ymj.py new file mode 100644 index 0000000..6c5e49e --- /dev/null +++ b/draw_ymj.py @@ -0,0 +1,80 @@ +import pandas as pd +import matplotlib.pyplot as plt +from matplotlib.patches import Rectangle + +def col(char): + return ord(char) - ord('A') + 0 + +# 读取Excel文件,指定表格路径 +file_path = '预埋件位置.xlsx' + +# 使用pandas读取Excel +df = pd.read_excel(file_path, sheet_name='测量结果') # 根据你的Excel表格来设置sheet_name + +# 打印列名 +print("DataFrame 列名:", df.columns) + +# 创建一个图形和坐标轴 +fig, ax = plt.subplots() + +# 初始化边界变量 +x_min, x_max = float('inf'), float('-inf') +y_min, y_max = float('inf'), float('-inf') + + +x_max_original = 0 +for index, row in df.iloc[2:30].iterrows(): + original_x = row[col('K')] - row[col('D')] / 2 + if(original_x > x_max_original): + x_max_original = original_x + + +for index, row in df.iloc[2:30].iterrows(): + # 打印B列和C列的值 + print(f"行 {index+2} -> A列-编号: {row[col('A')]}, D列-测量宽度: {row[col('D')]}, E列-测量高度: {row[col('E')]},K列-测量中心x: {row[col('K')]}, L列-测量中心y: {row[col('L')]}") + + # 转换x坐标 + x_original = row[col('K')] + x = x_max_original - x_original - row[col('D')] / 2 + y = row[col('L')] - row[col('E')] / 2 + + # 更新边界 + x_min = min(x_min, x) + x_max = max(x_max, x + row[col('D')]) + y_min = min(y_min, y) + y_max = max(y_max, y + row[col('E')]) + + # 绘制矩形 + rect = Rectangle((x, y), row[col('D')], row[col('E')], linewidth=1, edgecolor='r', facecolor='none') + ax.add_patch(rect) + + # 在矩形中心绘制编号(A列) + ax.text( + x + row[col('D')] / 2, y + row[col('E')] / 2, str(row[col('A')]), + ha='center', va='center', + fontsize=12, color='blue' # 编号字体大小为12,颜色为蓝色 + ) + + # 在矩形上边绘制宽度(D列) + ax.text( + x + row[col('D')] / 2, y + row[col('E')], str(row[col('D')]), + ha='center', va='bottom', + fontsize=6, color='green' # 宽度字体大小为10,颜色为绿色 + ) + + # 在矩形右边绘制高度(E列) + ax.text( + x + row[col('D')], y + row[col('E')] / 2, str(row[col('E')]), + ha='left', va='center', + fontsize=6, color='red' # 高度字体大小为10,颜色为红色 + ) + +# 设置坐标轴的范围,外扩100左右 +ax.set_xlim(x_min, x_max ) # 留出一些边距 +ax.set_ylim(y_min, y_max ) # 留出一些边距 + +# 设置坐标轴的比例 +ax.set_aspect('equal') + +# 显示图形 +plt.show() \ No newline at end of file diff --git a/environment.yaml b/environment.yaml new file mode 100644 index 0000000..13983c4 --- /dev/null +++ b/environment.yaml @@ -0,0 +1,60 @@ +name: ymj_test +channels: + - defaults + - https://repo.anaconda.com/pkgs/main + - https://repo.anaconda.com/pkgs/r + - https://repo.anaconda.com/pkgs/msys2 +dependencies: + - ca-certificates=2024.12.31=haa95532_0 + - openssl=3.0.15=h827c3e9_0 + - pip=25.0=py39haa95532_0 + - python=3.9.20=h8205438_1 + - setuptools=75.8.0=py39haa95532_0 + - sqlite=3.45.3=h2bbff1b_0 + - vc=14.42=haa95532_4 + - vs2015_runtime=14.42.34433=he0abc0d_4 + - wheel=0.45.1=py39haa95532_0 + - pip: + - bcrypt==4.2.1 + - cffi==1.17.1 + - colorama==0.4.6 + - colorlog==6.9.0 + - contourpy==1.3.0 + - cryptography==44.0.1 + - cycler==0.12.1 + - et-xmlfile==2.0.0 + - fonttools==4.56.0 + - imageio==2.37.0 + - importlib-resources==6.5.2 + - joblib==1.4.2 + - kiwisolver==1.4.7 + - lazy-loader==0.4 + - markdown-it-py==3.0.0 + - matplotlib==3.9.4 + - mdurl==0.1.2 + - networkx==3.2.1 + - numpy==2.0.2 + - opencv-python==4.11.0.86 + - openpyxl==3.1.5 + - packaging==24.2 + - pandas==2.2.3 + - paramiko==3.5.1 + - pillow==11.1.0 + - pycparser==2.22 + - pygments==2.19.1 + - pynacl==1.5.0 + - pyparsing==3.2.1 + - python-dateutil==2.9.0.post0 + - pytz==2025.1 + - rich==13.9.4 + - scikit-image==0.24.0 + - scikit-learn==1.6.1 + - scipy==1.13.1 + - scp==0.15.0 + - six==1.17.0 + - threadpoolctl==3.5.0 + - tifffile==2024.8.30 + - typing-extensions==4.12.2 + - tzdata==2025.1 + - zipp==3.21.0 +prefix: C:\Users\leon\miniconda3\envs\ymj_test diff --git a/excel_point_to_bm_json_.py b/excel_point_to_bm_json_.py new file mode 100644 index 0000000..bad2574 --- /dev/null +++ b/excel_point_to_bm_json_.py @@ -0,0 +1,115 @@ +import pandas as pd +import matplotlib.pyplot as plt +from matplotlib.patches import Rectangle + +def col(char): + return ord(char) - ord('A') + 0 + +# 读取Excel文件,指定表格路径 +file_path = '预埋件位置.xlsx' + +# 使用pandas读取Excel +df = pd.read_excel(file_path, sheet_name='测量结果') # 根据你的Excel表格来设置sheet_name + +# 打印列名 +print("DataFrame 列名:", df.columns) + +# 创建一个图形和坐标轴 +fig, ax = plt.subplots() + +# 初始化边界变量 +x_min, x_max = float('inf'), float('-inf') +y_min, y_max = float('inf'), float('-inf') + + +x_max_original = 0 +for index, row in df.iloc[2:30].iterrows(): + original_x = row[col('K')] - row[col('D')] / 2 + if(original_x > x_max_original): + x_max_original = original_x + + +bm_struct_array = [] + +for index, row in df.iloc[2:30].iterrows(): + # 打印B列和C列的值 + print(f"行 {index+2} -> A列-编号: {row[col('A')]}, D列-测量宽度: {row[col('D')]}, E列-测量高度: {row[col('E')]},K列-测量中心x: {row[col('K')]}, L列-测量中心y: {row[col('L')]}") + + # 转换x坐标 + x_original = row[col('K')] + x = x_max_original - x_original - row[col('D')] / 2 + y = row[col('L')] - row[col('E')] / 2 + + # 转换中心点坐标 + center_x = x_max_original - x_original + center_y = row[col('L')] + + # [{ + # "code": "PT001", + # "type": "250x1450", + # "x": "905", + # "y": "0", + # "center": "0.350", + # "w": "1450", + # "h": "250", + # "angle": "90\"" + # }, + bm_struct_array.append({ + "code": str(row[col('A')]), + "type": f"{str(row[col('E')])}x{str(row[col('D')])}",# w x h + "x": str(center_x), + "y": 0, + "center": str(center_y), + "w": str(row[col('D')]), + "h": str(row[col('E')]), + "angle" : "0°" + }) + + # 画出中心点 + ax.scatter(center_x, center_y, color='red', marker='o', s=10) + + # 更新边界 + x_min = min(x_min, x) + x_max = max(x_max, x + row[col('D')]) + y_min = min(y_min, y) + y_max = max(y_max, y + row[col('E')]) + + # 绘制矩形 + rect = Rectangle((x, y), row[col('D')], row[col('E')], linewidth=1, edgecolor='r', facecolor='none') + ax.add_patch(rect) + + # 在矩形中心绘制编号(A列) + ax.text( + center_x+60, center_y, str(row[col('A')]), + ha='center', va='center', + fontsize=8, color='blue' # 编号字体大小为12,颜色为蓝色 + ) + + # 在矩形上边绘制宽度(D列) + ax.text( + x + row[col('D')] / 2, y + row[col('E')], str(row[col('D')]), + ha='center', va='bottom', + fontsize=6, color='green' # 宽度字体大小为10,颜色为绿色 + ) + + # 在矩形右边绘制高度(E列) + ax.text( + x + row[col('D')], y + row[col('E')] / 2, str(row[col('E')]), + ha='left', va='center', + fontsize=6, color='red' # 高度字体大小为10,颜色为红色 + ) + +# 设置坐标轴的范围,外扩100左右 +ax.set_xlim(x_min, x_max ) # 留出一些边距 +ax.set_ylim(y_min, y_max ) # 留出一些边距 + +# 设置坐标轴的比例 +ax.set_aspect('equal') + +# 显示图形 +plt.show() + +# 保存bm_struct_array 为json到本地 +import json +with open('bm_struct_array.json', 'w') as f: + json.dump(bm_struct_array, f) \ No newline at end of file diff --git a/image_framework.dll b/image_framework.dll new file mode 100644 index 0000000..9e9e6a8 Binary files /dev/null and b/image_framework.dll differ diff --git a/imgProcess.py b/imgProcess.py new file mode 100644 index 0000000..447551c --- /dev/null +++ b/imgProcess.py @@ -0,0 +1,233 @@ +# -- coding: utf-8 -- +from ctypes import * +import numpy as np +from numpy.ctypeslib import as_array +import cv2 +from clog import logger + +TO_CLIENT_NOTIFY_BASE = 1000 + + +def process(msg): + record = msg.record + if msg.msg_type == 0 and record.code == (6 + TO_CLIENT_NOTIFY_BASE): + return doLocationProcessing(msg) + elif msg.msg_type == 1 and ( + record.code == (2 + TO_CLIENT_NOTIFY_BASE) or record.code == (8 + TO_CLIENT_NOTIFY_BASE) or record.code == ( + 12 + TO_CLIENT_NOTIFY_BASE)): + return doDetectionProcessing(msg) + else: + pass + return msg + + +def _darw_rect(im): + cv2.rectangle(im, (130, 135), (430, 375), (0, 255, 0), 5) + + +# 处理图像 +def doLocationProcessing(msg): + if msg is None: + return msg + im = msg.im2D + record = msg.record + ptsRoiPoints = None + ptsInnerConners = None + ptsInnerConners3D = None + if record.roi_size > 16: + return msg + for i in range(record.roi_size): + roi = record.subRois[i] + # if not roi.isGood: continue + roiPoints = roi.mRoiPoints + innerConners = roi.mInnerConners + innerConners3D = roi.mInnerConners3D + ptsRoiPoints = np.array([ + [roiPoints[0], roiPoints[1]], + [roiPoints[2], roiPoints[3]], + [roiPoints[4], roiPoints[5]], + [roiPoints[6], roiPoints[7]]], dtype=np.int32) + + # ptsInnerConners = np.array([ + # [innerConners[0],innerConners[1]], + # [innerConners[2],innerConners[3]], + # [innerConners[4],innerConners[5]],s + # [innerConners[6],innerConners[7]]], dtype=np.int32) + + # ptsInnerConners3D = np.array([ + # [innerConners3D[0],innerConners3D[1],innerConners3D[2]], + # [innerConners3D[3],innerConners3D[4],innerConners3D[5]], + # [innerConners3D[6],innerConners3D[7],innerConners3D[8]], + # [innerConners3D[9],innerConners3D[10],innerConners3D[11]]]) + + cv2.polylines(im, [ptsRoiPoints], True, (255, 0, 0), 2) + # cv2.polylines(im, [ptsInnerConners], True, (255,0,0) , 2) + _darw_rect(im) + + return msg + + +def doDetectionProcessing(msg): + if msg is None: + return msg + if msg.im2D is not None: + im = cv2.resize(msg.im2D, (int(msg.im2D.shape[1] / 16), int(msg.im2D.shape[0] / 16))) + record = msg.record + ptsRoiPoints = None + ptsInnerConners = None + ptsInnerConners3D = None + ptsRoiPointslist = [] + ptsInnerConnerslist = [] + ptsInnerConners3Dlist = [] + if record.roi_size > 16: + return msg + for i in range(record.roi_size): + roi = record.subRois[i] + if not roi.isGood: continue + roiPoints = roi.mRoiPoints + innerConners = roi.mInnerConners + innerConners3D = roi.mInnerConners3D + ptsRoiPoints = np.array([ + [roiPoints[0], roiPoints[1]], + [roiPoints[2], roiPoints[3]], + [roiPoints[4], roiPoints[5]], + [roiPoints[6], roiPoints[7]]], dtype=np.int32) + + ptsInnerConners = np.array([ + [innerConners[0], innerConners[1]], + [innerConners[2], innerConners[3]], + [innerConners[4], innerConners[5]], + [innerConners[6], innerConners[7]]], dtype=np.int32) + + ptsInnerConners3D = np.array([ + [innerConners3D[0], innerConners3D[1], innerConners3D[2]], + [innerConners3D[3], innerConners3D[4], innerConners3D[5]], + [innerConners3D[6], innerConners3D[7], innerConners3D[8]], + [innerConners3D[9], innerConners3D[10], innerConners3D[11]]]) + print(ptsInnerConners3D) + + for i in range(4): + ptsRoiPoints[i][0] = int(ptsRoiPoints[i][0] / 16) + ptsRoiPoints[i][1] = int(ptsRoiPoints[i][1] / 16) + ptsInnerConners[i][0] = int(ptsInnerConners[i][0] / 16) + ptsInnerConners[i][1] = int(ptsInnerConners[i][1] / 16) + ptsRoiPointslist.append(ptsRoiPoints) + ptsInnerConnerslist.append(ptsInnerConners) + ptsInnerConners3Dlist.append(ptsInnerConners3D) + + cv2.polylines(im, ptsRoiPointslist, True, (0, 255, 0), 1) + cv2.polylines(im, ptsInnerConnerslist, True, (0, 255, 255), 1) + cv2.imwrite("result.png", im) + print("================== cv2.imwrite success =================") + + if record.code == 12 + TO_CLIENT_NOTIFY_BASE: + for j in range(record.roi_size): + roi = record.subRois[j] + if not roi.isGood: continue + ptsInnerConners = ptsInnerConnerslist[j] + ptsInnerConners3D = ptsInnerConners3Dlist[j] + if len(ptsInnerConners) == 4: + for i in range(4): + p3d = ptsInnerConners3D[i] + p3d_next = ptsInnerConners3D[(i + 1) % 4] + p2d = ptsInnerConners[i] + p2d_next = ptsInnerConners[(i + 1) % 4] + edge = np.sqrt(pow((p3d[0] - p3d_next[0]), 2) + + pow((p3d[1] - p3d_next[1]), 2) + + pow((p3d[2] - p3d_next[2]), 2)) + cv2.putText(im, str(edge)[:6], \ + (int((ptsInnerConners[0][0] + ptsInnerConners[(0 + 1) % 4][0]) / 2), \ + int((ptsInnerConners[0][1] + ptsInnerConners[(0 + 1) % 4][1]) / 2 + i * 20)), \ + cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (255, 0, 0), 2) + print("================== draw =================") + msg.im2D = im + cv2.imshow("HQImage", im) + # cv2.waitKey(1) + return msg + + +# detect结果处理 +def detectionResProcessing(msg,data_img_path,data_detect_res_save_path): + # im = cv2.resize(msg.im2D, (int(msg.im2D.shape[1] / 16), int(msg.im2D.shape[0] / 16))) + # Read the image from the local directory + im = cv2.imread(data_img_path) + + # Resize the image + im = cv2.resize(im, (int(im.shape[1] / 16), int(im.shape[0] / 16))) + record = msg.record + ptsRoiPoints = None + ptsInnerConners = None + ptsInnerConners3D = None + ptsRoiPointslist = [] + ptsInnerConnerslist = [] + ptsInnerConners3Dlist = [] + logger.debug(f"record.roi_size:{record.roi_size}") + if record.roi_size > 16: + logger.warn(f"roi_size大于:{record.roi_size}个,请检查!") + for i in range(record.roi_size): + roi = record.subRois[i] + logger.debug(f"roi=====:{roi.isGood}") + # if not roi.isGood: continue + roiPoints = roi.mRoiPoints + innerConners = roi.mInnerConners + innerConners3D = roi.mInnerConners3D + ptsRoiPoints = np.array([ + [roiPoints[0], roiPoints[1]], + [roiPoints[2], roiPoints[3]], + [roiPoints[4], roiPoints[5]], + [roiPoints[6], roiPoints[7]]], dtype=np.int32) + + ptsInnerConners = np.array([ + [innerConners[0], innerConners[1]], + [innerConners[2], innerConners[3]], + [innerConners[4], innerConners[5]], + [innerConners[6], innerConners[7]]], dtype=np.int32) + + ptsInnerConners3D = np.array([ + [innerConners3D[0], innerConners3D[1], innerConners3D[2]], + [innerConners3D[3], innerConners3D[4], innerConners3D[5]], + [innerConners3D[6], innerConners3D[7], innerConners3D[8]], + [innerConners3D[9], innerConners3D[10], innerConners3D[11]]]) + print(ptsInnerConners3D) + + for i in range(4): + ptsRoiPoints[i][0] = int(ptsRoiPoints[i][0] / 16) + ptsRoiPoints[i][1] = int(ptsRoiPoints[i][1] / 16) + ptsInnerConners[i][0] = int(ptsInnerConners[i][0] / 16) + ptsInnerConners[i][1] = int(ptsInnerConners[i][1] / 16) + ptsRoiPointslist.append(ptsRoiPoints) + ptsInnerConnerslist.append(ptsInnerConners) + ptsInnerConners3Dlist.append(ptsInnerConners3D) + # 打印三个list + # for i in range(len(ptsRoiPointslist)): + # logger.debug("ptsRoiPointslist:", ptsRoiPointslist[i]) + # logger.debug(("ptsInnerConnerslist:", ptsInnerConnerslist[i])) + # logger.debug(("ptsInnerConners3Dlist:", ptsInnerConners3Dlist[i])) + + cv2.polylines(im, ptsRoiPointslist, True, (0, 255, 0), 1) + cv2.polylines(im, ptsInnerConnerslist, True, (0, 255, 255), 1) + cv2.imwrite(f"{data_detect_res_save_path}/result.jpg", im) + logger.success("================== cv2.imwrite result.jpg success =================") + + if record.code == 12 + TO_CLIENT_NOTIFY_BASE: + for j in range(record.roi_size): + roi = record.subRois[j] + # if not roi.isGood: continue + ptsInnerConners = ptsInnerConnerslist[j] + ptsInnerConners3D = ptsInnerConners3Dlist[j] + if len(ptsInnerConners) == 4: + for i in range(4): + p3d = ptsInnerConners3D[i] + p3d_next = ptsInnerConners3D[(i + 1) % 4] + p2d = ptsInnerConners[i] + p2d_next = ptsInnerConners[(i + 1) % 4] + edge = np.sqrt(pow((p3d[0] - p3d_next[0]), 2) + + pow((p3d[1] - p3d_next[1]), 2) + + pow((p3d[2] - p3d_next[2]), 2)) + cv2.putText(im, str(edge)[:6], \ + (int((ptsInnerConners[0][0] + ptsInnerConners[(0 + 1) % 4][0]) / 2), \ + int((ptsInnerConners[0][1] + ptsInnerConners[(0 + 1) % 4][1]) / 2 + i * 20)), \ + cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (255, 0, 0), 2) + cv2.imwrite(f"{data_detect_res_save_path}/result2.jpg", im) + logger.success("================== cv2.imwrite result2.jpg success =================") + diff --git a/myplot.png b/myplot.png new file mode 100644 index 0000000..dbb87d2 Binary files /dev/null and b/myplot.png differ diff --git a/myplot_2.png b/myplot_2.png new file mode 100644 index 0000000..67124f3 Binary files /dev/null and b/myplot_2.png differ diff --git a/myplot_3.png b/myplot_3.png new file mode 100644 index 0000000..d7e7bb0 Binary files /dev/null and b/myplot_3.png differ diff --git a/opencv_videoio_ffmpeg420.dll b/opencv_videoio_ffmpeg420.dll new file mode 100644 index 0000000..15ead7b Binary files /dev/null and b/opencv_videoio_ffmpeg420.dll differ diff --git a/opencv_videoio_ffmpeg420_64.dll b/opencv_videoio_ffmpeg420_64.dll new file mode 100644 index 0000000..16a4706 Binary files /dev/null and b/opencv_videoio_ffmpeg420_64.dll differ diff --git a/opencv_world420d.dll b/opencv_world420d.dll new file mode 100644 index 0000000..cb1af79 Binary files /dev/null and b/opencv_world420d.dll differ diff --git a/points.txt b/points.txt new file mode 100644 index 0000000..e4ccf1c --- /dev/null +++ b/points.txt @@ -0,0 +1,27 @@ +[(4366.0, 226.0), (5816.0, 226.0), (5816.0, 476.0), (4366.0, 476.0)] +[(4963.0, 604.0), (5263.0, 604.0), (5263.0, 904.0), (4963.0, 904.0)] +[(4147.5, 804.0), (4647.5, 804.0), (4647.5, 1305.0), (4147.5, 1305.0)] +[(4995.0, 1024.0), (5643.0, 1024.0), (5643.0, 1274.0), (4995.0, 1274.0)] +[(4574.0, 1418.0), (4824.0, 1418.0), (4824.0, 1668.0), (4574.0, 1668.0)] +[(4131.0, 1626.0), (4380.0, 1626.0), (4380.0, 1875.0), (4131.0, 1875.0)] +[(5287.0, 1755.0), (5686.0, 1755.0), (5686.0, 2155.0), (5287.0, 2155.0)] +[(4742.0, 2009.0), (5044.0, 2009.0), (5044.0, 2309.0), (4742.0, 2309.0)] +[(4140.0, 2007.0), (4442.0, 2007.0), (4442.0, 2307.0), (4140.0, 2307.0)] +[(2373.5, 221.0), (3820.5, 221.0), (3820.5, 469.0), (2373.5, 469.0)] +[(2940.0, 598.0), (3237.0, 598.0), (3237.0, 896.0), (2940.0, 896.0)] +[(2146.5, 791.0), (2645.5, 791.0), (2645.5, 1290.0), (2146.5, 1290.0)] +[(2968.5, 1024.0), (3616.5, 1024.0), (3616.5, 1269.0), (2968.5, 1269.0)] +[(2574.0, 1427.0), (2821.0, 1427.0), (2821.0, 1674.0), (2574.0, 1674.0)] +[(2165.0, 1634.0), (2412.0, 1634.0), (2412.0, 1880.0), (2165.0, 1880.0)] +[(3291.5, 1756.0), (3683.5, 1756.0), (3683.5, 2154.0), (3291.5, 2154.0)] +[(2746.0, 1997.0), (3039.0, 1997.0), (3039.0, 2297.0), (2746.0, 2297.0)] +[(2137.0, 1995.0), (2434.0, 1995.0), (2434.0, 2293.0), (2137.0, 2293.0)] +[(372.0, 222.0), (1819.0, 222.0), (1819.0, 467.0), (372.0, 467.0)] +[(962.0, 596.0), (1262.0, 596.0), (1262.0, 891.0), (962.0, 891.0)] +[(142.0, 781.0), (637.0, 781.0), (637.0, 1279.0), (142.0, 1279.0)] +[(986.0, 1028.0), (1634.0, 1028.0), (1634.0, 1273.0), (986.0, 1273.0)] +[(616.5, 1419.0), (862.5, 1419.0), (862.5, 1664.0), (616.5, 1664.0)] +[(172.0, 1613.0), (417.0, 1613.0), (417.0, 1858.0), (172.0, 1858.0)] +[(1294.5, 1756.0), (1686.5, 1756.0), (1686.5, 2154.0), (1294.5, 2154.0)] +[(746.5, 1993.0), (1042.5, 1993.0), (1042.5, 2291.0), (746.5, 2291.0)] +[(148.0, 1998.0), (443.0, 1998.0), (443.0, 2295.0), (148.0, 2295.0)] diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..dd42685 --- /dev/null +++ b/readme.md @@ -0,0 +1,7 @@ +```pycon +self.image_framework = ImageFramework() +self.image_framework.signals.on_image_detect_result.connect( +lambda data: self.image_result(data)) +self.image_framework.configure() +self.image_framework.start() +``` \ No newline at end of file diff --git a/roi_conners.txt b/roi_conners.txt new file mode 100644 index 0000000..b2d1803 --- /dev/null +++ b/roi_conners.txt @@ -0,0 +1,24 @@ +1131 +2007 +5900 +2007 +5900 +5376 +1131 +5376 +2004 +2958 +3142 +2958 +3142 +3948 +2004 +3948 +3887 +2919 +4839 +2919 +4839 +3902 +3887 +3902 diff --git a/util.py b/util.py new file mode 100644 index 0000000..284eeea --- /dev/null +++ b/util.py @@ -0,0 +1,28 @@ +import configparser +from clog import logger + +""" + 修改配置文件中的指定参数。 + + 参数: + s (str): 配置文件中的节(section)名称。 + o (str): 配置文件中的选项(option)名称。 + v (str): 要设置的新值。 + + 返回值: + 无 + """ +def change_config_ini(s, o, v): + + # 读取配置文件 + config = configparser.ConfigParser() + config.read("config.ini", encoding="utf-8") # 解决可能的编码问题 + + # 修改参数 + config.set(s, o, v) + + # 将修改后的配置写回文件 + with open("config.ini", "w", encoding="utf-8") as configfile: + config.write(configfile) + + logger.info(f"{s}.{o} 参数已更新为 {v}") \ No newline at end of file diff --git a/预埋件位置.xlsx b/预埋件位置.xlsx new file mode 100644 index 0000000..9bd23b6 Binary files /dev/null and b/预埋件位置.xlsx differ