背景:(https://blog.csdn.net/qq_36623595/article/details/109250114)因为本篇帖子中对米粒中黏连的部分没有精确分割,经过探索得出了以下结论
本帖太过于执迷于分割米粒,所以米粒面积计算偏小(介意的可以只看分水岭算法 )
设计思路:
- 使用分水岭算法进行图像分割,基本的步骤为:
- 通过形态学开运算对原始图像O 去噪。
- 通过腐蚀操作获取“确定背景 B”。需要注意,这里得到“原始图像-确定背景”即可。
- 利用距离变换函数 cv2.distanceTransform()对原始图像进行运算,并对其进行阈值处理,
- 得到“确定前景 F”。
- 计算未知区域UN(UN = O –B - F)。
- 利用函数 cv2.connectedComponents()对原始图像O 进行标注。
- 对函数 cv2.connectedComponents()的标注结果进行修正。
- 使用分水岭函数完成对图像的分割。
- 保存分水岭操作后的轮廓图像,再次进入之前米粒分割的函数
- 但是分水岭操作后有图片边框,进行了一次裁剪
这样之后我们可以得到这个图像(左边原图可直接下载,右边是计算分割出的图像,粒粒分明)
运行结果:头一个帖子重叠的米都分开了,只有一个还黏在一起(若想粒粒分明,把代码20行的开运算迭代次数改成3即可)
源码:
import cv2
import numpy as np
import matplotlib.pyplot as plt
def show(img,name):
cv2.namedWindow(name, 2) #创建一个窗口
cv2.imshow(name, img)
#? 求面积函数
def rice_area(img):
# 导入图片,图片放在程序所在目录
img = cv2.imread(img)
# 转换为灰度图
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 使用自适应阈值操作进行图像二值化
dst = cv2.adaptiveThreshold(gray,255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,101, 1)
# 形态学去噪
element = cv2.getStructuringElement(cv2.MORPH_CROSS,(3, 3))
# 开运算去噪(先腐蚀再膨胀)
dst=cv2.morphologyEx(dst,cv2.MORPH_OPEN,element,iterations=2)# 开运算3次可以全部分开,但是面积平均值少了10px
# 轮廓检测函数
contours, hierarchy = cv2.findContours(dst,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓
cv2.drawContours(dst,contours,-1,(120,0,0),2)
count=0 # 米粒总数
ares_avrg=0 # 米粒平均
# 遍历找到的所有米粒
for cont in contours:
# 计算包围性状的面积
ares = cv2.contourArea(cont)
# 过滤面积小于50的形状
if ares<50:
continue
count+=1
ares_avrg+=ares
# 打印出每个米粒的面积
print("{}-blob:{}".format(count,ares),end=" ")
# 提取矩形坐标
rect = cv2.boundingRect(cont)
# 打印坐标
print("x:{} y:{}".format(rect[0],rect[1]))
# 绘制矩形
cv2.rectangle(img,rect,(0,0,255),1)
# 防止编号到图片之外(上面),因为绘制编号写在左上角,所以让最上面的米粒的y小于10的变为10个像素
y=10 if rect[1]<10 else rect[1]
# 在米粒左上角写上编号
cv2.putText(img,str(count), (rect[0], y), cv2.FONT_HERSHEY_COMPLEX, 0.4, (0, 255, 0), 1)
# print('编号坐标:',rect[0],' ', y)
print('个数',count,' 总面积',ares_avrg,' ares',ares)
print("米粒平均面积:{}".format(round(ares_avrg/count,2))) #打印出每个米粒的面积
cv2.namedWindow("imgshow", 2) #创建一个窗口
cv2.imshow('imgshow', img) #显示原始图片(添加了外接矩形)
cv2.namedWindow("dst", 2) #创建一个窗口
cv2.imshow("dst", dst) #显示灰度图
cv2.waitKey()
#? 分水岭算法优化米粒分割
img = cv2.imread('rice.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
ishow=img.copy()
#! 使用局部阈值的大津算法进行图像二值化
thresh = cv2.adaptiveThreshold(gray,255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,101, 1)
# ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
element = cv2.getStructuringElement(cv2.MORPH_CROSS,(3, 3))
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,element)
# cv2.imshow("opening",opening)
# opening = cv2.erode(opening,element,iterations=1)# 偷偷执行一次腐蚀操作
ret, thresh = cv2.threshold(opening,0,255,
cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# cv2.imshow("thresh",thresh)
kernel = np.ones((3,3),np.uint8)
#* 优化
thresh = cv2.dilate(thresh,kernel,iterations=1)
# cv2.imshow("thresh2",thresh)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 1)
sure_bg = cv2.dilate(opening,kernel,iterations=2)
#* 优化bg
sure_bg = cv2.morphologyEx(sure_bg,cv2.MORPH_CLOSE,kernel,iterations=1)
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,cv2.DIST_MASK_PRECISE)
ret, sure_fg = cv2.threshold(dist_transform,0,255,0)
sure_fg = np.uint8(sure_fg)
#* 优化fg
# sure_fg = cv2.dilate(sure_fg,kernel,iterations=1)
# sure_fg = cv2.erode(sure_fg,kernel,iterations=1)
# sure_fg = cv2.morphologyEx(sure_fg,cv2.MORPH_OPEN,kernel,iterations=2)
unknown = cv2.subtract(sure_bg,sure_fg)
# cv2.imshow("sure_bg",sure_bg)
# cv2.imshow("sure_fg",sure_fg)
# unknown = np.uint8(unknown)
ret, markers = cv2.connectedComponents(sure_fg)
markers = markers+1
markers[unknown==255] = 0
markers = cv2.watershed(img,markers)
img[markers == -1] = [0,0,0]
retval, result = cv2.threshold(img,0,255,cv2.THRESH_BINARY_INV)
""" cv2.namedWindow("ishow", 2) #创建一个窗口
cv2.imshow('ishow', ishow) #显示原始图片
cv2.namedWindow("img", 2) #创建一个窗口
cv2.imshow("img", img) #显示灰度图
cv2.waitKey() """
print(result.shape)#分水岭算法后边缘也会出现一个边框(裁剪一像素)
img = img[1:427, 1:427]
result = result[1:427, 1:427]
img = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel,iterations=1)
cv2.imwrite('rice_result.png',img)
cv2.imwrite('result.png',result)
#? 调用函数算面积
rice_area('rice_result.png')