点击下方卡片,关注“OpenCV与AI深度学习”
视觉/图像重磅干货,第一时间送达
导读
本文主要介绍基于基于形态学 连通域处理实现粘连物体的分割与计数方法,并对比Halcon与OpenCV实现差异。
背景介绍
在实际的视觉应用场景中,我们常常会遇到物体/元件的计数问题,而计数时比较常见的情形就是物体相邻或粘连,对相邻或粘连物体的分割将直接影响着最终计数的准确性。后面将分篇介绍粘连物体分割计数的常用方法,包括:
【1】形态学 连通域处理方法
【2】距离变换 分水岭分割方法
【3】其他方法
本文将对第【1】种方法分别用Halcon和OpenCV实现并做简单对比。
实例演示与实现步骤
* 实例一:糖豆分割与计数
测试图像(图片来源--Halcon例程图):
实现步骤:
【1】阈值处理:区间固定阈值或OTSU阈值
【2】圆形结构元素腐蚀:断开粘连区域
【3】连通域处理:计数 提取中心用于标记结果
【4】对每个连通域做膨胀:还原轮廓大小用于绘制边缘
【5】结果标记与显示
Halcon实现代码:
代码语言:javascript复制dev_get_window (WindowHandle)
read_image (Image, 'pellets')
threshold (Image, Regions, 106, 255)
connection(Regions, ConnectedRegions)
select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 500.72, 10000)
erosion_circle (SelectedRegions, RegionErosion, 7.5)
connection (RegionErosion, ConnectedRegions2)
dilation_circle (ConnectedRegions2, RegionDilation, 7.5)
dev_set_draw ('margin')
dev_set_line_width (3)
dev_display (Image)
dev_display (RegionDilation)
area_center (RegionDilation, Area, Row, Column)
gen_cross_contour_xld (Cross, Row, Column, 15, 0.785398)
count_obj (RegionDilation, Number)
dev_set_color ('green')
set_display_font (WindowHandle, 30, 'mono', 'true', 'false')
set_tposition (WindowHandle, 10, 240)
write_string(WindowHandle, 'count=' Number)
OpenCV实现代码与效果:
代码语言:javascript复制#公众号:OpenCV与AI深度学习
#作者:Color Space
import numpy as np
import cv2
img = cv2.imread('B0.jpg')
cv2.imshow('src', img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thres= cv2.threshold(gray, 71, 255, cv2.THRESH_BINARY)
cv2.imshow('thres', thres)
k1=np.zeros((53,53), np.uint8)
cv2.circle(k1,(26,26),26,(1,1,1),-1, cv2.LINE_AA)
erode = cv2.morphologyEx(thres, cv2.MORPH_ERODE, k1)#膨胀操作
cv2.imshow('erode', erode)
# 连通域分析
num_labels, labels, stats, centers = cv2.connectedComponentsWithStats(erode, connectivity=8)
# 连通域数量
print('num_labels = ',num_labels)
# 连通域的信息:对应各个轮廓的x、y、width、height和面积
print('stats = ',stats)
# 连通域的中心点
print('centroids = ',centers)
# 每一个像素的标签1、2、3.。。,同一个连通域的标签是一致的
print('labels = ',labels)
# 不同的连通域赋予不同的颜色
output = np.zeros((img.shape[0], img.shape[1], 3), np.uint8)
for i in range(1, num_labels):
mask = labels == i
output[:, :, 0][mask] = np.random.randint(0, 255)
output[:, :, 1][mask] = np.random.randint(0, 255)
output[:, :, 2][mask] = np.random.randint(0, 255)
cv2.imshow('output', output)
dilate = cv2.morphologyEx(output, cv2.MORPH_DILATE, k1)#膨胀操作
cv2.imshow('dilate', dilate)
#k2=np.ones((3,3), np.uint8)
#dilate = cv2.morphologyEx(dilate, cv2.MORPH_GRADIENT, k2)#膨胀操作
result = cv2.addWeighted(img,0.8,dilate,0.5,0) #图像权重叠加
for i in range(1,len(centers)):
cv2.drawMarker(result, (int(centers[i][0]),int(centers[i][1])),(0,0,255),1,20,2)
cv2.putText(result,"count=%d"%(len(centers)-1),(20,30),0,1,(0,255,0),2)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
OpenCV标记结果(这里没做大小筛选,数量多2个):
* 实例二:工件分割与计数
测试图像(图片来源--CSDN用户-LearningAIways):
实现步骤与实例一相同,代码中只需要对部分参数微调即可,大家可以自己尝试,实现结果如下:
Halcon实现结果:
OpenCV实现结果:
对比与总结
【1】适用情形:形态学 连通域处理方法适用与一些粘连并不严重或粘连区域远比物体本身小的情况(比如粘连部分宽高10pixel, 物体本身宽高几百pixel, 这样不用担心腐蚀后物体本身消失);
【2】Halcon的将一个区域分成不同的连通域用connection算子,然后对每个连通域处理。OpenCV中使用connectedComponentsWithStats函数标记每个连通域,然后对单个连通域膨胀处理,最后将结果做权重叠加,并没有使用contours方法,也算是一次灵活应用。
如需C 与C#实现代码及素材,可加入下方知识星球获取。