实验1:图像的阈值与平滑
任务描述
本关任务:理解图像的色彩模式,掌握图像阈值分割的基本原理并对图像进行阈值分割处理。
相关知识
图像阈值化分割是一种最常用,同时也是最简单的图像分割方法,特别适用于处理目标和背景占据不同灰度级范围的图像。
为了完成本关任务,你需要掌握:
- 图像阈值分割的基本原理;
- 对图像进行阈值分割。
图像色彩模式转换
在生活中,大多数看到的彩色图片都是 RGB 类型。RGB 色彩模式是工业界的一种颜色标准,是通过对红 ( R )、绿 ( G )、蓝 ( B )三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色。
但是在执行不同的图像处理任务时,也需要用到灰度图、HSV 、YUV 等不同的颜色制式。OpenCV 提供了cvtColor()
函数来实现这些图像色彩模式的转换。函数表达式如下所示:
st = cv2.cvtColor(src, code)
函数中各参数含义为:
src
:源图片的地址;code
:转换方式。该参数包含了很多的颜色空间转换类型,例如将颜色空间 BGR 转化为 RGB 、RGB 转化为 HSV 空间等。所有的转换方式在enum ColorConversionCodes
中定义,具体参数枚举类型请参阅 API 文档 。
该代码的作用是把图片img
转换为经过图像色彩模式处理的图片st
。
本次任务要用到的转换方式是COLOR_BGR2GRAY
,它表示从 BGR 彩色图像转换为 GRAY 灰度图像。 举个例子:
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
该代码的作用是把img
转化为灰度图img_gray
。如图1所示,该代码把左侧彩色的熊的图片转化为右侧的灰度图。

