基于Halcon的三维点云处理(鞋点胶)
1.点云介绍
点云(Point Cloud)是一种用于表示三维空间中大量点的数据结构。每个点通常由其在三维空间中的坐标值(x、y、z)以及可能的其他属性(如颜色、法线、强度等)组成。点云通常用于描述物体的表面几何形状或场景的空间信息。
点云数据可以通过各种方式获取,包括激光扫描、结构光扫描、摄影测量等技术。处理点云数据通常涉及到点云的采样、滤波、配准、分割、特征提取等操作,以便进行后续的分析和应用。
2.点云处理
因为目前我正在学习Halcon三维点云处理,所以我采用的是Halcon中的三维点云处理算子,并没有使用PCL库
Halcon是一种功能强大的机器视觉库,广泛用于工业视觉系统和机器视觉应用中。Halcon也提供了对点云数据进行处理和分析的功能,可以帮助用户处理三维点云数据并应用于各种视觉任务中。
Halcon用于点云处理的功能和方法:
- 点云的读取和显示:Halcon提供了读取和显示点云数据的功能,可以从文件中读取点云数据并在视觉界面中显示。
- 点云的滤波:Halcon支持对点云数据进行滤波操作,如高斯滤波、中值滤波等,以去除噪音或平滑点云数据。
- 点云的配准:Halcon可以进行点云的配准操作,将两个或多个点云数据对齐,以便进行后续的比较和分析。
- 点云的分割:Halcon可以对点云数据进行分割操作,将点云分为不同的部分或对象,以便进行单独的分析或处理。
- 点云的特征提取:Halcon支持从点云数据中提取特征,如边缘、表面法向量、曲率等,用于后续的目标识别或建模。
- 点云的三维重建:Halcon可以通过点云数据进行三维重建,生成三维模型或表面网格模型,用于可视化或其他应用。
3.Halcon中点云处理的主要算子
3.1 点云的读取算子
*读取点云文件
read_object_model_3d( : : FileName, Scale, GenParamName, GenParamValue : ObjectModel3D, Status)
*参数
FileName (输入控制):要读取的文件的文件名。
数据类型:字符串
文件扩展名:.off, .ply, .dxf, .om3, .obj, .stl
Scale (输入控制):文件中数据的比例尺。
数据类型:字符串/实数/整数
默认值:'m'
建议取值:'m', 'cm', 'mm', 'microns', 'µm', 'nm', 'km', 'in', 'ft', 'yd', 1.0, 0.01, 0.001, 1.0e-6, 0.0254, 0.3048, 0.9144
GenParamName (输入控制):通用参数的名称。
数据类型:字符串数组
默认值:[]
可选值:'convert_to_triangles', 'file_type', 'invert_normals', 'max_approx_error', 'min_num_points', 'xyz_map_height', 'xyz_map_width'
GenParamValue (输入控制):通用参数的值。
数据类型:字符串/实数/整数
默认值:[]
建议取值:'true', 'false', 1, 0, 'om3', 'off', 'ply', 'dxf', 'obj', 'stl', 'stl_binary', 'stl_ascii'
--------------------------------
convert_to_triangles:指定是否将对象转换为三角形。可能的取值为 'true'(转换为三角形)或 'false'(不转换为三角形)。
file_type:文件类型。可能的取值包括 'om3', 'off', 'ply', 'dxf', 'obj', 'stl', 'stl_binary', 'stl_ascii',分别表示不同类型的3D文件格式。
invert_normals:指定是否反转法线。可能的取值为 'true'(反转法线)或 'false'(不反转法线)。
max_approx_error:最大近似误差。可以是实数,用于指定在读取对象模型时允许的最大近似误差。
min_num_points:最小点数。可以是整数,用于指定对象模型中允许的最小点数。
xyz_map_height:XYZ映射高度。可以是字符串/实数/整数,用于指定XYZ映射的高度值。
xyz_map_width:XYZ映射宽度。可以是字符串/实数/整数,用于指定XYZ映射的宽度值。
--------------------------------
ObjectModel3D (输出控制):3D对象模型的句柄。
数据类型:整数
Status (输出控制):状态信息。
数据类型:字符串数组
3.2 点云的显示
*点云的显示
visualize_object_model_3d( : : WindowHandle, ObjectModel3D, CamParam, PoseIn, GenParamName, GenParamValue, Title, Label, Information : PoseOut)
*参数
WindowHandle (输入控制):窗口标识符。
数据类型:整数
ObjectModel3D (输入控制):3D对象模型的句柄。
数据类型:整数数组
CamParam (输入控制):场景的相机参数。
数据类型:实数数组
默认值:[]
PoseIn (输入控制):对象的3D姿势。
数据类型:实数/整数数组
默认值:[]
GenParamName (输入控制):通用参数的名称。可以传递所有disp_object_model_3d的通用参数。
数据类型:字符串数组
默认值:[]
建议值:'alpha', 'attribute', 'color', 'colored', 'disp_lines', 'disp_normals', 'disp_pose', 'light_position', 'light_ambient', 'light_diffuse', 'line_color', 'line_width', 'normal_color', 'point_size', 'intensity', 'intensity_red', 'intensity_green', 'intensity_blue', 'lut', 'disp_background', 'inspection_mode'
GenParamValue (输入控制):通用参数的值。可以传递所有disp_object_model_3d的通用参数。
数据类型:任意类型数组
默认值:[]
建议值:'true', 'false', 'coord_x', 'coord_y', 'coord_z', 'red', 'green', 'blue', 'auto', 'faces', 'primitive', 'points', 'lines', 'normal_x', 'normal_y, 'normal_z', 'standard', 'surface'
--------------------------------------------
attribute:明确选择对象模型可视化的方式,可以是自动选择、面、基元、点或线。默认为自动选择。
color:对象的颜色,可以是预设颜色(使用query_color操作符查询)或以#rrggbb形式指定的RGB三元组。默认为白色。
alpha:对象的透明度,取值范围为0.0(完全透明)到1.0(完全不透明)。默认为1.0。
disp_lines:是否显示对象多边形的轮廓。默认为false。
disp_normals:是否显示3D对象模型的表面法线。默认为false。
line_color:当disp_lines设置为true时,线的颜色。默认为color的值。
line_width:线的宽度(以像素为单位)。默认为1.0。
normal_color:当disp_normals设置为true时,可视化法线的颜色。默认为color的值。
intensity, intensity_red, intensity_green, intensity_blue:描述显示点或面强度的属性。默认为none。
lut:将强度转换为颜色的查找表(LUT)。默认为default。
point_size:点的直径(以像素为单位)。默认为3.5。
inspection_mode:影响交互式修改姿势的方式,可选standard或surface。默认为standard。
--------------------------------------------
Title (输入控制):要显示在输出图形窗口左上角的文本。
数据类型:字符串数组
默认值:[]
Label (输入控制):要显示在每个显示对象模型位置的文本。
数据类型:字符串数组
默认值:[]
Information (输入控制):要显示在输出图形窗口左下角的文本。
数据类型:字符串数组
默认值:[]
PoseOut (输出控制):所有可能由用户交互更改的对象模型的姿势。
数据类型:姿势数组
3.3 求点云表面法向量
*用于计算3D对象模型的表面法线。
surface_normals_object_model_3d( : : ObjectModel3D, Method, GenParamName, GenParamValue : ObjectModel3DNormals)
*参数
ObjectModel3D:输入参数,包含3D点数据的3D对象模型的句柄。
Method:输入参数,法线计算方法。目前仅支持Moving Least Squares (MLS,方法为'mls') 法线估计方法。
GenParamName:输入参数,通用平滑参数的名称列表。包括'mls_abs_sigma', 'mls_force_inwards', 'mls_kNN', 'mls_order', 'mls_relative_sigma'等。
GenParamValue:输入参数,通用平滑参数的值列表。可以根据具体需求设置各个参数的值。
ObjectModel3DNormals:输出参数,包含计算出的3D法线的3D对象模型句柄。
3.3 三维重建
将离散的点重建成一个聚合的面
*用于为3D对象模型创建表面三角化(三角网格重建)
triangulate_object_model_3d( : : ObjectModel3D, Method, GenParamName, GenParamValue : TriangulatedObjectModel3D, Information)
-------------------------------------------
ObjectModel3D (输入参数):包含3D点数据的3D对象模型的句柄。
Method (输入参数):三角化方法。可选值有'greedy'、'implicit'和'polygon_triangulation'。默认值为'greedy'。其中:
'polygon_triangulation':是将多边形转换为三角形面表示的简单方法。
'greedy' 和 'implicit':这两种方法是用于从未知表面拓扑的纯3D点数据计算三角形面的复杂算法。
GenParamName (输入参数):通用三角化参数的名称列表。例如,'greedy_fix_flips', 'greedy_hole_filling'等。默认为空列表。
GenParamValue (输入参数):通用三角化参数的值列表。例如,6, 8, 'true', 'false'等。默认为空列表。
TriangulatedObjectModel3D (输出参数):包含三角化表面的3D对象模型句柄。
Information (输出参数):关于三角化过程的附加信息,以数字或字符串形式返回
-------------------------------------------
重建前:
重建后:
3.4 点云的筛选
筛选掉点云中的无关杂质(不感兴趣的位置)
*筛选孤立散点特征的算子
select_points_object_model_3d( : : ObjectModel3D, Attrib, MinValue, MaxValue : ObjectModel3DThresholded)
*参数
ObjectModel3D (输入参数):3D对象模型的句柄数组,即要应用阈值处理的对象模型集合。
Attrib (输入参数):要应用阈值处理的属性名称。默认值为'point_coord_z',可选的属性包括:
'mapping_col': 映射列
'mapping_row': 映射行
'point_coord_x': 点的X坐标
'point_coord_y': 点的Y坐标
'point_coord_z': 点的Z坐标
'point_normal_x': 点的法线向量X分量
'point_normal_y': 点的法线向量Y分量
'point_normal_z': 点的法线向量Z分量
MinValue (输入参数):指定属性的最小阈值。默认值为0.5。
MaxValue (输入参数):指定属性的最大阈值。默认值为1.0。
ObjectModel3DThresholded (输出参数):经过阈值处理后的3D对象模型的句柄数组。这些句柄指向经过属性阈值处理后的减少的3D对象模型。
筛选前:
筛选后:
3.5连通域断开处理
只有进行断开操作之后 才可以继续进行筛选操作
connection_object_model_3d( : : ObjectModel3D, Feature, Value : ObjectModel3DConnected)
*参数
ObjectModel3D (输入参数):3D对象模型的句柄数组,即要处理的3D对象模型。
Feature (输入参数):用于计算连通组件的属性。默认值为'distance_3d',可选的属性包括:
'angle': 角度
'distance_3d': 3D距离
'distance_mapping': 距离映射
'lines': 线
'mesh': 网格
Value (输入参数):两个连通组件之间的最大距离值。默认值为1.0。可选的值包括1.0, 1.1, 1.5, 10.0, 100.0等。
ObjectModel3DConnected (输出参数):代表连通组件的3D对象模型的句柄数组。这些句柄指向表示连接的部分的3D对象模型。
3.6 求点云的最下外界Box
smallest_bounding_box_object_model_3d( : : ObjectModel3D, Type : Pose, Length1, Length2, Length3)
*各参数的解释:
ObjectModel3D (输入参数):3D对象模型的句柄。用于指定要计算最小边界框的3D对象模型。
Type (输入参数):用于估计最小边界框的方法。可以选择'axis_aligned'(轴对齐)或'oriented'(定向)。'oriented'的算法在计算上要昂贵得多,它返回的是定向边界框的近似值。请注意,定向边界框的算法是随机的,每次调用可能返回不同的边界框。
Pose (输出参数):描述生成的边界框的姿势,包括位置和方向。姿势的原点位于边界框的中心,x轴与边界框的最长边对齐。
Length1 (输出参数):边界框最长边的长度。
Length2 (输出参数):边界框第二长边的长度。
Length3 (输出参数):边界框第三长边的长度。
如下图,灰色部分即为最小的外界box,这个外界box是为了便于确认点云的Pose和坐标
3.7 求物体的外边界
我采用的是一个简单粗暴的方法—做切平面
我做了一个竖直的切平面,不断沿着x轴进行移动,求出竖直的切平面与物体之间的交集,然后映射到二维平面,求出XLD线段,这样就可以得到交集线段的起点和终点坐标,这样就可以得到物体的边界
这个方法可以参考例程 inspect_3d_surface_intersections.hdev
4.Halcon源代码
*读取点云数据
path:='./2020-01-14-16832.om3'
read_object_model_3d (path, 'm', [], [], ObjectModel3D, Status)
*显示点云数据
dev_open_window (0, 0, 512, 512, 'black', WindowHandle)
visualize_object_model_3d (WindowHandle, ObjectModel3D, [], [], ['lut','intensity','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut)
*筛选去除噪声(通过点云之间的距离进行断开)
connection_object_model_3d (ObjectModel3D, 'distance_3d', 1, ObjectModel3DConnected)
get_object_model_3d_params (ObjectModel3DConnected, 'num_points', ParamValue)
select_object_model_3d (ObjectModel3DConnected, 'num_points', 'and', 20000, 2e7, ObjectModel3DSelected)
visualize_object_model_3d (WindowHandle, ObjectModel3DSelected, [], [], ['lut','intensity','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut)
*将鞋点云变换到原始坐标系上主轴-x y z
moments_object_model_3d (ObjectModel3DSelected, 'principal_axes', Moments)
*将pose进行翻转
pose_invert (Moments, PoseInvert)
*点云模型刚体变换模型
rigid_trans_object_model_3d (ObjectModel3DSelected, PoseInvert, ObjectModel3DRigidTrans)
visualize_object_model_3d (WindowHandle, ObjectModel3DRigidTrans, [], [], ['lut','intensity','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut)
*三角曲面重建
triangulate_object_model_3d (ObjectModel3DRigidTrans, 'greedy', [], [], TriangulatedObjectModel3D, Information)
*visualize_object_model_3d (WindowHandle, TriangulatedObjectModel3D, [], [], ['lut','intensity','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut)
stop()
*这一步是为了更好的将物体移动到主轴的位置(继续转正)-
*求出物体的外接box
smallest_bounding_box_object_model_3d (TriangulatedObjectModel3D, 'oriented', Pose, Length1, Length2, Length3)
*翻转姿态
pose_invert (Pose, PoseInvert1)
*刚体变换
rigid_trans_object_model_3d (TriangulatedObjectModel3D, PoseInvert1, ObjectModel3DRigidTrans1)
*visualize_object_model_3d (WindowHandle, ObjectModel3DRigidTrans1, [], [], ['lut','intensity','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut)
*生成物体的外接box
smallest_bounding_box_object_model_3d (ObjectModel3DRigidTrans1, 'oriented', PoseBox, Length11, Length21, Length31)
gen_box_object_model_3d (PoseBox, Length11, Length21, Length31, ObjectModel3D1)
*将物体和box一起显示(显示的时候 加'_0'代表第一个Object 加'_1'代表第二个Object)
visualize_object_model_3d (WindowHandle, [ObjectModel3DRigidTrans1,ObjectModel3D1], [], [], ['color_0','color_1','alpha_1','disp_pose'], ['green','gray',0.5,'true'], [], [], [], PoseOut)
objectsOut:=[]
Index_S:=[]
Index_E:=[]
color_S:=[]
color_E:=[]
colorsOut:=[]
colorvaluesOut:=[]
all_x:=[]
all_y:=[]
*做切平面与鞋子的点云相交
for index:=0 to 45 by 1
cutplanePose := PoseBox
cutplanePose[0]:=PoseBox[0] - Length11/2 + (index)*4+3
cutplanePose[3]:=0
cutplanePose[4]:=90
cutplanePose[5]:=0
*产生平面
gen_plane_object_model_3d (cutplanePose, [-1,-1,1,1]* 90, [-1,1,1,-1] * 90, GenPlane)
*visualize_object_model_3d (WindowHandle, [ObjectModel3DRigidTrans1,GenPlane], [], [], ['color_0','color_1','alpha_1','disp_pose'], ['green','gray',0.5,'true'], [], [], [], PoseOut)
*求交线
intersect_plane_object_model_3d (ObjectModel3DRigidTrans1, cutplanePose, ObjectModel3DIntersection)
*visualize_object_model_3d (WindowHandle, ObjectModel3DIntersection, [], [], ['lut','intensity','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut)
*变换到二维xld
*翻转姿态
pose_invert (cutplanePose, poseInvert)
*确认投影平面在前面
*获得box外接主轴的直径
get_object_model_3d_params (ObjectModel3DIntersection, 'diameter_axis_aligned_bounding_box', Diameter)
poseInvert[2]:=poseInvert[2]+Diameter
*用平行于投影平面的相机(1:1的比例)
Scale:=1
CamParam := [0,0,1.0/Scale, 1.0/Scale, 0, 0, 500,500]
project_object_model_3d (ModelContours, ObjectModel3DIntersection, CamParam, poseInvert, 'data', 'lines')
count_obj(ModelContours,Number)
Rows:=[]
Columns:=[]
Row:=[]
Column:=[]
for I:=1 to Number by 1
select_obj (ModelContours, EdgeContour, I)
get_contour_xld(EdgeContour, Row,Column)
Rows:=[Rows,Row]
Columns:=[Columns,Column]
endfor
*从上到下依次进行排序
tuple_sort_index (Rows, Indices)
tuple_length(Rows,Length)
OrderRow:=[]
OrderColumn:=[]
if(Length>=1)
for Row_Index:=0 to Length-1 by 1
OrderRow:=[OrderRow,Rows[Indices[Row_Index]]]
OrderColumn:=[OrderColumn,Columns[[Row_Index]]]
endfor
endif
*求最大和最小点(行方向)
tuple_sort_index (OrderRow, Indices)
tuple_length(OrderRow,Length)
*起点(xld)
StartRow:=OrderRow[Indices[0]]
StartColumn:=OrderColumn[Indices[0]]
*终点(xld)
EndRow:=OrderRow[Indices[Length-1]]
EndColumn:=OrderColumn[Indices[Length-1]]
*在起点和终点位置生成 x 图标
gen_cross_contour_xld(StartXP, StartRow, StartColumn, 6, 0.795296)
gen_cross_contour_xld(EndXP, EndRow,EndColumn, 6, 0.795296)
dev_display(ModelContours)
*转换成点云坐标
StartPose:=[cutplanePose[0],StartRow,-StartColumn, 0,0,0,0]
EndPose :=[cutplanePose[0],EndRow,-EndColumn, 0,0,0,0]
*生成球体的点云
gen_sphere_object_model_3d (StartPose, 2, StartPoint)
gen_sphere_object_model_3d (EndPose , 2, EndPoint )
*所有对点的边界点集合
objectsOut := [objectsOut,StartPoint]
objectsOut := [objectsOut,EndPoint ]
*显示时的颜色
Index_S:= 0+index*2
Index_E:= 0+index*2+1
color_S:='color_'+Index_S
color_E:='color_'+Index_E
colorsOut := [colorsOut,color_S]
colorsOut := [colorsOut,color_E]
colorvaluesOut := [colorvaluesOut,'blue']
colorvaluesOut := [colorvaluesOut,'blue']
all_x:=[all_x,cutplanePose[0]]
all_y:=[all_y,StartRow]
all_x:=[all_x,cutplanePose[0]]
all_y:=[all_y,EndRow]
endfor
*显示外边界模型点云
visualize_object_model_3d (WindowHandle,[objectsOut,ObjectModel3DRigidTrans1], [],[],[colorsOut,'color_88'],[colorvaluesOut,'red'], [], '', [], Pose)
*2二维显示
dev_open_window (0, 0, 512, 512, 'black', WindowHandle1)
dev_set_color('red')
gen_cross_contour_xld(Start,all_x,all_y, 3, 0.885398)