改为一次拍摄,增加品类配置,最后根据品类来进行拍摄和识别

master
LAPTOP-S9HJSOEB\昊天 8 months ago
parent 348ef50468
commit 399bb4d566

@ -2,7 +2,6 @@ import numpy as np
from PIL import Image
import math
import SimpleView_SaveImage
import config
from cat import clip_and_rotate_point_cloud, merge_point_clouds
from image import detect_large_holes
@ -126,6 +125,85 @@ def tiff_depth_to_point_cloud(tiff_path,sn, dedup=True):
return points
# 新增点云数据转换函数
def convert_sdk_points(sValue, width, height):
"""
SDK 原始点云数据转换为 NumPy 点云数组
:param sValue: tuple(float), SDK 输出的扁平化点云数据
:param width: int, 点云宽度
:param height: int, 点云高度
:return: np.ndarray(shape=(N,3)), 三维点云数组
"""
points = np.array(sValue).reshape(-1, 3) # 形状转换 (N,3)
# 剔除无效点X/Y/Z 任一为 NaN 或 Inf
mask = np.all(np.isfinite(points), axis=1)
return points[mask]
def process_point_cloud(points, sn, dedup=True):
"""
对原始点云应用旋转裁剪和去重
:param points: np.ndarray(shape=(N,3)), 原始点云数据
:param sn: str, 设备序列号用于加载配置
:param dedup: bool, 是否启用去重
:return: list of [x, y, z], 处理后的点云列表
"""
# 加载配置参数
clip_config = config.CUT_CONFIG_MAP.get(sn)
if clip_config:
min_pt = np.array(clip_config.get("min_pt", [-np.inf] * 3))
max_pt = np.array(clip_config.get("max_pt", [np.inf] * 3))
rotation = np.array(clip_config.get("rotation", [1, 0, 0, 0, 1, 0, 0, 0, 1])).reshape(3, 3)
else:
min_pt = max_pt = rotation = None
processed_points = []
seen_xy = set()
for point in points:
x, y, z = point
# 无效点过滤Z ≤ 0 或超出最大距离)
if z <= 0 or z > config.CAMERA_CONFIG_MAP[sn].get("max_z", np.inf):
continue
# 应用旋转矩阵
if clip_config:
rotated = rotation @ point
if not np.all(rotated >= min_pt) or not np.all(rotated <= max_pt):
continue
x_final, y_final, z_final = rotated
else:
x_final, y_final, z_final = x, y, z
# 去重逻辑(保留浮点精度)
if dedup:
# 使用浮点哈希避免离散化损失
key = (round(x_final, 3), round(y_final, 3))
if key in seen_xy:
continue
seen_xy.add(key)
processed_points.append([x_final, y_final, z_final])
return processed_points
def sValue_to_pcd(sValue,stPointCloudImage,sn):
# 数据格式转换
points = convert_sdk_points(sValue, stPointCloudImage.nWidth, stPointCloudImage.nHeight)
# 应用旋转、裁剪、去重(假设设备序列号已知)
processed_points = process_point_cloud(points, sn, dedup=True)
output_ply_path = config.save_path("pcd", sn + ".pcd")
# 保存结果
write_pcd(output_ply_path, processed_points)
def write_pcd(filename, points):
"""
将点云写入 PCD 文件
@ -155,8 +233,4 @@ def write_pcd(filename, points):
if __name__ == '__main__':
paths = SimpleView_SaveImage.pic("00DA6823936")
tiff_depth_to_point_clouds(paths, "00DA6823936")
# tiff_depth_to_point_clouds(["D:/PycharmProjects/Hik3D/image/2025-06-24/depth/145209062_-Depth.tiff"
# ,"D:/PycharmProjects/Hik3D/image/2025-06-24/depth/145209062_-Depth.tiff"
# ,"D:/PycharmProjects/Hik3D/image/2025-06-24/depth/145209062_-Depth.tiff"], "00DA6823936")
tiff_depth_to_point_cloud("D:/git/test/hik3d-python/image/2025-06-26/depth/191330147_-Depth.tiff", "00DA6823936")

@ -7,7 +7,7 @@ import os
import struct
from ctypes import *
from datetime import datetime
import Point as point
from Mv3dRgbdImport.Mv3dRgbdDefine import *
from Mv3dRgbdImport.Mv3dRgbdApi import *
from Mv3dRgbdImport.Mv3dRgbdDefine import DeviceType_Ethernet, DeviceType_USB, DeviceType_Ethernet_Vir, DeviceType_USB_Vir, MV3D_RGBD_FLOAT_EXPOSURETIME, \
@ -15,7 +15,6 @@ from Mv3dRgbdImport.Mv3dRgbdDefine import DeviceType_Ethernet, DeviceType_USB, D
FileType_BMP,FileType_TIFF,ImageType_Depth, ImageType_RGB8_Planar, ImageType_YUV420SP_NV12 ,ImageType_YUV420SP_NV21 , ImageType_YUV422, ImageType_Mono8
import config as configMap
# 全局变量
SN_MAP = {} # {sn: camera_instance}
@ -52,7 +51,6 @@ def initialize_devices():
SN_MAP[serial_number] = camera
print(f"Successfully added device {serial_number} to SN_MAP")
def pic(sn):
camera = SN_MAP.get(sn)
@ -65,19 +63,15 @@ def pic(sn):
print(f"No config found for SN: {sn}")
return
time_on = config.get("time_on", 0) # 延时开始(毫秒)
time_off = config.get("time_off", 0) # 拍照总时长(毫秒)
time_hop = config.get("time_hop", 0) # 每次拍照间隔(毫秒)
end_time = time.time() + (time_off / 1000.0)
time_on = config.get("time_on", 0) # 延时开始(毫秒)
print(f"Delaying start by {time_on}ms...")
time.sleep(time_on / 1000.0) # 转成秒
frame_count = 0
saved_tiff_files = [] # 用于存储保存的 TIFF 文件路径
print(f"Start continuous capturing for {time_off}ms...")
saved_files = {
"depth": [],
"color": [],
"pcd": []
}
# 开始取流
ret = camera.MV3D_RGBD_Start()
@ -86,56 +80,67 @@ def pic(sn):
return
try:
while time.time() < end_time:
stFrameData = MV3D_RGBD_FRAME_DATA()
# 获取帧数据
ret = camera.MV3D_RGBD_FetchFrame(pointer(stFrameData), 5000)
if ret == MV3D_RGBD_OK:
frame_count += 1
for i in range(stFrameData.nImageCount):
image_info = stFrameData.stImageData[i]
# 保存深度图
if image_info.enImageType == ImageType_Depth:
file_name = configMap.save_path("depth","-Depth")
ret_save = camera.MV3D_RGBD_SaveImage(pointer(image_info), FileType_TIFF, file_name)
print("Saved depth image." if ret_save == MV3D_RGBD_OK else "Failed to save depth image.")
if ret_save == MV3D_RGBD_OK:
saved_tiff_files.append(file_name) # 记录保存的 TIFF 文件路径
# print(f"Saved depth image: {file_name}")
else:
print("Failed to save depth image.")
# 保存彩色图
elif image_info.enImageType in (
stFrameData = MV3D_RGBD_FRAME_DATA()
# 获取单帧数据
ret = camera.MV3D_RGBD_FetchFrame(pointer(stFrameData), 5000)
if ret == MV3D_RGBD_OK:
for i in range(stFrameData.nImageCount):
image_info = stFrameData.stImageData[i]
# 保存深度图
if image_info.enImageType == ImageType_Depth:
file_name = configMap.save_path("depth", "-Depth")
ret_save = camera.MV3D_RGBD_SaveImage(pointer(image_info), FileType_TIFF, file_name)
print("Saved depth image." if ret_save == MV3D_RGBD_OK else "Failed to save depth image.")
if ret_save == MV3D_RGBD_OK:
saved_files["depth"].append(file_name)
# 点云转换与保存
stPointCloudImage = MV3D_RGBD_IMAGE_DATA()
ret = camera.MV3D_RGBD_MapDepthToPointCloud(pointer(stFrameData.stImageData[i]), pointer(stPointCloudImage))
if MV3D_RGBD_OK != ret:
print("_MapDepthToPointCloud() Run failed...")
else:
print(
"_MapDepthToPointCloud() Run Succeed: framenum (%d) height(%d) width(%d) len (%d)!" % (
stPointCloudImage.nFrameNum,
stPointCloudImage.nHeight, stPointCloudImage.nWidth, stPointCloudImage.nDataLen))
strMode = string_at(stPointCloudImage.pData, stPointCloudImage.nDataLen)
sValue = struct.unpack('f' * int(stPointCloudImage.nHeight * stPointCloudImage.nWidth * 3),
strMode)
pcd_file = point.sValue_to_pcd(sValue, stPointCloudImage, sn)
if pcd_file:
saved_files["pcd"].append(pcd_file)
# 保存彩色图
elif image_info.enImageType in (
ImageType_RGB8_Planar, ImageType_YUV420SP_NV12,
ImageType_YUV420SP_NV21, ImageType_YUV422
):
file_name = configMap.save_path("color","-_Color")
ret_save = camera.MV3D_RGBD_SaveImage(pointer(image_info), FileType_BMP, file_name)
print("Saved color image." if ret_save == MV3D_RGBD_OK else "Failed to save color image.")
):
file_name = configMap.save_path("color", "-_Color")
ret_save = camera.MV3D_RGBD_SaveImage(pointer(image_info), FileType_BMP, file_name)
print("Saved color image." if ret_save == MV3D_RGBD_OK else "Failed to save color image.")
if ret_save == MV3D_RGBD_OK:
saved_files["color"].append(file_name)
# 保存灰度图
elif image_info.enImageType == ImageType_Mono8:
file_name = configMap.save_path("Mono","-_Mono")
ret_save = camera.MV3D_RGBD_SaveImage(pointer(image_info), FileType_BMP, file_name)
print("Saved mono image." if ret_save == MV3D_RGBD_OK else "Failed to save mono image.")
else:
print(f"Unknown image type: {image_info.enImageType}")
else:
print("Failed to fetch frame.")
time.sleep(time_hop / 1000.0) # 控制采集频率
else:
print("Failed to fetch frame.")
finally:
# 停止取流
camera.MV3D_RGBD_Stop()
print("Continuous capture completed.")
return saved_tiff_files
print("Single capture completed.")
# 输出结果路径
print("Saved files:")
for key, paths in saved_files.items():
if paths:
print(f" {key.upper()}:")
for path in paths:
print(f" - {path}")
return saved_files
initialize_devices()

Binary file not shown.

@ -6,6 +6,7 @@ from datetime import datetime
CAMERA_CONFIG_MAP = {} # {sn: config_dict}
CUT_CONFIG_MAP = {} # {filename_without_ext: config_dict}
DIRECTION_CAMERA = {}
TEMPLATE_MAP = {}
def load_camera_configs(config_dir="./config/camera"):
"""
@ -30,6 +31,32 @@ def load_camera_configs(config_dir="./config/camera"):
print(f"[WARN] No 'sn' found in {filename}")
except Exception as e:
print(f"[ERROR] Failed to load {file_path}: {e}")
def load_template_configs(config_dir="./config/template"):
"""
加载 camera 配置 sn 建立映射
"""
if not os.path.exists(config_dir):
print(f"[ERROR] Camera config directory does not exist: {config_dir}")
return
for filename in os.listdir(config_dir):
if filename.endswith(".json"):
file_path = os.path.join(config_dir, filename)
try:
with open(file_path, "r", encoding="utf-8") as f:
config = json.load(f)
type = config.get("type")
if type:
TEMPLATE_MAP[type] = config
print(f"Loaded camera config: {type}")
else:
print(f"[WARN] No 'sn' found in {filename}")
except Exception as e:
print(f"[ERROR] Failed to load {file_path}: {e}")
load_camera_configs()
def load_cut_configs(config_dir="./config/cut"):
@ -57,8 +84,10 @@ def load_configs():
CAMERA_CONFIG_MAP = {} # {sn: config_dict}
CUT_CONFIG_MAP = {} # {filename_without_ext: config_dict}
DIRECTION_CAMERA = {}
TEMPLATE_MAP = {}
load_camera_configs()
load_cut_configs()
load_template_configs()
load_configs()

