OpenCV+Python
使用OpenCV构建图像识别算法,识别图片中的米粒个数,并计算米粒的平均面积和长度
模块:OpenCV 4.0.0.21
编程语言:Python 3.7.2
编译器:PyCharm 2018
首先介绍一下程序设计的思路:
img = cv2.imread("test.png") #导入图片,图片放在程序所在目录
使用cv2.imread()函数导入图片,括号内输入图片路径,如果图片位于程序所在目录,就可以直接写图片文件名。
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #转换为灰度图
主要使用cv2.cvtColor()函数将彩色图片转化为灰度图
#使用局部阈值的大津算法进行图像二值化
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) #开运算去噪
使用局部大津算法对图像进行二值化,全局大津法的优点在于可以快速有效的找到类间分割阈值,但其缺点也很明显,就是只能针对单一目标分割,或者感兴趣的目标都属于同一灰度范围,若需探测目标灰度范围分布较大,则必将有一部分目标探测丢失(例如上图中黑色和白色的汽车)。局部分割的优点在于可以进行多目标分割,缺点在于基于局部阈值分割出的目标连结性较差,包含噪声。
在这个米粒图片中,下部的图像亮度较暗,如果使用全局大津算法,下部的米粒将会被认为是背景,而被屏蔽掉。
如图所示:左边是使用局部大津算法,右边是使用全局大津算法,可以看到左边的效果比右边的效果好。
函数原型:
cv2. adaptiveThreshold(InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C)
参数解释:
**InputArray src:**源图像
**OutputArray dst:**输出图像,与源图像大小一致
**int adaptiveMethod:**在一个邻域内计算阈值所采用的算法,有两个取值,分别为 ADAPTIVE_THRESH_MEAN_C 和 ADAPTIVE_THRESH_GAUSSIAN_C 。
ADAPTIVE_THRESH_MEAN_C的计算方法是计算出领域的平均值再减去第七个参数double C的值
ADAPTIVE_THRESH_GAUSSIAN_C的计算方法是计算出领域的高斯均值再减去第七个参数double C的值
**int thresholdType:**这是阈值类型,只有两个取值,分别为 THRESH_BINARY 和THRESH_BINARY_INV 具体的请看官方的说明,这里不多做解释
**int blockSize:**adaptiveThreshold的计算单位是像素的邻域块,邻域块取多大,就由这个值作决定
**double C:**在对参数int adaptiveMethod的说明中,我已经说了这个参数的作用,从中可以看出,这个参数实际上是一个偏移值调整量
然后对图像进行形态学去噪,形态学去噪有一下几种:
我们使用开运算去除小噪声
主要步骤为:
1、检测轮廓- cv2.findContours()函数
2、提取轮廓的水平矩形坐标- rect = cv2.boundingRect( ) 函数
3、绘制矩形- cv2.rectangle()函数
4、 在米粒左上角写上编号-cv2.putText( ) 函数
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)#计算包围性状的面积
if ares<50: #过滤面积小于10的形状
continue
count+=1 #总体计数加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,0xff),1)#绘制矩形
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("米粒平均面积:{}".format(round(ares_avrg/ares,2))) #打印出每个米粒的面积
cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])
image参数是寻找轮廓的图像;
mode参数表示轮廓的检索模式,有四种(本文介绍的都是新的cv2接口):
cv2.RETR_EXTERNAL表示只检测外轮廓
cv2.RETR_LIST检测的轮廓不建立等级关系
cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
cv2.RETR_TREE建立一个等级树结构的轮廓。
method参数method为轮廓的近似办法
cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
cv2.findContours()函数返回两个值,一个是轮廓本身,还有一个是每条轮廓对应的属性。
cv2.boundingRect(InputArray points)
参数:
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
参数解释:
cv2.putText(img,str(count), (rect[0], y), cv2.FONT_HERSHEY_COMPLEX, 0.4, (0, 255, 0), 1)
各参数依次是:图片输入/添加的文字/左上角坐标/字体/字体大小/颜色/字体粗细
ares = cv2.contourArea(cont)
计算包围形状的面积,并使用一个for循环来计算面积平均值和长度平均值