图 1 彩色图像(左)与其灰度图(右)
注意:
- 在 OpenCV 2.x 时,颜色空间转换
code
用的宏定义是CV_
前缀开头;而在 OpenCV 3.x 版本,其颜色空间转换code
宏定义更改为COLOR_
开头; - 彩色图像与灰度图像的转换是不可逆的。
图像阈值分割
图像阈值分割是最简单的图像分割方法。为了从一副图像中提取出需要的部分,应该用图像中的每一个像素点的灰度值与选取的阈值进行比较,并作出相应的判断。
注意:阈值的选取依赖于具体的问题。即:物体在不同的图像中有可能会有不同的灰度值。
图像阈值化分割不仅可以极大的压缩数据量,而且也大大简化了分析和处理步骤。因此在很多情况下,图像阈值化分割是进行图像分析、特征提取与模式识别之前必要的图像预处理过程。
例如:从一副图像中利用阈值分割出需要的物体部分(当然这里的物体可以是一部分或者整体)。这样的图像分割方法是基于图像中物体与背景之间的灰度差异,而且此分割属于像素级的分割。
一旦找到了需要分割的物体的像素点,就可以对这些像素点设定一些特定的值来表示。
例如:可以将该物体的像素点的灰度值设定为:0
(黑色),其他的像素点的灰度值为:255
(白色)。当然像素点的灰度值可以任意,但最好设定的两种颜色对比度较强,方便观察结果。
在 OpenCV 中,cv2.threshold()
可以用来进行图像阈值分割处理。它的函数表达式为:
ret, dst=cv2.threshold(src,thresh,maxval,type)
其中它的输入参数有:
src
:输入图像,只能输入单通道图像,通常来说为灰度图;thresh
:阈值;maxval
:最大值。当像素值超过了阈值均设置为该值;type
:操作的类型,就是怎么处理阈值。该参数包含八种类型的阈值化操作。列举以下五种常用的类型:cv2.THRESH_BINARY
:超过阈值部分取maxval
(最大值),否则取0
;cV2.THRESH_BINARY_INV
:超过阈值部分取0
,否则取maxval
(最大值)。它与cv2.THRESH_BINARY
是互为反转的方法;cV2.THRESH_TRUNC
:大于阈值部分设为阈值,否则不变。即截断阈值;cV2.THRESH_TOZERO
:大于阈值部分不改变,否则设为0
;cV2.THRESH_TOZERO_INV
: 大于阈值部分为0
,否则不变。它与THRESH_TOZERO
互为反转。
threshold()
函数有两个返回值,其中第二个返回值dst
是处理后的灰度图。当指定了阈值参数thresh
,第一个返回值ret
就是指定的thresh
。
例如,图2中原始图片为 Original Image。使用以上五种阈值化操作类型处理它,可以得到相应的处理后的图片如图2中的 BINARY ~ TOZERO_INV,其中阈值为150
,最大值为255
。
从图2中可以很明显的观察到阈值操作是对图像的灰度图进行的操作,通过亮度来分离背景与物体。并且可以观察到互为反转方法,例如 BINARY 与 BINARY_INV ,对背景与物体的亮度处理相反。
图 2 不同 type 类型下的阈值分割得到的图像
编程要求
根据提示,在右侧编辑器补充 Begin-End 区间代码,实现对给定图片实现阈值分割图像的任务。具体要求如下:
- 使用 OpenCV 提供的函数读取路径下的图片
cat.jpg
; - 交换通道为
RGB
; - 把读入的图片转化为灰度图;
- 分别对灰度图进行阈值化操作:
THRESH_BINARY、THRESH_BINARY_INV、THRESH_TRUNC、THRESH_TOZERO、THRESH_TOZERO_INV
。阈值为150
, 最大值为255
,并且将返回的灰度图分别命名为thresh1 ~ thresh5
。
上代码:
import cv2
import matplotlib.pyplot as plt
def thd():
filepath='/data/workspace/myshixun/task1/'
# 请根据左侧编程要求,完成图像阈值化操作:
########## Begin ##########
img = cv2.imread(filepath+'cat.jpg')
#先将RGB通道转换成BGR通道
img = cv2.cvtColor(img,cv2.COLOR_RGB2BGR)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,thresh1 = cv2.threshold(img_gray,150,255,cv2.THRESH_BINARY)
ret,thresh2 = cv2.threshold(img_gray,150,255,cv2.THRESH_BINARY_INV)
ret,thresh3 = cv2.threshold(img_gray,150,255,cv2.THRESH_TRUNC)
ret,thresh4 = cv2.threshold(img_gray,150,255,cv2.THRESH_TOZERO)
ret,thresh5 = cv2.threshold(img_gray,150,255,cv2.THRESH_TOZERO_INV)
########## End ##########
# 作图并保存到指定路径
titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.savefig(filepath+'out/threthold.png')
实验2:图像的平滑
任务描述
本关任务:理解图像平滑的基本原理,掌握图像平滑的常用方法并对图像进行几种典型的图像平滑操作。
相关知识
图像平滑,即图像模糊,是一种简单且使用频率很高的图像处理方法。
为了完成本关任务,你需要掌握:
- 频域分析;
- 图像平滑的常用方法;
- 使用图像平滑的典型方法对图像进行图像平滑操作。
频域分析与空间域分析
当观察一张图片时,关注的是图像中有多少灰度级(或颜色)及其分布。图像视觉任务常常根据灰度分布的不同来区分不同的图像,这种通过观察图像灰度分布来分类图像称为空间域。例如图1中,右侧直方图就是空间域分析左侧示例图像的一种常用方法。
图 1 示例图片及其直方图
某些图像中包含大量的强度不变的区域(如蓝天),而在其他图像中的灰度变化可能会非常快(如包含许多物体的拥挤的图像)。因此,观察图像中这些变化的频率就构成了另一种分类图像的方法,这个观点称为频域。
频域分析将图像分成从低频到高频的不同部分,低频对应图像强度变化小的区域,而高频是图像强度变化非常大的区域。
频域分析一般对图像进行傅里叶变换,对图像的频谱图进行分析。通过分析图像的频率谱,再结合实际的任务需求,对图像进行高通或低通滤波。如图2频谱所示,值越大,颜色就越亮。可以发现,频谱图中其他地方的值有正有负,趋势是越靠近中央值的绝对值越大。
图 2 菱形白块(左)与其频谱图(右)
图像平滑是为了消除或减弱噪声对图像的干扰,有时也称图像滤波。图像平滑总体上讲可分为空域平滑(滤波)和频域平滑(滤波)两大类,根据滤波器特点也可分为线性滤波和非线性滤波。
空域滤波是直接对图像的数据做空间变换达到滤波的目的。它是一种邻域运算,即输出图像中任何像素的值都是通过采用一定的算法,根据输入图像中对用像素周围一定邻域内像素的值得来的。如果输出像素是输入像素邻域像素的线性组合则称为线性滤波(例如最常见的均值滤波和高斯滤波),否则为非线性滤波(中值滤波等)。
本任务的滤波是指在空间域内做中值滤波、均值滤波等滤波操作。
图像平滑的典型方法
原始图像在采样和传输过程中,不可避免的会受到环境、传感器、传输干扰等各种噪声影响,使图像质量下降。这些噪声会引起图像模糊、损坏图像特征,对图像的分析和处理不利。图像平滑则是抑制噪声,改善图像质量进行的处理。
图 3 带椒盐噪声的图片
下面以图3带椒盐噪声的图片为例,介绍以下几种图像平滑的典型方法。
均值滤波
均值滤波的目标是降低图像的变化率,将每个像素替换为该像素周围像素的均值,这样就可以平滑并替代那些强度变化明显的区域。
在 OpenCV 中,可以通过blur()
函数做到这一点:
dst = cv2.blur(image, ksize)
其中dst
是blur()
处理后返回的图像,参数image
是输入的待处理图像,参数ksize
是均值滤波器核的尺寸。
例如对于图3的带噪图片,可以用 5×5的核处理:
img = cv2.blur(img,(5,5))
处理后的图像为:
图 4 均值滤波后的图片
可以很明显的观察到经过均值滤波后的图片的椒盐噪声也减少了很多,但是图像也出现了明显模糊。
注意:均值滤波易受到噪声的干扰,不能完全消除噪声,只能相对减弱噪声。
方框滤波
方框滤波和均值滤波核基本一致,区别是需不需要均一化处理。
OpenCV 调用boxFilter()
函数实现方框滤波:
result = cv2.boxFilter(src, depth, ksize, normalize)
其中,
src
:表示原始图像;depth
:表示目标图像深度,是int
类型,通常用-1
表示与原始图像一致;ksize
:表示核大小,常用的例如(3,3)
和(5,5)
;normalize
:该属性表示是否对目标图像进行归一化处理。该属性有两个值,True
和False
。- 当
normalize
为True
时需要执行均值化处理,等价于均值滤波cv2.blur
; - 当
normalize
为False
时,不进行均值化处理,实际上为求周围各像素的和。但是这个结果很容易大于255
,发生溢出,当溢出时对应像素值置为255
。
- 当
使用方框滤波对图像进行处理:
img = cv2.boxFilter(img, -1, (5,5), False)
图像处理后的效果为:
图 5 方框滤波后的图片
因为normalize
参数让方框滤波更加灵活,一般在视觉任务中,方框滤波的使用要多于均值滤波的使用。
高斯滤波
在某些情况下,需要对一个像素的周围的像素给予更多的重视。因此,可通过分配权重来重新计算这些周围点的值,这可通过高斯函数的值作为权重方案来解决。
高斯滤波用下面的方法调用:
cv2.GuassianBlur(img, ksize, sigmaX, sigmaY)
高斯滤波函数的前两个参数与均值滤波函数相同。sigmaX, sigmaY
分别表示X,Y
方向的标准偏差。如果仅指定了sigmaX
,则sigmaY
与sigmaX
相同。如果两者都为0
,则函数根据ksize
来自动计算它们的值。
如果使用高斯滤波cv2.GaussianBlur(img, (5,5), 0, 0)
处理图像3,那么得到的平滑图片为:
图 6 高斯滤波后的图片
从图 6 中可以看出,图像也变得模糊,但是相较于均值滤波,高斯滤波在减少噪声的同时也对图像的细节进行了适当的保留。
均值滤波与高斯滤波的不同之处在于:均值滤波中滤波器中每个像素的权重是相同的,即滤波器是线性的,而高斯滤波器中像素的权重与其距中心像素的距离成比例。
中值滤波
均值滤波、高斯滤波和方框滤波都是线性过滤器,即利用模板对邻域内像素灰度进行加权平均。这里介绍非线性过滤器 —— 中值滤波器。
调用中值滤波器的方法与调用其他滤波器的方法类似:
img = cv2.medianBlur(src, ksize)
其中img
是函数返回处理结果。第一个参数src
是待处理图像,第二个参数ksize
表示孔径的线性尺寸(aperture linear size),是int
类型的数。例如ksize
为5,中值滤波器就会使用5×5的范围来计算,即把像素的中心值及其5×5邻域组成了一个数值集,对其进行处理计算,当前像素被其中值替换掉。
注意这个参数
ksize
必须是大于1的奇数,比如:3,5,7,9 ...。
中值滤波器对消除椒盐噪声特别有用,使用中值滤波对图23进行操作,可以得到如图7的图像。
图 7 中值滤波后的图片
由于中值滤波不会处理最大和最小值,所以不会受到噪声的影响。可以很明显地从图7看到,中值滤波已经过滤了几乎所有的椒盐噪声,是目前介绍的方法中效果最好的。
相反,如果直接采用blur()
进行均值滤波,则不会区分这些噪声点,滤波后的图像会受到噪声的影响。中值滤波器在处理图像边缘也有优势,但中值滤波器会清除掉某些区域的纹理(如背景中的树)。
编程要求
根据提示,在右侧编辑器补充 Begin-End 区间的代码,实现图像的平滑处理。具体要求如下:
- 读取
filepath
下的pic.png
作为待处理图像; - 更改图像通道为
RGB
通道顺序; - 分别使用均值滤波、高斯滤波、方框滤波、中值滤波对图像进行滤波操作。返回的图像变量请分别命名为
res1 ~ res4
。其中所有的滤波器核大小为5x5
;高斯滤波的sigmaX、sigmaY
由ksize
计算;方框滤波输出与原始图像一致,不使用归一化处理。
上代码:
import cv2
import matplotlib.pyplot as plt
# 使用图像平滑处理带噪声的图片
def flt():
filepath = '/data/workspace/myshixun/task2/'
# 请根据左侧编程要求,完成图像平滑操作:
########## Begin ##########
img = cv2.imread(filepath+'pic.png')
#注意按照要求转换颜色通道
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
res1 = cv2.blur(img,(5,5))
res2 = cv2.GaussianBlur(img,(5,5),0,0)
res3 = cv2.boxFilter(img,-1,(5,5),False)
res4 = cv2.medianBlur(img,5)
########## End ##########
# 作图并保存到指定路径
titles = ['Blur', 'GaussianBlur', 'boxFilter', 'medianBlur']
images = [res1, res2, res3, res4]
# 分别画出四个子图,并保存为filter.png
for i in range(4):
plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.savefig(filepath+'out/filter.png')