halcon例程track_wires_on_chip.hdev

主要是找线,遍历迭代跟踪起点和终点获得轮廓。方法可借鉴。

C:\Users\Public\Documents\MVTec\HALCON-23.05-Progress\examples\hdevelop\Applications\Measuring-2D\track_wires_on_chip.hdev

初始化操作
1
读入图并显示
1
1
1
1
获取搜索区域,根据已知的起点
1

初步筛选contour
1
角度筛选
1
连接共线轮廓
1

找到距离起点最近的轮廓
1

连接为线的一部分
1

更新起点,以及角度,搜索范围的半径等参数
1
判断终点
1

1

判断最小值,如果大于5,反向重新查找得到线段。
1

反向查找:
1
如果不是,直接联合在一起
1

1

1

1

其中有3个函数

第一个:可借鉴
获取扇形的搜索区域
gen_search_region (SearchRegion, StartPointRow, StartPointCol,
RefOrientation, min([SearchRadius,Distance]), AngleTolerance)

gen_ellipse_contour_xld (ContEllipse, StartRow, StartColumn, SearchAngle,\
                         SearchRadius, SearchRadius, -AngleTolerance,\
                         AngleTolerance, 'positive', 1.5)
get_contour_xld (ContEllipse, ContRow, ContCol)
ContRow := [StartRow,ContRow]
ContCol := [StartColumn,ContCol]
gen_contour_polygon_xld (Cone, ContRow, ContCol)
gen_region_contour_xld (Cone, SearchRegion, 'filled')
return ()

第二个:可借鉴

  • 尝试连接剩余的候选轮廓。
    union_collinear_wire_segments (ObjectSelected, UnionContours, 5, 0.5)
* 通常在芯片周围会有暗区,这些暗区对应于阴影,
* 会在跟踪键合线时产生间隙。我们需要通过连接独立的线段来跨越这些间隙。
* 
* 下面的思路是:线段越长,这些线段实际上对应于键合线的可能性就越大,
* 因此,即使它们之间的间隔较大,我们也决定将它们连接起来。


length_xld (Segments, Lengths)
MaxDistAbs := max(Lengths)
MaxDistRel := max(Lengths) / min(Lengths)
union_collinear_contours_ext_xld (Segments, UnionContours, MaxDistAbs, MaxDistRel, 2, MaxAngle, 0, -1, 1, 1, 1, 1, 1, 0, 'attr_keep')
* 
Iter := 0
length_xld (UnionContours, Lengths)
while (Iter < MaxIterations and |Lengths| > 1)
    MaxDistAbs := 0.4 * max(Lengths)
    MaxDistRel := 0.1 * max(Lengths) / min(Lengths)
    union_collinear_contours_ext_xld (UnionContours, UnionContours, MaxDistAbs, MaxDistRel, 2, 0.5, 0, -1, 1, 1, 1, 1, 1, 0, 'attr_keep')
    Iter := Iter + 1
endwhile
return ()

第三个:* * 更新搜索参数以用于下一次迭代。
update_search_parameters (WireSegment, StartPointRow, StartPointCol, RefOrientation, MinSearchRadius, MaxDistSegment, StartPointRow, StartPointCol, RefOrientation, SearchRadius, DistSegmentThreshold)

函数实现:

 RefOrientationOut := RefOrientation
StartPointColOut := StartPointCol
StartPointRowOut := StartPointRow
* 

* 找到轮廓上距离当前起点最远的极点,并将其定义为下一次迭代的新的起始搜索点。

get_contour_xld (WireSegment, Row, Col)
EndPointsRow := [Row[0],Row[|Row| - 1]]
EndPointsCol := [Col[0],Col[|Col| - 1]]
gen_region_line (RegionLines, EndPointsRow, EndPointsCol, [StartPointRowOut,StartPointRowOut], [StartPointColOut,StartPointColOut])

distance_pp (EndPointsRow, EndPointsCol, [StartPointRowOut,StartPointRowOut], [StartPointColOut,StartPointColOut], Distance)
if (Distance[0] > Distance[1])
    StartPointRowOut := EndPointsRow[0]
    StartPointColOut := EndPointsCol[0]
else
    StartPointRowOut := EndPointsRow[1]
    StartPointColOut := EndPointsCol[1]
endif
* 


* 计算局部轮廓的方向,并将其定义为下一次迭代的参考角度。其基本思想是,
*键合线在相邻的迭代(线段)之间弯曲得非常平滑(弯曲角度小)。