@ -1,13 +1,13 @@
{
"sn": "00DA6823936",
"direction": "1",
"x_angle": 55,
"y_angle": 84,
"x_angle": 125,
"y_angle": 75,
"save_pcd": true,
"resolution": 8,
"max_z": 2000,
"max_z": 1800,
"reverse_order": false,
"time_on": 300,
"time_off": 3500,
"time_hop": 500
"time_on": 100,
"time_off": 5500,
"time_hop": 800
}

@ -1,24 +0,0 @@
{
"floorHeight": 1,
"max_pt": [
59.17729568481445,
-177.37328052520752,
823.2836303710938
],
"min_pt": [
0,
-1263.9830322265625,
200.47930908203125
],
"rotation": [
-0.0070345401763916016,
-0.9998821020126343,
0.013652533292770386,
0.4579751193523407,
-0.015358328819274902,
-0.8888324499130249,
0.88893723487854,
-2.9802322387695312e-08,
0.45802903175354004
]
}

@ -0,0 +1,6 @@
{
"type": "45",
"width":"299",
"length":"355",
"high":"314"
}

@ -0,0 +1,14 @@
camera文档
{
"sn": "00DA6823936",#sn
"direction": "1",#方向
"x_angle": 55,#相机的水平视场角(相机自身的)
"y_angle": 84,#相机的垂直视场角
"save_pcd": true,#是否保存pcd
"resolution": 8,#像素大小(可以调整来区别
"max_z": 2000,#最大深度(裁剪之前的
"reverse_order": false,#是否pcd组成的图片是否翻转
"time_on": 300,#开始时间
"time_off": 3500,#结束时间
"time_hop": 500#时间间隔
}
Loading…
Cancel
Save