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_INVOSTU全局阈值
这里用到到的函数还是 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()