Halcon 3D应用 - 胶路提取

1. 需求

本文基于某手环(拆机打磨处理)做的验证性工作,为了项目保密性,只截取部分数据进行测试。

这里使用的是海康3D线激光轮廓相机+直线电机的方式进行的高度数据采集,我们拿到的是高度图+亮度图 数据。

提取手环上的胶路信息,检测胶路的胶宽、胶高、断胶等信息。

2.实现思路

  • 扫描没有涂胶水的手环作为模板数据,包含高度图+亮度图;
  • 扫描带胶水的手环作为被测数据,包含高度图+亮度图;
  • 分割模板数据高度图中一个公共部分作为匹配模板;
  • 高度图转点云图,利用模板点云信息,和被测数据的点云进行匹配靠模;
  • 匹配后的点云转高度图;
  • 高度图差分,只保留胶路数据;
  • 进行胶路分析。

3.图像格式分析

3.1高度图

海康相机返回的高度图是tiff格式,具体含义如下:

  • 图像高:直线电机移动中会触发拍照,每拍一次生成一个轮廓,高度对应了本次扫描拍摄的轮廓数。每行的间隔对应了直线电机的触发拍照两次间隔移动的距离。
  • 图像宽:和相机的X轴轮廓点数+X轴采样间隔有关,注意这里不等于轮廓点数,需要调节合适的参数,保证图像的行间隔和列间隔实际距离一致。
  • 像素值:代表高度信息,单位um。

本次测试调节的每个像素宽高都为40um。

3.2亮度图

最终提取的胶路,可以在亮度图上进行对比查看。

亮度图还可以进行缺陷检测、2D匹配定位等功能,但注意本验证中,亮度图不能用来匹配,因为手环放置的位姿可能会出现倾斜透视,需转到点云上进行3D位姿匹配。

4.代码实现

4.1 胶水和无胶水图像信息

无胶水高度图:

无胶水亮度图:

带胶水高度图:

带胶水亮度图:

4.2 读取数据

c 复制代码
read_image(HeightImg_no_glue, height_img_no_glue_path)
read_image (LumImage_no_glue, lum_img_no_glue_path)
read_image(HeightImg_glue, height_img_glue_path)
read_image (LumImage_glue, lum_img_glue_path)

get_image_type (HeightImg_no_glue, ImgType) //海康是uint2
get_image_size (LumImage_no_glue, Width, Height)

* 裁剪图片
crop_row_begin := 700
crop_row_end := 1060
gen_rectangle1 (ROI_Crop, crop_row_begin, 0, crop_row_end, Width)
reduce_domain (HeightImg_no_glue, ROI_Crop, HeightImg_no_glue)
reduce_domain (LumImage_no_glue, ROI_Crop, LumImage_no_glue)
reduce_domain (HeightImg_glue, ROI_Crop, HeightImg_glue)
reduce_domain (LumImage_glue, ROI_Crop, LumImage_glue)

get_image_size (LumImage_no_glue, Width, Height)
dev_open_window (0, 0, Width/2,Height/2, 'black', WindowHandle)

* 高度图像素间隔 单位um
step := 40

4.3 高度图预处理

高度图中干扰信息太多,比如底平面、高位杂质等,通过设定高度阈值,去除干扰的信息,聚焦胶水和模板位置的信息。

c 复制代码
* 对高度图阈值分割
height_min := 3000  
height_max := 7500
threshold (HeightImg_no_glue, region, height_min, height_max)
reduce_domain (HeightImg_no_glue, region, HeightImg_no_glue)
threshold (HeightImg_glue, region, height_min, height_max)
reduce_domain (HeightImg_glue, region, HeightImg_glue)

预处理结果:

4.4 模板位置提取

在高度图中,尽心如下位置的提取:

c 复制代码
* 分割模板区域
thr_min := 3500 
thr_max := 4500
gen_rectangle1 (ROI_1, 937, 118, 1060, 220) //选择这个两个孔的位置作为模板
reduce_domain (HeightImg_no_glue, ROI_1, HeightImg_no_glue_roi)
threshold (HeightImg_no_glue_roi, HeightImg_no_glue_roi_region, thr_min, thr_max)//缩小高度范围,聚焦模板平面
reduce_domain (HeightImg_no_glue, HeightImg_no_glue_roi_region, img_mod) //分割模板位置区域,注意是在高度图中分割,这样后面创建的模板3D图像的坐标系和原先的高度图是一样的

提取的模板如下:

