往期文章:
OpenCV 零基础到项目实战 | DAY 1:图像基础与核心操作
目录
[1. 普通加法](#1. 普通加法)
[2. 加权加法](#2. 加权加法)
[二、色彩空间转换:从 RGB 到 HSV](#二、色彩空间转换:从 RGB 到 HSV)
[1. RGB 与 HSV 的区别](#1. RGB 与 HSV 的区别)
[2. 转换示例代码](#2. 转换示例代码)
[1. 最大值法](#1. 最大值法)
[2. 平均值法](#2. 平均值法)
[3. 加权均值法(推荐)](#3. 加权均值法(推荐))
[1. 核心函数与参数](#1. 核心函数与参数)
[2. 图像阈值处理的标志位](#2. 图像阈值处理的标志位)
[4. 自适应二值化:应对光照不均](#4. 自适应二值化:应对光照不均)
[1. 图像翻转](#1. 图像翻转)
[2. 仿射变换](#2. 仿射变换)
图像预处理是计算机视觉任务的基础,它能提升图像质量、突出关键特征,为后续的检测、识别等任务奠定基础。本文基于 OpenCV 库,详细讲解图像预处理的核心技术,包括颜色操作、色彩空间转换、灰度化、二值化、几何变换等,附完整代码示例,适合入门者快速上手。
一、颜色操作:加法与加权融合
在处理图像时,经常需要对多张图像进行融合或叠加,OpenCV 提供了两种常用的加法操作:普通加法和加权加法。
1. 普通加法
cv.add () 与 Numpy 加法的区别
OpenCV 的cv.add()
函数和 Numpy 的直接加法(img1 + img2
)都能实现图像叠加,但存在本质区别:
- cv.add():饱和操作(像素值超过 255 时取 255);
- Numpy 加法:模运算(像素值超过 255 时对 256 取模)。
示例代码:
python
import cv2 as cv
import numpy as np
# 读取图像(需保证尺寸和类型一致)
img1 = cv.imread('./images/cao.png')
img2 = cv.imread('./images/pig.png')
# OpenCV加法(饱和操作)
dst1 = cv.add(img1, img2)
# Numpy加法(模运算)
dst2 = img1 + img2
# 验证差异(以像素值为例)
x = np.uint8([[250]])
y = np.uint8([[10]])
print(cv.add(x, y)) # 输出[[255]](饱和)
print(x + y) # 输出[[4]](250+10=260对256取模)
注意:两张图像必须具有相同的尺寸和数据类型。
2. 加权加法
cv.addWeighted () 实现平滑融合
加权加法可通过调整权重控制两张图像的融合比例,公式为:dst = img1*alpha + img2*beta + gamma
,其中gamma
用于调整亮度。
函数参数:
src1
/src2
:输入图像;alpha
/beta
:两张图像的权重(需满足alpha + beta = 1
以避免过曝);gamma
:亮度调整值(gamma>0
变亮,gamma<0
变暗)。
示例代码:
python
# 加权融合:img1占20%,img2占80%,亮度微调+0.15
dst3 = cv.addWeighted(img1, 0.2, img2, 0.8, 0.15)
cv.imshow('加权融合结果', dst3)
cv.waitKey(0)
cv.destroyAllWindows()
python
import cv2 as cv
import numpy as np
# 读图
cao = cv.imread('./images/cao.png')
pig = cv.imread('./images/pig.png')
# 饱和操作 cv.add(img1,img2)
dst1 = cv.add(cao,pig)
# numpy直接相加 取模运算 对256取模:250+10=4
dst2 = cao + pig
# 举个例子
x=np.uint8([[250]])
y=np.uint8([[10]])
xy1=cv.add(x,y)
xy2=x+y
print(xy1,xy2)
# 颜色加权加法 cv.addWeighted(img1,alpha,img2,beta,gamma)
dst3 = cv.addWeighted(cao,0.2,pig,0.8,0.15)
cv.imshow('addWeighted',dst3)
cv.waitKey(0)
cv.destroyAllWindows()

二、色彩空间转换:从 RGB 到 HSV
不同的色彩空间适用于不同的任务(如 RGB 适合显示,HSV 适合颜色分割),OpenCV 的cv.cvtColor()
函数可实现多种色彩空间的转换。
1. RGB 与 HSV 的区别
- RGB:基于红、绿、蓝三原色的加色模型,适合显示器显示,但对光照敏感;
- HSV:基于色调(H)、饱和度(S)、亮度(V),更符合人眼对颜色的感知,常用于颜色过滤。
HSV 各参数含义:
- 色调(H):0°~360°(OpenCV 中压缩为 0~179),红色 0°、绿色 120°、蓝色 240°;
- 饱和度(S):0%~100%(OpenCV 中 0~255),值越高颜色越鲜艳;
- 亮度(V):0%~100%(OpenCV 中 0~255),值越高颜色越亮。

2. 转换示例代码
python
import cv2 as cv
# 读取图像
img = cv.imread('./images/1.jpg')
# RGB转灰度图
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# RGB转HSV
hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
# HSV转RGB(注意OpenCV默认读取为BGR,需转RGB后显示)
rgb = cv.cvtColor(img, cv.COLOR_BGR2RGB)
cv.imshow('RGB图像', rgb)
cv.waitKey(0)
cv.destroyAllWindows()
三、灰度化:从彩色到单通道
灰度化将彩色图像(3 通道)转换为灰度图像(1 通道),减少数据量的同时保留关键轮廓信息。常见方法有 3 种:
1. 最大值法
取每个像素 R、G、B 三通道的最大值作为灰度值,优点是图像亮度高。
python
import cv2 as cv
import numpy as np
img = cv.imread('./images/pig.png')
h, w = img.shape[:2]
gray = np.zeros((h, w), dtype=np.uint8)
# 遍历每个像素,取三通道最大值
for i in range(h):
for j in range(w):
gray[i, j] = max(img[i,j,0], img[i,j,1], img[i,j,2])
cv.imshow('最大值法灰度图', gray)
cv.waitKey(0)
2. 平均值法
取三通道值的平均值作为灰度值,计算简单但可能丢失对比度。
python
import cv2 as cv
import numpy as np
# 读图
pig = cv.imread('./images/pig.png')
shape = pig.shape #(h,w,c)
img = np.zeros((shape[0],shape[1]),dtype=np.uint8)
# 循环遍历每一行
for i in range(shape[0]):
for j in range(shape[1]):
img[i,j] = (int(pig[i,j,0]) + int(pig[i,j,1]) + int(pig[i,j,2])) // 3
cv.imshow('img',img)
cv.waitKey(0)
cv.destroyAllWindows()
3. 加权均值法(推荐)
根据人眼对绿、红、蓝的敏感度分配权重(G:0.587、R:0.299、B:0.114),更符合视觉感知。
python
import cv2 as cv
import numpy as np
# 读取图像
pig =cv.imread("../images/pig.png")
shape=pig.shape #(h,w,c)
img=np.zeros((shape[0],shape[1]),dtype=np.uint8)
# 定义权重
wb,wg,wr=0.114,0.587,0.299
# 循环遍历 每一行 img[0,0,0]
for i in range(shape[0]): # [0,0] [0,1] [0,2] [1,0]
for j in range(shape[1]):
img[i,j]=round(wb*pig[i,j,0]+wg*pig[i,j,1]+wr*pig[i,j,2])
cv.imshow("dst",img)
cv.waitKey(0)
cv.destroyAllWindows()
四、图像二值化:黑白分明的世界
二值化将灰度图转换为仅含 0(黑)和 255(白)的图像,常用于文字识别、轮廓提取等场景。OpenCV 提供了多种阈值处理方法,核心函数是cv.threshold()
。
1. 核心函数与参数
python
ret, dst = cv.threshold(src, thresh, maxval, type)
src
:输入灰度图;thresh
:阈值(手动指定,自动阈值法设为 0);maxval
:超过阈值时的最大值(通常为 255);type
:阈值处理类型(如cv.THRESH_BINARY
)。
2. 图像阈值处理的标志位
中文名称 | 核心功能说明 | 典型使用场景 |
---|---|---|
cv.THRESH_BINARY |
二值化 | 像素>阈值→设为maxVal;否则→0 |
cv.THRESH_BINARY_INV |
反向二值化 | 像素>阈值→0;否则→maxVal |
cv.THRESH_TRUNC |
截断 | 像素>阈值→设为阈值;否则→保持原值 |
cv.THRESH_TOZERO |
阈值取零 | 像素>阈值→保持原值;否则→0 |
cv.THRESH_TOZERO_INV |
反向阈值取零 | 像素>阈值→0;否则→保持原值 |
cv.THRESH_OTSU |
大津算法 | 自动计算最优阈值(基于直方图双峰分布),需与基本类型组合,阈值参数设为0 |
cv.THRESH_TRIANGLE |
三角算法 | 自动计算阈值(基于单峰直方图),需与基本类型组合,阈值参数设为0 |
cv.THRESH_MASK |
掩码模式 | 仅处理掩码区域内的像素,其他区域不变,需配合掩码图像使用 |
阈值法(THRESH_BINARY)
阈值法就是通过设置一个阈值,将灰度图中的每一个像素值与该阈值进行比较,小于等于阈值的像素就被设置为0(通常代表背景),大于阈值的像素就被设置为maxval(通常代表前景)。
python
import cv2 as cv
import numpy as np
# 读图
flower = cv.imread('./images/flower.png')
flower = cv.resize(flower, (500, 500))
# 灰度化处理
gray = cv.cvtColor(flower, cv.COLOR_BGR2GRAY)
# 二值化处理,使用超阈值零处理
t1,binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
print(t1)
cv.imshow('binary', binary)
cv.waitKey(0)
cv.destroyAllWindows()
反阈值法(THRESH_BINARY_INV)
python
import cv2 as cv
import numpy as np
# 读图
flower = cv.imread('./images/flower.png')
flower = cv.resize(flower, (500, 500))
# 灰度化处理
gray = cv.cvtColor(flower, cv.COLOR_BGR2GRAY)
# 二值化处理,使用超阈值零处理
t1,binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY_INV)
print(t1)
cv.imshow('binary', binary)
cv.waitKey(0)
cv.destroyAllWindows()

截断阈值法(THRESH_TRUNC)
截断阈值法,指将灰度图中的所有像素与阈值进行比较,像素值大于阈值的部分将会被修改为阈值,小于等于阈值的部分不变。换句话说,经过截断阈值法处理过的二值化图中的最大像素值就是阈值。
python
import cv2 as cv
import numpy as np
# 读图
flower = cv.imread('./images/flower.png')
flower = cv.resize(flower, (500, 500))
# 灰度化处理
gray = cv.cvtColor(flower, cv.COLOR_BGR2GRAY)
# 二值化处理,使用截断阈值法
_, binary = cv.threshold(gray, 170, 255, cv.THRESH_TRUNC)
cv.imshow('binary', binary)
cv.waitKey(0)
cv.destroyAllWindows()

低阈值零处理(THRESH_TOZERO)
低阈值零处理,就是像素值小于等于阈值的部分被置为0(也就是黑色),大于阈值的部分不变。
python
import cv2 as cv
import numpy as np
# 读图
flower = cv.imread('./images/flower.png')
flower = cv.resize(flower, (500, 500))
# 灰度化处理
gray = cv.cvtColor(flower, cv.COLOR_BGR2GRAY)
# 二值化处理,使用低阈值零处理
_,binary = cv.threshold(gray, 127, 255, cv.THRESH_TOZERO)
cv.imshow('binary', binary)
cv.waitKey(0)
cv.destroyAllWindows()

超阈值零处理 cv.THRESH_TOZERO_INV
python
import cv2 as cv
import numpy as np
# 读图
flower = cv.imread('./images/flower.png')
flower = cv.resize(flower, (500, 500))
# 灰度化处理
gray = cv.cvtColor(flower, cv.COLOR_BGR2GRAY)
# 二值化处理,使用超阈值零处理
_,binary = cv.threshold(gray, 127, 255, cv.THRESH_TOZERO_INV)
cv.imshow('binary', binary)
cv.waitKey(0)
cv.destroyAllWindows()

自动阈值法:OTSU 算法
OTSU 算法的核心思想是最大化前景与背景之间的类间方差。类间方差越大,说明前景和背景的差异越明显,分割效果越好。
-
假设图像灰度值范围为[0,255],遍历所有可能的阈值k,将像素分为两类:
背景:灰度值 ≤ k 的像素; 前景:灰度值 > k 的像素。
-
计算两类像素的类间方差,选择使类间方差最大的k作为最优阈值。
注意:使用OTSU算法计算阈值时,组件中的thresh参数将不再有任何作用
python
import cv2 as cv
import numpy as np
# 读图
flower = cv.imread('./images/flower.png')
flower = cv.resize(flower, (500, 500))
# 灰度化处理
gray = cv.cvtColor(flower, cv.COLOR_BGR2GRAY)
# 二值化处理,使用超阈值零处理
t1,binary = cv.threshold(gray, 40, 255, cv.THRESH_BINARY|cv.THRESH_OTSU)
print(t1) # 132.0
cv.imshow('binary', binary)
cv.waitKey(0)
cv.destroyAllWindows()

4. 自适应二值化:应对光照不均
传统阈值法(如cv.threshold
)对整幅图像使用单一阈值,当图像存在光照不均匀时(如半边亮半边暗),会导致部分区域过度分割或分割不足。而自适应二值化的核心思想是:
将图像划分为多个小区域,每个区域单独计算适合的阈值,使亮区暗区都能得到良好的分割效果。
python
# 自适应二值化(均值法)
binary = cv.adaptiveThreshold(
gray, 255, # 输入图、最大值
cv.ADAPTIVE_THRESH_MEAN_C, # 块内均值作为阈值
cv.THRESH_BINARY, # 二值化类型
5, 1.2 # 块大小(5x5,需为奇数)、阈值微调值
)
maxval:最大阈值,一般为255
adaptiveMethod:小区域阈值的计算方式:
ADAPTIVE_THRESH_MEAN_C
:块内均值作为阈值;

ADAPTIVE_THRESH_GAUSSIAN_C
:高斯加权均值作为阈值(更平滑)。

thresholdType:二值化方法,只能使用阈值法和反阈值法
blocksize:选取的小区域的面积,如7就是7*7的小块。(blocksize必须是大于 1 的奇数)
c:最终阈值等于小区域计算出的阈值再减去此值
python
import cv2 as cv
import numpy as np
# 读图
flower = cv.imread('./images/flower.png')
flower = cv.resize(flower, (500, 500))
# 灰度化处理
gray = cv.cvtColor(flower, cv.COLOR_BGR2GRAY)
# 二值化处理,使用超阈值零处理
binary = cv.adaptiveThreshold(
gray, # 输入图像
255, # 最大值
cv.ADAPTIVE_THRESH_MEAN_C, # 自适应方法:邻域均值
# cv.ADAPTIVE_THRESH_GAUSSIAN_C, # 自适应方法:小区域内加权求和,权重是个高斯核
cv.THRESH_BINARY, # 二值化类型
5, # 邻域大小(7x7像素)
1.2 # 从均值中减去的常数(微调阈值)
)# 注意:cv.adaptiveThreshold() 不返回计算的阈值,直接返回二值化结果
cv.imshow('binary', binary)
cv.waitKey(0)
cv.destroyAllWindows()

五、几何变换:翻转与仿射变换
几何变换用于调整图像的位置、角度、形状,常见操作包括翻转、旋转、平移、缩放、剪切等。
1. 图像翻转
cv2.flip ()
快速实现水平、垂直或组合翻转:
python
# 翻转类型:0(垂直)、>0(水平)、<0(水平+垂直)
flip_vertical = cv.flip(img, 0) # 垂直翻转
flip_horizontal = cv.flip(img, 1) # 水平翻转
flip_both = cv.flip(img, -1) # 水平+垂直翻转
2. 仿射变换
cv2.warpAffine(img,M,dsize) // 仿射变换函数
img:输入图像。 M:2x3的变换矩阵,类型为np.float32
。
dsize:输出图像的尺寸,形式为(width,height)
。
仿射变换是线性变换 + 平移的组合,通过 2x3 矩阵实现,支持旋转、平移、缩放、剪切等。
仿射变换是一种线性变换 + 平移的组合,在二维空间中可表示为:


(1)旋转
使用cv2.getRotationMatrix2D()
生成旋转矩阵:
cv2.getRotationMatrix2D(center,angle,scale)
center:旋转中心点的坐标,格式为(x,y)
。
angle:旋转角度,单位为度,正值表示逆时针旋转负值表示顺时针旋转。
scale:缩放比例,若设为1,则不缩放。
返回值M:2x3的旋转矩阵。
python
import cv2 as cv
import numpy as np
# 读图
cat = cv.imread('./images/cat1.png')
# 获取旋转矩阵 cv2.getRotationMatrix2D(旋转中心,旋转角度,缩放因子) 2×3
M = cv.getRotationMatrix2D((300,400),-45,1)
cat = cv.warpAffine(cat,M,(600,800))
cv.imshow('cat',cat)
cv.waitKey(0)
cv.destroyAllWindows()
图像由无数个像素点组成,每个像素点的坐标都需通过单点旋转的方式计算新位置。因此,图像旋转本质上是对图像中每个像素点应用单点旋转变换。

(2)平移
移操作可以将图像中的每个点沿着某个方向移动一定的距离

python
import cv2 as cv
import numpy as np
# 读图
cat=cv.imread("../images/cat1.png")
cat=cv.resize(cat,(520,520))
# 定义平移量
tx=80
ty=120
# 定义平移矩阵
M=np.float32([[1,0,tx],[0,1,ty]])
# 仿射变换
dst=cv.warpAffine(cat,M,(400,400))
cv.imshow("old",cat)
cv.imshow("new",dst)
cv.waitKey(0)
cv.destroyAllWindows()
(3)缩放
相较于图像旋转中只能等比例的缩放,图像缩放更加灵活,可以在指定方向上进行缩放

python
import cv2 as cv
import numpy as np
# 读图
cat=cv.imread("../images/cat1.png")
cat=cv.resize(cat,(520,520))
# 定义平移量
sx=0.6
sy=0.5
# 定义缩放矩阵
M=np.float32([[sx,0,0],[0,sy,0]])
# 仿射变换
dst=cv.warpAffine(cat,M,(520,520))
cv.imshow("old",cat)
cv.imshow("new",dst)
cv.waitKey(0)
cv.destroyAllWindows()
(4)图像剪切
剪切操作可以改变图形的形状,以便其在某个方向上倾斜,它将对象的形状改变为斜边平行四边形,而不改变其面积。


六、总结
本文介绍了 OpenCV 图像预处理的核心技术,从颜色操作、色彩空间转换,到灰度化、二值化,再到几何变换,覆盖了计算机视觉任务的基础预处理步骤。实际应用中,需根据场景选择合适的方法(如光照不均用自适应二值化,颜色分割用 HSV 空间),并结合代码调试优化效果。
掌握这些技术后,可进一步学习滤波、边缘检测等高级预处理方法,为目标检测、图像识别等任务打下坚实基础。