orientation_points_xld (WireSegment, Orientation)
OrientationDiffAbs := fabs(fmod(Orientation - RefOrientation,rad(360)))
if (OrientationDiffAbs > rad(90) and OrientationDiffAbs < rad(270))
    Orientation := Orientation + rad(180)
endif
while (Orientation < -rad(180))
    Orientation := Orientation + rad(360)
endwhile
while (Orientation > rad(180))
    Orientation := Orientation - rad(360)
endwhile
RefOrientationOut := Orientation
* 


* 根据当前线段的长度定义搜索域的半径:线段越长,半径越大。
*其基本思想是,较长的线段对应于弯曲程度很小的键合线部分。
*因此,我们可以通过扩大搜索范围来加快跟踪速度。

length_xld (WireSegment, SearchRadius)
SearchRadius := 1.5 * max([SearchRadius,MinSearchRadius])
DistSegmentThreshold := min([SearchRadius / 2.0,MaxDistSegment])
return ()  

第4个:跟踪到线的区域 track_wire (Image, FwdWireSegments, StartPadRows[Pad], StartPadCols[Pad], StartInitOrientation[Pad], EndPadRows[Pad], EndPadCols[Pad], WireWidths[Img - 1], AngleTolerance, MinSegmentLength, MaxDistSegment)

gen_empty_obj (Wire)
get_image_size (Image, Width, Height)
* 重新开始跟踪键合线的点的坐标
StartPointRow := StartPadRows
StartPointCol := StartPadCols
* 上一次找到的线段的方向。它作为下一次迭代搜索的参考。
* 基本思想是,线的曲率在每次迭代之间不会发生太大变化。
RefOrientation := StartInitOrientation

* 键合线应当位于其中的圆形区域的半径。

SearchRadius := 30 * WireWidths
MinSearchRadius := 5 * WireWidths
DistSegmentThreshold := min([SearchRadius / 3.0,MaxDistSegment])
while (1)
    
    distance_pp (StartPointRow, StartPointCol, EndPadRows, EndPadCols, Distance)
    gen_search_region (SearchRegion, StartPointRow, StartPointCol, RefOrientation, min([SearchRadius,Distance]), AngleTolerance)
    reduce_domain (Image, SearchRegion, ImageReduced)
    * 迭代搜索候选点。当图像对比度较低时,
    * 我们需要降低高阈值。
    NXLDs := 0
    HighThreshold := 1
    while (NXLDs == 0 and HighThreshold >= 0.1)
        lines_gauss (ImageReduced, Lines, WireWidths / sqrt(3), 0.05, HighThreshold, 'dark', 'true', 'true', 'true')
        gen_polygons_xld (Lines, Polygons, 'ramer', 1)
        split_contours_xld (Polygons, Contours, 'polygon', 1, 5)
        select_contours_xld (Contours, SelectedContours, 'contour_length', MinSegmentLength, max([Width,Height]) / 2.0, -0.5, 0.5)
        count_obj (SelectedContours, NXLDs)
        HighThreshold := 0.5 * HighThreshold
    endwhile
    if (NXLDs == 0)
        break
    endif
    * 跟踪键合线
    * 首先,选择与前一段线具有相似方向的轮廓。
    LowerLimit := [RefOrientation - AngleTolerance,RefOrientation - AngleTolerance + rad(180),RefOrientation - AngleTolerance - rad(180)]
    UpperLimit := [RefOrientation + AngleTolerance,RefOrientation + AngleTolerance + rad(180),RefOrientation + AngleTolerance - rad(180)]
    select_shape_xld (SelectedContours, ObjectSelected, ['phi_points', 'phi_points', 'phi_points'], 'or', LowerLimit, UpperLimit)
    * 
    
    * 在这些轮廓中,选择与前一个最接近的轮廓。
    
    count_obj (ObjectSelected, NContours)
    if (NContours > 1)
        * * 尝试连接剩余的候选轮廓。
        union_collinear_wire_segments (ObjectSelected, UnionContours, 5, 0.5)
        count_obj (UnionContours, NUCont)
        DistMin := []
        ** 如果仍然有多个候选轮廓,
        * * 选择最接近起点的那个。
        for C := 1 to NUCont by 1
            select_obj (UnionContours, ObjectSelected1, C)
            distance_pc (ObjectSelected1, StartPointRow, StartPointCol, DistanceMin, DistanceMax)
            DistMin := [DistMin,DistanceMin]
        endfor
        tuple_find (DistMin, min(DistMin), IndMin)
        if (DistMin[IndMin[0]] > DistSegmentThreshold)
            break
        endif
        select_obj (UnionContours, WireSegment, IndMin[0] + 1)
    elseif (NContours == 0)
        break
    else
        * * 只有一个候选轮廓。
        copy_obj (ObjectSelected, WireSegment, 1, 1)
    endif
    concat_obj (Wire, WireSegment, Wire)
    * * 更新搜索参数以用于下一次迭代。
    update_search_parameters (WireSegment, StartPointRow, StartPointCol, RefOrientation, MinSearchRadius, MaxDistSegment, StartPointRow, StartPointCol, RefOrientation, SearchRadius, DistSegmentThreshold)
    ** 检查轮廓是否已经到达终点。
    * 
    * * V1:从起点(StartPoint)指向终点(EndPoint)的单位向量  
    V1 := [EndPadRows - StartPointRow,EndPadCols - StartPointCol]
    V1 := V1 / sqrt(sum(V1 * V1))
    *  V2:沿着键合线方向从起点(StartPoint)出发的单位向量
    V2 := [-sin(RefOrientation),cos(RefOrientation)]
    if (V1[0] * V2[0] + V1[1] * V2[1] < 0)
        break
    endif
