对应示例程序:
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 1select_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 1select_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 1select_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 1select_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)endifendfor
elsefor k := 1 to NStripes by 1select_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)endifendfor
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 rectangleMesAngle := 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 1RectRow := [RectRow,Index * CenterInterDist[s - 1] + MesRectHeight / 2]RectCol := [RectCol,MesRectCol[s - 1] * Ones]endfor
elseif (Orientation == 'horizontal')  //如果是水平方向的话  一样 都是分配点的坐标StripeHeight := Column2 - Column1  //宽和高StripeWidth := Row2 - Row1CenterInterDist := (StripeHeight - MesRectHeight) / real(NPoints)MesAngle := rad(90)tuple_gen_const (NPoints, 1, Ones)Index := cumul(Ones) - 1RectRow := []RectCol := []for s := 1 to NStripes by 1RectRow := [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 1select_obj (Stripes, ObjectSelected, k + 1)  //提取出每个框架进行单独处理REdgeFirst := []CEdgeFirst := []REdgeSecond := []CEdgeSecond := []* take the measurements at each point//以之前设定的点为中心,生成测量句柄,并进行一维测量//其实就是把框架分成NPoints端,分别进行测量for p := 0 to NPoints - 1 by 1gen_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]endifendfor* 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  画到widthgen_contour_polygon_xld (ExtendedContour, [RowBegin,RowEnd], [0,Width])
elseif (ColBegin == ColEnd) //如果相等  延长线 就是个垂直线* vertical line//从 0 画到 Heightgen_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])endifendif
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

LCD表面单元缺陷检测相关推荐

  1. Halcon 塑料制品表面的缺陷检测

    上结果: 上代码: *初始化 dev_update_off () dev_close_window () read_image (Image, 'plastics/plastics_01') get_ ...

  2. 论文研究 | 基于机器视觉的 PCB 缺陷检测算法研究现状及展望

    前面分享了机器视觉在汽车行业与交通行业的应用,其实机器视觉在工业上的应用是最广泛也是最具挑战性的,其中PCB板缺陷检测一直是机器视觉待攻克的难题.印刷电路板(PCB)是电子零件的基板,需求量极大,承载 ...

  3. hough变换检测圆周_【视觉】视觉检测人应该了解的缺陷检测方法

    本文参考并摘引:李少波, 杨静, 王铮, 朱书德, 杨观赐. 缺陷检测技术的发展与应用研究综述. 自动化学报, 2020, 46(11): 2319−2336. doi: 10.16383/j.aas ...

  4. 傅里叶变换进行缺陷检测detect_indent_fft.hdev(源代码与详细解析)

    文章目录 简介 程序解析 处理结果预览 算法讲解 简介 detect_indent_fft.hdev是halcon的示例程序,是傅里叶变换进行缺陷检测的一个例子,主要是傅里叶变换在复杂背景下的缺陷检测 ...

  5. 基于机器视觉的散热器钎焊缺陷检测系统研发

    人工智能技术与咨询 点击蓝字 · 关注我们 来源:< 图像与信号处理> ,作者 吕广贤 关键词: 机器视觉:缺陷检测:钎焊 摘要: 摘要: 为解决散热器钎焊缺陷在工业检测过程中效率低.差错 ...

  6. 【项目合作】瓷砖表面打印缺陷识别

    点击我爱计算机视觉标星,更快获取CVML新技术 工业生产中的产品缺陷检测是有广泛需求的,技术方案也往往需要高度定制,天池曾经上线过好几个不同的产品缺陷检测(布匹.白酒等). 52CV的一位粉丝身处传统 ...

  7. Halcon缺陷检测系列

    Halcon缺陷检测系列Halcon缺陷检测系列Halcon缺陷检测系列 一 注塑吸嘴缺口检测 二 铣刀刀口破损缺陷检测 三 网状产品表面破损检测 四 手机摄像头图像表面的轻微缺陷检测 五 皮革纹理表 ...

  8. 趋高智能注塑件表面视觉检测之机器视觉的缺陷检测方案

    趋高智能注塑件表面视觉检测之机器视觉的缺陷检测方案. 趋高智能专注机器视觉软件硬件开发有10年以上的经验. 机器视觉是人工智能正在快速发展的一个分支.简单说来,机器视觉就是用机器代替人眼来做测量和判断 ...

  9. 飞瞳单元工业级成熟AI集装箱识别集装箱箱号识别集装箱箱况残损检测集装箱缺陷检测解决方案,集装箱枢纽智能化解决方案智慧港航智能港航

    全球港航人工智能独角兽CIMCAI中集飞瞳,工业级成熟港航人工智能AI产品行业第一,飞瞳单元AI照片视频识别检测终端已规模化量产,飞瞳引擎AI集装箱检测云服务全球2000企业用户.中国上海人工智能独角 ...

最新文章

  1. linux命令dd创建虚拟硬盘,每日一题.PYTHON如何模拟LINUX的dd命令快速创建大文件?...
  2. 根据Android架构分层推荐开发书籍
  3. Android自定义控制(五)仿新浪微博的下拉刷新
  4. ce5e.cn fadian.php,空包网 PHP mysql
  5. 【转】 ConstraintLayout 完全解析 快来优化你的布局吧
  6. 64位linux并行计算大气模型效率优化研究,64位Linux并行计算大气模型效率优化研究...
  7. Springboot全局异常统一处理返回json
  8. Android倒计时案例展示
  9. 音视频开发(4)---Windows下Red5安装与使用入门
  10. TURBOMAIL邮件服务器功能—邮件归档
  11. DH参数标定原理推导
  12. Air202学习 一 (程序下载流程----GPIO简单控制)
  13. 爬虫实战 ——百度翻译
  14. 泛微oa 明细数据合计
  15. 写代码实现功能并不重要,重要的是一种独到的思维和架构
  16. 使用scrapy爬取前程无忧51job网站
  17. java pfx 和cer_pfx证书和CER证书
  18. DSSD(Deconvolutional Single Shot Detector)
  19. 9999*9999这样的命令在python中无法运行_智慧树形势与政策2018章节答案
  20. download and build swe

热门文章

  1. PyCharm简单使用教程
  2. Github相册博客搭建
  3. Windows服务器密码策略、账户锁定策略等安全设置
  4. 什么是Java?java是用来做什么的?
  5. nn.Sequential nn.ModuleList
  6. 图解HTTP读书笔记(http状态码)
  7. offset().top与offsetTop的区别
  8. 从VUCA到项目经济时代,项目经理能力发展新需求
  9. 美国联邦政府停摆刷新历史最长纪录
  10. python编译举例_PythonOCC开发-如何搭建开发环境和一个创建圆台例子