第2章 图像处理基础¶
📚 章节概述¶
本章介绍图像处理的基本操作,包括图像表示、像素操作、滤波、变换等核心技术。这些是计算机视觉的基础,掌握这些技术对于后续学习深度学习和高级算法至关重要。
学习时间:5-7天 难度等级:⭐⭐⭐ 前置知识:第1章、NumPy基础
🎯 学习目标¶
完成本章后,你将能够: - 理解数字图像的表示方法 - 掌握像素级操作技术 - 熟练使用各种图像滤波器 - 理解图像变换的原理 - 能够实现图像增强和复原 - 完成图像滤镜应用项目
2.1 图像表示与像素操作¶
2.1.1 数字图像基础¶
图像的数学表示:
代码示例:
import cv2
import numpy as np
# 创建图像
# 灰度图像
gray_img = np.zeros((100, 100), dtype=np.uint8)
# 彩色图像 (BGR格式)
color_img = np.zeros((100, 100, 3), dtype=np.uint8)
color_img[:, :] = [255, 0, 0] # 蓝色
# 读取图像
image = cv2.imread('image.jpg')
# ⚠️ imread() 失败时返回 None(如文件不存在),使用前应检查:
# if image is None: raise FileNotFoundError("无法加载图像")
# 图像属性
print(f"尺寸: {image.shape}") # (height, width, channels)
print(f"数据类型: {image.dtype}") # uint8
print(f"总像素数: {image.size}")
2.1.2 像素访问与修改¶
方法1:数组索引
import cv2
import numpy as np
image = cv2.imread('image.jpg')
# 访问单个像素
pixel = image[100, 100] # BGR值
print(f"像素值: {pixel}")
# 修改像素
image[100, 100] = [255, 255, 255] # 白色
# 访问通道
blue = image[100, 100, 0]
green = image[100, 100, 1]
red = image[100, 100, 2]
方法2:ROI(感兴趣区域)
# 提取ROI
roi = image[100:200, 100:200]
# 修改ROI
image[100:200, 100:200] = [0, 255, 0] # 绿色矩形
# 复制ROI
image[50:150, 50:150] = roi
方法3:通道操作
# 分离通道
b, g, r = cv2.split(image)
# 合并通道
image_merged = cv2.merge([b, g, r])
# 只保留红色通道
image_red = np.zeros_like(image)
image_red[:, :, 2] = r
2.1.3 图像算术运算¶
import cv2
import numpy as np
# 读取两张图像
img1 = cv2.imread('image1.jpg')
img2 = cv2.imread('image2.jpg')
# 确保尺寸相同
img2 = cv2.resize(img2, (img1.shape[1], img1.shape[0]))
# 加法
add = cv2.add(img1, img2)
# 或
add = img1 + img2 # 不同:模256加法
# 减法
sub = cv2.subtract(img1, img2)
# 加权混合
alpha = 0.7
beta = 0.3
gamma = 0
blended = cv2.addWeighted(img1, alpha, img2, beta, gamma)
# 位运算
and_img = cv2.bitwise_and(img1, img2)
or_img = cv2.bitwise_or(img1, img2)
xor_img = cv2.bitwise_xor(img1, img2)
not_img = cv2.bitwise_not(img1)
2.2 图像滤波与增强¶
2.2.1 线性滤波¶
均值滤波(模糊):
import cv2
import numpy as np
image = cv2.imread('image.jpg')
# 均值滤波
kernel_size = 5
blur = cv2.blur(image, (kernel_size, kernel_size))
# 方框滤波
box_filter = cv2.boxFilter(image, -1, (5, 5), normalize=True)
高斯滤波:
# 高斯模糊
gaussian_blur = cv2.GaussianBlur(image, (5, 5), sigmaX=1.5)
# 自定义高斯核
kernel = cv2.getGaussianKernel(ksize=5, sigma=1.5)
gaussian_2d = kernel * kernel.T
原理:
2.2.2 非线性滤波¶
中值滤波(去噪):
双边滤波(保边去噪):
# 双边滤波 - 保留边缘
d = 9 # 滤波器直径
sigma_color = 75 # 颜色空间标准差
sigma_space = 75 # 坐标空间标准差
bilateral = cv2.bilateralFilter(image, d, sigma_color, sigma_space)
2.2.3 边缘检测¶
Sobel算子:
# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Sobel边缘检测
sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
# 转换为绝对值
sobel_x = cv2.convertScaleAbs(sobel_x)
sobel_y = cv2.convertScaleAbs(sobel_y)
# 合并
sobel = cv2.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0)
Laplacian算子:
# Laplacian边缘检测
laplacian = cv2.Laplacian(gray, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
Canny边缘检测:
# Canny边缘检测
threshold1 = 50
threshold2 = 150
canny = cv2.Canny(gray, threshold1, threshold2)
# 自适应阈值
median = np.median(gray)
lower = int(max(0, 0.7 * median))
upper = int(min(255, 1.3 * median))
canny_auto = cv2.Canny(gray, lower, upper)
2.2.4 图像增强¶
对比度和亮度调整:
def adjust_brightness_contrast(image, brightness=0, contrast=0):
"""
调整亮度和对比度
brightness: -100 到 100
contrast: -100 到 100
"""
if brightness != 0:
if brightness > 0:
shadow = brightness
highlight = 255
else:
shadow = 0
highlight = 255 + brightness
alpha_b = (highlight - shadow) / 255
gamma_b = shadow
image = cv2.addWeighted(image, alpha_b, image, 0, gamma_b)
if contrast != 0:
f = 131 * (contrast + 127) / (127 * (131 - contrast))
alpha_c = f
gamma_c = 127 * (1 - f)
image = cv2.addWeighted(image, alpha_c, image, 0, gamma_c)
return image
# 使用
enhanced = adjust_brightness_contrast(image, brightness=20, contrast=30)
直方图均衡化:
# 灰度图直方图均衡化
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
equalized = cv2.equalizeHist(gray)
# CLAHE(对比度受限的自适应直方图均衡化)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
clahe_img = clahe.apply(gray)
# 彩色图像CLAHE
lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
l = clahe.apply(l)
lab = cv2.merge([l, a, b])
clahe_color = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)
Gamma校正:
def gamma_correction(image, gamma=1.0):
"""
Gamma校正
gamma < 1: 变亮
gamma > 1: 变暗
"""
inv_gamma = 1.0 / gamma
table = np.array([((i / 255.0) ** inv_gamma) * 255 # np.array创建NumPy数组
for i in np.arange(0, 256)]).astype("uint8")
return cv2.LUT(image, table)
# 使用
gamma_img = gamma_correction(image, gamma=0.5)
2.3 图像变换¶
2.3.1 几何变换¶
缩放:
# 按比例缩放
scale_factor = 0.5
resized = cv2.resize(image, None, fx=scale_factor, fy=scale_factor)
# 指定尺寸
new_size = (640, 480)
resized = cv2.resize(image, new_size, interpolation=cv2.INTER_LINEAR)
# 插值方法
# cv2.INTER_NEAREST - 最近邻
# cv2.INTER_LINEAR - 双线性(默认)
# cv2.INTER_CUBIC - 双三次
# cv2.INTER_LANCZOS4 - Lanczos
旋转:
# 旋转
center = (image.shape[1] // 2, image.shape[0] // 2)
angle = 45
scale = 1.0
M = cv2.getRotationMatrix2D(center, angle, scale)
rotated = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
平移:
# 平移
tx, ty = 50, 30 # x和y方向的平移量
M = np.float32([[1, 0, tx], [0, 1, ty]])
translated = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
仿射变换:
# 仿射变换(需要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)
affine = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
透视变换:
# 透视变换(需要4个点对)
src_points = np.float32([[56, 65], [368, 52], [28, 387], [389, 390]])
dst_points = np.float32([[0, 0], [300, 0], [0, 300], [300, 300]])
M = cv2.getPerspectiveTransform(src_points, dst_points)
perspective = cv2.warpPerspective(image, M, (300, 300))
2.3.2 频域变换¶
傅里叶变换:
# 傅里叶变换
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# DFT
dft = cv2.dft(np.float32(gray), flags=cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
# 频谱
magnitude_spectrum = 20 * np.log(cv2.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))
# 逆DFT
idft_shift = np.fft.ifftshift(dft_shift)
idft = cv2.idft(idft_shift)
idft_img = cv2.magnitude(idft[:, :, 0], idft[:, :, 1])
高通滤波(锐化):
# 高通滤波器
rows, cols = gray.shape
crow, ccol = rows // 2, cols // 2
mask = np.ones((rows, cols, 2), np.uint8)
r = 80 # 半径
mask[crow-r:crow+r, ccol-r:ccol+r] = 0
# 应用滤波器
fshift = dft_shift * mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1])
2.4 形态学操作¶
2.4.1 基本操作¶
腐蚀:
# 腐蚀(白色区域变小)
kernel = np.ones((5, 5), np.uint8)
erosion = cv2.erode(image, kernel, iterations=1)
膨胀:
开运算:
闭运算:
2.4.2 高级操作¶
形态学梯度:
顶帽:
黑帽:
2.5 实战案例:图像滤镜应用¶
项目概述¶
创建一个图像滤镜应用,实现多种滤镜效果。
完整代码¶
import cv2
import numpy as np
from enum import Enum
class FilterType(Enum):
"""滤镜类型"""
ORIGINAL = "原图"
GRAYSCALE = "灰度"
BLUR = "模糊"
SHARPEN = "锐化"
EDGE = "边缘检测"
BRIGHTNESS = "亮度增强"
CONTRAST = "对比度增强"
VINTAGE = "复古"
CARTOON = "卡通"
EMBOSS = "浮雕"
class ImageFilter:
"""图像滤镜类"""
def __init__(self, image_path):
self.image = cv2.imread(image_path)
if self.image is None:
raise ValueError(f"无法加载图像: {image_path}")
self.original = self.image.copy()
def apply_filter(self, filter_type):
"""应用滤镜"""
if filter_type == FilterType.ORIGINAL:
return self.original
elif filter_type == FilterType.GRAYSCALE:
return self.to_grayscale()
elif filter_type == FilterType.BLUR:
return self.blur()
elif filter_type == FilterType.SHARPEN:
return self.sharpen()
elif filter_type == FilterType.EDGE:
return self.edge_detection()
elif filter_type == FilterType.BRIGHTNESS:
return self.brightness_enhance()
elif filter_type == FilterType.CONTRAST:
return self.contrast_enhance()
elif filter_type == FilterType.VINTAGE:
return self.vintage()
elif filter_type == FilterType.CARTOON:
return self.cartoon()
elif filter_type == FilterType.EMBOSS:
return self.emboss()
else:
return self.original
def to_grayscale(self):
"""灰度滤镜"""
return cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
def blur(self):
"""模糊滤镜"""
return cv2.GaussianBlur(self.image, (15, 15), 0)
def sharpen(self):
"""锐化滤镜"""
kernel = np.array([[-1, -1, -1],
[-1, 9, -1],
[-1, -1, -1]])
return cv2.filter2D(self.image, -1, kernel)
def edge_detection(self):
"""边缘检测滤镜"""
gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 100, 200)
return cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
def brightness_enhance(self):
"""亮度增强"""
hsv = cv2.cvtColor(self.image, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
v = cv2.add(v, 50)
v[v > 255] = 255
hsv = cv2.merge([h, s, v])
return cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
def contrast_enhance(self):
"""对比度增强"""
lab = cv2.cvtColor(self.image, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
l = clahe.apply(l)
lab = cv2.merge([l, a, b])
return cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)
def vintage(self):
"""复古滤镜"""
# 降低饱和度
hsv = cv2.cvtColor(self.image, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
s = cv2.multiply(s, 0.7)
hsv = cv2.merge([h, s, v])
vintage = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
# 添加暖色调
vintage[:, :, 2] = cv2.add(vintage[:, :, 2], 30) # 增加红色
vintage[:, :, 0] = cv2.subtract(vintage[:, :, 0], 20) # 减少蓝色
return vintage
def cartoon(self):
"""卡通滤镜"""
# 双边滤波
bilateral = cv2.bilateralFilter(self.image, 15, 80, 80)
# 边缘检测
gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
edges = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 9, 9)
# 合并
edges = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
cartoon = cv2.bitwise_and(bilateral, edges)
return cartoon
def emboss(self):
"""浮雕滤镜"""
kernel = np.array([[-2, -1, 0],
[-1, 1, 1],
[ 0, 1, 2]])
emboss = cv2.filter2D(self.image, -1, kernel)
emboss = cv2.add(emboss, 128) # 增加亮度
return emboss
def save_filtered_image(self, filter_type, output_path):
"""保存滤镜后的图像"""
filtered = self.apply_filter(filter_type)
cv2.imwrite(output_path, filtered)
print(f"已保存: {output_path}")
# 使用示例
if __name__ == '__main__':
# 创建测试图像
test_image = np.random.randint(0, 256, (480, 640, 3), dtype=np.uint8)
cv2.imwrite('test.jpg', test_image)
# 应用滤镜
filter_app = ImageFilter('test.jpg')
# 应用所有滤镜
for filter_type in FilterType:
output_path = f"output_{filter_type.value}.jpg"
filter_app.save_filtered_image(filter_type, output_path)
print("所有滤镜已应用完成!")
2.6 练习题¶
基础题¶
- 简答题:
- 什么是图像的ROI?如何提取和修改ROI?
ROI(Region of Interest)是图像中感兴趣的区域。提取:通过数组切片
roi = img[y1:y2, x1:x2]获取子区域;修改:直接对切片赋值img[y1:y2, x1:x2] = new_value。 -
均值滤波和中值滤波有什么区别?
均值滤波用邻域像素的算术平均值替代中心像素,属于线性滤波,会模糊边缘;中值滤波用邻域像素的中值替代,属于非线性滤波,能有效去除椒盐噪声且较好地保留边缘。
-
编程题:
- 编写一个函数,实现图像的镜像翻转。
- 实现图像的亮度调整函数。
进阶题¶
- 编程题:
- 实现一个自定义的卷积核,用于边缘增强。
-
编写一个函数,实现图像的直方图匹配。
-
思考题:
- 为什么双边滤波可以保留边缘?
双边滤波同时考虑空间距离和像素值差异两个权重:空间距离近的像素权重大,像素值相似的权重大。在边缘处两侧像素值差异大,跨边缘像素的权重被抑制,因此不会模糊边缘。
- Canny边缘检测的三个步骤是什么?
核心三步:①高斯滤波去噪;②利用Sobel算子计算梯度幅值和方向;③非极大值抑制细化边缘。完整流程还包括双阈值检测和边缘连接(共五步)。
挑战题¶
- 项目题:
- 实现一个完整的图像编辑器,支持裁剪、旋转、滤镜等功能。
2.7 面试准备¶
大厂面试题¶
Q1: 什么是卷积?卷积和相关性有什么区别?
参考答案: - 卷积:信号处理中的基本操作,用于滤波、特征提取 - 数学定义:(f * g)(t) = ∫f(τ)g(t-τ)dτ - 相关性:不翻转核,直接计算 - 区别:卷积会翻转核,相关性不翻转 - 在图像处理中:通常使用相关性,但称为卷积
Q2: 高斯滤波为什么比均值滤波好?
参考答案: - 高斯滤波:加权平均,中心像素权重更大 - 均值滤波:等权平均 - 优势: - 更好的平滑效果 - 保留更多细节 - 频域特性更好(低通滤波) - 应用:图像预处理、去噪
Q3: Canny边缘检测的步骤是什么?
参考答案: 1. 高斯滤波:去噪 2. 计算梯度:Sobel算子 3. 非极大值抑制:细化边缘 4. 双阈值检测:强边缘和弱边缘 5. 边缘跟踪:连接弱边缘
Q4: 如何实现图像的锐化?
参考答案: - 方法1:高通滤波器 text kernel = [[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]] - 方法2:拉普拉斯算子 - 方法3:USM锐化(Unsharp Mask) text sharpened = original + (original - blurred) * amount
2.8 本章小结¶
核心知识点¶
- 图像表示:矩阵/张量形式
- 像素操作:访问、修改、ROI
- 图像滤波:线性、非线性滤波
- 边缘检测:Sobel、Canny
- 图像增强:对比度、亮度、直方图
- 几何变换:缩放、旋转、仿射、透视
- 形态学操作:腐蚀、膨胀、开闭运算
下一步¶
完成本章后,你应该: - [ ] 理解图像的表示方法 - [ ] 掌握基本的像素操作 - [ ] 熟练使用各种滤波器 - [ ] 理解图像变换原理 - [ ] 完成图像滤镜项目
下一章:03-特征提取与描述.md - 学习特征提取技术
恭喜完成第2章! 🎉