二维测量--迭代的跟踪PCB上的导线

2023-10-16 07:30

本文主要是介绍二维测量--迭代的跟踪PCB上的导线,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

对应示例程序:
      track_wries_chip.hdev

目标:对图像中的PCB焊线进行定位 主要是利用迭代的方式实现的

思路为:
      1.窗口初始化
      2.读入图像,以及提前准备好的已知参数。包括:
         ⑴每条焊线的起点和终点坐标
         ⑵导线的偏离角度(导线偏离上述各点的角度)
         ⑶焊线的近似宽度(像素)
      3.设置一些容忍度参数,包括导线的最小长度,搜索的角度,半径等
      4.一条一条的对焊线进行处理:以每条焊线的起点为圆心 做一个搜索圆弧(自己参数决定),把圆弧对应的区域从原图中抠出来,然后利用lines_gauss算子寻找圆弧中的曲线 ,然后再以得到的曲线终点为起始点 再画圆弧 找曲线 进行迭代 按照一个一个小圆弧的形式 进行曲线的查找和合并
      5. 将上一步得到的线段 拟合成一条曲线。因为上一步可能得到几条断开的曲线,现在就是对其进行细化 :先合并最近的 然后选择最长的一条作为备选。 再根据之前的先验知识,对曲线进行裁剪到要求的范围内 因为在迭代过程中 会因为干扰 拟合过度 。其实就是把曲线裁剪到我们设置的起始 终止坐标范围左右,不能因为拟合 导致曲线一直延伸,延伸出图像的范围。
      6. 通过最后得到的曲线的端点 与 已知的终点坐标做两点距离计算,如果距离小于某一个值,就认为我们找到了一条合适的焊线曲线。但是如果距离过大,说明该算法失败了,需要再从终点坐标开始,往回找。以准备好的焊线终点为搜索起点,以之前得到的曲线(步骤5得到的曲线)的最后一段曲线的中点为终点进行画圆弧迭代查找焊线。
      7.如果步骤6中,从终点往中点查找曲线的时候,不止找到了一条曲线,那就再按步骤8进行处理。
      8.对从起点拟合得到的曲线 与 从终点开始拟合的曲线 进行合并,先计算二者的交点,然后分别进行裁剪,只保留联合的部分,其余部分裁剪掉。
      9.对最终得到的焊线进行一次合并和平滑,最后把全部焊线放到一个Obj进行显示即可。
图像:
*                                                                                                       原图:
在这里插入图片描述

*                                                                             利用圆弧的形式迭代法进行曲线拟合(从起点往终点方向查找)
在这里插入图片描述

*                                                                                 从终点开始,往回找曲线

在这里插入图片描述
*                                                                                                      **从起点到终点找到的曲线

在这里插入图片描述
*                                                                                                      **从终点到中点找到的曲线
在这里插入图片描述
*                                                                                  *两条曲线的交点

在这里插入图片描述
*                                                                                  最后合并得到的曲线

在这里插入图片描述
*                                                                                                结果曲线

在这里插入图片描述

代码:

