跳转至

第2章 图像处理基础

图像处理基础图

📚 章节概述

本章介绍图像处理的基本操作,包括图像表示、像素操作、滤波、变换等核心技术。这些是计算机视觉的基础,掌握这些技术对于后续学习深度学习和高级算法至关重要。

学习时间:5-7天 难度等级:⭐⭐⭐ 前置知识:第1章、NumPy基础

🎯 学习目标

完成本章后,你将能够: - 理解数字图像的表示方法 - 掌握像素级操作技术 - 熟练使用各种图像滤波器 - 理解图像变换的原理 - 能够实现图像增强和复原 - 完成图像滤镜应用项目


2.1 图像表示与像素操作

2.1.1 数字图像基础

图像的数学表示

Text Only
灰度图像: I(x, y) ∈ [0, 255]  (8位)
彩色图像: I(x, y, c) ∈ [0, 255]  (c ∈ {R, G, B})

代码示例

Python
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:数组索引

Python
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(感兴趣区域)

Python
# 提取ROI
roi = image[100:200, 100:200]

# 修改ROI
image[100:200, 100:200] = [0, 255, 0]  # 绿色矩形

# 复制ROI
image[50:150, 50:150] = roi

方法3:通道操作

Python
# 分离通道
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 图像算术运算

Python
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 线性滤波

均值滤波(模糊)

Python
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)

高斯滤波

Python
# 高斯模糊
gaussian_blur = cv2.GaussianBlur(image, (5, 5), sigmaX=1.5)

# 自定义高斯核
kernel = cv2.getGaussianKernel(ksize=5, sigma=1.5)
gaussian_2d = kernel * kernel.T

原理

Text Only
高斯核: G(x, y) = (1/(2πσ²)) * e^(-(x²+y²)/(2σ²))

2.2.2 非线性滤波

中值滤波(去噪)

Python
# 中值滤波 - 对椒盐噪声效果好
median_blur = cv2.medianBlur(image, 5)

双边滤波(保边去噪)

Python
# 双边滤波 - 保留边缘
d = 9  # 滤波器直径
sigma_color = 75  # 颜色空间标准差
sigma_space = 75  # 坐标空间标准差
bilateral = cv2.bilateralFilter(image, d, sigma_color, sigma_space)

2.2.3 边缘检测

Sobel算子

Python
# 转换为灰度图
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算子

Python
# Laplacian边缘检测
laplacian = cv2.Laplacian(gray, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)

Canny边缘检测

Python
# 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 图像增强

对比度和亮度调整

Python
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)

直方图均衡化

Python
# 灰度图直方图均衡化
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校正

Python
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 几何变换

缩放

Python
# 按比例缩放
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

旋转

Python
# 旋转
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]))

平移

Python
# 平移
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]))

仿射变换

Python
# 仿射变换(需要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]))

透视变换

Python
# 透视变换(需要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 频域变换

傅里叶变换

Python
# 傅里叶变换
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])

高通滤波(锐化)

Python
# 高通滤波器
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 基本操作

腐蚀

Python
# 腐蚀(白色区域变小)
kernel = np.ones((5, 5), np.uint8)
erosion = cv2.erode(image, kernel, iterations=1)

膨胀

Python
# 膨胀(白色区域变大)
dilation = cv2.dilate(image, kernel, iterations=1)

开运算

Python
# 开运算(先腐蚀后膨胀)- 去除小物体
opening = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)

闭运算

Python
# 闭运算(先膨胀后腐蚀)- 填充小孔
closing = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)

2.4.2 高级操作

形态学梯度

Python
# 形态学梯度(膨胀-腐蚀)
gradient = cv2.morphologyEx(image, cv2.MORPH_GRADIENT, kernel)

顶帽

Python
# 顶帽(原图-开运算)- 提取亮区域
tophat = cv2.morphologyEx(image, cv2.MORPH_TOPHAT, kernel)

黑帽

Python
# 黑帽(闭运算-原图)- 提取暗区域
blackhat = cv2.morphologyEx(image, cv2.MORPH_BLACKHAT, kernel)


2.5 实战案例:图像滤镜应用

项目概述

创建一个图像滤镜应用,实现多种滤镜效果。

完整代码

Python
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 练习题

基础题

  1. 简答题
  2. 什么是图像的ROI?如何提取和修改ROI?

    ROI(Region of Interest)是图像中感兴趣的区域。提取:通过数组切片 roi = img[y1:y2, x1:x2] 获取子区域;修改:直接对切片赋值 img[y1:y2, x1:x2] = new_value

  3. 均值滤波和中值滤波有什么区别?

    均值滤波用邻域像素的算术平均值替代中心像素,属于线性滤波,会模糊边缘;中值滤波用邻域像素的中值替代,属于非线性滤波,能有效去除椒盐噪声且较好地保留边缘。

  4. 编程题

  5. 编写一个函数,实现图像的镜像翻转。
  6. 实现图像的亮度调整函数。

进阶题

  1. 编程题
  2. 实现一个自定义的卷积核,用于边缘增强。
  3. 编写一个函数,实现图像的直方图匹配。

  4. 思考题

  5. 为什么双边滤波可以保留边缘?

    双边滤波同时考虑空间距离和像素值差异两个权重:空间距离近的像素权重大,像素值相似的权重大。在边缘处两侧像素值差异大,跨边缘像素的权重被抑制,因此不会模糊边缘。

  6. Canny边缘检测的三个步骤是什么?

    核心三步:①高斯滤波去噪;②利用Sobel算子计算梯度幅值和方向;③非极大值抑制细化边缘。完整流程还包括双阈值检测和边缘连接(共五步)。

挑战题

  1. 项目题
  2. 实现一个完整的图像编辑器,支持裁剪、旋转、滤镜等功能。

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 本章小结

核心知识点

  1. 图像表示:矩阵/张量形式
  2. 像素操作:访问、修改、ROI
  3. 图像滤波:线性、非线性滤波
  4. 边缘检测:Sobel、Canny
  5. 图像增强:对比度、亮度、直方图
  6. 几何变换:缩放、旋转、仿射、透视
  7. 形态学操作:腐蚀、膨胀、开闭运算

下一步

完成本章后,你应该: - [ ] 理解图像的表示方法 - [ ] 掌握基本的像素操作 - [ ] 熟练使用各种滤波器 - [ ] 理解图像变换原理 - [ ] 完成图像滤镜项目

下一章03-特征提取与描述.md - 学习特征提取技术


恭喜完成第2章! 🎉