使用分水岭算法对米粒进行精准分割(2)

背景:(https://blog.csdn.net/qq_36623595/article/details/109250114)因为本篇帖子中对米粒中黏连的部分没有精确分割,经过探索得出了以下结论

本帖太过于执迷于分割米粒,所以米粒面积计算偏小(介意的可以只看分水岭算法 )

设计思路:

  1. 使用分水岭算法进行图像分割,基本的步骤为: 
    1.  通过形态学开运算对原始图像O 去噪。 
    2. 通过腐蚀操作获取“确定背景 B”。需要注意,这里得到“原始图像-确定背景”即可。
    3. 利用距离变换函数 cv2.distanceTransform()对原始图像进行运算,并对其进行阈值处理,
    4. 得到“确定前景 F”。 
    5. 计算未知区域UN(UN = O –B - F)。 
    6.  利用函数 cv2.connectedComponents()对原始图像O 进行标注。 
    7.  对函数 cv2.connectedComponents()的标注结果进行修正。 
    8. 使用分水岭函数完成对图像的分割。 
  2.  保存分水岭操作后的轮廓图像,再次进入之前米粒分割的函数
    1. 但是分水岭操作后有图片边框,进行了一次裁剪

这样之后我们可以得到这个图像(左边原图可直接下载,右边是计算分割出的图像,粒粒分明)

        

运行结果:头一个帖子重叠的米都分开了,只有一个还黏在一起(若想粒粒分明,把代码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')

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Helloworld-睖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值