之前曾经写过一道数米粒的作业,当时用的是c++中的opencv,这次用python试了一下。
原图及效果图
咦?水印怎么去啊
程序流程
程序的大致流程为:
1.读取图片
2.灰度化
3.二值化
4.膨胀、腐蚀等
5.边缘检测
6.输出
图片读取
用imread加上绝对路径进行读取
# 读取
img_rice = cv2.imread("d:/Python/rice/rice2.jpg")
cv2.imshow('rice', img_rice)
灰度化
基本的灰度化函数
# 灰度化
img_gray = cv2.cvtColor(img_rice, cv2.COLOR_BGR2GRAY)
cv2.imshow('gray', img_rice)
二值化
这个最好画出直方图,从直方图中选取合适的阈值,需要调整
# 二值化
ret,thresh1 = cv2.threshold(img_gray, 123, 255, cv2.THRESH_BINARY)
cv2.imshow('thresh', thresh1)
膨胀、腐蚀
矩阵的结构元素,以及腐蚀、膨胀的迭代次数要注意
# 腐蚀和膨胀
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(2,2)) #定义矩形结构元素
img_erode = cv2.erode(thresh1, kernel, iterations=3)
# cv2.imshow('erode', img_erode)
img_dilated = cv2.dilate(img_erode, kernel)
边缘检测
在边缘检测中对面积进行筛选,如果面积小于20,则认为不是米粒,如果面积大于150,则认为两个粘连,没有分开,具体的参数选取可以输出全部的面积之后再定,比如函数中加入
print(area)
用count统计个数
用ave_area统计面积
# 边缘检测
contours, hierarchy = cv2.findContours(img_dilated,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
count = 0
ave_area = 0
for i in range(len(contours)):
area = cv2.contourArea(contours[i])
if area > 20:
count = count + 1
ave_area = ave_area + area
rect = cv2.boundingRect(contours[i]) #提取矩形坐标
print("number:{} x:{} y:{} area:{}".format(count,rect[0],rect[1], area))#打印坐标
cv2.rectangle(img_rice,rect,(0,255,0),1)#绘制矩形
if area > 150:
count = count + 1
cv2.putText(img_rice,str({count,count-1}), (rect[0], rect[1]), cv2.FONT_HERSHEY_COMPLEX, 0.4, (0, 255, 0), 1) #在米粒左上角写上编号
else:
cv2.putText(img_rice,str(count), (rect[0], rect[1]), cv2.FONT_HERSHEY_COMPLEX, 0.4, (0, 255, 0), 1) #在米粒左上角写上编号
输出
输出总个数和平均面积
ave_area = ave_area / count
# 输出
print('总个数是:{},平均面积是:{}'.format(count,ave_area))
cv2.imshow("Contours", img_rice)
完整代码
完整代码如下:
from cv2 import cv2
# 读取
img_rice = cv2.imread("d:/Python/rice/rice2.jpg")
cv2.imshow('rice', img_rice)
# 灰度化
img_gray = cv2.cvtColor(img_rice, cv2.COLOR_BGR2GRAY)
cv2.imshow('gray', img_rice)
# 二值化
ret,thresh1 = cv2.threshold(img_gray, 123, 255, cv2.THRESH_BINARY)
cv2.imshow('thresh', thresh1)
# 腐蚀和膨胀
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(2,2)) #定义矩形结构元素
img_erode = cv2.erode(thresh1, kernel, iterations=3)
# cv2.imshow('erode', img_erode)
img_dilated = cv2.dilate(img_erode, kernel)
# 边缘检测
contours, hierarchy = cv2.findContours(img_dilated,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
count = 0
ave_area = 0
for i in range(len(contours)):
area = cv2.contourArea(contours[i])
if area > 20:
count = count + 1
ave_area = ave_area + area
rect = cv2.boundingRect(contours[i]) #提取矩形坐标
print("number:{} x:{} y:{} area:{}".format(count,rect[0],rect[1], area))#打印坐标
cv2.rectangle(img_rice,rect,(0,255,0),1)#绘制矩形
if area > 150:
count = count + 1
cv2.putText(img_rice,str({count,count-1}), (rect[0], rect[1]), cv2.FONT_HERSHEY_COMPLEX, 0.4, (0, 255, 0), 1) #在米粒左上角写上编号
else:
cv2.putText(img_rice,str(count), (rect[0], rect[1]), cv2.FONT_HERSHEY_COMPLEX, 0.4, (0, 255, 0), 1) #在米粒左上角写上编号
ave_area = ave_area / count
# 输出
print('总个数是:{},平均面积是:{}'.format(count,ave_area))
cv2.imshow("Contours", img_rice)
cv2.waitKey(0)
cv2.destroyAllWindows()
总结
对于一些米粒粘连严重的图片,这个方法不是很好。但是对于一般的还是可以的,不仅可以用于数米粒,还可以数一些其他的东西,比如玉米粒、芝麻粒、小麦。。。