Halcon条码定位算法(一维码)

  Halcon自带条码定位的算子create_bar_code_model和find_bar_code ,但有时检测不到条码,故写下一种定位一维码的方法。
  条码识别的应用大多数在物流行业中,即需要定位的条码一般在快递包裹中,而快递标签处有很多的文字,符号和边框,增大了定位的难度。由于有很多噪声的影响,首先排除了用Blob分析法。观察一维码的特征,排列规则的黑粗线条,相较于文字和边框,一维码突出的特征是线条多且排列规则。
Halcon条码定位算法(一维码)_第1张图片

  于是采用边缘提取的方式定位一维码。但是,提取边缘后受到一些无关的线条的影响,该如何去除与条码相同特征的噪声呢。如下图是边缘提取之后的区域:
Halcon条码定位算法(一维码)_第2张图片
  第一种采用拟合直线的方式,条码区域的线条都是直线,如果噪声有弯曲则拟合不了。如下图是拟合直线后的区域,此时仍然受到噪声的影响:
Halcon条码定位算法(一维码)_第3张图片

  第二种采用区域膨胀的方法,往垂直于条码线条的方向膨胀,即离条码区域较远的线条将被排除。但是又有一个问题,如何让区域往一个方向上膨胀呢?可以采用自定义结构元素的方式膨胀,用closing_rectangle1(Region : RegionClosing : Width, Height : )定义结构元素的高度和宽度。很遗憾,Halcon不能定义任意形状的结构元素,只能定义矩形的结构元素。为了解决这个问题,只能将边缘提取的线条仿射变换到垂直方向,再定义一个长度20,宽度2的结构元素往水平方向上膨胀,至此定位成功。只要图像上有一维码,不管几个都能识别到。
Halcon条码定位算法(一维码)_第4张图片
Halcon条码定位算法(一维码)_第5张图片
  为了确保提取到的区域是条码,我们还需要做一些验证,避免出错。判断是否条码区域:
①基于形状,判断区域宽高比例,如果区域太扁或者近似正方形,则该区域不是条码区域。
②基于颜色,条码区域,黑白相间,黑色区域的面积和白色区域的面积差不多大小。
③基于纹理,中间画测量线求正负跳变次数,少于阈值说明不是条码区域。

代码如下:

* Image Acquisition 01: Code generated by Image Acquisition 01
read_image (Image, 'C:/Users/liu/Desktop/1.png')
get_image_size (Image, Width, Height)
dev_open_window (0, 0, Width/2, Height/2, 'black', WindowHandle)
dev_display (Image)
rgb1_to_gray (Image, GrayImage)

*第一种边缘提取初步提取轮廓
laplace_of_gauss(GrayImage, ImageLaplace, 2)//拉布拉斯算子
threshold(ImageLaplace, Region3, 5, 127)
skeleton(Region3, Skeleton3)
connection (Skeleton3, ConnectedRegions2)
select_shape (ConnectedRegions2, SelectedRegions, ['area','width','height'], 'and', [0,0,0], [220,80,200])

*第二种Blob分析法初步提取条码
*(Blob分析法可能对相机像素有较大的要求,否则二值化后可能提取不出条码线,个人觉得用边缘提取的方式较稳定)
threshold (GrayImage, Regions, 7, 107)
connection (Regions, ConnectedRegions)
*这里用第一种边缘提取的方法
*通过面积和矩形度筛选
select_shape (ConnectedRegions, SelectedRegions1, 'area', 'and', 60, 1500)
select_shape_std (SelectedRegions1, SelectedRegions, 'rectangle2', 60)

