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、数据类型的转换,举…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...