Halcon 3D应用 - 胶路提取
1. 需求
本文基于某手环(拆机打磨处理)做的验证性工作,为了项目保密性,只截取部分数据进行测试。
这里使用的是海康3D线激光轮廓相机+直线电机的方式进行的高度数据采集,我们拿到的是高度图+亮度图数据。
提取手环上的胶路信息,检测胶路的胶宽、胶高、断胶等信息。
2.实现思路
- 扫描没有涂胶水的手环作为模板数据,包含高度图+亮度图;
- 扫描带胶水的手环作为被测数据,包含高度图+亮度图;
- 分割模板数据高度图中一个公共部分作为匹配模板;
- 高度图转点云图,利用模板点云信息,和被测数据的点云进行匹配靠模;
- 匹配后的点云转高度图;
- 高度图差分,只保留胶路数据;
- 进行胶路分析。
3.图像格式分析
3.1高度图
海康相机返回的高度图是tiff格式,具体含义如下:
- 图像高:直线电机移动中会触发拍照,每拍一次生成一个轮廓,高度对应了本次扫描拍摄的轮廓数。每行的间隔对应了直线电机的触发拍照两次间隔移动的距离。
- 图像宽:和相机的X轴轮廓点数+X轴采样间隔有关,注意这里不等于轮廓点数,需要调节合适的参数,保证图像的行间隔和列间隔实际距离一致。
- 像素值:代表高度信息,单位um。
本次测试调节的每个像素宽高都为40um。
3.2亮度图
最终提取的胶路,可以在亮度图上进行对比查看。
亮度图还可以进行缺陷检测、2D匹配定位等功能,但注意本验证中,亮度图不能用来匹配,因为手环放置的位姿可能会出现倾斜透视,需转到点云上进行3D位姿匹配。
4.代码实现
4.1 胶水和无胶水图像信息
无胶水高度图:

无胶水亮度图:

带胶水高度图:

带胶水亮度图:

4.2 读取数据
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 高度图预处理
高度图中干扰信息太多,比如底平面、高位杂质等,通过设定高度阈值,去除干扰的信息,聚焦胶水和模板位置的信息。
* 对高度图阈值分割
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 模板位置提取
在高度图中,尽心如下位置的提取:
* 分割模板区域
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算子只保留原高度图中分割的部分。

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 1tuple_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 ()
最后显示三个高度图的点云图像:
* 转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 点云匹配
先上代码:
* 点云匹配校准
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 点云转高度图
现在需要将靠模后的无胶水点云转为高度图,这里又是自己封装的算子:

* 将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] :=0rows[Index] :=0point_z_no_glue_t[Index] :=0endifif (rows[Index] < 0 or rows[Index]>=Height)cols[Index] :=0rows[Index] :=0point_z_no_glue_t[Index] :=0endif
endfor
gen_image_const (Height_img, img_type, Width, Height)
set_grayval (Height_img, rows, cols, point_z_no_glue_t) //将对应行列的,设置为对应的z值,比较绕这里。
return ()
实际代码:
* x y z的实际坐标是变了,所以深度图需要由这三个实际坐标来重新构建
obj3D_to_height (Height_img_no_glue_t, obj3D_hk_no_glue_t, step, Width, Height, ImgType)
带胶水的高度图:

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

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

4.8 差分
* 差分
sub_image (HeightImg_glue, Height_img_no_glue_t, ImageSub, 1, 0)
threshold (ImageSub, Image_Glue, 80, 1000)
背景为带胶水的亮度图,红色的为差分出的胶路Region:

5. 胶路分析
略。
相关文章:
Halcon 3D应用 - 胶路提取
1. 需求 本文基于某手环(拆机打磨处理)做的验证性工作,为了项目保密性,只截取部分数据进行测试。 这里使用的是海康3D线激光轮廓相机直线电机的方式进行的高度数据采集,我们拿到的是高度图亮度图数据。 提取手环上的胶…...
【Redis】Redis线程模型
目录 1. Redis 是单线程的,还是多线程的?2. Redis单线程模式是怎么样的?Redis 单线程模式的优势Redis 单线程的局限性Redis 单线程的优化策略 3. Redis采用单线程为什么还这么快4. Redis 6.0 之前为什么使用单线程?5. Redis 6.0 之…...
Electron构建桌面应用程序,服务于项目的自主学习记录(持续更新...
无所畏惧地面对未知,并将其视为成长的机会 大纲官网快速入门1.安装node.js -- 这里推荐用nvm管理2.脚手架创建3.electron 包安装到应用的开发依赖4.创建主进程(main.js)并启动项目1.创建页面2.配置main.js3.启动项目 -- 效果 进阶 -- 基于项目场景功能使用场景一&am…...
linux Load Average 计算
在内核代码 kernel/sched/loadavg.c 中有一个公式: a1 a0 * e a * (1 - e) 此算法是指数加权移动平均法(Exponential Weighted Moving Average,EMWA),是一种特殊的加权移动平均法,它考虑当前和历史的所有数据&#…...
pandas常用数据格式IO性能对比
前言 本文对pandas支持的一些数据格式进行IO(读写)的性能测试,大数据时代以数据为基础,经常会遇到操作大量数据的情景,数据的IO性能尤为重要,本文对常见的数据格式csv、feather、hdf5、jay、parquet、pick…...
【D3.js in Action 3 精译_031】3.5.2 DIY实战:在 Observable 平台实现带数据标签的 D3 条形图并改造单元测试模块
当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一部分 D3.js 基础知识 第一章 D3.js 简介(已完结) 1.1 何为 D3.js?1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践(上)1.3 数据可…...
华为OD机试真题-字符串分割
题目描述: 给定非空字符串s,将该字符串分割成一些子串,使每个子串的ASCII码值的和均为水仙花数。 1、若分割不成功,则返回0。 2、若分割成功且分割结果不唯一,则返回-1。 3、若分割成功且分割结果唯一,则返…...
编程技巧:提高代码健壮性与可维护性的关键方法(以 Shell 为例)
在脚本编写和自动化工作中,良好的编程技巧对于确保代码的健壮性和可维护性至关重要。以下是一些关键的编程技巧,包括模块化设计、单元测试、版本控制、处理边界条件、错误处理、中间值保存和创建 Flag。本文将通过 Shell 脚本示例来阐述这些技巧的应用。 1. 模块化设计 **定…...
【无标题】ReadableStream is not defined
升级 node 版本到 18 及以上即可解决...
【JVM】高级篇
1 GraalVM 1.1 什么是GraalVM GraalVM是Oracle官方推出的一款高性能JDK,使用它享受比OpenJDK或者OracleJDK更好的性能。 GraalVM的官方网址:https://www.graalvm.org/ 官方标语:Build faster, smaller, leaner applications。 更低的CPU…...
nacos1.4源码-服务发现、心跳机制
nacos的服务发现主要采用服务端主动推送客户端定时拉取;心跳机制通过每5s向服务端发送心跳任务来保活,当超过15s服务端未接收到心跳任务时,将该实例设置为非健康状态;当超过30s时,删除该实例。 1.服务发现 nacos主要采…...
C++ 2D平台游戏开发案例
关于2D平台游戏的C开发案例,包括游戏设计、实现细节、图形渲染和音效处理等内容。虽然无法一次性提供3000字,但我会尽量详细描述各个部分,并确保有足够的深度和广度。 2D平台游戏开发案例 一、游戏设计 游戏概述 游戏名称:“冒险…...
【Webpack--019】TreeShaking
🤓😍Sam9029的CSDN博客主页:Sam9029的博客_CSDN博客-前端领域博主 🐱🐉若此文你认为写的不错,不要吝啬你的赞扬,求收藏,求评论,求一个大大的赞!👍* &#x…...
Docker基本操作命令
Docker 是一个开源的应用容器引擎,允许开发者打包应用以及其依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。主要功能是为开发者提供一个简单…...
开源计算器应用的全面测试计划:确保功能性和可靠性
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
uni.requestPayment 支付成功之后会走 wx.onAppRoute
uni.requestPayment 是用于发起微信支付的统一接口,而 wx.onAppRoute 是用于监听小程序的路由变化。当 uni.requestPayment 支付成功后,如果发生了页面跳转或者其他路由变化,wx.onAppRoute 会被触发。这个行为是正常的,因为支付成…...
统⼀服务入口 - Gateway
网关介绍 问题 在 spring cloud 体系中我们通过 Eureka,Nacos 解决了服务注册,服务发现的问题,使⽤Spring Cloud LoadBalance解决了负载均衡的问题,使⽤ OpenFeign 解决了远程调⽤的问题. 但是当前所有微服务的接⼝都是直接对外暴露的,可以直接通过外部访问.为了保证对外服务的…...
QGraphicsWidget Class
Header:#include < QGraphicsWidget > qmake:QT += widgets Since:Qt 4.4 Inherits:QGraphicsObject and QGraphicsLayoutItem Inherited By:QGraphicsProxyWidget This class was introduced in Qt 4.4. Public Types enum anonymous {Type }Properties autoFi…...
探讨最好用的AI工具:从日常到创新的应用
文章目录 引言常用AI工具1. 语音助手2. 图像识别软件3. 机器翻译工具4. 智能客服系统 创新AI应用1. 自动驾驶汽车2. 虚拟试衣间3. 医疗影像分析4. 个性化推荐系统 个人体验分享1. 通义灵码2. 文心一言3. 智能写作助手4. 智能家居设备5. DALLE6. Whisper7. Codex8. Gym9. ChatGP…...
Python系统教程005(字符串的格式化输出)
知识回顾 1、默认情况下,input函数接收的数据是字符串类型。 2、字符串类型的关键词是str。 3、\n和\t都是转义字符,\n用来换行,\t用来留出一段固定长度的空白。 4、type函数能够用来查看变量的数据类型 5、数据类型的转换,举…...
智能信道建模实战指南:从技术选型到落地实施的决策框架
智能信道建模实战指南:从技术选型到落地实施的决策框架 【免费下载链接】DeepMIMO-matlab DeepMIMO dataset and codes for mmWave and massive MIMO applications 项目地址: https://gitcode.com/gh_mirrors/de/DeepMIMO-matlab 在5G向6G演进的过程中&#…...
如何评估单网页SEO的ROI
如何评估单网页SEO的ROI 在当今的数字化时代,网站的成功与否往往取决于其搜索引擎优化(SEO)的效果。而在SEO的众多策略中,单网页SEO的ROI(投资回报率)评估尤为重要。了解如何评估单网页SEO的ROI࿰…...
5大突破让暗黑2单机体验翻倍:PlugY插件全方位应用指南
5大突破让暗黑2单机体验翻倍:PlugY插件全方位应用指南 【免费下载链接】PlugY PlugY, The Survival Kit - Plug-in for Diablo II Lord of Destruction 项目地址: https://gitcode.com/gh_mirrors/pl/PlugY 当你第10次因储物箱满被迫丢弃装备时,当…...
es查询是否存在某个字段
1 如果字段就是整个文档json的字段{"query": {"bool": {"must": [{"exists": {"field": "recordUrl"}}]}} }2 如果要查询文档的字段下的子字段,前提是patient是一个objcet,可以涌点访问子属…...
广州邮科如何为你的系统选择合适的在线式充电机?
设备运行最怕断电。在线式充电机,就是那个能让设备“永不断电”的充电神器。今天咱们用大白话,把它讲清楚。它到底是什么?简单说,就是能一边给设备供电,一边给电池充电的智能设备。设备不用停机,电池也能充…...
DOL-CHS-MODS:一站式游戏体验优化整合方案
DOL-CHS-MODS:一站式游戏体验优化整合方案 【免费下载链接】DOL-CHS-MODS Degrees of Lewdity 整合 项目地址: https://gitcode.com/gh_mirrors/do/DOL-CHS-MODS 方案价值:为何选择整合方案 DOL-CHS-MODS 提供了一种智能化的游戏资源整合解决方案…...
告别手动记录:清音听真语音识别系统快速部署,中英文混合转录一键搞定
告别手动记录:清音听真语音识别系统快速部署,中英文混合转录一键搞定 1. 系统概述与核心优势 清音听真语音识别系统搭载了Qwen3-ASR-1.7B旗舰引擎,是专为复杂语音场景设计的高精度转录解决方案。相比前代0.6B版本,1.7B参数模型在…...
告别迷茫!Quartus II 13.1 从新建工程到烧录FPGA的保姆级避坑指南
Quartus II 13.1实战指南:从零开始玩转FPGA开发 第一次打开Quartus II 13.1时,那个灰蒙蒙的界面和密密麻麻的菜单栏确实容易让人望而生畏。作为Altera(现已被Intel收购)旗下经典的FPGA开发工具,它在高校实验室和企业研…...
LangChain串联DeepSeek时,如何用自定义OutputParser解决‘思考污染’问题?
LangChain串联DeepSeek时如何用自定义OutputParser解决"思考污染"问题 当我们在LangChain框架中串联使用具备"思考过程"输出的推理模型(如DeepSeek)时,经常会遇到一个棘手的问题:前序节点的思考标签会污染后续…...
Python自动化测试框架入门教程
Python自动化测试框架入门教程:从零开始掌握Pytest和unittest 📝 摘要 自动化测试是现代软件开发不可或缺的一部分,能够显著提高代码质量和开发效率。本文将带你从零开始了解Python主流自动化测试框架——Pytest和unittest,包含…...