endwhile
return ()

第二个:* 将线的所有线段合并为一个覆盖整个轨迹的长轮廓
fuse_wire_segments (FwdWireSegments, Wire, StartPadRows[Pad], StartPadCols[Pad], EndPadRows[Pad], EndPadCols[Pad], MinWireLength, MaxDistSegment, MinWireDeviation)

count_obj (WireSegments, NumWires)
if (NumWires > 0)
    smooth_contours_xld (WireSegments, SmoothedContours, 9)
    union_adjacent_contours_xld (SmoothedContours, UnionContours, MaxDistSegment, 3, 'attr_keep')
    select_longest_contour (UnionContours, Wire, Length)
    if (Length < MinWireLength)
        gen_empty_obj (Wire)
        return ()
    endif
    
    * 有时,跟踪过程可能会超出终点,因为它在图像中发现了类似于线的结构
    *(一个细长区域,其暗像素被两侧较亮的像素包围)。
    * 我们对最终的轮廓进行裁剪,使其在终点处停止。
    
    crop_contours_xld (Wire, Wire, min([StartPadRows,EndPadRows]) - MinWireDeviation, min([StartPadCols,EndPadCols]) - MinWireDeviation, max([StartPadRows,EndPadRows]) + MinWireDeviation, max([StartPadCols,EndPadCols]) + MinWireDeviation, 'true')
    select_longest_contour (Wire, Wire, MaxLength)
    * 确保轮廓的方向是从起始焊盘指向终止焊盘。
    DPRow := EndPadRows - StartPadRows
    DPCol := EndPadCols - StartPadCols
    get_contour_xld (Wire, Row, Col)
    if (fabs(DPRow) > fabs(DPCol))
        DCRow := Row[|Row| - 1] - Row[0]
        if (DPRow * DCRow < 0)
            gen_contour_polygon_xld (Wire, inverse(Row), inverse(Col))
        endif
    else
        DCCol := Col[|Col| - 1] - Col[0]
        if (DPCol * DCCol < 0)
            gen_contour_polygon_xld (Wire, inverse(Row), inverse(Col))
        endif
    endif
endif
return ()
* 本程序展示了如何迭代跟踪键合线。
* 它假设以下值来自之前的初始化阶段:
* 1. 对应于键合点的起点和终点的坐标。
* 2. 线从上述各点出发的角度。
* 3. 近似的线宽(以像素为单位)。

