(一)掰开了,揉碎了,说经典halcon中的那些算子


最重要的写在前面: 

文末:我曾尝试用OpenCV中算子来还原Halcon中的算子,但是受时间和经验的限制,只能进行到如下:如果后续有必须应用OpenCV的场景,再结合起来搞。

但是最重要的经验是:

1、Halcon提供了一整套解决问题的方案,而不像OpenCV那样从头造轮子。OpenCV除非经验和使用很熟,否则很难找到思路。而Halcon提供了大量的案例。

2、在Halcon中,提供了大量的方案,基于方案提供的思路用OpenCV复现的意义不大了。

3、如果非要用OpenCV,那么在Halcon中有F1帮助文档:可以查询算子的用法。除了对算法的用法解释外,还有【Possible Predecessors】和【Possible Successors】,这两者也提示了我们这个算子的前后应用操作。而且,通过算子最下面的【HDevelop例程】可以更好的找到实际的应用场景,以此来找到解决我们的问题的方案。

所以:专心学Halcon吧。

当你做一件事的时候,你就专心做一件事,把它学精。然后才有拓展的资本。发散、迁移、联想必须建立在一定专注力和理解力上,学不深,发散也不准确。画虎不像反类犬。

千万不要让OpenCV的思想,影响Halcon的学习。底层的思维可以相同,但不能依靠。灵活应变。

HALCON中的算子大全(中英对照)


视觉由四门学科构成:

  1. 图像处理:
  2. 数学:
  3. 光学:《工程光学》
  4. 软件(语言)工具、软件架构 

数学:矩阵变换、导数。

光学:几何光学(光路,相差、)、物理光学(衍射、干涉)。


图像有十大学科分支:

  1. 图像基本理论
  2. 图像灰度变换
  3. 图像增强
  4. 图像几何变换
  5. 形态学
  6. 图像分割
  7. 图像复原
  8. 图像频域
  9. 运动图像
  10. 图像配准

图像处理的进阶:模式识别、行为识别:真正接近人工智能的东西。:(人脸识别、车牌识别、指纹识别、虹膜识别)


安防视觉、无人机视觉、工业视觉。


二维客户需求:

  1. 识别定位
  2. 符号识别
  3. 测量需求
  4. 缺陷检测(六大方法)(难度最大)
  5. 手眼标定和抓取:(人手和脑的关系。)手脑运动三种通信方式:串口、网口、运动板卡。

识别定位:

基于条件:打光条件的稳定。否则视觉检测效果不好。

  1. blob分析+仿射变换(几何变换):blob分析:连通域分析。
    Blob分析就是对前景、背景分离(阈值筛选二值化)后的 二值图像,进行连通域提取标记。标记完成的每一个Blob都代表一个前景目标(halcon默认标红,颜色可以换),然后就可以计算连通区域Blob的一些相关特征,如:面积、质心、外接矩形等几何特征,还可以计算Blob的颜色、纹理特征,这些特征都可以作为跟踪的依据。ROI提取。
  2. 模板匹配+仿射变换

相机的采集方式: 

  • 同步采集:
  • 异步采集:

图像处理的常规套路:

第一步:采集图像

第二步:预处理:灰色对比度拉开。便于提取Blob。几何变换(旋转)

              去噪声,中值滤波、高斯滤波(距离加权)、均值滤波。孤立点去除。

               抠图,ROI提取。去掉边缘干扰。

第三步:图像分割:二值化、形态学、特征选择

第四步:识别显示、通信


形态学变换:膨胀腐蚀:

膨胀:轮廓变大

腐蚀:轮廓变小

开运算Opening:侵蚀然后膨胀。先侵蚀掉前景目标外围周边的小噪声,然后再膨胀恢复。完成前景之外的去噪。

闭运算Closing:膨胀然后侵蚀。关闭前景对象内的小孔或对象上的小黑点。区域内去噪。保证区域内的纯度。

一个是去掉前景目标中外围周边的噪点。一个是去掉目标前景内部的噪点。


第一步:图像采集:

打开Halcon中菜单栏【助手】中第一个【打开新的Image Acquisition】,在【资源】栏中【图像获取接口】中点击【自动检测接口】,然后搜索到你需要的设备。点击【连接】栏中,选择颜色空间:BGR还是Gray,代表了采集灰色还是彩色。