*这个程序展示了如何迭代地跟踪导线连接。*它假定以下值来自*初始化阶段:*1。与键合位点相对应的起点和终点坐标*2。导线偏离上述各点的角度*3。近似线宽度(像素)
//初始化
dev_update_off ()
dev_get_preferences ('suppress_handled_exceptions_dlg', PreferenceValue)
dev_close_window ()
read_image (Image, 'die/die_01')
get_image_size (Image, Width, Height)
dev_open_window (0, 0, Width, Height, 'black', WindowHandle)
set_display_font (WindowHandle, 16, 'mono', 'true', 'false')
dev_set_draw ('margin')
dev_set_colored (12)
dev_set_line_width (2)
* 
* SEARCH PARAMETERS ********************************
* Approximated half-widths of wires in image
*搜索参数********************************
*图像中导线的近似半宽度WireWidths := [1.8,0,0,8,6,1.7,7.5]* Minimum length of contours which may correspond to a wire
*与金属丝相对应的最小轮廓长度
MinSegmentLength := 5
* Angular search domain
*角搜索域
AngleTolerance := rad(20)
* Radius of the circular section defining the search region
*定义搜索区域的圆形截面的半径
MinSearchRadius := 10
MinWireDeviation := 5
* Radius of the circle with the center at the end point
* that the tracking algorithm must reach to
* consider the tracking as successful
*圆心位于跟踪算法必须达到的终点处的圆半径,才能认为跟踪成功
AcceptanceRadius := 5
MinWireLength := 80
* ***************************************************
* 
* 
for Img := 1 to 7 by 1//从文件中读入需要设置的参数//存放地址:C:\Users\Public\Documents\MVTec\HALCON-12.0\examples\hdevelop\Applications\Measuring-2Ddev_set_preferences ('suppress_handled_exceptions_dlg', 'true') //在程序中设置HDevelop首选项tryread_tuple ('die_' + Img$'02' + '_params.tup', Parameters) //从文件中读取Tuplecatch (Exception)continueendtrydev_set_preferences ('suppress_handled_exceptions_dlg', PreferenceValue)//根据先验知识 将要提取的导线的数量 开始/结束 坐标 方向 存放在一个文件中 现在再读出来//存放的数据顺序按照:  数量 开始行 开始列 开始方向 结束行 结束列 结束方向 进行//行列 是导线的两个端点附近的坐标//方向 是导线的大体方向NPads := Parameters[0]   //数量StartPadRows := Parameters[1:NPads]  //开始行StartPadCols := Parameters[NPads + 1:2 * NPads]  //开始列StartInitOrientation := Parameters[2 * NPads + 1:3 * NPads] //开始方向EndPadRows := Parameters[3 * NPads + 1:4 * NPads]  //结束行EndPadCols := Parameters[4 * NPads + 1:5 * NPads]  //结束列EndInitOrientation := Parameters[5 * NPads + 1:6 * NPads] //结束方向//gen_empty_obj (Wires)read_image (Image, 'die/die_' + Img$'02')dev_display (Image)for Pad := 0 to |StartPadRows| - 1 by 1   //一条一条线的处理* Maximum distance between two consecutive contours* corresponding to the same wire//同一导线对应的两个连续轮廓之间的最大距离MaxDistSegment := 17 * WireWidths[Img - 1]//本地函数 检测图中的线  //以开始点为圆心 做一个起始方向和终止方向 指定大小的圆弧 //寻找圆弧中的曲线 然后再以得到的曲线终点为起始点  再画圆弧 找曲线  进行迭代 按照一个一个小圆弧的形式 进行曲线的查找和合并track_wire (Image, FwdWireSegments, StartPadRows[Pad], StartPadCols[Pad], StartInitOrientation[Pad], EndPadRows[Pad], EndPadCols[Pad], WireWidths[Img - 1], AngleTolerance, MinSegmentLength, MaxDistSegment)* Fuse all segments of the wire into a long contour covering the* whole trajectory  将导线的所有部分熔合成覆盖整个轨迹的长轮廓//将上一步得到的线段 拟合成一条曲线//上一步可能得到几条曲线 现在 就是对其进行细化  先合并最近的  然后选择最长的一条作为备选 再根据之前的先验知识//对曲线进行裁剪到要求的范围内  因为在迭代过程中 会因为干扰 拟合过度  其实就是把曲线裁剪到我们设置的起始  终止坐标范围左右//不能因为拟合 导致曲线跑出去fuse_wire_segments (FwdWireSegments, Wire, StartPadRows[Pad], StartPadCols[Pad], EndPadRows[Pad], EndPadCols[Pad], MinWireLength, MaxDistSegment, MinWireDeviation)* * Check whether the algorithm could successfully track the whole wire.* If not, try to track the wire starting at the end point back to the* start point, and merge the results*检查该算法是否能够成功跟踪整个导线。*如果没有,请尝试跟踪从终点开始到起点的导线,并合并结果//计算点到Contour的距离//计算预设值中的终止点坐标与提取出的曲线的距离distance_pc (Wire, EndPadRows[Pad], EndPadCols[Pad], DistanceMin, DistanceMax)  if (DistanceMin > AcceptanceRadius)  //最小距离 大于可接受的阈值* The new end points correspond to the end points of the contour* where the algorithm left off in the previous attempt*新的端点对应于算法在上一次尝试中停止的轮廓的端点count_obj (FwdWireSegments, NXLDs)select_obj (FwdWireSegments, ObjectSelected, NXLDs)get_contour_xld (ObjectSelected, Row, Column)//本地函数  以预先知道的终点 为 起点  反方向往回找曲线track_wire (Image, BckWireSegments, EndPadRows[Pad], EndPadCols[Pad], EndInitOrientation[Pad], sum([Row[0],Row[|Row| - 1]]) / 2, sum([Column[0],Column[|Column| - 1]]) / 2, WireWidths[Img - 1], AngleTolerance, MinSegmentLength, MaxDistSegment)count_obj (BckWireSegments, WiresFound)if (WiresFound > 0) //如果找到了多条曲线 其实就是没连住* * Find the point at which both contours cross each other 找出两个轮廓相交的点//计算从起始点拟合得到的曲线   与  从终点拟合得到的曲线  二者的交点  用来最终合并//下面这个函数是把两条曲线的最后一段进行曲线拟合  然后计算曲线的交点contour_intersection_point (FwdWireSegments, BckWireSegments, EndSegmentForwards, EndSegmentBackwards, IntersecRow, IntersecColumn)* Crop contours so that they do not go beyond the crossing point//本地函数:裁剪contour,使其不超出交叉点  其实就是根据上一步的交点 把每一个曲线进行分割 //将多余的部分都踢掉 只保留可以连在一起的部分crop_to_cross_point (FwdWireSegments, EndSegmentForwards, CroppedSegmentsFwd, IntersecRow, IntersecColumn)crop_to_cross_point (BckWireSegments, EndSegmentBackwards, CroppedSegmentsBck, IntersecRow, IntersecColumn)concat_obj (CroppedSegmentsFwd, CroppedSegmentsBck, WireSegments) //合并elsecount_obj (FwdWireSegments, NSegments)Sequence := [1:NSegments]select_obj (FwdWireSegments, WireSegments, Sequence)endif* Merge the resulting contours  合并生成的contourlength_xld (WireSegments, Length)  //每段XLD的长度//合并XLDunion_adjacent_contours_xld (WireSegments, FusedSegments, max(Length), max(Length) / max([min(Length),1]), 'attr_keep')smooth_contours_xld (FusedSegments, Wire, 11)  //平滑concat_obj (Wires, Wire, Wires) //把裁剪处理过的obj放到一个obj中 用于最后的合并elsesmooth_contours_xld (Wire, SmoothedContours1, 11)  //一幅图中的曲线全处理后 再平滑一次concat_obj (Wires, SmoothedContours1, Wires)  //把曲线全合并到一个obj中endifendfordev_display (Image)dev_display (Wires)if (Img < 7)disp_continue_message (WindowHandle, 'black', 'true')stop ()endif
endfor