dev_update_off ()
dev_close_window ()
read_image (Image, 'die/die_01')
get_image_size (Image, Width, Height)
dev_open_window (0, 0, 500, 500, 'black', WindowHandle)
set_display_font (WindowHandle, 16, 'mono', 'true', 'false')
dev_set_draw ('margin')
dev_set_colored (12)
dev_set_line_width (2)
* 
* 搜索参数 ********************************
*图像中的铜线的大约半宽度 
WireWidths := [1.8, 0, 0, 8, 6, 1.7, 7.5]
* 可能对应于键合线的轮廓的最小长度
MinSegmentLength := 5
* 角度搜索范围
AngleTolerance := rad(35)
* 定义搜索区域的圆形部分的半径
MinSearchRadius := 10
MinWireDeviation := 5
* 以终点为中心的圆的半径,
* 跟踪算法必须到达该圆内,
* 才能认为跟踪成功。
AcceptanceRadius := 5
MinWireLength := 80
* ***************************************************
* 
* 
for Img := 1 to 7 by 1
    try
        read_tuple ('die_' + Img$'02' + '_params.tup', Parameters)
    catch (Exception)
        continue
    endtry
    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
        * 两条连续轮廓之间对应于同一根线的最大距离
        MaxDistSegment := 17 * WireWidths[Img - 1]
        gen_region_line (RegionLines,  StartPadRows[Pad], StartPadCols[Pad], EndPadRows[Pad], EndPadCols[Pad])
        
        
        track_wire (Image, FwdWireSegments, StartPadRows[Pad], StartPadCols[Pad], StartInitOrientation[Pad], EndPadRows[Pad], EndPadCols[Pad], WireWidths[Img - 1], AngleTolerance, MinSegmentLength, MaxDistSegment)

        * 将线的所有线段合并为一个覆盖整个轨迹的长轮廓
        fuse_wire_segments (FwdWireSegments, Wire, StartPadRows[Pad], StartPadCols[Pad], EndPadRows[Pad], EndPadCols[Pad], MinWireLength, MaxDistSegment, MinWireDeviation)
        * 
        *    检查算法是否能够成功跟踪整根线
        *如果不能,尝试从终点开始反向跟踪到起点,并合并结果。
        distance_pc (Wire, EndPadRows[Pad], EndPadCols[Pad], DistanceMin, DistanceMax)
        if (DistanceMin > AcceptanceRadius)

            * 新的终点对应于算法在上一次尝试中离开的轮廓的终点。
            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], StartPadRows[Pad], StartPadCols[Pad], WireWidths[Img - 1], AngleTolerance, MinSegmentLength, MaxDistSegment)
            fuse_wire_segments (BckWireSegments, WireBck, StartPadRows[Pad], StartPadCols[Pad], EndPadRows[Pad], EndPadCols[Pad], DistanceMin, MaxDistSegment, MinWireDeviation)
            count_obj (WireBck, WiresFound)
            if (WiresFound > 0)
                 * 找到正向轮廓上最接近反向轮廓的点。
                distance_contours_xld (Wire, WireBck, ContourOut, 'point_to_segment')
                query_contour_attribs_xld (ContourOut, Attribs)
                get_contour_attrib_xld (ContourOut, 'distance', Distance)
                MinDistIdx := sort_index(Distance)[0]
     
                  * 在该点处截断正向轮廓
                get_contour_xld (Wire, WireRow, WireCol)
                gen_contour_polygon_xld (WireFwdPart, WireRow[0:MinDistIdx], WireCol[0:MinDistIdx])
  
                * 找到反向轮廓上最接近正向轮廓新终点的点。
                get_contour_xld (WireBck, WireBckRow, WireBckCol)
                distance_pp (WireBckRow, WireBckCol, gen_tuple_const(|WireBckRow|,WireRow[MinDistIdx]), gen_tuple_const(|WireBckCol|,WireCol[MinDistIdx]), DistancePP)
                MinDistBckIdx := sort_index(DistancePP)[0]
          
                 * 在该点处截断反向轮廓。
                gen_contour_polygon_xld (WireBckPart, WireBckRow[MinDistBckIdx + 1:|WireBckRow| - 1], WireBckCol[MinDistBckIdx + 1:|WireBckRow| - 1])
    
                *将两个轮廓线段拼接在一起(注意它们已经通过`fuse_wire_segments`过程一致地定向了)
                
                concat_obj (WireFwdPart, WireBckPart, WireSegments)
            else
                count_obj (FwdWireSegments, NSegments)
                Sequence := [1:NSegments]
                select_obj (FwdWireSegments, WireSegments, Sequence)
            endif
            *  * 合并最终的轮廓。
            
            length_xld (WireSegments, Length)
            union_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)
        else
            smooth_contours_xld (Wire, SmoothedContours1, 11)
            concat_obj (Wires, SmoothedContours1, Wires)
        endif
    endfor
    dev_display (Image)
    dev_display (Wires)
    if (Img < 7)
        disp_continue_message (WindowHandle, 'black', 'true')
        stop ()
    endif
endfor
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值