本文主要是介绍二维测量--迭代的跟踪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上的导线的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!