用到的几个算子:
    lines_gauss–检测线条及其宽度
    union_collinear_wire_segments–合并周围相近的曲线
    fit_line_contour_xld–用线段近似XLD轮廓
    distance_pc–计算点到Contour的距离
    crop_contours_xld–利用矩形裁剪一个Contours
    smooth_contours_xld–平滑XLD

这篇关于二维测量--迭代的跟踪PCB上的导线的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/219993

相关文章

poj2576(二维背包)

题意:n个人分成两组,两组人数只差小于1 , 并且体重只差最小 对于人数要求恰好装满,对于体重要求尽量多,一开始没做出来,看了下解题,按照自己的感觉写,然后a了 状态转移方程:dp[i][j] = max(dp[i][j],dp[i-1][j-c[k]]+c[k]);其中i表示人数,j表示背包容量,k表示输入的体重的 代码如下: #include<iostream>#include<

hdu2159(二维背包)

这是我的第一道二维背包题,没想到自己一下子就A了,但是代码写的比较乱,下面的代码是我有重新修改的 状态转移:dp[i][j] = max(dp[i][j], dp[i-1][j-c[z]]+v[z]); 其中dp[i][j]表示,打了i个怪物,消耗j的耐力值,所得到的最大经验值 代码如下: #include<iostream>#include<algorithm>#include<

