当前位置 博文首页 > 文章内容

    爪哇人的博客:python opencv 数米粒

    作者:21344 栏目:未分类 时间:2021-11-25 22:32:46

    1.介绍

    opencv入门之数米粒:给出一幅米粒图数出图中米粒数目以及最大米粒位置。在原图上画出最大米粒的位置。并输出最大米粒的面积和最大米粒的周长。使用pycharm编码,亲测功能强大。
    米粒分布原图

    2.思路

    首先利用opencv轮廓检测函数来分别提取每一个米粒,并计算出每个米粒的面积,然后对最大米粒求周长,最后在原图上标记最大米粒的位置,并且输出米粒信息。

    3.预处理

    opencv轮廓检测函数所需要的图像为二值化图片,所以在进行轮廓检测前,需要对图像进行预处理。预处理过程为:

    (1).对图片进行灰度化:

    • #打开图片 img=cv2.imread(‘Img/rice.jpg’,0) (-1为原图,0为强制灰度图,1为强制彩图)
    • 灰度化函数gray=cv2.cvtColor(frame,cv2.COLOR_RGB2GRAY)

    (2)获取去背景的米粒图
    利用腐蚀膨胀,对原图进行5次腐蚀,然后进行5次膨胀,得到背景图
    原图减去背景图可以得到去背景的米粒图

    • 腐蚀:就像土壤侵蚀一样,这个操作会把前景物体的边界腐蚀掉(但是前景仍然 是白色)。这是怎么做到的呢?卷积核沿着图像滑动,如果与卷积核对应的原图 像的所有像素值都是 1,那么中心元素就保持原来的像素值,否则就变为零。
    
    import import cv2
    import numpy as np
    
    #打开图片
    img=cv2.imread('Img/rice.jpg',0)
    
    #构造模板,5次腐蚀
    kernel=np.ones((5,5),np.uint8)#构造5x5卷积核
    erosion=cv2.erode(img,kernel,iterations=5)#进行5次腐蚀
    • 膨胀:与腐蚀相反,与卷积核对应的原图像的像素值中只要有一个是 1,中心元 素的像素值就是 1。所以这个操作会增加图像中的白色区域(前景)。一般在去 噪声时先用腐蚀再用膨胀。因为腐蚀在去掉白噪声的同时,也会使前景对象变 小。
    dilation=cv2.dilate(erosion,kernel,iterations=5) #进行5次膨胀
    • 获取去背景的米粒图
      用原图减去5次腐蚀,5次膨胀后的背景图
    #原图减去背景得到米粒形状
    backImg=dilation
    rice=img-backImg

    去背景米粒效果图:
    去背景

    (3)图像二值化

    • 简单阈值
      与名字一样,这种方法非常简单。但像素值高于阈值时,我们给这个像素 赋予一个新值(可能是白色),否则我们给它赋予另外一种颜色(也许是黑色)。 这个函数就是 cv2.threshhold()。这个函数的第一个参数就是原图像,原图 像应该是灰度图。第二个参数就是用来对像素值进行分类的阈值。第三个参数 就是当像素值高于(有时是小于)阈值时应该被赋予的新的像素值。OpenCV 提供了多种不同的阈值方法,这是有第四个参数来决定的。这些方法包括:
      cv2.THRESH_BINARY
      cv2.THRESH_BINARY_INV
      cv2.THRESH_TOZERO
      cv2.THRESH_TOZERO_INV

    • OSTU全局阈值
      这里用到到的函数还是 cv2.threshold(),但是需要多传入一个参数 (flag):cv2.THRESH_OTSU。这时要把阈值设为 0。然后算法会找到最 优阈值,这个最优阈值就是返回值 retVal。如果不使用 Otsu 二值化,返回的 retVal 值与设定的阈值相等。

    #OSTU二值化,th1返回阈值,ret1返回二值化的结果图像
    th1,ret1=cv2.threshold(rice,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

    二值化效果图如下
    二值化

    4.轮廓分析

    轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同 的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。 python3.x-opencv开发指导用书
    (1) 轮廓提取

    • 为了更加准确,要使用二值化图像。在寻找轮廓之前,要进行阈值化处理 或者 Canny 边界检测。
    • 查找轮廓的函数会修改原始图像。如果你在找到轮廓之后还想使用原始图 像的话,你应该将原始图像存储到其他变量中。
    • 在 OpenCV 中,查找轮廓就像在黑色背景中超白色物体。你应该记住, 要找的物体应该是白色而背景应该是黑色。
    
    **#轮廓检测,注意输出结果应有3个,其他著作上为2个输出结果,亲测错误**
    ret1,contours,hierarchy=cv2.findContours(ret1,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)

    (2)轮廓数据

    • 轮廓面积
      轮廓的面积可以使用函数 cv2.contourArea() 计算得到,也可以使用矩 (0 阶矩),M[‘m00’]。
    cv2.contourArea(cnt)
    • 轮廓周长
      也被称为弧长。可以使用函数 cv2.arcLength() 计算得到。这个函数 的第二参数可以用来指定对象的形状是闭合的(True),还是打开的(一条曲 线)。
    cv2.arcLength(cnt,True)

    (3)输出结果
    遍历所有轮廓,比较轮廓数据,得出最大轮廓,并在原图上标注。
    函数 cv2.drawContours() 可以被用来绘制轮廓。它可以根据你提供 的边界点绘制任何形状。它的第一个参数是原始图像,第二个参数是轮廓,一 个 Python 列表。第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设 置为 -1 时绘制所有轮廓)。接下来的参数是轮廓的颜色和厚度等。

    #遍历得到最大面积的米粒
    maxC=-1
    maxS=-1
    for cnt in contours:
        tempS=cv2.contourArea(cnt)
        if maxS<tempS:
            maxS=tempS
            maxC=tempC=cv2.arcLength(cnt,True)
            contour=cnt
    
    #在img中画出最大面积米粒
    cv2.drawContours(img,[contour],-1,(0,0,255,),1)

    改变参数可以获取更好的效果

    这里写图片描述


    整体代码

    import cv2
    import numpy as np
    
    #打开图片
    img=cv2.imread('Img/rice.jpg',0)
    
    #构造模板,5次腐蚀,5次膨胀,得到背景
    kernel=np.ones((5,5),np.uint8)
    erosion=cv2.erode(img,kernel,iterations=5)
    dilation=cv2.dilate(erosion,kernel,iterations=5)
    
    #原图减去背景得到米粒形状
    backImg=dilation
    rice=img-backImg
    
    #OSTU二值化
    th1,ret1=cv2.threshold(rice,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    
    #轮廓检测
    ret1,contours,hierarchy=cv2.findContours(ret1,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
    
    #遍历得到最大面积的米粒
    maxC=-1
    maxS=-1
    for cnt in contours:
        tempS=cv2.contourArea(cnt)
        if maxS<tempS:
            maxS=tempS
            maxC=tempC=cv2.arcLength(cnt,True)
            contour=cnt
    
    #在img中画出最大面积米粒
    cv2.drawContours(img,[contour],-1,(0,0,255,),1)
    
    cv2.imshow('image',rice)
    print('面积最大:',maxS)
    print('对应米粒周长:',maxC)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    cs