这是我大学课程的数字图像处理的实验报告,代码大部分是从网上直接复制使用,小部分是我自己改写的(例如matplotlib的使用),可以直接运行。内容比较详细,但是希望大家能够先理解一下思路再使用,学习图像处理的思路最重要。
综合利用滤波、分割、图像分割、特征描述等知识,实现大米实际尺度、整精米的检测
检测出图中的碎米,并在相应的米粒上打上标志。
①米粒与背景对比不明显,需要用一个阈值对其进行二值化
②背景存在噪声需要对其进行去噪声
③找到对应的米粒需要对其进行轮廓检测
④找到其中的碎米需要对其进行面积阈值处理,找出面积小的碎米
流程图如下
def adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None):
自适应阈值法(adaptiveThreshold),它的思想不是计算全局图像的阈值,而是根据图像不同区域亮度分布,计算其局部阈值,所以对于图像不同区域,能够自适应计算不同的阈值,因此被称为自适应阈值法。(其实就是局部阈值法)
参数说明:
作用: 因地制宜的二值化处理
def getStructuringElement(shape, ksize, anchor=None):
参数说明:
def morphologyEx(src, op, kernel, dst=None, anchor=None, iterations=None, borderType=None, borderValue=None):
参数说明:
对图像进行一系列的膨胀腐蚀组合
作用:
腐蚀:删除对象边界的某些像素
膨胀:给图像中的对象边界添加像素
def findContours(image, mode, method, contours=None, hierarchy=None, offset=None):
参数说明:
image:单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边 缘检测算子处理过的二值图像
mode:定义轮廓的检索模式,
method:定义轮廓的近似方法
作用: 检测出米粒的轮廓
def fitEllipse(points):
参数说明:
作用: 实现椭圆的拟合
def ellipse(img, center, axes, angle, startAngle, endAngle, color, thickness=None, lineType=None, shift=None):
参数说明:
作用:椭圆的轮廓绘制
cv2.adaptiveThreshold(gray,255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,103, 1)
adaptiveThreshold这个函数我们采用的是ADAPTIVE_THRESH_MEAN_C为领域内均值,THRESH_BINARY黑白二值化
图1为二值化处理后的图像,可以看到虽然整体已经二值化成功但是,还是有一些白点噪声,因此我们还需要进行开运算去噪
element = cv2.getStructuringElement(cv2.MORPH_CROSS,(3, 3))
dst = cv2.morphologyEx(dst,cv2.MORPH_OPEN ,element) #开运算去噪
先腐蚀后膨胀的过程称为开运算。用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。我们利用3*3的十字形模板对其进行开运算处理去除噪声,得到下图,可以看到相比起二值化的图像,开运算处理后的图像噪声点明显减少了很多,可以为我们接下来绘制轮廓奠定基础。
contours, hierarchy = cv2.findContours(dst,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) #轮廓检测函数
代码中用的是CV_RETR_EXTERNAL,只检测最外围轮廓,包含在外围轮廓内的内围,CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours,所有米粒轮廓图如图3
def find_rices(contours):
count=0 #米粒总数
ares_avrg=0 #米粒平均
#遍历找到的所有米粒
# print(contours)
for cont in contours:
# 用于计算图像轮廓的面积。
ares = cv2.contourArea(cont)
print(ares)
if ares < 5:
continue
# ares_avrg += ares
if 100<ares<430:
count += 1
# 当得到对象轮廓后,可用boundingRect()得到包覆此轮廓的最小正矩形
rect = cv2.boundingRect(cont)
# 绘制椭圆拟合曲线
ellipse = cv2.fitEllipse(cont)
# 绘制椭圆圆弧
cv2.ellipse(img, ellipse,(0xff),2)
y = 10 if rect[1] < 10 else rect[1]
cv2.putText(img, str(count), (rect[0], y), cv2.FONT_HERSHEY_COMPLEX, 0.5, (247, 247, 25), 1)
return count
得到轮廓列表后,对其中的轮廓面积值进行判断,找到面积小的碎米,如下图3
import cv2 #导入opencv模块
import matplotlib.pyplot as plt
from matplotlib import font_manager
font = font_manager.FontProperties(fname=r".\OPPOSans-H.ttf")
def origin_image_handle(img):
# 转换为灰度图
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 使用局部阈值的大津算法进行图像二值化
dst = cv2.adaptiveThreshold(gray,255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY,103, 1)
# cv2.imshow('111',dst)
# 返回一个十字形的Mat型矩阵。
element = cv2.getStructuringElement(cv2.MORPH_CROSS,(3, 3))
dst=cv2.morphologyEx(dst,cv2.MORPH_OPEN ,element) #开运算去噪
cv2.imshow('2222',dst)
contours, hierarchy = cv2.findContours(dst,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) #轮廓检测函数
return dst,contours
def find_rices(contours):
count=0 #米粒总数
ares_avrg=0 #米粒平均
#遍历找到的所有米粒
# print(contours)
for cont in contours:
# 用于计算图像轮廓的面积。
ares = cv2.contourArea(cont)
print(ares)
if ares < 5:
continue
# ares_avrg += ares
if 100<ares<430:
count += 1
# 当得到对象轮廓后,可用boundingRect()得到包覆此轮廓的最小正矩形
rect = cv2.boundingRect(cont)
# 绘制椭圆拟合曲线
ellipse = cv2.fitEllipse(cont)
# 绘制椭圆圆弧
cv2.ellipse(img, ellipse,(0xff),2)
y = 10 if rect[1] < 10 else rect[1]
cv2.putText(img, str(count), (rect[0], y), cv2.FONT_HERSHEY_COMPLEX, 0.5, (247, 247, 25), 1)
# count += 1
# # 当得到对象轮廓后,可用boundingRect()得到包覆此轮廓的最小正矩形
# rect = cv2.boundingRect(cont)
#
# # 绘制椭圆拟合曲线
# ellipse = cv2.fitEllipse(cont)
# # 绘制椭圆圆弧
# cv2.ellipse(img, ellipse, (0xff), 2)
# y = 10 if rect[1] < 10 else rect[1]
# cv2.putText(img, str(count), (rect[0], y), cv2.FONT_HERSHEY_COMPLEX, 0.5, (247, 247, 25), 1)
return count
if __name__ == '__main__':
img = cv2.imread("img.png")
# 打开一个新的窗口
plt.figure()
plt.subplot(1, 3, 1,)
plt.title('原始图像',fontproperties = font)
# 显示原始图像
plt.imshow(img)
dst, contours= origin_image_handle(img)
plt.subplot(1, 3, 2)
plt.title('二值化图',fontproperties = font)
# 由于opencv和matplotlib的色彩通道不同,由于OpenCV是以BGR模式加载图像,而matplotlib则是以常见的RGB模式显示图像
dst1 = cv2.cvtColor(dst, cv2.COLOR_RGB2BGR)
# 二值化图
plt.imshow(dst1)
### 显示标记图
count = find_rices(contours)
plt.subplot(1,3,3)
plt.title('标记图',fontproperties = font)
plt.text(s='估计有{}颗碎粒'.format(count), x=130, y=500, fontproperties=font)
plt.imshow(img)
cv2.imshow('121212',img)
plt.show()
cv2.waitKey()
如图5为原始图像,二值化开运算处理图像,碎米标记图的三个对比图,可以估计到碎米的数量为18颗
这次实验相比起之前的实验所涉及的图像处理方法丰富了许多,首先利用的到了图像分割的技术,阈值处理,也利用到了滤波对图像的噪声进行滤除,最后通过特征描述找到米粒的轮廓,并且利用米粒的大小进行碎米选择,描绘出轮廓。其中我也遇到了许多的问题,例如碎米阈值的选择,开运算中模板算子的选择,在尝试许多次后才找到最优解
如果觉得写的不错的小伙伴记得给个赞哦!欢迎评论与我交流!