影像数据处理
影像数据处理
- 数据理解
- 图片数据
- 图片数据的特征
- 图片数据的性质
- 数据预处理
- 降噪
- 什么是噪声?
- 均值滤波
- 中值滤波
- 仿射变换 (AffineTransform)
- 灰度变换
- 对数&幂次变换
- 直方图均衡
- 代数域变换
- Mosaic数据增强
- 自适应图片缩放
- CV数据增强方法汇总
- 数据可视化
- 结语
影像数据处理是计算机视觉
和图像分析
的基础。无论是训练AI模型还是优化图片质量,理解数据特征并掌握预处理技术都至关重要。本文将从数据理解
和数据预处理
两大方向出发,带你系统学习了解影像数据处理的核心方法。
数据理解
图片数据
图片数据由像素矩阵构成,每个像素包含颜色信息(如RGB三通道)。常见的格式包括JPEG、PNG、BMP等,不同格式在压缩方式、透明通道支持等方面各有优劣。
图片数据的特征
-
分辨率
:图像的宽高像素数(如1920×1080),直接影响清晰度。 -
颜色深度
:每个像素的颜色信息位数(如8位、24位),决定色彩丰富度。 -
通道类型
:RGB(彩色)、灰度(单通道)等,影响数据维度。 -
元数据
:EXIF信息(拍摄设备、时间等),可能包含重要上下文。
图片数据的性质
局部相关性
:单独的像素值没有意义,相邻的像素值组合在一起形成特征。平移不变性
:图片的特征不受位置的影响。
数据预处理
降噪
降噪的过程就是在减少干扰信息的过程。
什么是噪声?
噪声是图像中非真实的随机干扰,常见类型包括高斯噪声
和椒盐噪声
。下面两幅图可以直观感受一下两种噪声对图像的影响。
高斯噪声:由传感器发热引起,呈正态分布。
椒盐噪声:随机黑白像素点,常见于传输错误。
接下来介绍两种常见的去除这两种噪声的方式,分别是均值滤波和中值滤波。
均值滤波
基本原理:用邻域像素平均值替代中心像素,平滑噪声但可能导致模糊。
公式解析:均值滤波的图像去噪原理
公式定义
f ^ ( x , y ) = 1 m n ∑ ( r , c ) ∈ s x y g ( r , c ) (1) \hat{f}(x,y) = \frac{1}{mn} \sum_{(r,c) \in s_{xy}} g(r,c) \tag{1} f^(x,y)=mn1(r,c)∈sxy∑g(r,c)(1)
- 输入:带噪声的图像 g ( r , c ) g(r,c) g(r,c)
- 输出:复原后的图像 f ^ ( x , y ) \hat{f}(x,y) f^(x,y)
- 窗口:以 ( x , y ) (x,y) (x,y) 为中心、大小为 m × n m \times n m×n 的矩形子窗口 s x y s_{xy} sxy 。
核心思想:局部平均化
均值滤波是一种线性空间滤波方法,其核心是通过对噪声图像中每个像素的邻域像素取平均值,抑制随机噪声。具体步骤为:
- 滑动窗口:在噪声图像 g g g 上,以每个待处理像素 ( x , y ) (x,y) (x,y) 为中心,定义一个 m × n m \times n m×n 的矩形窗口。
- 计算均值:将窗口内所有像素的灰度值相加,再除以像素总数 m × n m \times n m×n,结果作为复原图像中 ( x , y ) (x,y) (x,y) 处的新值。
数学意义
- 噪声平滑:假设噪声是随机分布的(如高斯噪声),通过取邻域均值,噪声的正负波动会部分抵消,从而降低噪声强度。
- 模糊代价:均值滤波会模糊图像细节(如边缘、纹理),因为高频信号(细节)也被平均化了。
实例说明
假设窗口大小为 3 × 3 3 \times 3 3×3,噪声图像局部如下:
50 | 55 | 48 |
---|---|---|
52 | 200 | 53 |
49 | 54 | 51 |
- 原始中心像素:200(噪声点)
- 均值计算: 50 + 55 + 48 + 52 + 200 + 53 + 49 + 54 + 51 9 ≈ 68.9 \frac{50+55+48+52+200+53+49+54+51}{9} \approx 68.9 950+55+48+52+200+53+49+54+51≈68.9
- 复原结果:中心像素值从 200 降为 68.9,噪声被抑制,但周围像素也被平滑。
50 | 55 | 48 |
---|---|---|
52 | 68.9 | 53 |
49 | 54 | 51 |
应用场景与局限性
- 适用场景:高斯噪声、轻微随机噪声。
- 局限性:
- 对椒盐噪声效果差(极端值会显著影响均值)。
- 导致图像模糊,边缘信息丢失。
- 改进方法:
- 中值滤波:取邻域中值,更适合椒盐噪声。
- 自适应滤波:根据局部噪声强度动态调整窗口大小。
边界处理
当窗口靠近图像边缘时,部分像素可能超出图像范围,常用处理方式包括:
- 补零:超出部分填充0。
- 镜像填充:复制边缘像素值。
- 截断窗口:仅使用图像内的像素计算均值。
可以看出,其实在边缘处理方面,均值滤波的效果并不是很好。
均值滤波通过“以空间换质量”的策略,用图像局部平滑性抑制噪声,是图像预处理中最基础的算法之一。尽管简单高效,但需权衡去噪强度与细节保留,实际应用中常与其他方法(如非局部均值滤波)结合使用以优化效果。
中值滤波
核心思想:用一个滑动窗口遍历图像的每个像素,用窗口内像素的中值来替代中心像素的值。
- 🐳感性地理解一下,椒盐噪声是由于随机的黑白小点产生的,而
黑
和白
在计算机图像中存储起来无非就是255
,0
这样最极端的值,所以我们排完序取中值就可以将这些噪点滤去。
仿射变换 (AffineTransform)
OpenCV中的仿射变换是一种基于线性变换和平移组合的图像几何变换方法,其核心原理如下:
1. 仿射变换的数学基础
仿射变换可以用 2 × 3 2 \times 3 2×3 矩阵 表示,形式为:
M = [ a b c d e f ] M = \begin{bmatrix} a & b & c \\ d & e & f \end{bmatrix} M=[adbecf]
其作用是将原始坐标 ( x , y ) (x, y) (x,y) 映射到新坐标 ( x ′ , y ′ ) (x', y') (x′,y′):
x ′ = a ⋅ x + b ⋅ y + c y ′ = d ⋅ x + e ⋅ y + f x' = a \cdot x + b \cdot y + c \\ y' = d \cdot x + e \cdot y + f x′=a⋅x+b⋅y+cy′=d⋅x+e⋅y+f
若用齐次坐标( 3 × 3 3 \times 3 3×3 矩阵),则可统一平移和线性变换:
[ x ′ y ′ 1 ] = [ a b c d e f 0 0 1 ] [ x y 1 ] \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} a & b & c \\ d & e & f \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} ⎣⎡x′y′1⎦⎤=⎣⎡ad0be0cf1⎦⎤⎣⎡xy1⎦⎤
2. 仿射变换的六自由度
仿射变换矩阵包含 6 个自由度,对应以下基本变换的组合:
- 平移(Translation):由 ( c ) (c) (c) 和 ( f ) (f) (f)控制。
- 旋转(Rotation):由 ( a , b , d , e ) (a, b, d, e) (a,b,d,e) 组成的旋转矩阵部分。
- 缩放(Scaling):由对角线元素 ( a , e ) (a, e) (a,e) 控制。
- 剪切(Shearing):由非对角线元素 ( b , d ) (b, d) (b,d) 控制。
3. OpenCV中的关键函数
-
cv2.getAffineTransform(src_points, dst_points)
根据 3 组点对 计算仿射变换矩阵。
原理:通过解线性方程组确定 6 个参数。
要求:3 组点必须非共线,否则矩阵不可逆。 -
cv2.warpAffine(src, M, dsize)
应用仿射矩阵 (M) 对图像进行变换。dsize
:输出图像的尺寸。- 支持填充选项(如
borderMode
和borderValue
)。
4. 仿射变换的特性
- 保持平行性:变换后平行线仍保持平行。
- 不保持角度和长度:旋转、缩放、剪切会改变这些属性。
- 适用场景:图像校正、图像配准、简单形变(如平移、旋转、倾斜)。
6. 示例代码
import cv2
import numpy as np# 定义原始点和目标点(3组非共线点)
src_points = np.float32([[50,50], [200,50], [50,200]])
dst_points = np.float32([[10,100], [200,50], [100,250]])# 计算仿射变换矩阵
M = cv2.getAffineTransform(src_points, dst_points)# 应用变换
img_transformed = cv2.warpAffine(img, M, (width, height), borderValue=(255,255,255))# 仿射变换矩阵 M:
# [[ 1.26666667 0.6 -83.33333333]
# [ -0.33333333 1. 66.66666667]]
7. 注意事项
- 若目标点共线,会导致矩阵计算失败(返回空矩阵)。
- 变换后超出图像边界的部分默认填充黑色,可通过
borderValue
自定义颜色。 - 对于复杂形变(如非仿射变换),需使用透视变换(
cv2.getPerspectiveTransform
)。
注:
- 所有变换均使用齐次坐标,形式为 [ x ′ y ′ 1 ] = M ⋅ [ x y 1 ] (2) \begin{bmatrix}x' \\ y' \\ 1\end{bmatrix} = M \cdot \begin{bmatrix}x \\ y \\ 1\end{bmatrix} \tag{2} ⎣⎡x′y′1⎦⎤=M⋅⎣⎡xy1⎦⎤(2)
其中 x ′ x' x′是变换后的x, y ′ y' y′是变换后的y, M M M如下
M = [ R 00 R 01 T x R 10 R 11 T y 0 0 1 ] M= \begin{bmatrix} R_{00} & R_{01} & T_x \\ R_{10} & R_{11} & T_y \\ 0 & 0 & 1 \end{bmatrix} M=⎣⎡R00R100R01R110TxTy1⎦⎤
1. 恒等变换(无变化)
[ 1 0 0 0 1 0 0 0 1 ] (3) \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \tag{3} ⎣⎡100010001⎦⎤(3)
2. 平移变换(Translate)
平移量: [ 1 0 T x 0 1 T y 0 0 1 ] (4) \begin{bmatrix} 1 & 0 & T_x \\ 0 & 1 & T_y \\ 0 & 0 & 1 \end{bmatrix} \tag{4} ⎣⎡100010TxTy1⎦⎤(4)
3. 绕原点旋转变换(Rotate about origin)
旋转角度: θ \theta θ
[ cos θ − sin θ 0 sin θ cos θ 0 0 0 1 ] (5) \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} \tag{5} ⎣⎡cosθsinθ0−sinθcosθ0001⎦⎤(5)
4. 沿x方向剪切(Shear in x direction)
剪切因子: k k k
[ 1 k 0 0 1 0 0 0 1 ] \begin{bmatrix} 1 & k & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} ⎣⎡100k10001⎦⎤
5. 反射变换
-
关于x轴反射(Reflect about x-axis)
[ 1 0 0 0 − 1 0 0 0 1 ] \begin{bmatrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ 0 & 0 & 1 \end{bmatrix} ⎣⎡1000−10001⎦⎤ -
关于y轴反射(Reflect about y-axis)
[ − 1 0 0 0 1 0 0 0 1 ] \begin{bmatrix} -1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} ⎣⎡−100010001⎦⎤ -
关于原点反射(Reflect about origin)
[ − 1 0 0 0 − 1 0 0 0 1 ] \begin{bmatrix} -1 & 0 & 0 \\ 0 & -1 & 0 \\ 0 & 0 & 1 \end{bmatrix} ⎣⎡−1000−10001⎦⎤
上述这些变换具体细节可以不用了解,opencv
已经封装好了现成的方法。在本篇的数据可视化部分有代码实现。
灰度变换
- 先直观感受一下灰度变换带来的变化。
灰度变换是图像处理中用于增强图像对比度
或调整亮度
的一种基础技术,通过对每个像素的灰度值进行数学映射来改善视觉效果。
以下是几种常见的灰度变换方法:
- 图像反转
对于灰度级为 L − 1 L-1 L−1的图像数据
公式: s = L − 1 − r s = L-1 - r s=L−1−r
- 原理:将原灰度值 r r r 取反,使得黑色变为白色,白色变为黑色,中间灰度值相应反转。
- 作用:
增强图像中暗区域的细节
,适用于医学图像或夜间摄影的增强。
- 线性变换
公式: s = A ⋅ r + B s = A \cdot r + B s=A⋅r+B
- 参数意义:
A A A控制对比度 A > 1 A > 1 A>1 增强对比度, A < 1 A < 1 A<1 降低对比度。
B B B 控制亮度 B > 1 B > 1 B>1 增加亮度, B < 0 B < 0 B<0 降低亮度。 - 应用:整体调整图像的亮度和对比度,如提升过暗图像的细节。
- 分段线性变换
公式:
s = { c a ⋅ r , 0 ≤ r < a d − c b − a ⋅ ( r − a ) + c , a ≤ r ≤ b M − d N − b ⋅ ( r − b ) + d , b < r ≤ N s = \begin{cases} \frac{c}{a} \cdot r, & 0 \leq r < a \\ \frac{d - c}{b - a} \cdot (r - a) + c, & a \leq r \leq b \\ \frac{M - d}{N - b} \cdot (r - b) + d, & b < r \leq N \end{cases} s=⎩⎪⎨⎪⎧ac⋅r,b−ad−c⋅(r−a)+c,N−bM−d⋅(r−b)+d,0≤r<aa≤r≤bb<r≤N
- 参数意义:
- ( a , b ) (a, b) (a,b):输入灰度区间的分割点。
- ( c , d ) (c, d) (c,d):输出灰度区间的对应值。
- N N N:输入最大灰度值,如 L − 1 L-1 L−1
- M M M:输出最大灰度值。
- 作用:
- 低灰度区 0 ≤ r < a 0 \leq r < a 0≤r<a:通过斜率 c a \frac{c}{a} ac 扩展或压缩对比度。
- 中灰度区 a ≤ r ≤ b a \leq r \leq b a≤r≤b:斜率 d − c b − a \frac{d - c}{b - a} b−ad−c 决定对比度调整方向。
- 高灰度区 b < r ≤ N b < r \leq N b<r≤N:斜率 M − d N − b \frac{M - d}{N - b} N−bM−d 控制亮部细节。
- 应用:灵活增强特定灰度范围的细节,如同时突出暗部和高光区域。
- 灰度拉伸
- 原理:通过分段线性变换中的斜率调整,扩展某一灰度范围的对比度。例如,将原本集中在狭窄区间的灰度拉伸到更广范围,提升图像层次感。
- 典型场景:处理低对比度图像(如雾天拍摄的图片),增强整体清晰度。
灰度变换的核心是通过函数映射重新分配像素灰度值。图像反转和线性变换适用于全局调整,而分段线性变换和灰度拉伸则支持局部优化,可根据需求定制不同区域的对比度和亮度,广泛应用于医学成像、遥感图像增强等领域。
对数&幂次变换
灰度变换是数字图像处理的核心技术之一,通过数学映射关系调整像素灰度值,从而增强图像对比度、优化视觉效果。对数变换与幂次变换是两种经典的非线性灰度变换方法。
一、幂次变换(伽马变换)
- 数学公式
s = c ⋅ r γ s = c \cdot r^\gamma s=c⋅rγ
- 参数说明:
- r r r:原始灰度值(范围: 0 ≤ r ≤ L − 1 0 \leq r \leq L-1 0≤r≤L−1,如8位图像为0~255)。
- s s s:变换后灰度值。
- c c c:归一化常数,通常取 c = L − 1 ( L − 1 ) γ c = \frac{L-1}{(L-1)^\gamma} c=(L−1)γL−1,确保输出范围与输入一致。
- γ \gamma γ(伽马值):控制灰度分布的压缩或拉伸方向。
- 伽马值的影响
伽马值 | 作用效果 | 适用场景 |
---|---|---|
γ > 1 \gamma > 1 γ>1 | 压缩低灰度区域,拉伸高亮度区域。 图像整体变暗,突出亮部细节。 | X光片增强、高光细节修复 |
γ < 1 \gamma < 1 γ<1 | 拉伸低灰度区域,压缩高亮度区域。 图像整体变亮,增强暗部层次。 | 核磁共振图像、低照度照片优化 |
γ = 1 \gamma = 1 γ=1 | 退化为线性变换 s = c ⋅ r s = c \cdot r s=c⋅r。 | 均匀调整对比度 |
如下图,是人体上部脊椎骨折核磁共振图像,左边第一幅图为原始图片,暗部比较重。其余3张图片不同程度增强了暗部层次。从左往右依次 γ = 0.6 , 0.4 , 0.3 \gamma=0.6,0.4,0.3 γ=0.6,0.4,0.3
二、对数变换
- 数学公式
s = c ⋅ log ( 1 + r ) s = c \cdot \log(1 + r) s=c⋅log(1+r)
- 参数说明:
- log ( 1 + r ) \log(1 + r) log(1+r):避免 r = 0 r = 0 r=0时计算结果为负无穷。
- c c c:缩放常数,通常取 c = L − 1 log ( 1 + L − 1 ) c = \frac{L-1}{\log(1 + L-1)} c=log(1+L−1)L−1。例如,8位图像中 L = 256 L=256 L=256,则 c ≈ 45.9 c \approx 45.9 c≈45.9。
- 核心作用
- 压缩高光区域:对数函数增长缓慢,抑制过曝的高亮度像素。
- 扩展暗部细节:低灰度值被非线性拉伸,提升暗区可见性。
- 应用场景
- 天文图像处理:增强微弱星光的细节,压缩明亮恒星的过曝区域。
- 傅里叶频谱图增强:扩展低频分量,压缩高频噪声。
三、对比与优化技巧
- 方法对比
特性 | 幂次变换 | 对数变换 |
---|---|---|
核心公式 | s = c ⋅ r γ s = c \cdot r^\gamma s=c⋅rγ | s = c ⋅ log ( 1 + r ) s = c \cdot \log(1 + r) s=c⋅log(1+r) |
适用场景 | 动态调整局部对比度 | 压缩高动态范围图像 |
参数敏感度 | 伽马值需精细调节(易影响噪声) | 常数 c c c 影响输出范围 |
-
幂次变换:
- γ < 1 \gamma < 1 γ<1 时,需注意暗部噪声可能被放大,建议结合滤波处理。
- 结合直方图均衡化,可进一步提升全局对比度。
-
对数变换:
- 适用于处理传感器捕获的高动态范围(HDR)图像。
- 调整 c c c 时需确保输出灰度值落在有效区间内(如0~255)。
-
幂次变换擅长针对性调整明暗区域的对比度,广泛应用于医学影像和显示设备校正。
-
对数变换则更适用于扩展暗部细节并抑制高光溢出,是处理低曝光或高动态范围图像的利器。
直方图均衡
一、什么是直方图均衡?
直方图均衡(Histogram Equalization) 是一种通过重新分配图像像素灰度值来增强图像对比度的技术。其核心目标是解决以下两类问题:
- 灰度级动态范围窄:图像灰度值集中在狭窄区间,导致整体对比度低。
- 直方图分布不均匀:某些灰度级像素数量过多或过少,细节难以辨识。
通过将原始直方图调整为近似均匀分布,直方图均衡能够扩展灰度范围,显著提升图像的视觉效果和信息利用率。
二、直方图均衡的原理与步骤
- 核心思想
将原始图像的累积分布函数(CDF)映射为线性函数,使输出直方图近似均匀分布。 - 数学公式
- 输入图像:灰度范围为 [ 0 , L − 1 ] [0, L-1] [0,L−1](如8位图像为0~255)。
- 输出灰度值 s k s_k sk:
s k = ( L − 1 ) ⋅ ∑ i = 0 k n i N s_k = (L-1) \cdot \sum_{i=0}^{k} \frac{n_i}{N} sk=(L−1)⋅i=0∑kNni
其中:- n i n_i ni:灰度级 i i i 的像素数量。
- N N N:图像总像素数。
- ∑ i = 0 k n i N \sum_{i=0}^{k} \frac{n_i}{N} ∑i=0kNni:灰度级 k k k 的累积分布概率。
3 . 实现步骤
- 统计原始直方图:计算每个灰度级的像素数量 n i n_i ni。
- 计算累积分布概率:对每个灰度级 k k k,累加 n 0 + n 1 + ⋯ + n k N \frac{n_0 + n_1 + \dots + n_k}{N} Nn0+n1+⋯+nk。
- 映射到新灰度级:将累积概率乘以 L − 1 L-1 L−1,四舍五入得到输出灰度值 s k s_k sk。
- 生成均衡化图像:将原图中每个像素替换为对应的 s k s_k sk。
三、直方图均衡的应用场景
- 低对比度图像增强
- 示例:医学X光片、雾天拍摄的风景照。
- 效果:拉伸灰度范围,使骨骼纹理或远景细节更清晰。
- 自动化与人脸识别
- 作用:
提升人脸区域对比度,改善特征提取精度。
标准化不同光照条件下的人脸图像。
- 预处理任务
- 应用:作为图像分类、目标检测的前置步骤,减少光照差异对算法的影响。
四、直方图均衡 vs. 其他灰度变换方法
特性 | 直方图均衡 | 对数/幂次变换 |
---|---|---|
核心目标 | 全局均匀化直方图 | 局部调整对比度 |
适用场景 | 灰度集中或分布不均的图像 | 动态范围压缩或特定区域增强 |
计算复杂度 | 较高(需统计和映射全局直方图) | 较低(仅需逐像素计算) |
结果可控性 | 自动调整,灵活性较低 | 手动调节参数,灵活性高 |
直方图均衡通过重新分配像素灰度值,将原始直方图转换为均匀分布,是解决低对比度问题的经典方法。尽管存在噪声敏感性和局部细节损失等局限,但其自动化、高效的特点使其在医学影像、人脸识别和工业检测中广泛应用。
- 原始直方图:集中在中间区域,呈现单峰分布。
- 均衡化后直方图:分布平坦,覆盖更广灰度范围。
代数域变换
代数域变换 是一种基于像素级数学运算的图像处理技术,通过对两幅或多幅图像的像素值进行加法、减法、乘法等操作,实现图像增强、噪声抑制、特征提取等目标。其核心思想是通过代数运算的叠加或对比,凸显或消除特定信息。
- 加法:可以去除高斯噪声
- 减法:可以分割领域,检测变化,例如下面这幅图片,刚开始人走过来时可以记录当前所有的像素值,当人消失后,再记录一次所有像素点,对比两次记录,做减法,如果不是全0的话,明显可以发现中间区域像素值变化了很多。
- 乘法: 可以加滤镜,遮罩
Mosaic数据增强
Mosaic数据增强 是一种基于多图拼接的图像增强技术,最早在2020年由YOLOv4目标检测模型提出并广泛应用。其核心思想是通过随机组合多张训练图像,生成一张包含丰富上下文信息的合成图像,从而提升模型对目标尺度、位置和背景多样性的适应能力。与2019年提出的CutMix方法类似,Mosaic通过混合样本增强数据多样性,但其独特的多图拼接策略使其在目标检测任务中表现尤为突出。
二、Mosaic的实现步骤
- 图像选择与缩放
从数据集中随机选取4张图像。
对每张图像进行随机缩放(如缩放至原始尺寸的20%~150%),以增加目标尺度的多样性。 - 拼接与布局
将4张缩放后的图像按左上、右上、左下、右下四个象限拼接为一张大图。
拼接时允许图像部分重叠,增强上下文关联性。 - 随机裁剪与填充
对拼接后的大图进行随机裁剪,保留目标密集区域。
若裁剪后图像尺寸不足,使用灰度填充或镜像填充补全。 - 标签调整
根据拼接和裁剪后的坐标,调整每张原始图像中目标的边界框(Bounding Box)位置。
过滤超出裁剪区域或尺寸过小的无效标注。
三、Mosaic vs. CutMix:关键差异
特性 | Mosaic | CutMix |
---|---|---|
图像数量 | 4张图像拼接 | 2张图像区域交换 |
目标分布 | 多目标密集分布 | 单目标替换 |
适用任务 | 目标检测(YOLO系列) | 分类、检测(如ResNet) |
计算开销 | 较高(需处理多图拼接与复杂标签) | 较低 |
上下文关联 | 强(多场景混合) | 弱(局部替换) |
四、实际应用与效果
- 在YOLOv4中的应用
- 训练阶段:每批次输入图像均通过Mosaic生成,显著提升COCO数据集的检测精度(AP提升约5%)。
- 推理阶段:关闭Mosaic,直接使用原始图像测试。
- 小目标检测优化
- 案例:无人机航拍图像中,Mosaic合成多张小目标(如车辆、行人)图像,使模型mAP提升10%~15%。
- 缺陷与改进
- 问题:Mosaic可能引入不合理的上下文组合(如空中车辆+水下鱼类)。
- 解决方案:结合数据分布分析,限制拼接图像的场景一致性(如仅混合同类别图像)。
自适应图片缩放
CV数据增强方法汇总
数据可视化
- 以这张蝴蝶在花中停留的照片为例子,结合代码观察数据处理前后图片的变化。
import cv2
import numpy as np# 图像路径
image_path = 'buttefly.jpeg'
# 载入图像
image = cv2.imread(image_path)
# 旋转 Rotation# 获取图像尺寸
(h, w) = image.shape[:2]# 设置旋转的中心为图像中心
center = (w / 2, h / 2)# 生成一个-90到90之间的随机旋转角度
angle = np.random.uniform(-90, 90)# 获取旋转矩阵,其中1.0表示图像旋转后不改变大小
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)# 执行旋转操作
rotated_image = cv2.warpAffine(image, rotation_matrix, (w, h))# 显示原图和旋转后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Rotated Image', rotated_image)# 按任意键退出
cv2.waitKey(0)
cv2.destroyAllWindows()
# 平移 Translation
# 获取图像的高和宽
height, width = image.shape[:2]# 随机生成平移量(注意:这里需要确保平移量不会使图像超出边界)
tx = np.random.randint(-100, 100) # 水平方向上的平移量,单位:像素
ty = np.random.randint(-100, 100) # 垂直方向上的平移量,单位:像素# 确保平移后的图像不会超出原始图像的边界
tx = max(min(tx, width-1), 0)
ty = max(min(ty, height-1), 0)# 创建仿射变换矩阵
translation_matrix = np.float32([[1, 0, tx], [0, 1, ty]])# 应用仿射变换
translated_image = cv2.warpAffine(image, translation_matrix, (width, height))# 显示或保存平移后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Translated Image', translated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 缩放 Scaling
def random_scale_image(image, scale_range=(0.5, 1.5)):"""随机缩放图像的大小。:param image: 要缩放的图像,应为numpy数组形式:param scale_range: 缩放比例的范围,默认(0.5, 1.5),即缩放后的大小在原始大小的50%到150%之间:return: 缩放后的图像"""# 生成一个随机的缩放比例scale = np.random.uniform(scale_range[0], scale_range[1])# 获取原始图像的高度和宽度height, width = image.shape[:2]# 计算缩放后的新高度和宽度new_height = int(height * scale)new_width = int(width * scale)# 使用cv2.resize()进行缩放scaled_image = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_LINEAR)return scaled_image# 随机缩放图像
scaled_image = random_scale_image(image)# 显示缩放后的图像(如果需要)
cv2.imshow('Original Image', image)
cv2.imshow('Scaled Image', scaled_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 剪切变换 Shear Transformimport random# 步骤1:定义随机剪切参数
# 注意:这里我们假设剪切的区域不能超出原始图像的范围
shear_height = random.randint(1, height // 2) # 随机剪切高度(这里设定为图像高度的一半以下)
shear_width = random.randint(1, width // 2) # 随机剪切宽度(这里设定为图像宽度的一半以下)
start_y = random.randint(0, height - shear_height)
start_x = random.randint(0, width - shear_width)# 步骤2:剪切图像
sheared_image = image[start_y:start_y+shear_height, start_x:start_x+shear_width]# 步骤3:显示或保存剪切后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Sheared Image', sheared_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 翻转 Flipping# 定义一个函数来随机翻转图像
def random_flip(image):# 随机选择翻转模式flip_code = random.choice([0, 1]) # 0为垂直翻转,1为水平翻转# 使用cv2.flip函数进行翻转flipped_image = cv2.flip(image, flip_code)return flipped_image# 调用函数并显示翻转后的图像
flipped_image = random_flip(image)cv2.imshow('Original Image', image)
cv2.imshow('Flipped Image', flipped_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 亮度调整 Brightness Adjustmentdef adjust_brightness_randomly(image, alpha_range=(0.5, 1.5)):# 生成一个随机亮度调整因子alpha = random.uniform(alpha_range[0], alpha_range[1])# 亮度调整adjusted_image = cv2.convertScaleAbs(image, alpha=alpha, beta=0)return adjusted_image# 随机调整亮度
adjusted_image = adjust_brightness_randomly(image)# 显示或保存调整后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Adjusted Image', adjusted_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 对比度调整 Contrast Adjustmentdef random_contrast(image, contrast_range=[0.5, 1.5]):# 获取图像的维度h, w, _ = image.shape# 随机选择对比度系数contrast_factor = np.random.uniform(contrast_range[0], contrast_range[1])# 创建一个空的浮点型图像来存储调整后的图像adjusted_image = np.zeros((h, w, 3), dtype=np.float32)# 对图像进行对比度调整for i in range(h):for j in range(w):# 将像素值归一化到0-1之间pixel = image[i, j] / 255.0# 调整对比度adjusted_pixel = np.clip(contrast_factor * (pixel - 0.5) + 0.5, 0, 1)# 将像素值转换回0-255adjusted_image[i, j] = adjusted_pixel * 255# 将浮点数图像转换为无符号整数adjusted_image = np.uint8(adjusted_image)return adjusted_image# 随机调整图像的对比度
adjusted_image = random_contrast(image)# 显示原始图像和调整后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Adjusted Image', adjusted_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 添加噪声 Adding Noise# 将图像数据转换为浮点型以支持噪声添加
image_N = image.astype(np.float32)# 定义高斯噪声的均值和标准差
mean = 0
stddev = 50.0 # 可以根据需要调整这个值来控制噪声的强度# 生成高斯噪声
noise = np.random.normal(mean, stddev, image_N.shape)# 将噪声添加到图像上
noisy_image = np.clip(image_N + noise, 0, 255).astype(np.uint8)# 显示或保存带噪声的图像
cv2.imshow('Original Image', image)
cv2.imshow('Noisy Image', noisy_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 颜色抖动 Color Jitteringdef random_color_shift(image, hue_shift_range=18, sat_shift_range=30, val_shift_range=40):# 将BGR图像转换为HSV图像hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)# 分解HSV图像的通道h, s, v = cv2.split(hsv)# 随机改变色调h = cv2.add(h, np.random.randint(-hue_shift_range, hue_shift_range+1))h = np.clip(h, 0, 179) # 确保色调值在0-179之间# 随机改变饱和度s = cv2.add(s, np.random.randint(-sat_shift_range, sat_shift_range+1))s = np.clip(s, 0, 255) # 确保饱和度值在0-255之间# 随机改变亮度v = cv2.add(v, np.random.randint(-val_shift_range, val_shift_range+1))v = np.clip(v, 0, 255) # 确保亮度值在0-255之间# 合并HSV通道hsv_shifted = cv2.merge((h, s, v))# 将HSV图像转换回BGR图像image_shifted = cv2.cvtColor(hsv_shifted, cv2.COLOR_HSV2BGR)return image_shifted# 随机改变颜色属性
shifted_image = random_color_shift(image)# 显示或保存图像
cv2.imshow('Original Image', image)
cv2.imshow('Shifted Image', shifted_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 随即擦除 Random Erasingdef random_mask_image(image, min_mask_area=0.05, max_mask_area=0.2):image_c = image.copy()height, width = image_c.shape[:2]# 确定要遮挡的面积(基于图像尺寸的百分比)min_mask_width_height = int(min(width, height) * np.sqrt(min_mask_area))max_mask_width_height = int(min(width, height) * np.sqrt(max_mask_area))# 随机选择遮挡区域的左上角坐标和大小start_x = random.randint(0, width - max_mask_width_height)start_y = random.randint(0, height - max_mask_width_height)mask_width = random.randint(min_mask_width_height, max_mask_width_height)mask_height = random.randint(min_mask_width_height, max_mask_width_height)# 在图像上绘制遮挡矩形image_c[start_y:start_y+mask_height, start_x:start_x+mask_width, :] = 0 # 使用黑色(RGB值为0,0,0)进行遮挡return image_c# 使用示例
masked_image = random_mask_image(image)cv2.imshow('Original Image', image)
cv2.imshow('Masked Image', masked_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 随机高斯模糊 Random Gaussian Blur# 应用高斯模糊
blurred_image = cv2.GaussianBlur(image, (15, 15), 0)# 显示模糊后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Blurred Image', blurred_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 随机灰度 Random Grayscale# 随机决定是否将图像转换为灰度图
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 等待用户按任意键后关闭所有窗口
cv2.imshow('Converted to Grayscale', gray_image)
cv2.imshow('Original Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 随机通道交换 Random Channel Swap# 随机生成一个颜色通道交换的顺序
channels = [0, 1, 2]
random.shuffle(channels)# 使用numpy的高级索引来重排颜色通道
swapped_image = image[:, :, channels]# 显示原图和颜色通道交换后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Swapped Channels Image', swapped_image)# 等待用户按任意键后关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
# 随机伽马校正 Random Gamma Correctiondef adjust_gamma(image, gamma=1.0):# 构建一个查找表映射每个像素值inv_gamma = 1.0 / gammatable = np.array([((i / 255.0) ** inv_gamma) * 255for i in np.arange(0, 256)]).astype("uint8")# 使用查找表应用伽马校正return cv2.LUT(image, table)# 随机生成一个伽马值(范围可以根据需要调整)
gamma = random.uniform(0.5, 12.0)# 应用伽马校正
gamma_corrected_image = adjust_gamma(image, gamma)# 显示原图和伽马校正后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Gamma Corrected Image', gamma_corrected_image)# 等待用户按任意键后关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
# 随机透视变换 Random Perspective Transformdef random_perspective_transform(image):# 获取图像尺寸height, width = image.shape[:2]# 定义原始图像的四个顶点src_points = np.float32([[0, 0], [width, 0], [width, height], [0, height]])# 定义目标图像的四个顶点并添加随机偏移dst_points = src_points + np.float32([[random.uniform(-30, 30), random.uniform(-30, 30)],[random.uniform(-30, 30), random.uniform(-30, 30)],[random.uniform(-30, 30), random.uniform(-30, 30)],[random.uniform(-30, 30), random.uniform(-30, 30)]])# 计算透视变换矩阵matrix = cv2.getPerspectiveTransform(src_points, dst_points)# 应用透视变换transformed_image = cv2.warpPerspective(image, matrix, (width, height))return transformed_image# 应用随机透视变换
transformed_image = random_perspective_transform(image)# 显示原图和透视变换后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Transformed Image', transformed_image)# 等待用户按任意键后关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
# 随机光照变化 Random Brightness Adjustmentdef random_brightness_contrast(image):# 随机调整亮度brightness = random.randint(-50, 50)# 随机调整对比度contrast = random.uniform(0.5, 1.5)# 调整图像亮度和对比度new_image = cv2.convertScaleAbs(image, alpha=contrast, beta=brightness)return new_image# 应用随机光照变化
transformed_image = random_brightness_contrast(image)# 显示原图和光照变化后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Transformed Image', transformed_image)# 等待用户按任意键后关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
# 直方图均衡import cv2
import numpy as npimg = cv2.imread('buttefly.jpeg', 0)
equ = cv2.equalizeHist(img)
cv2.imwrite('equalized_image.jpg', equ)
# true
# 对比度拉伸def contrast_stretching(image):# 获取图像的最小值和最大值min_val = np.min(image)max_val = np.max(image)print(f"Min value: {min_val}, Max value: {max_val}")# Min value: 0, Max value: 252# 防止除以零的情况if min_val == max_val:return image# 应用线性变换,将像素值拉伸到0-255范围stretched_image = (image - min_val) * (255 / (max_val - min_val))stretched_image = np.clip(stretched_image, 0, 255) # 确保值在0-255范围内stretched_image = np.uint8(stretched_image)return stretched_image# 读取图像
image = cv2.imread('buttefly.jpeg', cv2.IMREAD_GRAYSCALE) # 读取灰度图像# 检查图像是否成功读取
if image is None:print("Error: Image not found or unable to open.")
else:# 应用对比度拉伸stretched_image = contrast_stretching(image)# 显示原图和对比度拉伸后的图像cv2.imshow('Original Image', image)cv2.imshow('Stretched Image', stretched_image)# 等待用户按任意键后关闭所有窗口cv2.waitKey(0)cv2.destroyAllWindows()
# 锐化def sharpen_image(image):# 定义一个锐化卷积核kernel = np.array([[0, -1, 0],[-1, 5,-1],[0, -1, 0]])# 应用卷积核进行图像锐化sharpened_image = cv2.filter2D(image, -1, kernel)return sharpened_image# 读取图像
image = cv2.imread('buttefly.jpeg')# 检查图像是否成功读取
if image is None:print("Error: Image not found or unable to open.")
else:# 应用锐化sharpened_image = sharpen_image(image)# 显示原图和锐化后的图像cv2.imshow('Original Image', image)cv2.imshow('Sharpened Image', sharpened_image)# 等待用户按任意键后关闭所有窗口cv2.waitKey(0)cv2.destroyAllWindows()
# 均值滤波def average_blur(image, ksize=5):blurred_image = cv2.blur(image, (ksize, ksize))return blurred_image# 读取图像
image = cv2.imread('buttefly.jpeg')# 检查图像是否成功读取
if image is None:print("Error: Image not found or unable to open.")
else:# 应用均值滤波blurred_image = average_blur(image)# 显示原图和去噪后的图像cv2.imshow('Original Image', image)cv2.imshow('Average Blurred Image', blurred_image)# 等待用户按任意键后关闭所有窗口cv2.waitKey(0)cv2.destroyAllWindows()
# 高斯滤波def gaussian_blur(image, ksize=5):blurred_image = cv2.GaussianBlur(image, (ksize, ksize), 0)return blurred_image# 读取图像
image = cv2.imread('buttefly.jpeg')# 检查图像是否成功读取
if image is None:print("Error: Image not found or unable to open.")
else:# 应用高斯滤波blurred_image = gaussian_blur(image)# 显示原图和去噪后的图像cv2.imshow('Original Image', image)cv2.imshow('Gaussian Blurred Image', blurred_image)# 等待用户按任意键后关闭所有窗口cv2.waitKey(0)cv2.destroyAllWindows()
# 中值滤波def median_blur(image, ksize=5):blurred_image = cv2.medianBlur(image, ksize)return blurred_image# 读取图像
image = cv2.imread('buttefly.jpeg')# 检查图像是否成功读取
if image is None:print("Error: Image not found or unable to open.")
else:# 应用中值滤波blurred_image = median_blur(image)# 显示原图和去噪后的图像cv2.imshow('Original Image', image)cv2.imshow('Median Blurred Image', blurred_image)# 等待用户按任意键后关闭所有窗口cv2.waitKey(0)cv2.destroyAllWindows()
# 双边滤波def bilateral_filter(image, d=9, sigmaColor=75, sigmaSpace=75):blurred_image = cv2.bilateralFilter(image, d, sigmaColor, sigmaSpace)return blurred_image# 读取图像
image = cv2.imread('buttefly.jpeg')# 检查图像是否成功读取
if image is None:print("Error: Image not found or unable to open.")
else:# 应用双边滤波blurred_image = bilateral_filter(image)# 显示原图和去噪后的图像cv2.imshow('Original Image', image)cv2.imshow('Bilateral Filtered Image', blurred_image)# 等待用户按任意键后关闭所有窗口cv2.waitKey(0)cv2.destroyAllWindows()
结语
影像数据处理是计算机视觉任务的核心基石,其目标是通过系统化的方法提升数据质量、丰富数据多样性,并为模型训练与推理奠定坚实基础。