点击【连接】【实时】从【图形窗口】中检查是否看到了图像。

【参数】中,有相机采集参数设置:

grab_timeout:抓取延时。

shutter:快门,曝光时间。

contrast:对比度

hue:色调:是整个图像的色彩倾向:可见光光谱中能量最强时的波长。

saturation:饱和度:越高色彩越鲜艳。可见光的相对带宽。

brightness:可见光的能量强度。

sharpness:锐化,清晰度。

gamma:灰度值。参数微调。

(一)掰开了,揉碎了,说经典halcon中的那些算子_第1张图片

点击【代码生成】中【插入代码】在【程序窗口】中看到生成的代码。在【采集】中:有个【控制流】和【采集模式】:控制流:有在循环中采集、仅初始化、采集单幅图像。采集模式:同步和异步。

以下是异步:先开始采集,然后同时Do something。

* Image Acquisition 01: Code generated by Image Acquisition 01
* Image Acquisition 01: Attention: The initialization may fail in case parameters need to
* Image Acquisition 01: be set in a specific order (e.g., image resolution vs. offset).
open_framegrabber ('DirectShow', 1, 1, 0, 0, 0, 0, 'default', 8, 'rgb', -1, 'false', 'default', '[0] Integrated Camera', 0, -1, AcqHandle)
set_framegrabber_param (AcqHandle, 'grab_timeout', 5000)
set_framegrabber_param (AcqHandle, 'sharpness', 2)
grab_image_start (AcqHandle, -1)
while (true)
    grab_image_async (Image, AcqHandle, -1)
    * Image Acquisition 01: Do something
endwhile
close_framegrabber (AcqHandle)

认识算子函数:按【F1】即可打开Halcon的【HDevelop帮助窗口】

认识第一个算子函数:open_framegrabber:Open and configure an image acquisition device.打开图像采集终端。

set_framegrabber_param:设置终端参数

grab_image获取图像。

下面是同步采集代码:采集和Do something一起干。

close_framegrabber (AcqHandle):关闭终端。

下面是同步所产生的代码:

* Image Acquisition 01: Code generated by Image Acquisition 01
* Image Acquisition 01: be set in a specific order (e.g., image resolution vs. offset).
open_framegrabber ('DirectShow', 1, 1, 0, 0, 0, 0, 'default', 8, 'rgb', -1, 'false', 'default', '[0] Integrated Camera', 0, -1, AcqHandle)
set_framegrabber_param (AcqHandle, 'grab_timeout', 5000)
set_framegrabber_param (AcqHandle, 'sharpness', 2)
* Image Acquisition 01: Attention: The initialization may fail in case parameters need to
while (true)
    grab_image (Image, AcqHandle)
    * Image Acquisition 01: Do something
endwhile
close_framegrabber (AcqHandle)

【F5】是连续运行,【F6】是单步运行。工具栏中也可操作。当执行到程序最下方后。可以通过【F2】重置程序执行或【shift + F2】重置程序执行。将程序窗口中的执行光标移动到最初位置。


通过【Ctrl + E】可以打开halcon的【实例程序】,通过实例程序中的的招数算子拆分出来,学习,应用到自己的场景中。

我们打开第一个案例:【方法】【Blob分析】中【ball.hdev】(Inspect ball bondings)   焊点定位检测。