HDU 2159 二维完全背包

FATE 最近xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务。久而久之xhd开始对杀怪产生的厌恶感,但又不得不通过杀怪来升完这最后一级。现在的问题是,xhd升掉最后一级还需n的经验值,xhd还留有m的忍耐度,每杀一个怪xhd会得到相应的经验,并减掉相应的忍耐度。当忍耐度降到0或者0以下时,xhd就不会玩这游戏。xhd还说了他最多只杀s只怪。请问他能

迭代器模式iterator

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素

Verybot之OpenCV应用三:色标跟踪

下面的这个应用主要完成的是Verybot跟踪色标的功能,识别部分还是居于OpenCV编写,色标跟踪一般需要将图像的颜色模式进行转换,将RGB转换为HSV,因为对HSV格式下的图像进行识别时受光线的影响比较小,但是也有采用RGB模式来进行识别的情况,这种情况一般光线条件比较固定,背景跟识别物在颜色上很容易区分出来。         下面这个程序的流程大致是这样的:

多线程篇(阻塞队列- LinkedBlockingDeque)(持续更新迭代)

目录 一、LinkedBlockingDeque是什么 二、核心属性详解 三、核心方法详解 addFirst(E e) offerFirst(E e) putFirst(E e) removeFirst() pollFirst() takeFirst() 其他 四、总结 一、LinkedBlockingDeque是什么 首先queue是一种数据结构,一个集合中

多线程篇(阻塞队列- LinkedBlockingQueue)(持续更新迭代)

目录 一、基本概要 1. 构造函数 2. 内部成员 二、非阻塞式添加元素:add、offer方法原理 offer的实现 enqueue入队操作 signalNotEmpty唤醒 删除线程(如消费者线程) 为什么要判断if (c == 0)时才去唤醒消费线程呢? 三、阻塞式添加元素:put 方法原理 图解:put线程的阻塞过程 四、非阻塞式移除:poll方法原理 dequ

Linux内置的审计跟踪工具:last命令

如果你是一个服务器管理员,你或许知道你要保护你的服务器的话,不仅是从外部,还要从内部保护。Linux有一个内置工具来看到最后登陆服务器的用户,可以帮助你保护服务器。   这个命令是last。它对于追踪非常有用。让我们来看一下last可以为你做些什么。   last命令的功能是什么   last显示的是自/var/log/wtmp文件创建起所有登录(和登出)的用户。这个文件是二进制

YOLOv8/v10+DeepSORT多目标车辆跟踪(车辆检测/跟踪/车辆计数/测速/禁停区域/绘制进出线/绘制禁停区域/车道车辆统计)

01:YOLOv8 + DeepSort 车辆跟踪 该项目利用YOLOv8作为目标检测模型,DeepSort用于多目标跟踪。YOLOv8负责从视频帧中检测出车辆的位置,而DeepSort则负责关联这些检测结果,从而实现车辆的持续跟踪。这种组合使得系统能够在视频流中准确地识别并跟随特定车辆。 02:YOLOv8 + DeepSort 车辆跟踪 + 任意绘制进出线 在此基础上增加了用户

二维旋转公式

二维旋转公式 ros的tf工具包可以很方便的实现任意坐标系之间的坐标转换。但是,如果只是想简单的测试想法,而又不想编写过于庞杂的代码,考虑自己写二维旋转的函数。而与二维旋转问题对偶的另一个问题便是二维坐标系旋转变换。这两个问题的形式基本一样,只是旋转的角度相差一个负号。就是这个容易搞混,所以做个笔记,以备查用。 1. 二维旋转公式(算法) 而(此文只针对二维)旋转则是表示某一坐标点 ( x