*提取轮廓
union1 (SelectedRegions, RegionUnion)
skeleton (RegionUnion, Skeleton4)
gen_contours_skeleton_xld (Skeleton4, Contours1, 1, 'filter')
*通过轮廓长度过滤小噪声
select_contours_xld (Contours1, SelectedContours, 'contour_length', 30, 200, -0.5, 0.5)
*分割条码
*因为条码都是直线,可以通过角度相同的直线分割条码
*拟合直线
fit_line_contour_xld (SelectedContours, 'tukey', -1, 0, 5, 2, RowBegin, ColBegin, RowEnd, ColEnd, Nr, Nc, Dist1)
*画拟合线
gen_region_line (RegionLines1, RowBegin, ColBegin, RowEnd, ColEnd)
*分两种情况,分别是正角度和负角度
*0~180°里每30°查找一次条码
for i:=-1 to 150 by 30
    *筛选角度差30°的拟合线
    select_shape (RegionLines1, SelectedRegions2, 'phi', 'and', rad(i), rad(i+30))
    *计算拟合线的个数
    count_obj (SelectedRegions2, Number)
    *少于12条说明不是条码
    if(Number>=12)
        *将所有拟合线联合为一个区域
        union1 (SelectedRegions2, RegionUnion1)
        *求坐标和角度
        area_center (RegionUnion1, Area1, Row, Column)
        orientation_region (RegionUnion1, Phi)
        *如果角度不在-90°~90°之间,说明求角度的凸性反向错了,因为我们都要转至0°的反向
        if(Phi>rad(90))
            Phi:=Phi-rad(180)
        endif
        if(Phi<-rad(90))
            Phi:=rad(180)+Phi
        endif
        *旋转句柄
        vector_angle_to_rigid (Row, Column, Phi, Row, Column,rad(0), HomMat2D)
        *旋转区域和图像,即将条码转至水平位置
        affine_trans_region (RegionUnion1, RegionAffineTrans1, HomMat2D, 'nearest_neighbor')
        affine_trans_image (GrayImage, ImageAffineTrans1, HomMat2D, 'constant', 'false')
        *宽度80高度2的结构元素用于膨胀水平方向,连接条码为一个区域
        closing_rectangle1 (RegionAffineTrans1, RegionClosing, 80, 2)
        *有些线段与条码的角度相同,此时通过面积过滤
        connection (RegionClosing, ConnectedRegions1)
        select_shape (ConnectedRegions1, SelectedRegions4, 'area', 'and', 2954.26, 50000)
        shape_trans (SelectedRegions4, RegionTrans, 'rectangle2')
        *为了保证提取的区域是条码区域,可以通过一些条件进一步判断是否条码区域 
        *第一种判断,通过宽高比例
        *求得区域的宽度和高度
        region_features (RegionTrans, 'width', w)
        region_features (RegionTrans, 'height', h)
        *如果宽高比例超过范围,说明选中的区域不是条码位置
        if(w/h<2.5 or w/h>10)
            continue
        endif
        
        *第二种判断,通过颜色特征(条码区域,黑白相间,面积应该差不多)
        reduce_domain (ImageAffineTrans1, RegionTrans, ImageReduced1)
        *白色区域
        threshold (ImageReduced1, Regions1, 160, 254)
        area_center (Regions1, Area, Row1, Column1)
        *黑色区域
        threshold (ImageReduced1, Regions2, 0, 120)
        area_center (Regions2, Area2, Row2, Column2)
        *这里的除法没有小数点,直接等于0,所以先判断大小
        if(Area>Area2)
            if(Area/Area2>2)
                continue
            endif
        endif
        if(Area2>Area)
            if(Area2/Area>2)
                continue
            endif
        endif
          
        *膨胀,防止条码区域太小
        dilation_circle (RegionTrans, RegionDilation, 6)
        *抠图
        reduce_domain (ImageAffineTrans1, RegionDilation, ImageReduced)
    endif
endfor
stop()
*0~-180°里每-30°查找一次条码
for i:=-1 to -150 by -30
    *步骤同上一样
    select_shape (RegionLines1, SelectedRegions3, 'phi', 'and', rad(i-30),  rad(i))
    count_obj (SelectedRegions3, Number)
    if(Number>12)
        union1 (SelectedRegions3, RegionUnion1)
        area_center (RegionUnion1, Area1, Row, Column)
        orientation_region (RegionUnion1, Phi)
        if(Phi>rad(90))
            Phi:=Phi-rad(180)
        endif
        if(Phi<-rad(90))
            Phi:=rad(180)+Phi
        endif
        vector_angle_to_rigid (Row, Column, Phi, Row, Column,rad(0), HomMat2D)
        affine_trans_region (RegionUnion1, RegionAffineTrans1, HomMat2D, 'nearest_neighbor')
        affine_trans_image (GrayImage, ImageAffineTrans, HomMat2D, 'constant', 'false')   
        closing_rectangle1 (RegionAffineTrans1, RegionClosing, 80, 2)
        connection (RegionClosing, ConnectedRegions1)
        select_shape (ConnectedRegions1, SelectedRegions4, 'area', 'and', 2954.26, 50000)
        shape_trans (SelectedRegions4, RegionTrans, 'rectangle2')
        region_features (RegionTrans, 'width', w)
        region_features (RegionTrans, 'height', h)
        if(w/h<2.5 or w/h>10)
            continue
        endif
        reduce_domain (ImageAffineTrans, RegionTrans, ImageReduced1)
        threshold (ImageReduced1, Regions1, 160, 254)
        area_center (Regions1, Area, Row1, Column1)
        threshold (ImageReduced1, Regions2, 0, 120)
        area_center (Regions2, Area2, Row2, Column2)
        if(Area>Area2)
            if(Area/Area2>2)
                continue
            endif
        endif
        if(Area2>Area)
            if(Area2/Area>2)
                continue
            endif
        endif
        dilation_circle (RegionTrans, RegionDilation, 6)
        reduce_domain (ImageAffineTrans, RegionDilation, ImageReduced)
    endif
endfor

测试图片链接:https://pan.baidu.com/s/1FchyE2KhBspp9ugBuy63EQ
提取码:8gbn

原创不易,点个关注,点个赞‎|•’-’•)و✧,后续我会继续更新机器视觉与运动控制的文章。

#版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mao_hui_fei/article/details/104403483

你可能感兴趣的:(Halcon,图像识别,定位)