* ball.hdev: Inspection of Ball Bonding
* 1、采集图像
dev_update_window ('off')
dev_close_window ()
dev_open_window (0, 0, 728, 512, 'black', WindowID)
read_image (Bond, 'die/die_03')
dev_display (Bond)
set_display_font (WindowID, 14, 'mono', 'true', 'false')
disp_continue_message (WindowID, 'black', 'true')
stop ()
* 2、预处理+区域抠图
threshold (Bond, Bright, 100, 255)
shape_trans (Bright, Die, 'rectangle2')
dev_set_color ('green')
dev_set_line_width (3)
dev_set_draw ('margin')
dev_display (Die)
disp_continue_message (WindowID, 'black', 'true')
stop ()
reduce_domain (Bond, Die, DieGrey)
* 3、图像分割
threshold (DieGrey, Wires, 0, 50)
fill_up_shape (Wires, WiresFilled, 'area', 1, 100)
dev_display (Bond)
dev_set_draw ('fill')
dev_set_color ('red')
dev_display (WiresFilled)
disp_continue_message (WindowID, 'black', 'true')
stop ()
opening_circle (WiresFilled, Balls, 15.5)
dev_set_color ('green')
dev_display (Balls)
disp_continue_message (WindowID, 'black', 'true')
stop ()
connection (Balls, SingleBalls)
dev_set_color ('blue')
dev_display (SingleBalls)
disp_continue_message (WindowID, 'black', 'true')
stop ()
select_shape (SingleBalls, IntermediateBalls, 'circularity', 'and', 0.85, 1.0)
sort_region (IntermediateBalls, FinalBalls, 'first_point', 'true', 'column')
dev_display (Bond)
dev_set_colored (12)
dev_display (FinalBalls)
disp_continue_message (WindowID, 'black', 'true')
stop ()
* 4、计算显示
smallest_circle (FinalBalls, Row, Column, Radius)
NumBalls := |Radius|
Diameter := 2 * Radius
meanDiameter := mean(Diameter)
minDiameter := min(Diameter)
dev_display (Bond)
disp_circle (WindowID, Row, Column, Radius)
dev_set_color ('white')
disp_message (WindowID, 'D: ' + Diameter$'.4', 'image', Row - 2 * Radius, Column, 'white', 'false')
dev_update_window ('on')

1、采集图像:

程序启动或者关闭期间。自动切换图像对象窗口。关闭原窗口,打开新的窗口,句柄。读取图像,并显示。图像所在位置为:C:\Users\Public\Documents\MVTec\HALCON-17.12-Progress\examples\images\die下:即用户\公用\文档\MVTec中。

2、区域分割:

第12行:threshold:二值化(输入图像、输出变量、最小阈值、最大阈值);在变量窗口可以看到Bright变量,点击即可查看每个变量的图像。

第13行:shape_trans:返回带方框的矩形。凸性检测(图形上任意两个点之间相连,如果线上的点没有在区域内,则将线内所有区域填充。)返回区域凸性匹配形状。匹配的形状由第三个参数拟合。第三个参数可以为:convex:凸多边形,凸包;ellipse:椭圆形;outer_circle:最小外接圆;inner_circle:最大范围圆;rectangle1:平行于坐标系的最小外接矩形;rectangle2:最小外接矩形;inner_rectangle1:最大轴平行区域矩形;inner_center:中心骨架。形状转换的是为了抠图。粗略的确定要操作的区域。返回值Die相当于一个mask。

第20行:reduce_domain:Reduce the domain of an image。相当于原图与Die做了mask与运算。以此来实现切割。

3、图像分割:

第22行:threshold:进一步二值化滤波范围在(0, 50)之间;在【工具栏】中找到【打开灰度直方图】在【范围筛选及代码生成中】点击小图标【使用该输出】,然后在直方图中拖动左右的两个定标尺,即可通过阈值来选择出我们需要的区域。

注意:两个标尺,分别移动,查看效果。

该案例中,左标尺,从左移动到右端无法选出我们的roi,但是向左滑动右标尺即把上限降低,可以筛选出我们的ROI。因为图中焊点区域为黑色,所以应该设上限。

拖动右标尺,直到我们的黑色区域被选中。

在该工具界面中【输入和输出】中,通过设置颜色,可以标定我们所选中的区域。比如我们这是blue,那么蓝色区域即为我们当前阈值筛选下来的区域。

点击【插入代码】即可生成我们过程程序。

注意一个小技巧:在【可视化】菜单栏中,选择【更新窗口】在【单步模式】选择【清空并显示】将显示你当前变量的图像。

第23行:fill_up_shape:Fill up holes in regions having given shape features.类似分水岭法。通过多次腐蚀,提取sure_fg,通过开运算,求临近区域,做差提取周边区域。

有噪声点:首先想到腐蚀开运算。

(一)掰开了,揉碎了,说经典halcon中的那些算子_第2张图片

第31行:opening_circle:Open a region with a circular structuring element。开运算。类似与watershed之前的连通域之后的的markers。观察变量空间的图像可知。剩余七个圆点和右下角一个方框。然后对这个八个点用圆去查找。

