LCD表面单元缺陷检测

对应示例程序:
measure_lcd_cells.hdev

目标:测量LCD框架的宽度 以及缺陷

思路为:
      1.读取图像
      2.将图像进行R,G,B三通道图像分离,单独对R通道的图像进行处理。
      3.利用Blob分析,均值滤波,阈值分割等手段,提取到垂直方向上的LCD框架和水平方向上的LCD框架区域。
      4.根据上述区域的中心坐标,结合由它们生成的最小外接矩形得到的长和宽,生成N个和中心点在一列或一行的测量中心点,再以这个点做一维测量句柄,用于测量每个小段的框架边缘。将得到的边缘点进行连接,就是测量出的LCD框架边缘。但是误差比较大,因此将他们再通过拟合的方式,进行显示。取两条直线的距离作为框架的宽度。
      5利用Blob分析,阈值分割的方式,测量出整个LCD区域的全部缺陷。
      6.将之前提取得到的水平框架区域和垂直框架区域求并集,合成一个区域后,再与原区域R进行求差,就得到一个个单独分离开的LCD小单元。
      7.将每个LCD小单元,与全部缺陷,求交集,就得到了包含缺陷的一个个小单元,再统计单元中的缺陷数目,就实现了 检测目标。

图像:
                                                                    原图
在这里插入图片描述
                                               由N个测量中心点检测出的LCD框架边缘在这里插入图片描述
                                                               拟合出的直线在这里插入图片描述
                                                        直线间的距离作为框架的宽度在这里插入图片描述
                                                               小单元里面的缺陷
在这里插入图片描述

代码:

//设置图像路径 显示窗口的相关参数
dev_close_window ()
dev_update_off ()
Path := ‘lcd/lcd_cells_’ //读取图像路径
read_image (Image, Path + ‘01’)
get_image_size (Image, Width, Height)
dev_open_window_fit_size (0, 0, Width, Height, 640, 480, WindowHandle)
set_display_font (WindowHandle, 14, ‘mono’, ‘true’, ‘false’)
dev_set_color (‘green’)
dev_set_colored (12)
dev_set_draw (‘margin’)
dev_set_line_width (3)
for f := 1 to 8 by 1
read_image (Image, Path + f$’.2i’) //8幅图像

dev_display (Image)
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
decompose3 (Image, R, G, B)  //R,G,B三通道图像分离
//自定义函数
segment_stripes (R, VerticalStripes, 'vertical', 20)  //找图像中的垂直方向的框架边缘(利用Blob分析)
segment_stripes (R, HorizontalStripes, 'horizontal', 20)//找图像中的垂直方向的框架边缘
//测量框架的宽度
measure_stripes_width (R, VerticalStripes, VertContours, VertFittedLines, 50, 40, 'vertical', VertWidth)
measure_stripes_width (R, HorizontalStripes, HorzContours, HorzFittedLines, 50, 40, 'horizontal', HorzWidths)
* 
* Visualization of resulting measurements.
//可视化测量结果  包括选择的每个测量点的位置 以及根据这些点提取出的边缘直线段  还有拟合出的直线
concat_obj (HorzContours, VertContours, Contours)
count_obj (Contours, NContours)
for c := 1 to NContours by 1
    select_obj (Contours, ObjectSelected, c)
    get_contour_xld (ObjectSelected, Row, Col)
    gen_cross_contour_xld (Cross, Row, Col, 12, rad(0))
    dev_set_color ('green')
    dev_display (Cross)
    dev_set_color ('blue')
    dev_display (ObjectSelected)
