资料来源:超人视觉免费启蒙三维课程入门(第六节)3D鞋点胶的点云边界(轮廓)提取
相似案例:Halcon三维测量(3):鞋底涂胶+边缘提取
Halcon学习方法强调:从案例当中学习:最重要是思路和算子的用法、为我们所用。不要机械式套用。
视觉:
- 机器视觉(2D、3D):
- 2D:
- 识别定位(对位贴合)(深度学习)
- 测量
- 缺陷(外观检测)(深度学习)
- 符号需求(一维码、二维码、三维码、OCR)
- 视觉+运动控制板卡+机器人
- 3D:
- 鞋点胶
- 无序抓取:在Halcon【实例程序】中【方法】【多目立体视觉】locate_pipe_joints-stereo.hdev案例中。
- 点云数据+深度学习
- 工具:Halcon、VisionPro、LabView、OpenCV、Sherlock、Evsion、Insight、Matlab、(Emgucv+C#)
- 推荐:Halcon===>>>> OpenCV ====>>>> 源码自己编写
- 语言主流:C#+halcon、C+++QT+halcon、C+++MFC+halcon
- 2D:
- 计算机视觉:
- 室外:(深度学习)
- 工业:
视觉项目思路:(算法+软件框架)
1、需求分析:2D还是3D的?
2、项目评估仿真:
3、合同
4、方案设计:(硬件方案、软件方案)点云直接买现成的,因为精度更高。
5、精度和速度,考虑硬件方案:
6、概要设计
7、详细设计
8、编码
9、测试调试
10、交付
11、维护
鞋点胶边界检测思路:
1、点的法向量重建:K邻域、体素。法向量非常重要:
2、当前教程:(不是通用、不太灵活的方法)(初学者)
1)获取点云切平面
2)切平面与点云的交线
3)交线在点云上的起点、终点求出来
4)起点、终点就是外边界。
起点、终点如何求?
将交平面转化为一个2D图像求XLD轮廓。XLD上的起点、终点可求。
激光三角成像:线轮廓+移动
第一步:读入点云(鞋)的文件。
第二步:分割出鞋子的点云集合。(去除噪声等。)
第三步:仿射变换到与长轴和短轴平行的位置。(3D一样可以仿射变换):方便切平面的定义。
第四步:定义切平面,求鞋的3D点云集合跟切平面的点云交集。
第五步:把交集的点云坐标映射成2D的XLD轮廓,求每段轮廓起点、终点坐标。
第六步:把得到的每段XLD的起点、终点坐标又映射转换成3D点云坐标。
第七步:显示鞋的点云集合以及鞋的外边界的点云集合。
资料来源:超人视觉免费启蒙三维课程入门(第七节)3D鞋点胶的点云边界提取
超人视觉免费启蒙三维课程入门第八节(3D鞋点胶的点云边界提取)
第八节还有一个链接
第一步:加载并显示扫描生成的点云数据。
dev_close_window ()
dev_update_off ()
dev_open_window (0, 0, 512, 512, 'black', WindowHandle)
* 1、读取点云文件
read_object_model_3d ('./2.om3', 'mm', [], [], ObjectModel3D, Status)
visualize_object_model_3d (WindowHandle, ObjectModel3D, [], [], [], [], [], [], [], PoseOut)
对visualize_object_model_3d参数的详解:visualize_object_model_3d (窗口句柄, 3D模型, [内参], [外参], [参数名], [参数名对应的值], [标题信息], [模型信息], [窗口提示信息:比如旋转、放大、缩小], PoseOut)
- 相机的内外参数。
- 参数名和参数名对应的值:对应下面的:GenParamName和GenParamValue:定义了:显示的颜色,透明度。可以定义显示参数。
- poseOut:输出点云的姿态。相对于坐标系的平移和旋转。
双击【变量窗口】中的ObjectModel3D变量可以打开【三维对象模型检查】。
点击勾选【Display Models】,会打开【显示三维对象模型】,在显示窗口左下方状态栏中可以显示模型的姿态信息。(7个数字:偏移X,偏移Y,偏移Z,偏转x,偏转y,偏转z,旋转类型(旋转顺序));
在【显示三维对象模型】窗口上面工具栏可以【插入代码】:
插入代码如下:
CamParam := ['area_scan_division',0.06,0,8.5e-006,8.5e-006,468,161,936,323]
Pose := [-899.05,-1306.69,750249,3.85575,13.3872,36.4233,0]
GenParamName := ['colored','alpha','color_0']
GenParamValue := [12,0.9,'cyan']
disp_object_model_3d (200000, ObjectModel3D, CamParam, Pose, GenParamName, GenParamValue)
这里的这个显示函数为disp_object_model_3d,相比于visualize_object_model_3d的交互式显示不同。看F1帮助文档的描述:
visualize_object_model_3d — Interactively display 3D object models
disp_object_model_3d — Display 3D object models.
一个交互式显示,可以点击旋转,缩放,一个只是展示。
插入的代码:
- 其中会把内参(相机配置参数)和外参(当前的姿态信息)输出。同时对显示进行定义:GenParamName和GenParamValue:定义了:显示的颜色,透明度。
- 如果曾经求解过法向量,那么就可以点击【显示三维对象模型的法向量】显示法向量,如果没有求过,不会显示。
- 【显示三维对象模型的坐标系】:在halcon中红颜色代表的是X轴方向,绿颜色代表的是Y轴方向,蓝颜色代表的是Z轴方向。
halcon中可以用create_pose — Create a 3D pose.来创建一个姿态。
在【显示三维对象模型】中:【Base Parameter】有点云的中心点、直径、外接矩形框。
在视频中作者特别强调了:【案例教程中】【方法】【三维对象处理】中【moments_object_model_3d.hdev】这个教程。显示方法。
前面英文就说如果点云有山脊状走势,那么山脊更适合做X轴方向,我们原来的坐标系就需要转到以山脊走向为X轴的坐标系统下。
选坐标轴的一个原则是:沿着数据方差最大的方向作为主轴。
In this example the color encodes the value of the z-component of the data。对z轴高度信息颜色编码。
x-axis in red, the y-axis in green and the z-axis in blue.
第32行和第33行:显示参数列表。
GenParamName := ['lut','color_attrib','light_position','disp_pose','alpha']
GenParamValue := ['color1','coord_z','0.0 0.0 -0.3 1.0','true',0.9]
根据z轴坐标的高度不同,颜色不同。disp_pose显示坐标系:true。
第39行和第40行:创建姿态。
create_pose (-0.0005, -0.0005, 0.04, 280, 0, 20, 'Rp+T', 'gba', 'point', DispPose1)
create_pose (0, -0.0005, 0.04, 280, 0, 20, 'Rp+T', 'gba', 'point', DispPose2)
此外,在第四步中会介绍提到的两种3D点云模型刚体变换:一、放射变换:affine_trans_object_model_3d和二、刚体变换:rigid_trans_object_model_3d。
第二步:去噪声
通过connection_object_model_3d基于某种特征(三角网格(需要计算生成三角网格)、距离、)来将连通域断开,以此来实现:区域切割。独立集合对象。
关于connection_object_model_3d算子,作者推荐核心案例教程:【方法】【三维对象处理】【connection_object_model_3d.hdev】
通过分割完成后,用get_object_model_3d_params来计算每个连通域的点云个数。
connection_object_model_3d (ObjectModel3D, 'distance_3d', 1000, ObjectModel3DConnected)
get_object_model_3d_params (ObjectModel3DConnected, 'num_points', GenParamValue)
第三步:滤波筛选
关于点云筛选的两个案例:【select_points_object_model_3d_by_density.hdev】和【select_object_model_3d.hdev】代表了点云筛选的两种方法。
*3、滤波筛选:
select_object_model_3d (ObjectModel3DConnected, 'num_points', 'and', 120000, 200000, ObjectModel3DSelected)
visualize_object_model_3d (WindowHandle, ObjectModel3DSelected[1], [], PoseOut, ['lut','color_attrib','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut2)
第四步:将鞋点云集合变换到原始坐标系下主轴-X、Y、Z轴
所谓主轴:用最小外接长方体将点云囊括,长轴与X轴重合,短轴与Y轴重合,高度与Z轴重合。重合移动肯定会有一个姿态关系:pose。
moments_object_model_3d (ObjectModel3DSelected, 'principal_axes', Pose)第二个参数意思求主轴,第三个参数输出pose姿态。moments_object_model_3d 用来求矩,还有两个参数分别是一阶矩,二阶矩。
然后将姿态反变换:求姿态的反变换:这样变换就可以将长轴作为X轴,变换就涉及刚体变换(仿射变换)。变换就需要用到矩阵和姿态。
点云模型刚体变换
rigid_trans_object_model_3d:刚体变换算子。既可以平移,也可以旋转。
* 4、将鞋点云集合变换到原始坐标系下主轴-X、Y、Z轴
* 第二个参数意思求主轴,第三个参数,输出pose姿态。
moments_object_model_3d (ObjectModel3DSelected[1], 'principal_axes',PoseOut1)
pose_invert (PoseOut1, PoseInvert)* 这样变换就可以将长轴作为X轴,变换就涉及刚体变换(仿射变换)。变换就需要用到矩阵和姿态。
* rigid_trans_object_model_3d (ObjectModel3DSelected[1], PoseInvert, ObjectModel3DRigidTrans)
* visualize_object_model_3d (WindowHandle, ObjectModel3DRigidTrans, [], PoseInvert, [], [], [], [], [], PoseOut)
用仿射变换一样可以达到同样的效果。 放射变换需要的是一个矩阵。
将姿态变换为矩阵:pose_to_hom_mat3d
仿射变换:affine_trans_object_model_3d
* 用仿射变换也可以实现这个变换:
pose_to_hom_mat3d (PoseInvert, HomMat3D) \\ 将姿态变换为矩阵
affine_trans_object_model_3d (ObjectModel3DSelected[1], HomMat3D, ObjectModel3DAffineTrans)
visualize_object_model_3d (WindowHandle, ObjectModel3DAffineTrans, [], PoseInvert, ['lut','color_attrib','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut3)
moments_object_model_3d 确定主轴X轴方向,这样做的原因是为了方便沿着x轴做切平面的时候,方便分割。这就是确定长轴的原因。
标准位置的好处:做切平面与鞋面垂直。与轴平行,沿轴切,不会乱。整齐。
第五步:如果鞋子还是没有转正:那么就需要求一个最小外接box。
推荐案例【smallest_bounding_box_object_model_3d.hdev】中的smallest_bounding_box_object_model_3d算子完成。
求box之前可以做一个三角曲面重建:使得点云更加圆滑。
三角曲面重建。将无序点云三角化。内部算子实际使用的是贪婪投影三角法。将有向点云投影到一个二维平面内,平面内三角化。根据平面内的三角拓扑关系,生成一个三角网格曲面模型。如果使用膨胀腐蚀,就会使网格变大变小。
*三角曲面重建。将无序点云三角化。内部算子实际使用的是贪婪投影三角法。将有向点云投影到一个二维平面内,平面内三角化。根据平面内的三角拓扑关系,生成一个三角网格曲面模型。如果使用膨胀腐蚀,就会使网格变大变小。
triangulate_object_model_3d (ObjectModel3DAffineTrans, 'greedy', [], [], TriangulatedObjectModel3D, Information)
* 三角曲面重建需要点时间。如果点数太多的话,可以简化点云:
* 三角曲面重建比较适用于表面连续比较光滑的曲面,或者点云密度比较均匀的情况。速度会比较快。否则会出现因点云不连续导致的奇形怪状。甚至悬空点。
visualize_object_model_3d (WindowHandle, TriangulatedObjectModel3D, [], PoseOut3, ['lut','color_attrib','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut4)
* 三角曲面重建需要点时间。如果点数太多的话,可以简化点云:
* 三角曲面重建比较适用于表面连续比较光滑的曲面,或者点云密度比较均匀的情况。速度会比较快。否则会出现因点云不连续导致的奇形怪状。甚至悬空点。
求最小外接box:
* 最小外接box
smallest_bounding_box_object_model_3d (TriangulatedObjectModel3D, 'oriented', Pose, Length1, Length2, Length3)
gen_box_object_model_3d (Pose, Length1, Length2, Length3, ObjectModel3D1)
pose_invert (Pose, PoseInvert1)
rigid_trans_object_model_3d (TriangulatedObjectModel3D, PoseInvert1, ObjectModel3DRigidTrans)
visualize_object_model_3d (WindowHandle, ObjectModel3DRigidTrans, [], [], ['lut','color_attrib','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut5)
* 联合显示:
visualize_object_model_3d (WindowHandle, [ObjectModel3DRigidTrans,ObjectModel3D1], [], [], ['color_0','color_1','alpha_1', 'disp_pose'], ['green','gray',0.5,'true'], 'RectBOX', [], [], PoseOut2)
特别注意联合显示时的操作:要注意将外面图像设置透明度。
超人视觉免费启蒙三维课程入门(第九讲-求切平面)
第六步:在box中做切平面,求与轮廓的交线。
在X轴,做切平面,每次沿X轴移动指定均匀长度。切平面与点云有一个点云交集,把点云交集映射到二维平面上形成一个XLD二维的轮廓交线,交线的两端(起点、终点)就是我们需要的轮廓点。
做切平面需要给出切平面的姿态。
一段段的做切割:肯定要引入一个循环问题:
推荐案例:inspect_3d_surface_intersections.hdev,
*5、求切平面交线点云
for Index1 := 0 to 50 by 1CutPlanePose := PoseCutPlanePose[0]:=Pose[0]-Length1/2+(Index1)*40+3 // pose中的参数值:第一个为沿着x轴的平移。CutPlanePose[3]:=0CutPlanePose[4]:=90 // pose第五个值为绕着y轴旋转90度,立起来。CutPlanePose[5]:=0* 产生平面,去看一下切平面的情况。第一个参数是姿态pose,第二、第三个参数是限定大小。gen_plane_object_model_3d (CutPlanePose, [-1, -1, 1, 1]*1, [-1, 1, 1, -1]*1,IntersectionPlane)
* visualize_object_model_3d (WindowHandle, [ObjectModel3DRigidTrans,IntersectionPlane], [], Pose, ['color_0','color_1','alpha_1', 'disp_pose'], ['green','gray',0.5,'true'], [], [], [], PoseOut)visualize_object_model_3d (WindowHandle, [aim_object,IntersectionPlane], [], Pose, ['disp_pose'], ['true'], [], [], [], PoseOut)* 得到XLD交线。intersect_plane_object_model_3d (IntersectionPlane, CutPlanePose, ObjectModel3DIntersection)visualize_object_model_3d (WindowHandle, ObjectModel3DIntersection, [], [], [], [], [], [], [], PoseOut7)* 然后求起点、终点。先变换到2维XLD。pose_invert (CutPlanePose,PoseInvert2)*确定投影平面在前面get_object_model_3d_params (ObjectModel3DIntersection, 'diameter_axis_aligned_bounding_box', Diameter)PoseInvert2[2]:=PoseInvert2[2]+Diameter // 切平面往上抬升。沿着z轴往上升高一个直径大小。凸显效果哦。// 基于长轴x轴切割,投影在yoz投影面上。* 用平行于投影平面的相机(1:1的比例)Scale:=1CamParam:=[0, 0, 1.0 /Scale, 1.0/Scale, 0, 0, 500, 500]project_object_model_3d (IntersectionXld, ObjectModel3DIntersection, CamParam, PoseInvert2, 'data', 'lines')* XLD有可能是多段的。count_obj (IntersectionXld, Number)* 点按照由上到下Rows:=[]Columns:=[]Row:=[]Column:=[]for I := 1 to Number by 1select_obj (IntersectionXld, EdgeContour,I)get_contour_xld (EdgeContour, Row1, Colum) // 获得XLD关键点坐标。Rows:=[Rows, Row]Columns:=[Columns, Column]endfortuple_sort_index(Rows, Indices)tuple_length (Rows, Length)OrderRow:=[]OrderColumn:=[]* 点从上往下排序。if (Length>=1)for Row_Index:=0 to Length-1 by 1OrderRow:=[OrderRow, Row[Indices[Row_Index]]]OrderColumn:=[OrderColumn, Columns[[Row_Index]]]endforendifgen_contour_polygon_xld (Intersection, OrderRow, OrderColumn)* 求最大和最小值(行方向)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]]dev_display (Intersection)gen_cross_contour_xld (StartXP, StartRow, StartColumn, 6, 0.795296)gen_cross_contour_xld (EndXP, EndRow, EndColumn, 6, 0.785398)* 转成点云的坐标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)* dev_display (Intersection)visualize_object_model_3d (WindowHandle, [StartPoint, EndPoint], [], [], [], [], [], [], [], PoseOut6)* 所有对点的边界点集合objectsOut:=[objectOut, StartPoint]objectsOut:=[objectOut, EndPoint]* 显示时的颜色Index_S:=0+Index1*2Index_E:=0+Index1*2+1color_S:='color_'+Index_Scolor_E:='color_'+Index_EcolorsOut:=[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,ObjectModel3DRigidTrans], [], [], ['colorsOut','color_88'], ['colorvaluesOut','red'], [], [], [], PoseOut2)* 二维显示
dev_open_window (0, 0, 512, 512, 'black', WindowHandle1)
dev_set_color ('red')
gen_cross_contour_xld (Start, all_x, all_y, 3, 0.785398)
基于当前的算法稳定不好,而且有些边缘处理粗糙。最好的做法是基于法向量求解。