开运算结构元素形状问题:用圆去做开运算,那么结束以后,它越接近于圆。没有棱角。如果用矩形,之后就会有棱角。半径越大,腐蚀越厉害。半径越小,擦除越少。

开运算之后。

(一)掰开了,揉碎了,说经典halcon中的那些算子_第3张图片

第36行:connection: Compute connected components of a region。计算连通域。我在36行之后多加了一个显示:如下:只是为了看看connect返回值:SingleBalls。四邻域,八邻域。

将整个region分割8个区域。由一个region变量变成8个region区域变量。(砍开)

返回的变量singleBalls在图像上右键点击:【显示目录】发现8个变量。

dev_set_color ('blue')
dev_display (SingleBalls)
disp_continue_message (WindowID, 'black', 'true')
stop ()

第41行select_shape: Choose regions with the aid of shape features.:借助形状特征选择regions。第三个参数可选各种形状指标:circularity:圆度。形状匹配功能。类似与opencv中的cv2.matchShapes()。只不过opencv自己去找特征轮廓遍历设阈值。halcon通过选择形状参数来拟合。更定制性。形状自动匹配好还是,指定好,这个看场景来决定,没有绝对之说。

形状选择完之后就只剩下七个焊点图。

调用【打开特征直方图】横坐标表示特征的值,纵坐标表示值的个数。从图上可以看到有一个单独孤立的直线。那就是右下方的方块。还可以通过【特征选择及代码生成】中的【加号+】来增加特征和通道。以此增加特征来区分众多区域。最终实现对于特定区域的筛选定位。在当前特征area中通过与【灰度直方图】中一样,拖动左右定标尺实现对于目标的选择。最大最小面积筛选目标区域。

(一)掰开了,揉碎了,说经典halcon中的那些算子_第4张图片

在案例代码中:采用的特征是圆度circularity。圆度这个特征更明显好用些。上面开运算也是采用了圆形结构元素

(一)掰开了,揉碎了,说经典halcon中的那些算子_第5张图片

第42行sort_region:Sorting of regions with respect to their relative position.编码排序

4、计算显示

第49行smallest_circle:最小外接圆。计算行列位置、半径、从而实现对焊点区域的大小判断。


Halcon使用技巧:

在【图形窗口】中:按住Ctrl可以查看图中的行列坐标和灰度值。 


我们通过第二个案例来继续来熟悉这个处理过程:count_pellets.hdev。

* This programs demonstrates the use of basic morphology
* operators.
* The aim of the program is to detect each single pellet
* (bright particle on a darker background).
* 
dev_update_off ()
read_image (Image, 'pellets')
dev_close_window ()
get_image_size (Image, Width, Height)
dev_open_window (0, 0, Width, Height, 'black', WindowID)
dev_set_part (0, 0, Height - 1, Width - 1)
set_display_font (WindowID, 16, 'mono', 'true', 'false')
dev_set_colored (6)
dev_set_draw ('margin')
dev_set_line_width (3)
dev_display (Image)
disp_message (WindowID, 'Detect each single pellet', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowID, 'black', 'true')
stop ()
* 
* Segment the regions of the pellets from the background
binary_threshold (Image, LightRegion, 'max_separability', 'light', UsedThreshold)
opening_circle (LightRegion, Region, 3.5)
dev_display (Region)
disp_message (WindowID, 'First, segment the pellets', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowID, 'black', 'true')
stop ()
* 
* Compute the connected pellet regions
* Note, that this approach fails, because some of
* the pellets are still connected.
connection (Region, ConnectedRegionsWrong)
dev_display (Image)
dev_display (ConnectedRegionsWrong)
disp_message (WindowID, 'Simple connection fails', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowID, 'black', 'true')
stop ()
* 
* Separate each pellet from the others by erosion
erosion_circle (Region, RegionErosion, 7.5)
dev_display (Image)
dev_display (RegionErosion)
disp_message (WindowID, 'Erosion of the pellet regions', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowID, 'black', 'true')
stop ()
* 
* Now, compute the connected pellet regions
connection (RegionErosion, ConnectedRegions)
dev_display (Image)
dev_display (ConnectedRegions)
disp_message (WindowID, 'Perform connection now', 'window', 12, 12, 'black', 'true')
disp_continue_message (WindowID, 'black', 'true')
stop ()
* 
* Turn back to the original pellet size by applying a dilation
dilation_circle (ConnectedRegions, RegionDilation, 7.5)
count_obj (RegionDilation, Number)
dev_display (Image)
dev_display (RegionDilation)
* 求行列坐标和面积。
area_center (RegionDilation, Area, Row, Column)  
disp_message (WindowID, Number + ' pellets detected', 'window', 12, 12, 'black', 'true')

 还是采集图像,分割、识别、显示计算。