endfor
disp_message (WindowHandle, 'Initial measurements', 'window', -1, -1, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
//显示拟合出的直线
concat_obj (HorzFittedLines, VertFittedLines, FittedLines)
dev_set_color ('yellow')
dev_display (FittedLines)  //拟合线段

disp_message (WindowHandle, 'Fitted lines using a robust statistics', 'window', -1, -1, 'black', 'true')
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
* 
//显示直线间的距离 或者说是框架间的宽度
dev_display (Image)
dev_set_color ('yellow')
dev_display (FittedLines)
disp_message (WindowHandle, 'Measured stripe widths in pixels', 'window', -1, -1, 'black', 'true')
concat_obj (VerticalStripes, HorizontalStripes, Stripes) //连接垂直方向和水平方向的框架区域
Measurements := [VertWidth,HorzWidths]  //存放计算出的两条拟合直线的距离
//获取字符串的空间大小
get_string_extents (WindowHandle, 'A', Ascent, Descent, TxtWidth, TxtHeight)
count_obj (Stripes, NStripes)
for i := 1 to NStripes by 1
    select_obj (Stripes, ObjectSelected, i)
    smallest_rectangle2 (ObjectSelected, StripeRow, StripeColumn, Phi, Length1, Length2) //最小外接矩形
    dev_set_color ('red')
    gen_rectangle2 (Rectangle, StripeRow, StripeColumn, Phi, Length1, Length2)
    disp_message (WindowHandle, Measurements[i - 1]$'3.1f', 'image', StripeRow - TxtHeight / 2, StripeColumn, 'green', 'false')
endfor
disp_continue_message (WindowHandle, 'black', 'true')
stop ()
* 
* Count the amount of defects per cell.
* First segment cells by using the crossing stripes.
*计算每个单元的缺陷数量。
*使用交叉条纹分割第一个单元格
detect_defects (R, Defects)  //检测黑点 其实就是Blob分析 阈值分割,把符合面积的点筛选出来

union2 (HorizontalStripes, VerticalStripes, RegionUnion)  //取水平和垂直方向框架区域的并集
union1 (RegionUnion, RegionUnion1)  //再将并集合并成一个区域

difference (R, RegionUnion1, RegionDifference)  //用原图区域 减去 并集  剩下的就是一个一个框架围城的小格子

opening_rectangle1 (RegionDifference, RegionOpening, 3, 3)  //开操作
connection (RegionOpening, Cells)  //连通域分割 分成一个一个的格子
dev_display (Image)
dev_set_color ('green')
dev_display (Cells)

* 
* Find the defects contained in each lcd cell.
//找出每个液晶单元中包含的缺陷
union1 (Defects, UnionDefects)  //合并所有的Defects 为一个区域
dev_set_color ('red')
dev_display (Defects)
count_obj (Cells, NCells)  //统计分成的格子的数目
//获取字体中所有字符的最大大小  用来做显示处理
get_font_extents (WindowHandle, MaxAscent, MaxDescent, MaxWidth, MaxHeight)
TxtWith := strlen(10 + ' defects') * MaxWidth
for c := 1 to NCells by 1
    select_obj (Cells, ObjectSelected, c) //选一个格子 与 之前检测的缺陷点区域进行交集计算 得到含有缺陷点的格子
    intersection (ObjectSelected, UnionDefects, RegionIntersection)
    
    connection (RegionIntersection, DefectsInCell)  //把包含的缺陷点根据连通域分开
    
    count_obj (DefectsInCell, NDefectsInCell)  //统计缺陷点的数目
    
    if (NDefectsInCell != 0)  //缺陷点数目 不等于0  就显示出来
        area_center (ObjectSelected, Area, CellRow, CellColumn)
        disp_message (WindowHandle, NDefectsInCell + ' defects', 'image', CellRow, CellColumn - TxtWidth / 2, 'red', 'true')
    endif
endfor
if (f < 8)
    disp_continue_message (WindowHandle, 'black', 'true')
endif
stop ()
endfor
 segment_stripes (R, VerticalStripes, 'vertical', 20) 中的函数内部:
 * StripeWidth is an approximate value for the width of the frame to measure
//StripeWidth是要测量的框架宽度的近似值
get_image_size (Image, Width, Height)  //获取图像的大小
if (Orientation == 'vertical')   //竖直图像
    
    mean_image (Image, ImageMean, 0.5, Height / 3)  //均值滤波
    mean_image (ImageMean, ImageMean1, StripeWidth + 1, StripeWidth + 1)  
    dyn_threshold (ImageMean, ImageMean1, RegionDynThresh, 1, 'light')   //阈值分割
    connection (RegionDynThresh, ConnectedRegions)  //连通域分割
    //区域筛选
    select_shape (ConnectedRegions, SelectedRegions, ['height','width'], 'and', [Height * 0.9,StripeWidth], [99999,99999])
else  
    mean_image (Image, ImageMean, Width / 3, 0.5)
    mean_image (ImageMean, ImageMean1, StripeWidth + 1, StripeWidth + 1)
    dyn_threshold (ImageMean, ImageMean1, RegionDynThresh, 1, 'light')
    connection (RegionDynThresh, ConnectedRegions)
    select_shape (ConnectedRegions, SelectedRegions, ['width','height'], 'and', [Width * 0.9,StripeWidth], [99999,99999])
endif
fill_up (SelectedRegions, RegionFillUp)  //填充孔洞
union1 (RegionFillUp, RegionUnion)  //合并区域

closing_rectangle1 (RegionUnion, RegionClosing, StripeWidth / 2, StripeWidth / 2)  //矩形闭操作
connection (RegionClosing, Candidates)  //连通域分割

* discard stripes lying at the border of the image, for its width cannot be measured
*丢弃位于图像边缘的条纹,因为其宽度无法测量
count_obj (Candidates, NStripes)
gen_empty_obj (Stripes)
if (Orientation == 'vertical')
    for k := 1 to NStripes by 1
        select_obj (Candidates, ObjectSelected, k)   //单个区域进行处理
        get_region_runs (ObjectSelected, Row, ColumnBegin, ColumnEnd)  //访问区域的运行长度编码
        
        if ((min(ColumnBegin) != 0) and (max(ColumnEnd) != Width - 1))
            shape_trans (ObjectSelected, RegionTrans, 'convex')  //凸包
            
            concat_obj (Stripes, RegionTrans, Stripes)
        endif
    endfor
else
    for k := 1 to NStripes by 1
        select_obj (Candidates, ObjectSelected, k)
        get_region_runs (ObjectSelected, Row, ColumnBegin, ColumnEnd)
        if ((max(Row) != Height - 1) and (min(Row) != 0))
            shape_trans (ObjectSelected, RegionTrans, 'convex')
            concat_obj (Stripes, RegionTrans, Stripes)
        endif
    endfor
endif
return ()

 measure_stripes_width (R, VerticalStripes, VertContours, VertFittedLines, 50, 40, 'vertical', VertWidth)中的函数内部:
*MesRectHeight是测量矩形的高度。

*它的值应该足够高,以减少噪声的影响,
*并精确地确定边缘的位置,尽管对比度很低,但是足够小,以便黑点影响的测量数量最少
Widths := []
get_image_size (Image, Width, Height)  //获取图像的大小
smallest_rectangle1 (Stripes, Row1, Column1, Row2, Column2)  //每个框架边的最小外接矩形
gen_rectangle1 (Rectangle, Row1, Column1, Row2, Column2)
count_obj (Stripes, NStripes)  //框架的数目
tuple_gen_const (NStripes, 1, Ones)  //生成特定长度的元组并初始化其元素
area_center (Stripes, Area, MesRectRow, MesRectCol)  //框架区域的中心点
* 
* calculate center position and orientation of the measure rectangles
*计算测量矩形的中心位置和方向
if (Orientation == 'vertical')   //垂直方向的框架
    
    StripeWidth := Column2 - Column1   //框架的宽等于最小外接矩形的宽(也就是两个端点列坐标的差)
    StripeHeight := Row2 - Row1        //框架的高等于最小外接矩形的长(也就是两个端点行坐标的差)
    
    * distance between the centers of consecutive measure rectangles
    *连续测量矩形中心距  就是在边缘上取NPoints时,每个点的距离
    CenterInterDist := (StripeHeight - MesRectHeight) / real(NPoints)
    * orientation of measure rectangle
    MesAngle := rad(0)
    tuple_gen_const (NPoints, 1, Ones)
    Index := cumul(Ones) - 1  //计算元组的累积和
    //Index:=[0,1,2,3,4,5,6,7,8,9......]
    * center coordinates of each measure rectangle
    //每个测量矩形的中心坐标  这里就是在框架上按照之前计算的每个点的距离 分配row 和col
    //MesRectHeight可以理解为在框架上留这么长一段不分配点,其中上半部分留一半长,下半部分留一半长
    RectRow := []
    RectCol := []
    for s := 1 to NStripes by 1
        RectRow := [RectRow,Index * CenterInterDist[s - 1] + MesRectHeight / 2]
        RectCol := [RectCol,MesRectCol[s - 1] * Ones]
    endfor
elseif (Orientation == 'horizontal')  //如果是水平方向的话  一样 都是分配点的坐标
    StripeHeight := Column2 - Column1  //宽和高
    StripeWidth := Row2 - Row1
    CenterInterDist := (StripeHeight - MesRectHeight) / real(NPoints)
    MesAngle := rad(90)
    tuple_gen_const (NPoints, 1, Ones)
    Index := cumul(Ones) - 1
    RectRow := []
    RectCol := []
    for s := 1 to NStripes by 1
        RectRow := [RectRow,MesRectRow[s - 1] * Ones]
        RectCol := [RectCol,Index * CenterInterDist[s - 1] + MesRectHeight / 2]
    endfor
endif
* 
gen_empty_obj (Contours)   //生成空边缘
gen_empty_obj (FittedLines)   //生成空的拟合线段

for k := 0 to NStripes - 1 by 1
    select_obj (Stripes, ObjectSelected, k + 1)  //提取出每个框架进行单独处理
    REdgeFirst := []
    CEdgeFirst := []
    REdgeSecond := []
    CEdgeSecond := []
    * take the measurements at each point
    //以之前设定的点为中心,生成测量句柄,并进行一维测量
    //其实就是把框架分成NPoints端,分别进行测量
    for p := 0 to NPoints - 1 by 1
        gen_measure_rectangle2 (RectRow[k * NPoints + p], RectCol[k * NPoints + p], MesAngle, StripeWidth[k] * 1.5, MesRectHeight / 2, Width, Height, 'bilinear', MeasureHandle)
        measure_pairs (Image, MeasureHandle, 3.0, 4, 'all_strongest', 'all', RowEdgeFirst, ColumnEdgeFirst, AmplitudeFirst, RowEdgeSecond, ColumnEdgeSecond, AmplitudeSecond, IntraDistance, InterDistance)
        close_measure (MeasureHandle)
        if (|RowEdgeFirst| != 0)
            REdgeFirst := [REdgeFirst,RowEdgeFirst]
            CEdgeFirst := [CEdgeFirst,ColumnEdgeFirst]
            REdgeSecond := [REdgeSecond,RowEdgeSecond]
            CEdgeSecond := [CEdgeSecond,ColumnEdgeSecond]
        endif
    endfor
    * for each edge, create an xld-contour whose points are given by the resulting
    * measurements and fit a line to each of them via a robust regression algorithm
    *对于每条边,创建一个xld轮廓,其点由结果测量给出,并通过稳健的回归算法将一条线拟合到每条线上
    
    if (|REdgeFirst| * |CEdgeFirst| * |REdgeSecond| * |CEdgeSecond| > 1)
        gen_contour_polygon_xld (Contour, REdgeFirst, CEdgeFirst)  //将之前分成的每个段的First边缘连起来
        //直线拟合
        fit_line_contour_xld (Contour, 'tukey', -1, 0, 5, 1, RowBegin, ColBegin, RowEnd, ColEnd, Nr, Nc, Dist)
        gen_contour_polygon_xld (StraightLine, [RowBegin,RowEnd], [ColBegin,ColEnd]) //把这条拟合的First直线连起来
        gen_contour_polygon_xld (Contour1, REdgeSecond, CEdgeSecond) //再把Second边缘连起来
        fit_line_contour_xld (Contour1, 'tukey', -1, 0, 5, 1, RowBegin1, ColBegin1, RowEnd1, ColEnd1, Nr1, Nc1, Dist1)
        gen_contour_polygon_xld (StraightLine1, [RowBegin1,RowEnd1], [ColBegin1,ColEnd1]) //把拟合的直线连起来
        * the width of the frame is given by the distance between the two fitted lines
        //框架的宽度由两条拟合线之间的距离给出
        distance_cc_min (StraightLine, StraightLine1, 'point_to_segment', DistanceMin) //计算两条线之间的最小距离(可用)
        Widths := [Widths,DistanceMin]
        * 
        //这里是根据之前生成的直线段 进行延长
        //如果Row相等 那延长线 就是从0到width的水平线
        //如果Col相等 那延长线 就是从0到Col的垂直线
        //如果都不相等 那就计算与窗口的交点,交点之间的连线 就是需要的延长线
        gen_image_extent_line (ExtendedContour, Width, Height, RowBegin, RowEnd, ColBegin, ColEnd)
        gen_image_extent_line (ExtendedContour1, Width, Height, RowBegin1, RowEnd1, ColBegin1, ColEnd1)
        concat_obj (Contours, Contour, Contours)  //测量出的轮廓
        concat_obj (Contours, Contour1, Contours)
        concat_obj (FittedLines, ExtendedContour, FittedLines)
        concat_obj (FittedLines, ExtendedContour1, FittedLines)
    endif
endfor
return ()
gen_image_extent_line (ExtendedContour, Width, Height, RowBegin, RowEnd, ColBegin, ColEnd)中的函数内部:
if (RowBegin == RowEnd)   //如果相等  延长线 就是个水平线
    * horizontal line  水平线
    //从0  画到width
    gen_contour_polygon_xld (ExtendedContour, [RowBegin,RowEnd], [0,Width])
else
    if (ColBegin == ColEnd) //如果相等  延长线 就是个垂直线
        * vertical line
        //从 0 画到 Height
        gen_contour_polygon_xld (ExtendedContour, [0,Height], [ColBegin,ColEnd])
    else  //如果都不满足
        Slope := abs((RowEnd - RowBegin) / real(ColEnd - ColBegin))  //斜率
        
        * determine with which image border does the line intersect
        //确定线条与哪个图像边框相交
        RefSlope := real(Height) / Width    //参考斜率
        if (Slope > RefSlope)
            * intersects with the upper and lower borders  与上下边界相交
            //计算之前拟合出的直线与上边界的交点
            intersection_lines (0, 0, 0, Width, RowBegin, ColBegin, RowEnd, ColEnd, Row, Column, IsOverlapping)
            //计算之前拟合出的直线与下边界的交点
            intersection_lines (Height, 0, Height, Width, RowBegin, ColBegin, RowEnd, ColEnd, Row1, Column1, IsOverlapping)
           //生成延长线
            gen_contour_polygon_xld (ExtendedContour, [Row,Row1], [Column,Column1])
        else
            * intersects with the left and right borders  与左右边界相交
            //计算之前拟合出的直线与左边界的交点
            intersection_lines (0, 0, Height, 0, RowBegin, ColBegin, RowEnd, ColEnd, Row, Column, IsOverlapping)
           //计算之前拟合出的直线与右边界的交点
            intersection_lines (0, Width, Height, Width, RowBegin, ColBegin, RowEnd, ColEnd, Row1, Column1, IsOverlapping)
            //生成延长线
            gen_contour_polygon_xld (ExtendedContour, [Row,Row1], [Column,Column1])
        endif
    endif
endif
return ()

用到的几个算子:
    gen_measure_rectangle2 – 一维矩形测量句柄
    measure_pos --检测区域内垂直于长轴 的边缘 。返回的是边缘的中点坐标
    measure_pairs–加强版的measure_pos ,找出的都是边缘对(两个边形成一对边缘),边缘给出的是边缘对间距值。
    distance_cc_min --计算两条线之间的最小距离。
    intersection_lines–计算两条直线的交点。
   concat_obj–将两个对象连接起来,放进一个新的元组中。其实就是从两个元组,合并到一个新的元组,元组间不存在运算。
   union2 --计算两个元组对象的并集,将结果放在一个新元组中。
   union1–合并元组中所有的对象,形成一个唯一的对象元素。

参考资料:
[1]. https://blog.csdn.net/weixin_43491924/article/details/96100164
[2]. https://www.gkbc8.com/thread-13630-1-1.html

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值