第一章 数字图像基础
文章目录
前言
数字图像处理是计算机科学和工程学中的一个重要领域,它涉及从图像的获取到处理、分析以及最终应用的全过程。现在,数字图像已经成为我们生活中不可或缺的一部分。从手机相机拍摄的照片到社交媒体上的图片,从医疗诊断、卫星遥感到自动驾驶和娱乐媒体等领域,都离不开图像处理技术的支持。
1.什么是数字图像
数字图像是通过数字化方式表示的图像数据,它可以在计算机和其他数字设备上存储、处理和显示。数字图像是对现实世界图像的离散化表示,通过将图像分解为像素并对每个像素赋予数值来实现,像素值由数组或者矩阵来存储。
1.1. 什么是像素
像素(Pixel)是图像的最小单元,通常用来表示数字图像中的一个点,像素是在模拟图像数字化时对连续空间进行离散化得到的。每个像素具有整数行(高)和列(宽)位置坐标,同时每个像素都具有整数灰度值或颜色值。
2.图像的表示
图像有黑白图像、灰度图像和彩色图像,黑白图像又叫灰度图像
或者二值图像
,不理解的可以看看这篇文章
一个灰度图像和二值图像可以看成一个由二维像素矩阵组成的网格,每个像素都用一个数值来表示黑白亮度。
一个彩色图像可以看成一个三维像素矩阵(由三个二维矩阵组成)组成 的网格,彩色图像一般有RGB图像
和HSV图像
,每个像素有3个值表示。在RGB图像
中,每个像素每个像素由三个值(R、G、B)组成,每个值的范围也是0到255。在HSV(色相、饱和度、明度)图像中,每个像素的颜色由三个分量表示:色相(Hue,H)、饱和度(Saturation,S)和明度(Value,V),其中H取值范围0到360,S取值范围0到1,V取值范围0到1。
3.图像分辨率
图像分辨率是描述图像细节和清晰度的一个重要指标,它指的是图像中像素的数量。高分辨率图像包含更多的像素,因此高分辨率的图像可以展示更多的细节和更高的清晰度。
3.1.图像分辨率的定义
-
像素数(Pixel Count):
- 图像分辨率通常以图像的宽度和高度(以像素为单位)表示。例如,一个图像的分辨率为1920x1080,表示图像的宽度为1920像素,高度为1080像素。
- 总像素数等于宽度和高度的乘积。例如,一个1920x1080的图像总共有2,073,600像素。
-
PPI(Pixels Per Inch)(拓展):
- 表示每英寸内的像素数量,用于描述图像在物理显示设备上的密度。例如,300 PPI的分辨率意味着每英寸内有300个像素。
- PPI值高的图像在打印或显示时会更加清晰。常见的打印分辨率为300 PPI,屏幕显示通常在72到96 PPI之间。
-
DPI(Dots Per Inch)(拓展):
- 类似于PPI,但通常用于描述打印设备的分辨率。DPI表示打印机每英寸打印点的数量。
- DPI值越高,打印图像的细节和质量越高。打印分辨率的标准通常为300 DPI。
4.图像深度
图像深度(Image Depth),也称为色彩深度(Color Depth),指的是图像中每个像素可以表示的颜色信息的位数。它决定了图像中每个像素可以显示的颜色数量。
常见的图像深度包括:
-
1位深度: 每个像素只能表示两种(
2^1
)颜色,通常是黑色和白色(即二值图像)。 -
8位深度: 每个像素可以表示256(
2^8
)种颜色。常见的灰度图像使用8位深度,其中每个像素的值代表灰度级别。对于彩色图像,8位深度通常用于每种颜色通道(红色、绿色、蓝色),形成24位图像(即True Color)。 -
16位深度: 每个像素可以表示65,536种(
2^16
)颜色。16位深度可以提供更丰富的色彩渐变和更高的图像质量,通常用于高动态范围(HDR)图像和某些高质量图像处理应用。 -
32位深度: 每个像素可以表示4,294,967,296种(
2^32
)颜色(即True Color加上Alpha通道)。32位图像通常包含8位的红色、绿色、蓝色通道,以及8位的Alpha通道(透明度),用于实现更高的颜色精度和透明度效果。
图像深度越高,表示的颜色范围越广,图像质量通常也越高。然而,更高的图像深度也会导致文件大小增加。
5.图像处理的基本操作
5.1.缩放
图像缩放是指改变图像尺寸的过程,可以使图像变大或变小。这是一种常见的图像处理操作,用于调整图像以适应不同的显示需求或进行图像分析。
5.1.1. 缩放常用的方法
- 最近邻插值(Nearest Neighbor Interpolation):
原理:对目标像素进行插值时,选择最接近的源像素的值。
优点:计算简单、速度快。
缺点:可能导致锯齿状边缘,图像质量较差。
- 双线性插值(Bilinear Interpolation):
原理:对目标像素进行插值时,考虑目标像素周围四个邻近像素的加权平均值。
优点:比最近邻插值提供更平滑的图像。
缺点:相对于最近邻插值,计算更复杂,但结果更好。
- 双三次插值(Bicubic Interpolation):
原理:对目标像素进行插值时,考虑目标像素周围16个邻近像素的加权平均值。
优点:提供更高的图像质量和更平滑的结果,适用于高质量的图像缩放。
缺点:计算复杂度较高,处理速度比双线性插值慢。
- Lanczos插值:
原理:基于Sinc函数的插值方法,使用周围多个像素进行加权平均。
优点:能够提供非常高质量的缩放效果,减少锯齿状和模糊现象。
缺点:计算较为复杂,处理时间较长。
- 高斯模糊(Gaussian Blur):
原理:在缩放过程中对图像应用高斯模糊,以减少图像缩放带来的伪影和锯齿状边缘。
优点:可以有效地减少缩放过程中产生的视觉瑕疵。
缺点:可能导致图像细节丢失。
- 插值方法:
1.最近邻插值:通常用于快速的实时处理。
2.双线性插值:适用于要求较低的图像缩放。
3.双三次插值和Lanczos插值:适用于需要高质量图像缩放的情况。
也就是说上面提到插值算法都属于插值方法这一大类
代码:
import cv2
# 读取图像
image = cv2.imread('example.jpg')
# 定义目标尺寸
target_size = (800, 600)
#cv2.INTER_NEAREST:最近邻插值。快速但可能产生锯齿效果。
#cv2.INTER_LINEAR:双线性插值。常用于图像缩放,平滑度较好。
#cv2.INTER_CUBIC:双三次插值。高质量的缩放,但计算更复杂。
#cv2.INTER_LANCZOS4:Lanczos插值。非常高质量的缩放,适用于高分辨率图像。
# 使用不同的插值方法进行缩放
resized_nearest = cv2.resize(image, target_size, interpolation=cv2.INTER_NEAREST)
resized_linear = cv2.resize(image, target_size, interpolation=cv2.INTER_LINEAR)
resized_cubic = cv2.resize(image, target_size, interpolation=cv2.INTER_CUBIC)
resized_lanczos = cv2.resize(image, target_size, interpolation=cv2.INTER_LANCZOS4)
# 保存缩放后的图像
cv2.imwrite('resized_nearest.jpg', resized_nearest)
cv2.imwrite('resized_linear.jpg', resized_linear)
cv2.imwrite('resized_cubic.jpg', resized_cubic)
cv2.imwrite('resized_lanczos.jpg', resized_lanczos)
5.1.2 缩放为什么要有额外的插值方法操作
当图像进行缩放时,像素的坐标位置会发生变化,可能产生浮点数。但是我们浮点位置需要转换成实际的整数位置,所以会采用插值方法来而是通过估算来确定浮点坐标位置上的像素值,然后再将这个值应用到整数坐标上。
5.2.旋转
图像旋转是通过将图像围绕一个中心点或指定点旋转一定角度来改变图像的方向。这一过程涉及将每个像素的位置根据旋转角度进行变换。
5.2.1. 旋转矩阵(Rotation Matrix)
假如平面上有一个点(x, y),你希望将这个点围绕原点(0, 0)旋转一个角度 𝜃(比如90度)。旋转矩阵在这里的作用是将点 (x, y) 的位置重新计算到旋转后的新位置 (x’, y’)。
首先,二维旋转矩阵可以表示为:
[ c o s ( θ ) − s i n ( θ ) s i n ( θ ) c o s ( θ ) ] (1) \left[ \begin{array}{cc} cos(\theta) & -sin(\theta) \\ sin(\theta) & cos(\theta) \end{array} \right] \tag{1} [cos(θ)sin(θ)−sin(θ)cos(θ)](1)
旋转矩阵的值为什么是这种形式而不是其他的呢?不要急,我慢慢讲。
cos( 𝜃) 和 sin( 𝜃) 是角度 𝜃 的三角函数值,决定了 O A OA OA旋转到 O A ′ OA' OA′的方向和幅度。
这个旋转矩阵(1)可以将图像中每个像素的位置从原位置转换到旋转后的新位置。
转换公式公式(2)所示:
[ x ′ y ′ ] = [ c o s ( θ ) − s i n ( θ ) s i n ( θ ) c o s ( θ ) ] [ x y ] (2) \left[ \begin{matrix} x' \\ y' \end{matrix} \right] = \left[ \begin{array}{cc} cos(\theta) & -sin(\theta) \\ sin(\theta) & cos(\theta) \end{array} \right] \ \left[ \begin{array}{c} x \\ y \end{array} \right] \tag{2} [x′y′]=[cos(θ)sin(θ)−sin(θ)cos(θ)] [xy](2)
通过上诉转换,点 (x, y) 可以通通过旋转矩阵(1)得到新的坐标 (x’, y’):
是不是不理解这个公式,让我给出它的推导,你就廓然开朗了:
旋转公式推导如下
1.对于 O A 的 A OA的A OA的A点坐标 ( x , y ) (x,y) (x,y),有: x = O A c o s ( β ) x=OAcos(\beta) x=OAcos(β), y = O A s i n ( θ ) y=OAsin(\theta) y=OAsin(θ), O A = O A ′ OA=OA' OA=OA′
==================================================================================================
2.对于 O A ′ 的 A ′ OA'的A' OA′的A′点坐标 ( x ′ , y ′ ) (x',y') (x′,y′),有:
x ′ = O A ′ c o s ( θ + β ) = O A ′ ( c o s ( θ ) c o s ( β ) − s i n ( θ ) s i n ( β ) ) = x c o s ( θ ) − y s i n ( θ ) x' = OA'cos(\theta+\beta)=OA'(cos(\theta)cos(\beta)-sin(\theta)sin(\beta))=xcos(\theta)-ysin(\theta) x′=OA′cos(θ+β)=OA′(cos(θ)cos(β)−sin(θ)sin(β))=xcos(θ)−ysin(θ)
y ′ = O A ′ s i n ( θ + β ) = O A ′ ( s i n ( θ ) c o s ( β ) + c o s ( θ ) s i n ( β ) ) = y c o s ( θ ) + x s i n ( θ ) y' = OA'sin(\theta+\beta)=OA'(sin(\theta)cos(\beta)+cos(\theta)sin(\beta))=ycos(\theta)+xsin(\theta) y′=OA′sin(θ+β)=OA′(sin(θ)cos(β)+cos(θ)sin(β))=ycos(θ)+xsin(θ)
则点 ( x , y ) (x,y) (x,y)旋转到 ( x ′ , y ′ ) (x',y') (x′,y′)的旋转矩阵为
[ c o s ( θ ) − s i n ( θ ) s i n ( θ ) c o s ( θ ) ] (3) \left[ \begin{array}{cc} cos(\theta) & -sin(\theta) \\ sin(\theta) & cos(\theta) \end{array} \right] \tag{3} [cos(θ)sin(θ)−sin(θ)cos(θ)](3)
2. 插值方法
旋转后的图像通常会有像素位置不规则或不精确的情况1(原因见文末的注释注脚),我们要把不规则和不精确的值变成整数,因此需要一些手段来处理像素位置不规则或不精确的情况,通常使用插值方法来估算新位置上的像素值。常用的插值方法包括:
- 最近邻插值: 将旋转后目标像素的值赋予最接近的原像素的值。这种方法简单但可能导致明显的锯齿状边缘。
- 双线性插值: 在旋转后的目标像素位置周围选择四个邻近像素的加权平均值。比最近邻插值提供更平滑的图像。
- 双三次插值: 在旋转后的目标像素位置周围选择16个邻近像素的加权平均值。计算复杂度较高,处理速度比双线性插值慢。
3. 完整步骤
1.计算旋转矩阵:根据旋转角度计算旋转矩阵。
2.变换每个像素的位置:使用旋转矩阵将每个像素从源图像的位置转换到目标图像的位置。
3.应用插值方法:对目标图像的每个像素应用插值方法以填充像素值。
4.处理边界和裁剪:旋转后图像可能会有空白区域或需要裁剪以适应原始图像的边界。
opencv实现:
import cv2
import numpy as np
# 读取图像
image = cv2.imread('example.jpg')
# 获取图像的尺寸
(h, w) = image.shape[:2]
# 计算图像的中心
center = (w // 2, h // 2)
# 旋转角度
angle = 45
# 计算旋转矩阵
M = cv2.getRotationMatrix2D(center, angle, 1.0)
# 计算旋转后图像的尺寸
# 这里计算的尺寸确保了图像不会被裁剪
cos_theta = np.abs(M[0, 0])
sin_theta = np.abs(M[0, 1])
new_w = int((h * sin_theta) + (w * cos_theta))
new_h = int((h * cos_theta) + (w * sin_theta))
# 更新旋转矩阵以保持图像中心
M[0, 2] += (new_w / 2) - center[0]
M[1, 2] += (new_h / 2) - center[1]
# 旋转图像并扩展边界
rotated_image = cv2.warpAffine(image, M, (new_w, new_h), flags=cv2.INTER_LINEAR)
# 保存旋转后的图像
cv2.imwrite('rotated_example_expanded.jpg', rotated_image)
# 可选:裁剪图像
# 计算裁剪区域(示例:裁剪到原始图像尺寸)
start_x = (new_w - w) // 2
start_y = (new_h - h) // 2
cropped_image = rotated_image[start_y:start_y + h, start_x:start_x + w]
# 保存裁剪后的图像
cv2.imwrite('rotated_example_cropped.jpg', cropped_image)
5.3.裁剪
图像裁剪(Cropping)是从图像中提取一个子区域的过程。裁剪通常用于去除图像中不需要的部分,集中于感兴趣的区域。
5.3.1 裁剪方法
- 矩形裁剪: 根据给定的矩形区域裁剪图像。
- 圆形裁剪: 基于圆形区域裁剪图像,通常需要掩膜。
- 多边形裁剪: 根据自定义的多边形区域裁剪图像。
- 自定义裁剪: 根据特定的需求裁剪图像。
使用opencv进行矩形裁剪
import cv2
def crop_image_opencv(image_path, crop_box, output_path):
"""
使用 OpenCV 裁剪图像并保存
:param image_path: 输入图像路径
:param crop_box: 裁剪框,格式为 (startX, startY, width, height)
:param output_path: 输出图像路径
"""
# 读取图像
image = cv2.imread(image_path)
# 定义裁剪框 (startX, startY, width, height)
startX, startY, width, height = crop_box
endX = startX + width
endY = startY + height
# 裁剪图像
cropped_image = image[startY:endY, startX:endX]
# 保存裁剪后的图像
cv2.imwrite(output_path, cropped_image)
# 定义裁剪框 (startX, startY, width, height)
crop_box = (100, 100, 300, 300) # 裁剪一个区域
# 使用函数裁剪图像
crop_image_opencv('example.jpg', crop_box, 'cropped_example_opencv.jpg')
5.3.2. 裁剪的应用
- 图像预处理: 裁剪可以用来集中处理图像中最重要的部分。
- 视觉效果: 裁剪可以用于创建适合特定比例或尺寸的图像。
- 去除干扰: 裁剪可以去掉图像中的无关背景或不必要的部分。
5.4.平移
图像平移(Translation)是将图像的所有像素点沿着水平和垂直方向移动一个固定的距离。平移通常用于图像处理、图像对齐或创建动画效果。
下面是关于在 Python 中实现图像平移的实现。
import cv2
import numpy as np
def translate_image_opencv(image_path, tx, ty, output_path):
"""
使用 OpenCV 平移图像并保存
:param image_path: 输入图像路径
:param tx: 水平平移距离
:param ty: 垂直平移距离
:param output_path: 输出图像路径
"""
# 读取图像
image = cv2.imread(image_path)
height, width = image.shape[:2]
# 定义平移矩阵
translation_matrix = np.float32([[1, 0, tx], [0, 1, ty]])
# 应用平移矩阵
translated_image = cv2.warpAffine(image, translation_matrix, (width, height), borderMode=cv2.BORDER_CONSTANT, borderValue=(255, 255, 255))
# 保存平移后的图像
cv2.imwrite(output_path, translated_image)
# 使用函数平移图像
translate_image_opencv('example.jpg', 50, 30, 'translated_example_opencv.jpg')
5.5.滤波
图像滤波是图像处理中的一个重要技术,主要用于主要用于平滑图像、去除噪声、增强特征等。常见的图像滤波方法包括:
- 均值滤波(Mean Filtering):
用于去除图像中的随机噪声。它通过用滤波器窗口内的像素值的平均值来替换中心像素值。
- 高斯滤波(Gaussian Filtering):
通过高斯函数加权窗口内的像素值,达到平滑图像的效果,适用于去除图像的高斯噪声。
- 中值滤波(Median Filtering):
用于去除椒盐噪声。通过将滤波器窗口内的像素值排序,并用中值替换中心像素值。
- 边缘检测滤波(Edge Detection Filtering):
用于提取图像中的边缘特征,如Sobel、Prewitt和Canny边缘检测算法。
- 锐化滤波(Sharpening Filtering):
增强图像中的细节和边缘特征。例如,拉普拉斯滤波器和高通滤波器可以用于锐化图像。
- 双边滤波(Bilateral Filtering):
在保留边缘的同时,平滑图像中的噪声。结合了空间距离和像素值差异的加权。
关于滤波的详解我们在后面章节给出,这路不做详细解释。
注释
旋转图像时,每个像素的新位置通常是通过旋转矩阵计算得出的。这些计算结果通常是浮点数,而实际图像中的像素只能位于整数位置。因此,计算得到的浮点位置需要进行四舍五入或插值,以确定最近的像素位置。当然也有在旋转图像时,图像的某些区域可能会超出原有边界,导致出现空白区域。这些空白区域需要被填充或裁剪,这个暂不做讨论。 ↩︎