第22行中:binary_threshold (Image, LightRegion, 'max_separability', 'light', UsedThreshold):通过【F1】帮助文档,我们得知:第三个参数为Method。用这个方法来确定阈值:UsedThreshold。函数描述中,指明:两种方法只能用于双峰柱状图。

Both methods should only be used for images that have a bimodal histogram。从图上我们得知:原图冰雹和地面两个实体,颜色灰度值比较容易区分。可以自动确定聚类阈值(本例中程序自动运行为阈值UsedThreshold为112)。

(一)掰开了,揉碎了,说经典halcon中的那些算子_第6张图片

第14行dev_set_draw (Operator):Define the region fill mode.定义填充模式。有两种填充模式:一个是margin和fill模式下。再边缘情况下只显示轮廓: the appearance of the contours can be affected by dev_set_line_width and set_line_style。可以受到后两种算子的影响。第十五行,限制轮廓线的宽度。


fill_up_shape这一步:难以复现:我的思路肯定让halcon限制住了,所以有时间可以重新思考。操作类似与watershed方法锁定焊点临近区,然后在区域内,然后画圆,连通域找圆。select_shape。类似:Hough 圆环变换。找圆。找到之后,区域region编号。

然后最小外接圆、计算中心,半径,显示直径,完成对焊点区域的测量。

import cv2
import numpy as np


# 1、加载图像
# 在路径前面加r,即保持字符原始值的意思。相当于C#中的@符号。
img = cv2.imread(r"C:\Users\Public\Documents\MVTec\HALCON-17.12-Progress\examples\images\die\die_03.png")
Bond = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# cv2.imshow("black", Bond)

# 2、预处理
_, Bright = cv2.threshold(Bond, 100, 255, cv2.THRESH_BINARY)
# 切割出Die
# Halcon中的通过shape_trans()来覆盖整个二值区域的形状,返回一个指定形状的mask。然后通过reduce_domain()做初步的切割。
# 目的是对大区域的切割。
# OpenCV中我们有两种方法来实现:
# 第一种方法:从非零点实现即可。
# 第二种方法:先闭运算填充白色中的黑点,后查找轮廓,然后通过轮廓近似cv2.approxPolyDP()。
# 补充,若是形状匹配:OpenCV中需要先定轮廓,再拟合。先findContours()得到cnt,然后fit.shape_f(cnt)。
# shape_trans()中第三个参数:
''' 形状:             OpenCV中的函数:
    convex:             cv2.convexHull()凸包检测
    ellipse:            cv2.fitEllipse(cnt)
    outer_circle:       cv2.minEnclosingCircle()
    inner_circle:       待查找
    rectangle1:         边界矩形:cv2.boundingRect(cnt)
    rectangle2:         最小外接矩形:cv2.minAreaRect(cnt)
    inner_rectangle1:   待查找
    inner_center:       待查找
'''
# 第一种:非零点查找
tuple_nonzeros = np.array(Bright).nonzero()
x1, x2, y1, y2 = min(tuple_nonzeros[0]), max(tuple_nonzeros[0]), min(tuple_nonzeros[1]), max(tuple_nonzeros[1])
print(x1, x2, y1, y2)   # 坐标的概念和行列概念顺序相反。
img0 = img.copy()  # 避免原图被污染。
Die = cv2.rectangle(img0, (y1, x1), (y2, x2), (0, 255, 0), 3)
# cv2.imshow("Bright", Die)
# 由此得到实际Die区域为mask = Bright[x1:x2, y1:y2]=255
mask = np.zeros(Bond.shape, np.uint8)
mask[x1:x2, y1:y2] = 255
# cv2.imshow("mask", mask)

