OpenCV 图像预处理:颜色操作与灰度、二值化处理详解
在计算机视觉任务中,图像预处理是至关重要的一步,它直接影响后续算法的性能和结果的准确性。OpenCV 作为强大的计算机视觉库,提供了丰富的图像预处理函数。本文将聚焦于图像预处理中的颜色加法、颜色转换、灰度实验以及二值化处理,深入探讨它们的原理、实现方法和关键要点。
目录
颜色加法
原理
OpenCV 实现
关键要点
颜色转换
常见颜色空间及转换
关键要点
灰度实验
转换原理
最大值法
平均值法
加权均值法
关键要点
二值化处理
阈值法
反阈值法
截断阈值法
低阈值零处理与超阈值零处理
OTSU阈值法
自适应二值化
关键要点
总结
颜色加法
颜色加法是图像预处理中一种简单但常用的操作,它通过对图像的像素值进行加法运算,来改变图像的亮度、对比度或实现特殊的视觉效果。
原理
图像由众多像素点组成,每个像素点的颜色可以用不同的通道值来表示(如 RGB 图像有红、绿、蓝三个通道)。颜色加法就是将两个图像对应像素点的通道值或一个图像与一个常量的通道值进行相加。需要注意的是,像素值通常在 0-255 的范围内,相加后如果超过 255,需要进行截断处理,保持在 255。
OpenCV 实现
在 OpenCV 中,可以使用cv2.add()函数实现图像的颜色加法。其语法如下:
dst = cv2.add(src1, src2)
其中,src1和src2可以是两个图像(需大小和通道数相同),也可以是一个图像和一个标量(常量)。例如,将图像与一个常量相加可以提高图像的亮度:
import cv2
import numpy as np
img = cv2.imread('image.jpg')
# 定义一个常量,每个通道都增加50
value = np.ones_like(img, dtype=np.uint8) * 50
# 进行颜色加法
dst = cv2.add(img, value)
cv2.imshow('Original', img)
cv2.imshow('Added', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
图像与图像相加:
图像相加常用饱和操作(截断至255),颜色加权加法,(不推荐numpy直接相加,可能导致失真)。
import cv2
import numpy as np# 读取图像
cao = cv2.imread('../src/cao.png')
pig = cv2.imread('../src/pig.png')
# 饱和操作 cv2.add(img1, img2) np.uint8 0~255 250+44 = 255
dst1 = cv2.add(cao, pig)
cv2.imshow('饱和操作', dst1)
# print(dst1)
# numpy直接相加 像素值超过255不会截断到255而是取溢出部分,250 + 10 = 4
dst2 = cao + pig
cv2.imshow('numpy直接相加', dst2)
x = np.uint8([[250]])
y = np.uint8([[44]])
xy1 = cv2.add(x, y)
print(xy1)
xy2 = x + y
print(xy2)# 颜色加权加法 cv2.addWeighted(img1, alpha, img2, beta, gamma)
dst3 = cv2.addWeighted(cao, 0.7, pig, 0.3, 10)
cv2.imshow('颜色加权加法', dst3)cv2.waitKey(0)
cv2.destroyAllWindows()
三种加法所得的图像特征十分明显:
饱和操作 | numpy直接加法 | 颜色加权加法 |
![]() | ![]() | ![]() |
关键要点
-
像素值截断:由于像素值范围限制,相加后超过 255 的部分会被截断为 255,这可能导致图像细节丢失,在使用时需根据实际情况调整加法的数值。
-
图像与标量相加:这种方式常用于整体调整图像的亮度,标量值越大,图像越亮,但过大可能导致图像过曝。
-
图像与图像相加:可用于叠加两个图像的信息,但要求两个图像的尺寸和通道数必须一致,否则会报错。
颜色转换
在不同的计算机视觉任务中,往往需要不同的颜色空间。颜色转换就是将图像从一种颜色空间转换到另一种颜色空间,以更好地适应后续的处理需求。
常见颜色空间及转换
-
RGB 颜色空间:是最常用的颜色空间之一,基于红、绿、蓝三种基本颜色的组合来表示各种颜色。
-
HSV 颜色空间:由色调(Hue)、饱和度(Saturation)和明度(Value)组成,更符合人眼对颜色的感知,在颜色分割等任务中应用广泛。
色调H:
使用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,紫色为300°。通过改变H的值,可以选择不同的颜色
饱和度S:
饱和度S表示颜色接近光谱色的程度。一种颜色可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例越大,颜色接近光谱色的程度就越高,颜色的饱和度就越高。饱和度越高,颜色就越深而艳,光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,其中0%表示灰色或无色,100%表示纯色,通过调整饱和度的值,可以使颜色变得更加鲜艳或者更加灰暗。
明度V:
明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白),通过调整明度的值,可以使颜色变得更亮或者更暗。|
-
灰度空间:将彩色图像转换为黑白图像,每个像素点只有一个亮度值,灰度空间是单通道图像。
OpenCV 中提供了cv2.cvtColor()函数来实现颜色空间的转换,其语法如下:
dst = cv2.cvtColor(src, code)
其中,src是输入图像,code是颜色转换的标志,如:
cv2.COLOR_BGR2RGB表示将 BGR 图像转换为 RGB 图像(OpenCV 读取图像默认是 BGR 格式),
cv2.COLOR_BGR2HSV表示将 BGR 图像转换为 HSV 图像,
cv2.COLOR_BGR2GRAY表示将 BGR 图像转换为灰度图像。
关键要点
-
转换标志的选择:不同的颜色空间转换需要对应正确的转换标志,错误的标志会导致转换结果错误。
-
数据类型:某些颜色空间的通道值范围与 RGB 不同(如 HSV 中 H 的范围是 0-179,S 和 V 是 0-255),在后续处理中需注意数据范围的差异。
-
应用场景:根据具体任务选择合适的颜色空间,例如在目标跟踪中,HSV 颜色空间对光照变化的鲁棒性更强,比 RGB 更适合用于目标的颜色建模。
灰度实验
灰度图像是只有亮度信息而没有颜色信息的图像,灰度实验就是将彩色图像转换为灰度图像,并研究其在不同场景下的应用效果。
转换原理
常了解的转灰度图有三种方法,分别是最大值法、平均值法以及加权均值法。cv2.COLOR_BGR2 GRAY本质就是使用的加权均值法。
最大值法
对于彩色图像的每个像素,它会从R、G、B三个通道的值中选出最大的一个,并将其作为灰度图像中对应位置的像素值。
import cv2
import numpy as npcat = cv2.imread('../src/1.jpg')
cat_shape = cat.shape
cat_gray = np.zeros((cat_shape[0], cat_shape[1]), dtype=np.uint8)
# 最大值法
for i in range(cat_shape[0]):for j in range(cat_shape[1]):cat_gray[i, j] = max(cat[i, j, 0], cat[i, j, 1], cat[i, j, 2])cv2.imshow('cat', cat)
cv2.imshow('cat_gray', cat_gray)
cv2.waitKey(0)
平均值法
对于彩色图像的每个像素,它会将R、G、B三个通道的像素值全部加起来,然后再除以三,得到的平均值就是灰度图像中对应位置的像素值。
import cv2
import numpy as npcat = cv2.imread('../src/1.jpg')
cat_shape = cat.shape
cat_gray = np.zeros((cat_shape[0], cat_shape[1]), dtype=np.uint8)
# 平均值法
for i in range(cat_shape[0]):for j in range(cat_shape[1]):# int():转换为整型,防止溢出cat_gray[i, j] = (int(cat[i, j, 0]) + int(cat[i, j, 1]) + int(cat[i, j, 2])) // 3cv2.imshow('cat', cat)
cv2.imshow('cat_gray', cat_gray)
cv2.waitKey(0)
加权均值法
对于彩色图像的每个像素,它会按照一定的权重去乘以每个通道的像素值,并将其相加,得到最后的值就是灰度图像中对应位置的像素值。本实验中,权重的比例为: R乘以0.299,G乘以0.587,B乘以0.114,这是经过大量实验得到的一个权重比例,也是一个比较常用的权重比例。
import cv2
import numpy as npcat = cv2.imread('../src/1.jpg')
cat_shape = cat.shape
cat_gray = np.zeros((cat_shape[0], cat_shape[1]), dtype=np.uint8)
# 加权均值法
for i in range(cat_shape[0]):for j in range(cat_shape[1]):cat_gray[i, j] = round(cat[i, j, 0] * 0.3 + cat[i, j, 1] * 0.59 + cat[i, j, 2] * 0.11)cv2.imshow('cat', cat)
cv2.imshow('cat_gray', cat_gray)
cv2.waitKey(0)
最大值法 | 平均值法 | 加权均值法 |
![]() | ![]() | ![]() |
关键要点
-
信息简化:灰度图像去除了颜色信息,减少了数据量,能提高后续算法的运行效率,适合用于特征提取、边缘检测等任务。
-
转换效果:不同的灰度转换公式可能会得到不同的灰度效果,在一些对灰度精度要求较高的场景,需要选择合适的转换公式。
-
应用场景:在文字识别、人脸识别等任务中,灰度图像往往能取得较好的效果,因为这些任务的关键信息更多地体现在亮度差异上。
二值化处理
二值化处理是将灰度图像转换为只有黑白两种颜色的图像,即像素值非 0 即 255(或非 0 即 1),它能进一步简化图像信息,突出目标区域。
二值化处理主要的几种方法:阈值法,反阈值法,截断阈值法,低阈值零处理,超阈值零处理,OSTU阈值法,自适应二值化。
阈值法
阈值法就是通过设置一个阈值,将灰度图中的每一个像素值与该阈值进行比较,小于等于阈值的像素就被设置为0(黑),大于阈值的像素就被设置为maxval。
import cv2
import numpy as np
# 阈值法
image = cv2.imread('../src/flower.png', cv2.IMREAD_GRAYSCALE)
# image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)_,binary = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)
# print(thresh)
cv2.imshow('binary', binary)
cv2.waitKey(0)
cv2.destroyAllWindows()
![]() | ![]() |
灰度图 | 阈值法二值化图 |
反阈值法
顾名思义,就是与阈值法相反。反阈值法是当灰度图的像素值大于阈值时,该像素值将会变成0(黑),当灰度图的像素值小于等于阈值时,该像素值将会变成maxval。
import cv2
import numpy as np
# 反阈值法
image = cv2.imread('../src/flower.png', cv2.IMREAD_GRAYSCALE)
image = cv2.resize(image, (500, 500))
_,binary_inv = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY_INV)cv2.imshow('binary_inv', binary_inv)
cv2.waitKey(0)
cv2.destroyAllWindows()
![]() | ![]() |
灰度图 | 反阈值法二值化图 |
截断阈值法
截断阈值法,指将灰度图中的所有像素与阈值进行比较,像素值大于阈值的部分将会被修改为阈值,小于等于阈值的部分不变。换句话说,经过截断阈值法处理过的二值化图中的最大像素值就是阈值。
import cv2
import numpy as np
# 截断阈值法
image = cv2.imread('../src/flower.png', cv2.IMREAD_GRAYSCALE)
image = cv2.resize(image, (500, 500))
cv2.imshow('image', image)
_,binary_trunc = cv2.threshold(image, 255, 255, cv2.THRESH_TRUNC)cv2.imshow('binary_trunc', binary_trunc)
cv2.waitKey(0)
![]() | ![]() |
灰度图 | 截断阈值法二值化图 |
当截断阈值为255时,如上图所示,可以看到灰度图与二值化图没有任何的区别。
低阈值零处理与超阈值零处理
低阈值零处理,字面意思,就是像素值小于等于阈值的部分被置为0(也就是黑色),大于阈值的部分不变。超阈值零处理就是将灰度图中的每个像素与阈值进行比较,像素值大于阈值的部分置为0(也就是黑色),像素值小于等于阈值的部分不变。
import cv2
import numpy as np# 低阈值零处理
image = cv2.imread('../src/flower.png', cv2.IMREAD_GRAYSCALE)
image = cv2.resize(image, (500, 500))
cv2.imshow('image', image)
_, binary1 = cv2.threshold(image, 127, 255, cv2.THRESH_TOZERO)
# 超阈值零处理
__, binary2 = cv2.threshold(image, 127, 255, cv2.THRESH_TOZERO_INV)cv2.imshow('Binary Image', binary1)
cv2.imshow('Binary Image Inverted', binary2)
cv2.waitKey(0)
![]() | ![]() | ![]() |
灰度图 | 低阈值零处理 | 超阈值零处理 |
OTSU阈值法
在介绍OTSU阈值法之前,我们首先要了解一下双峰图的概念。
双峰图就是指灰度图的直方图上有两个峰值,直方图就是对灰度图中每个像素值的点的个数的统计图,如下图所示。
OTSU算法是通过一个值将这张图分前景色和背景色(也就是灰度图中小于这个值的是一类,大于这个值的是一类),通过统计学方法(最大类间方差)来验证该值的合理性,当根据该值进行分割时,使用最大类间方差计算得到的值最大时,该值就是二值化算法中所需要的阈值。通常该值是从灰度图中的最小值加1开始进行迭代计算,直到灰度图中的最大像素值减1,然后把得到的最大类间方差值进行比较,来得到二值化的阈值。
import cv2
import numpy as np
# OTSU阈值法
image = cv2.imread('../src/flower.png', cv2.IMREAD_GRAYSCALE)
image = cv2.resize(image, (500, 500))
cv2.imshow('image', image)
thresh,binary_OTSU = cv2.threshold(image, 200, 255, cv2.THRESH_OTSU)
print(thresh)
cv2.imshow('binary_OTSU', binary_OTSU)thresh,binary_OTSU_inv = cv2.threshold(image, 200, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)
print(thresh)
cv2.imshow('binary_OTSU_inv', binary_OTSU_inv)cv2.waitKey(0)
OTSU阈值法使用API时需指定使用阈值法或饭阈值法(默认使用阈值法),因为OTSU本质上是通过算法算出最合适的阈值,需搭配阈值法或反阈值法进行对应操作。
![]() | ![]() |
OTSU搭配阈值法 | OTSU搭配反阈值法 |
自适应二值化
与二值化算法相比,自适应二值化更加适合用在明暗分布不均的图片,因为图片的明暗不均,导致图片上的每一小部分都要使用不同的阈值进行二值化处理,这时候传统的二值化算法就无法满足我们的需求了,于是就出现了自适应二值化。
自适应二值化分为取平均值和加权求和,两者解释原理较多,所以这里只给出二值化代码和效果图0.o!!
假如我们使用的小区域是3*3的,那么就会从图片的左上角开始(也就是像素值为162的地方)计算其区域内的阈值,如果处于边缘地区就会对边界进行填充,填充值就是边界的像素点,如下图所示:
![]() | ![]() |
取平均值 | 加权求和 |
import cv2
import numpy as np
# 自适应阈值法
image = cv2.imread('../src/xp.jpg', cv2.IMREAD_GRAYSCALE)
image = cv2.resize(image, (500, 500))
cv2.imshow('image', image)
# 取均值
auto_mean = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 7, 10)
# 取高斯均值
auto_gaussian = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 7, 10)
cv2.imshow('auto_mean', auto_mean)
cv2.imshow('auto_gaussian', auto_gaussian)
cv2.waitKey(0)
![]() | ![]() |
取平均值 | 加权均值 |
可以看出加权均值所得出来的二值图会比取平均值得到的轮廓更细致。
关键要点
-
阈值选择:阈值的选择对二值化效果影响极大,合适的阈值能很好地分离目标和背景,可通过实验法、Otsu 算法(cv2.THRESH_OTSU)等方法确定阈值。
-
二值化类型:根据目标和背景的明暗关系选择合适的二值化类型,以确保目标区域被正确突出。
-
图像质量:二值化处理对图像的噪声比较敏感,在二值化之前通常需要进行降噪处理,如高斯模糊,以提高二值化效果。
总结
颜色加法、颜色转换、灰度实验和二值化处理是 OpenCV 图像预处理中的重要操作。颜色加法可调整图像视觉效果,颜色转换能适应不同任务需求,灰度实验简化图像信息,二值化处理进一步突出目标。掌握这些操作的原理、实现方法和关键要点,能帮助我们更好地进行图像预处理,为后续的计算机视觉任务奠定坚实的基础。在实际应用中,需根据具体场景灵活选择和组合这些操作,以达到最佳的预处理效果。