4.5 高度图转点云图

要想进行点云匹配,必须首先将高度图转为点云图。halcon中没有该算子(可能没发现??),这里自己实现了一个算子height_to_3d_obj ,可以将高度图转为halcon点云对象。

核心是构建X坐标图、Y坐标图和Z坐标图,然后调用halcon的xyz_to_object_model_3d算子,转为3D图。

三个图的宽高和原始的高度图一致:

  • X图:每个像素存储的是对应高度图相同行列像素的X世界坐标,使用间隔step(高度图像素间隔)构建。可能结果为[(第一行)0 40 80 120 ...(第二行)...]
  • Y图:和X图类似,存储的是对应高度图相同行列像素的Y世界坐标,也使用step构建。可能的结果为[(第一行)0 0 0 ... (第二行)40 40... ]
  • Z图:就是高度图。

最后需要使用reduce_domain算子只保留原高度图中分割的部分。

c 复制代码
gen_image_const (ImageY, 'real', Width, Height)
gen_image_const (ImageZ, 'real', Width, Height)

* 获取高度图中的region区域
get_domain (HeightImg, DomainH)
 
* x图
tupleX :=[]
tuple_gen_sequence (0, (Width-1)*step, step, row_x) //一行
for Index := 0 to Height-1 by 1 //多行拼接成图像数据
    tupleX :=[tupleX,row_x]
endfor
get_domain(ImageX,domainX)
get_region_points(domainX,rows,cols)
set_grayval (ImageX, rows, cols, tupleX) //技巧,通过获取domain定义域,直接将序列赋值为像素
* 整体赋值完毕后再抠出region
reduce_domain (ImageX, DomainH, ImageX)

* y图
tupleY :=[]
for Index := 0 to Height-1 by 1
    tuple_gen_const (Width, Index*step, row_y)
    tupleY :=[tupleY, row_y]
endfor
set_grayval (ImageY, rows, cols, tupleY)
reduce_domain (ImageY, DomainH, ImageY)

* z图 - 转为real格式的图,原高度图非real格式
get_region_points(DomainH,rows_Z,cols_Z)
get_grayval(HeightImg,rows_Z,cols_Z,tupleZ)
zv0 := tupleZ
set_grayval (ImageZ, rows_Z, cols_Z, zv0) //ImageZ在前面是real格式
reduce_domain (ImageZ, DomainH, ImageZ)

xyz_to_object_model_3d (ImageX, ImageY, ImageZ, ObjectModel3D3)
return ()

最后显示三个高度图的点云图像:

c 复制代码
* 转3D对象模型查看
height_to_3d_obj (HeightImg_no_glue, step, obj3D_hk_no_glue)
height_to_3d_obj (HeightImg_glue, step, obj3D_hk_glue)
height_to_3d_obj (img_mod, step, obj3D_mod)
visualize_object_model_3d (WindowHandle, obj3D_hk_no_glue, [], [], ['lut','color_attrib','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut)
visualize_object_model_3d (WindowHandle, obj3D_hk_glue, [], [], ['lut','color_attrib','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut)
visualize_object_model_3d (WindowHandle, obj3D_mod, [], [], ['lut','color_attrib','disp_pose'], ['color1','coord_z','true'], [], [], [], PoseOut)

无胶水:

胶水:

模板:

4.6 点云匹配

先上代码:

c 复制代码
* 点云匹配校准
create_surface_model (obj3D_mod, 0.03, [], [], SurfaceModel)//创建surface模型,因为上面的模型非点云型,需进行采样
MinScore := 0.1
* find返回的Pose指的是模型到目标的位姿变换关系,可以用rigid_trans_object_model_3d将模板转换到目标附近靠模
find_surface_model (SurfaceModel, obj3D_hk_glue, 0.03, 0.1, MinScore, 'true', ['num_matches', 'use_3d_edges'], [1, 'false'], Pose_glue, Score, SurfaceMatchingResult)
get_surface_matching_result (SurfaceMatchingResult, 'sampled_scene', [], SampledScene)
get_surface_matching_result (SurfaceMatchingResult, 'key_points', [], KeyPoints)

* 胶路点云对齐到模型,注意rigid_trans_object_model_3d算子是将模型转变到目标场景中
* pose_invert (Pose_glue, Pose_glue_Invert)
rigid_trans_object_model_3d (obj3D_hk_no_glue, Pose_glue, obj3D_hk_no_glue_t)
visualize_object_model_3d (WindowHandle, [obj3D_hk_glue,obj3D_modTrans,obj3D_hk_no_glue_t], [], [], \
                           ['color_' + [0, 1, 2],'point_size_' + [0, 1, 2],'disp_pose'], ['red', 'blue', 'yellow', 1.0, 3.0, 5.0,'true'], [], [], [], PoseOut)
  • 首先create_surface_model 进行采样,作为基准;
  • find_surface_model 进行点云匹配,返回的Pose是模型到目标的位姿变换关系,这里的参数不再详细解释,需自己实验调节;
  • rigid_trans_object_model_3d 靠模,这里是将模型(无胶水点云)转变到目标场景中(胶水点云),用这个算子就可以进行点云对齐,为后面的高度图差分做准备。

其中红色的带胶的,黄色是无胶水,蓝色是无胶水的模板。

4.7 点云转高度图

现在需要将靠模后的无胶水点云转为高度图,这里又是自己封装的算子:

c 复制代码
* 将3D对象中的xyz点云,转为z向高度图
get_object_model_3d_params (object_3d, 'point_coord_x', point_x_no_glue_t)
get_object_model_3d_params (object_3d, 'point_coord_y', point_y_no_glue_t)
get_object_model_3d_params (object_3d, 'point_coord_z', point_z_no_glue_t)
cols := int(point_x_no_glue_t/step) //在高度图中的行列坐标,cols[index]和rows[index]是一对。 
rows := int(point_y_no_glue_t/step) 
* 高度图赋值
for Index := 0 to |cols|-1 by 1
    * 注意其中可能有负数!下面将负数都转移到0,0点
    * 可能有越界的点,都转移到0点
    if (cols[Index] < 0 or cols[Index]>=Width)
        cols[Index] :=0
        rows[Index] :=0
        point_z_no_glue_t[Index] :=0
    endif
    if (rows[Index] < 0 or rows[Index]>=Height)
        cols[Index] :=0
        rows[Index] :=0
        point_z_no_glue_t[Index] :=0
    endif
endfor
gen_image_const (Height_img, img_type, Width, Height)
set_grayval (Height_img, rows, cols, point_z_no_glue_t) //将对应行列的,设置为对应的z值,比较绕这里。
return ()

实际代码:

c 复制代码
* x y z的实际坐标是变了,所以深度图需要由这三个实际坐标来重新构建
obj3D_to_height (Height_img_no_glue_t, obj3D_hk_no_glue_t, step, Width, Height, ImgType)

带胶水的高度图:

转换之后的高度图(无胶水):

转换之前的高度图(无胶水):

4.8 差分

c 复制代码
* 差分
sub_image (HeightImg_glue, Height_img_no_glue_t, ImageSub, 1, 0)
threshold (ImageSub, Image_Glue, 80, 1000)

背景为带胶水的亮度图,红色的为差分出的胶路Region:

5. 胶路分析

略。

相关推荐
健忘的派大星1 小时前
什么是RAG,有哪些RAG引擎?看完这一篇你就知道了!!
人工智能·ai·语言模型·langchain·llm·agi·rag
LeapMay2 小时前
3D Gaussian Splatting代码详解(一):模型训练、数据加载
人工智能·深度学习·3d
LeapMay2 小时前
3D Gaussian Splatting 入门
计算机视觉·3d
AI原吾3 小时前
构建灵活、高效的HTTP/1.1应用:探索h11库
网络·python·网络协议·http·ai·h11
小城哇哇4 小时前
【AI多模态大模型】基于AI的多模态数据痴呆病因鉴别诊断
人工智能·ai·语言模型·llm·agi·多模态·rag
BH042509094 小时前
VQGAN(2021-06:Taming Transformers for High-Resolution Image Synthesis)
计算机视觉·扩散模型·图像生成·cv
xhload3d4 小时前
掌控物体运动艺术:图扑 Easing 函数实践应用
大数据·3d·智慧城市·html5·webgl·数字孪生·可视化·数据可视化·工业互联网·轻量化·demo·hightopo·大屏展示·图形组件
BH042509094 小时前
VQ-VAE(2018-05:Neural Discrete Representation Learning)
人工智能·计算机视觉
贵州晓智信息科技7 小时前
Three.js Shader 与自定义材质—深入理解与应用
开发语言·javascript·3d·材质
Extraovo8 小时前
利用 Direct3D 绘制几何体—8.光栅器状态
c++·笔记·学习·3d