# 第二种:轮廓查找:
# kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
# closing = cv2.morphologyEx(Bright, cv2.MORPH_CLOSE, kernel, iterations=2)
# # cv2.imshow("closing", closing)
# contours, hierarchy = cv2.findContours(closing, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# cnt = contours[0]
# # print(cnt)
# x, y, w, h = cv2.boundingRect(cnt)
# print(x, y, w, h)
# img0 = img.copy()
# Die2 = cv2.rectangle(img0, (x, y), (x+w, y+h), (0, 255, 0), 3)
# cv2.imshow("margin", Die2)
# # 实际的mask也就是Die的区域为:
# mask = np.zeros(Bond.shape, np.uint8)
# mask[y: y+h, x: x+w] = 255

# 两种方法最后差有一个像素误差,因为第二种方法有闭运算,所以首选第一种算法,最精确。
# halcon中对mask填充了颜色以显示,opencv中可以通过[x:x1, y:y1, :] = [0, 255, 0],来实现,
# 也可以通过cv2.floodFill()填充,或者轮廓查找之后通过cv2.drawContours()等。

DieGrey = cv2.bitwise_and(Bond, Bond, mask=mask)
# cv2.imshow("DieGrey", DieGrey)

# 3、图形分割:
_, Wires = cv2.threshold(DieGrey, 100, 255, cv2.THRESH_BINARY_INV)
# _, Wires1 = cv2.threshold(DieGrey, 100, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
Wires = cv2.bitwise_and(Wires, Wires, mask=mask)
# cv2.imshow("Wires", Wires)
# noise removal
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
opening = cv2.morphologyEx(Wires, cv2.MORPH_OPEN, kernel, iterations=2)
# sure background area
sure_bg = cv2.dilate(opening, kernel, iterations=3)

erode = cv2.erode(Wires, kernel, iterations=7)
# cv2.imshow("erode", erode)
# 用于计算图像中每一个非零点距离离自己最近的零点的距离,
# distanceTransform的第二个Mat矩阵参数dst保存了每一个点与最近的零点的距离信息,
# 图像上越亮的点,代表了离零点的距离越远。
dist_transform = cv2.distanceTransform(erode, 1, 5)
# cv2.imshow("temp", dist_transform)
ret, sure_fg = cv2.threshold(dist_transform, 0.6*dist_transform.max(), 255, 0)
cv2.imshow("sure_fg", sure_fg)
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)  # 图像相减
# cv2.imshow('unknown', unknown)

# Marker labelling
_, markers1 = cv2.connectedComponents(sure_fg)

# Add one to all labels so that sure background is not 0, but 1
# 把将背景标 为 0 其他的对 使用从 1 开始的正整数标
markers = markers1 + 1
# Now, mark the region of unknown with zero
markers[unknown == 255] = 0

markers3 = cv2.watershed(img, markers)
print(markers3.shape)
cv2.imshow("markers", ret)
img[markers3 == -1] = [255, 0, 0]
cv2.imshow("WatarShed", img)


# halcon中通过fill_up_shape (Wires, WiresFilled, 'area', 1, 100)函数通过给定的形状,
# 来计算面积或者轮廓特征,以此来匹配目标,进一步靠近我们关系的区域。
# 主旨思路就是一步步靠近我们的目标区域。
# 在深度学习中可以通过Pix2Pix算法,用PS标注数据集来实现。
# # 经过实测:用计算轮廓面积方法需要先经过膨胀腐蚀,断开区域之间的连同独立区域之后。再计算面积来着色分割。这个过程就是水漫法。
# contours1, hierarchy1 = cv2.findContours(Wires, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# for cnt in contours1:
#     s = cv2.contourArea(cnt)
#     if (s > 100) & (s < 1000):
#         # print(s)
#         cv2.drawContours(img, [cnt], -1, (0, 0, 255), 3)
# cv2.imshow("filled up", img)

# 类似于OpenCV找硬币分割中的方法,通过水漫法(分水岭法)来分割目标。

cv2.waitKey(0)
cv2.destroyAllWindows()

你可能感兴趣的:(#,Halcon,计算机视觉,图像识别,Halcon)