# 第 1 章 引言 (Introduction) ## 📌 本章知识点概览 1. 什么是数字图像处理 2. 数字图像处理的起源与发展 3. 典型应用领域 4. 图像处理系统的基本组成 5. 本书的知识框架 --- ## 1.1 什么是数字图像处理? ### 📖 核心概念 **图像 (Image)**:对人类视觉感知的二维表示,包含亮度和颜色信息。 **数字图像 (Digital Image)**:将连续图像离散化为有限个像素点的二维数组,每个像素有特定的灰度值或颜色值。 **数字图像处理 (Digital Image Processing)**: > 使用计算机算法对数字图像进行分析、操作和处理,以改善图像质量、提取有用信息或为后续任务做准备。 ### 🎯 为什么要处理图像? | 目的 | 说明 | 例子 | |------|------|------| | **改善视觉效果** | 让人眼更容易观察 | 增强对比度、去噪、锐化 | | **提取信息** | 为机器分析做准备 | 边缘检测、特征提取 | | **压缩存储** | 减少数据量 | JPEG 压缩 | | **自动识别** | 模式识别基础 | 人脸识别、字符识别 | --- ## 1.2 数字图像处理的起源与发展 ### 📜 发展历程 | 年代 | 里程碑事件 | |------|------------| | 1920s | 巴特兰电缆传输第一张数字照片(报纸行业) | | 1960s | 喷气推进实验室处理月球照片(计算机处理开端) | | 1970s | CT 扫描发明(医学图像处理革命) | | 1980s | 个人计算机普及,图像处理工具出现 | | 1990s | JPEG/MPEG 标准确立,互联网图像传输 | | 2000s | 数码相机普及,消费级应用爆发 | | 2010s | 深度学习崛起,CNN 在图像识别中取得突破 | | 2020s | 生成式 AI(GAN、Diffusion)改变图像生成 | ### 💡 关键洞察 数字图像处理的发展始终由**硬件进步**和**应用需求**双轮驱动: - 硬件:计算能力提升 → 能处理更复杂的算法 - 需求:从科研→医疗→消费→工业,应用场景不断扩展 --- ## 1.3 典型应用领域 ### 🏥 医学成像 - **X 射线成像**:肺部检查、骨折检测 - **CT(计算机断层扫描)**:三维重建内部器官 - **MRI(磁共振成像)**:软组织高分辨率成像 - **PET(正电子发射断层扫描)**:功能成像,癌症检测 - **应用**:病灶检测、手术规划、疾病诊断 ### 🛰️ 遥感与地理信息 - **卫星图像分析**:土地利用分类、农作物监测 - **气象预报**:云图分析、台风追踪 - **环境监测**:森林覆盖变化、水体污染检测 - **灾害评估**:洪水、地震灾后评估 ### 🏭 工业检测 - **产品质量检测**:表面缺陷识别 - **自动分拣**:基于颜色、形状分类 - **尺寸测量**:非接触式精密测量 - **机器人视觉**:引导机械臂定位抓取 ### 🚔 安防与监控 - **人脸识别**:门禁系统、身份验证 - **行为分析**:异常行为检测 - **车牌识别**:停车场管理、交通违章 - **视频摘要**:从长时间监控中提取关键事件 ### 🎨 多媒体与娱乐 - **图像编辑**:Photoshop 等工具 - **电影特效**:绿幕抠图、CGI 合成 - **游戏开发**:纹理映射、光照渲染 - **虚拟现实**:360°全景图像处理 ### 📱 消费级应用 - **手机摄影**:HDR、夜景模式、人像虚化 - **社交媒体**:滤镜、美颜 - **文档扫描**:透视矫正、文字增强 - **AR 应用**:实时图像叠加 --- ## 1.4 图像处理系统的基本组成 ### 🔧 系统架构 ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 图像获取 │ ──→ │ 图像处理 │ ──→ │ 输出/显示 │ │ (采集) │ │ (算法) │ │ (结果) │ └─────────────┘ └─────────────┘ └─────────────┘ ↑ ↑ ↑ 传感器 计算机 显示器/ 摄像头 算法库 打印机/ 扫描仪 存储 网络传输 ``` ### 📦 核心组件 #### 1. 图像获取子系统 | 组件 | 功能 | 例子 | |------|------|------| | **传感器** | 将光信号转换为电信号 | CCD、CMOS | | **采样** | 空间离散化 | 决定图像分辨率 | | **量化** | 灰度/颜色离散化 | 8 位 (256 级)、16 位 | | **数字化** | 模数转换 (ADC) | 生成数字矩阵 | #### 2. 处理子系统 - **硬件**:CPU、GPU、专用图像处理芯片 - **软件**:OpenCV、MATLAB、scikit-image - **算法**:滤波、变换、分割、识别 #### 3. 存储与传输 - **存储格式**:BMP(无压缩)、JPEG(有损)、PNG(无损) - **传输协议**:HTTP、RTSP(视频流) #### 4. 显示与输出 - **显示器**:LCD、OLED - **打印**:喷墨、激光 - **网络传输**:社交媒体、云存储 --- ## 1.5 数字图像处理的知识框架 ### 📚 本书结构概览 ``` 数字图像处理 ├── 基础篇 │ ├── 第 2 章:数字图像基础(采样、量化、像素关系) │ └── 第 3 章:灰度变换与空间滤波 │ ├── 频域篇 │ └── 第 4 章:频率域滤波(傅里叶变换) │ ├── 增强与复原篇 │ ├── 第 5 章:图像复原与重建 │ └── 第 6 章:彩色图像处理 │ ├── 压缩与变换篇 │ ├── 第 7 章:小波和多分辨率处理 │ └── 第 8 章:图像压缩 │ ├── 分割与描述篇 │ ├── 第 9 章:形态学图像处理 │ ├── 第 10 章:图像分割 │ └── 第 11 章:表示与描述 │ └── 识别篇 └── 第 12 章:目标识别 ``` ### 🎓 学习路径建议 ``` 入门(1-2 周) ↓ 第 1-2 章:理解什么是数字图像、如何表示 ↓ 基础技能(2-3 周) ↓ 第 3 章:学会基本的图像增强操作 ↓ 进阶(3-4 周) ↓ 第 4-6 章:频域分析、复原、彩色处理 ↓ 高级应用(4-6 周) ↓ 第 7-12 章:压缩、分割、识别 ↓ 实战项目 ↓ 综合运用所学知识完成实际项目 ``` --- ## 💻 第 1 章实践任务 ### 任务 1:环境搭建 ```bash # 安装 Python 图像处理库 pip install opencv-python pip install scikit-image pip install pillow pip install matplotlib pip install numpy ``` ### 任务 2:Hello World 图像程序 ```python import cv2 import matplotlib.pyplot as plt # 读取图像 img = cv2.imread('test.jpg') # 转换为 RGB(OpenCV 默认 BGR) img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 显示图像 plt.imshow(img_rgb) plt.axis('off') plt.title('My First Image') plt.show() # 保存图像 cv2.imwrite('output.jpg', img) ``` ### 任务 3:查看图像基本信息 ```python import cv2 img = cv2.imread('test.jpg') print(f"图像尺寸:{img.shape}") # (高,宽,通道数) print(f"数据类型:{img.dtype}") # uint8 print(f"像素总数:{img.size}") ``` --- ## 📝 本章小结 ### ✅ 你应该掌握 - [ ] 理解什么是数字图像和数字图像处理 - [ ] 了解数字图像处理的主要应用领域 - [ ] 知道图像处理系统的基本组成 - [ ] 理解本书的知识框架和学习路径 - [ ] 完成环境搭建和第一个图像程序 ### 🤔 思考题 1. 数字图像处理与计算机视觉有什么区别? 2. 为什么医学成像对图像处理技术要求最高? 3. 手机拍照中的"夜景模式"用到了哪些图像处理技术? ### 📖 延伸阅读 - 维基百科:Digital image processing - OpenCV 官方教程:https://docs.opencv.org/ - scikit-image 示例库:https://scikit-image.org/docs/stable/auto_examples/ --- _第 1 章完 | 下一章:第 2 章 数字图像基础_ # 第 2 章 数字图像基础 (Digital Image Fundamentals) ## 📌 本章知识点概览 1. 视觉感知基础 2. 光和电磁波谱 3. 图像感知与获取 4. **采样与量化**(核心重点 ⭐) 5. 像素间的基本关系 6. 线性与非线性操作 --- ## 2.1 视觉感知基础 ### 👁️ 人眼结构 ``` 光线 → 角膜 → 瞳孔 → 晶状体 → 视网膜 → 视神经 → 大脑 ↓ 虹膜控制进光量 ``` **关键结构:** - **角膜**:透明外层,初步聚焦 - **晶状体**:可变焦,精细调节 - **视网膜**:感光细胞分布层 - **锥状细胞**:600-700 万,负责彩色视觉(明亮环境) - **杆状细胞**:7500-1.5 亿,负责灰度视觉(暗环境) ### 🎨 颜色视觉原理 **三原色理论**:人眼有三种锥状细胞,分别对红 (R)、绿 (G)、蓝 (B) 敏感 ``` 红光 (564-580nm) → L 型锥细胞 绿光 (534-545nm) → M 型锥细胞 蓝光 (420-440nm) → S 型锥细胞 ``` **颜色混合**:所有颜色 = R + G + B 的不同强度组合 --- ## 2.2 光和电磁波谱 ### 🌈 电磁波谱 ``` γ射线 → X 射线 → 紫外线 → 可见光 → 红外线 → 微波 → 无线电波 (短波长) (长波长) ``` **可见光范围**:380nm - 780nm | 颜色 | 波长范围 (nm) | |------|---------------| | 紫色 | 380-450 | | 蓝色 | 450-495 | | 绿色 | 495-570 | | 黄色 | 570-590 | | 橙色 | 590-620 | | 红色 | 620-780 | ### 💡 核心公式 **波长 - 频率关系**: ``` c = λ × f ``` - c = 光速 (3×10⁸ m/s) - λ = 波长 (m) - f = 频率 (Hz) **光子能量**: ``` E = h × f = h × c / λ ``` - h = 普朗克常数 (6.626×10⁻³⁴ J·s) - 波长越短 → 频率越高 → 能量越大 --- ## 2.3 图像感知与获取 ### 📷 图像获取过程 ``` 三维场景 → 光学成像 → 传感器 → 电信号 → 数字化 → 数字图像 ``` ### 🔍 传感器类型 | 传感器 | 原理 | 应用 | |--------|------|------| | **CCD** | 电荷耦合器件,高质量 | 专业相机、天文摄影 | | **CMOS** | 互补金属氧化物,低功耗 | 手机、消费级相机 | | **红外传感器** | 检测热辐射 | 夜视、温度测量 | | **X 射线传感器** | 检测 X 射线穿透 | 医疗、安检 | --- ## 2.4 采样与量化 ⭐⭐⭐(核心重点) ### 📐 采样 (Sampling) **定义**:将连续图像在**空间上离散化**,决定图像的**分辨率**。 ``` 连续图像 → 采样网格 → 离散像素点 ``` **采样定理(奈奎斯特)**: > 采样频率必须大于信号最高频率的**2 倍**,才能无失真地恢复原信号。 **欠采样后果**:出现**混叠 (Aliasing)** 现象 - 表现为:锯齿边缘、摩尔纹 - 解决:采样前进行低通滤波(抗混叠滤波) ### 🎚️ 量化 (Quantization) **定义**:将每个像素的**灰度值离散化**,决定图像的**灰度级数**。 **量化级别**: - **1 位**:2 级(黑白二值图像) - **8 位**:256 级(标准灰度图像)⭐ 最常用 - **16 位**:65536 级(医学图像、高动态范围) - **24 位**:真彩色(RGB 各 8 位) ### 📊 采样与量化的关系 ``` 图像质量 = f(采样密度,量化级数) ``` | 采样 | 量化 | 效果 | |------|------|------| | 高 | 高 | 高质量,大文件 | | 高 | 低 | 细节多但有伪轮廓 | | 低 | 高 | 马赛克但灰度平滑 | | 低 | 低 | 质量差,文件小 | ### 💻 代码实践:采样与量化 ```python import cv2 import numpy as np import matplotlib.pyplot as plt # 读取图像 img = cv2.imread('test.jpg', cv2.IMREAD_GRAYSCALE) # 1. 降低采样(空间分辨率) def reduce_sampling(img, factor): h, w = img.shape # 缩小 small = cv2.resize(img, (w//factor, h//factor), interpolation=cv2.INTER_AREA) # 放大回原尺寸 large = cv2.resize(small, (w, h), interpolation=cv2.INTER_NEAREST) return large # 2. 降低量化(灰度级数) def reduce_quantization(img, levels): # 量化公式:new_value = round(old_value / (256/levels)) * (256/levels) factor = 256 // levels return (img // factor) * factor # 对比效果 fig, axes = plt.subplots(2, 3, figsize=(15, 10)) axes[0, 0].imshow(img, cmap='gray') axes[0, 0].set_title('原始图像') axes[0, 1].imshow(reduce_sampling(img, 4), cmap='gray') axes[0, 1].set_title('采样降低 4 倍') axes[0, 2].imshow(reduce_sampling(img, 8), cmap='gray') axes[0, 2].set_title('采样降低 8 倍') axes[1, 0].imshow(img, cmap='gray') axes[1, 0].set_title('原始图像') axes[1, 1].imshow(reduce_quantization(img, 16), cmap='gray', vmin=0, vmax=255) axes[1, 1].set_title('量化到 16 级') axes[1, 2].imshow(reduce_quantization(img, 4), cmap='gray', vmin=0, vmax=255) axes[1, 2].set_title('量化到 4 级') plt.tight_layout() plt.show() ``` --- ## 2.5 像素间的基本关系 ### 🔗 像素邻域 对于像素 p 在坐标 (x, y): **4-邻域 (N₄)**:上下左右 ``` (x,y-1) (x-1,y) p (x+1,y) (x,y+1) ``` **8-邻域 (N₈)**:4-邻域 + 四个对角 ``` (x-1,y-1) (x,y-1) (x+1,y-1) (x-1,y) p (x+1,y) (x-1,y+1) (x,y+1) (x+1,y+1) ``` ### 🔗 连通性 (Connectivity) **定义**:两个像素是否"相连"的判断标准 | 连通类型 | 条件 | 应用 | |----------|------|------| | **4-连通** | 灰度相似且在 N₄ 邻域 | 简单场景 | | **8-连通** | 灰度相似且在 N₈ 邻域 | 复杂场景 | | **m-连通** | 混合连通,消除歧义 | 精确分割 | **连通性示例**: ``` 4-连通路径:只能上下左右走 8-连通路径:可以走对角线 ``` ### 📏 距离度量 像素 p(x,y) 和 q(s,t) 之间的距离: **欧氏距离**: ``` Dₑ = √[(x-s)² + (y-t)²] ``` **城区距离 (D₄/曼哈顿距离)**: ``` D₄ = |x-s| + |y-t| ``` **棋盘距离 (D₈)**: ``` D₈ = max(|x-s|,|y-t|) ``` **距离可视化**: ``` 中心像素到各点的 D₄距离: 中心像素到各点的 D₈距离: 2 1 2 2 2 2 1 0 1 2 1 2 2 1 2 2 2 2 ``` --- ## 2.6 线性与非线性操作 ### 📐 线性操作 **定义**:满足**叠加原理**的操作 ``` H(a·f₁ + b·f₂) = a·H(f₁) + b·H(f₂) ``` **常见线性操作**: - 傅里叶变换 - 卷积滤波(均值滤波、高斯滤波) - 几何变换(平移、旋转、缩放) **优势**:数学分析方便,可预测 ### 📈 非线性操作 **定义**:不满足叠加原理的操作 **常见非线性操作**: - 直方图均衡化 - 中值滤波(去椒盐噪声) - 形态学操作(腐蚀、膨胀) - 伽马校正 **优势**:处理复杂场景更有效 --- ## 💻 第 2 章实践任务 ### 任务 1:理解采样定理 ```python import numpy as np import matplotlib.pyplot as plt # 生成正弦波信号 x = np.linspace(0, 10, 1000) y = np.sin(x) # 不同采样率 sample_rates = [100, 20, 10, 5] # 采样点数量 fig, axes = plt.subplots(2, 2, figsize=(12, 8)) axes = axes.flatten() for i, rate in enumerate(sample_rates): sample_x = np.linspace(0, 10, rate) sample_y = np.sin(sample_x) axes[i].plot(x, y, 'b-', alpha=0.3, label='原始信号') axes[i].plot(sample_x, sample_y, 'ro-', label=f'采样 {rate}点') axes[i].legend() axes[i].set_title(f'采样率:{rate}') axes[i].grid(True) plt.tight_layout() plt.show() ``` ### 任务 2:像素邻域操作 ```python import cv2 import numpy as np def get_neighbors(img, x, y, connectivity=8): """获取像素的邻域值""" h, w = img.shape neighbors = [] # 遍历 3x3 区域 for dx in [-1, 0, 1]: for dy in [-1, 0, 1]: if dx == 0 and dy == 0: continue # 跳过中心像素 nx, ny = x + dx, y + dy # 边界检查 if 0 <= nx < w and 0 <= ny < h: if connectivity == 8 or (dx == 0 or dy == 0): neighbors.append(img[ny, nx]) return neighbors # 测试 img = cv2.imread('test.jpg', cv2.IMREAD_GRAYSCALE) neighbors = get_neighbors(img, 100, 100, connectivity=8) print(f"8-邻域像素值:{neighbors}") print(f"邻域均值:{np.mean(neighbors):.2f}") ``` ### 任务 3:距离度量可视化 ```python import numpy as np import matplotlib.pyplot as plt def distance_map(size, center, metric='euclidean'): """生成距离图""" h, w = size cy, cx = center dist = np.zeros((h, w)) for y in range(h): for x in range(w): if metric == 'euclidean': dist[y, x] = np.sqrt((x-cx)**2 + (y-cy)**2) elif metric == 'manhattan': dist[y, x] = abs(x-cx) + abs(y-cy) elif metric == 'chessboard': dist[y, x] = max(abs(x-cx), abs(y-cy)) return dist size = (21, 21) center = (10, 10) fig, axes = plt.subplots(1, 3, figsize=(15, 5)) im0 = axes[0].imshow(distance_map(size, center, 'euclidean'), cmap='hot') axes[0].set_title('欧氏距离') plt.colorbar(im0, ax=axes[0]) im1 = axes[1].imshow(distance_map(size, center, 'manhattan'), cmap='hot') axes[1].set_title('城区距离 (D₄)') plt.colorbar(im1, ax=axes[1]) im2 = axes[2].imshow(distance_map(size, center, 'chessboard'), cmap='hot') axes[2].set_title('棋盘距离 (D₈)') plt.colorbar(im2, ax=axes[2]) plt.tight_layout() plt.show() ``` --- ## 📝 本章小结 ### ✅ 你应该掌握 - [ ] 理解人眼视觉系统和颜色感知原理 - [ ] 掌握采样定理和混叠现象 - [ ] 理解量化的概念和灰度级数选择 - [ ] 能够解释像素的邻域、连通性、距离度量 - [ ] 区分线性和非线性操作 - [ ] 完成采样、量化、邻域操作的代码实践 ### 🤔 思考题 1. 为什么手机拍照时快速移动会产生模糊?(提示:采样定理) 2. 医学 CT 图像为什么常用 16 位量化而不是 8 位? 3. 4-连通和 8-连通在图像分割中会产生什么不同结果? 4. 中值滤波为什么是非线性操作? ### 📖 关键公式总结 ``` 采样定理:f_sample > 2 × f_max 欧氏距离:Dₑ = √[(x-s)² + (y-t)²] 城区距离:D₄ = |x-s| + |y-t| 棋盘距离:D₈ = max(|x-s|,|y-t|) ``` --- _第 2 章完 | 下一章:第 3 章 灰度变换与空间滤波_ # 第 3 章 灰度变换与空间滤波 (Intensity Transformations and Spatial Filtering) ## 📌 本章知识点概览 1. 背景知识 2. **灰度变换函数**(图像增强基础) 3. **直方图处理**(核心重点 ⭐⭐⭐) 4. **空间滤波基础**(核心重点 ⭐⭐⭐) 5. 平滑空间滤波器 6. 锐化空间滤波器 --- ## 3.1 背景知识 ### 📐 空间域处理的基本公式 ``` g(x, y) = T[f(x, y)] ``` - f(x, y):输入图像 - g(x, y):输出图像 - T:操作符(变换函数) ### 🎯 处理策略分类 | 类型 | 作用范围 | 例子 | |------|----------|------| | **点操作** | 单个像素 | 灰度变换、对比度调整 | | **邻域操作** | 像素 + 周围邻域 | 空间滤波、卷积 | | **全局操作** | 整幅图像 | 直方图均衡化、傅里叶变换 | --- ## 3.2 灰度变换函数 ### 📈 基本灰度变换 #### 1. 图像反转 (Image Negatives) **公式**: ``` s = L - 1 - r ``` - r:输入灰度值 - s:输出灰度值 - L:灰度级数(通常 256) **效果**:黑白颠倒,类似底片 **应用**:增强白色/灰色细节(如 X 光片中的骨骼) ```python import cv2 import numpy as np def invert_image(img): return 255 - img # 或使用 OpenCV inverted = cv2.bitwise_not(img) ``` #### 2. 对数变换 (Log Transformations) **公式**: ``` s = c × log(1 + r) ``` - c:常数,c = 255 / log(1 + max_r) **效果**:扩展低灰度值,压缩高灰度值 **应用**:显示频谱图、增强暗部细节 ```python def log_transform(img, c=1): img_float = img.astype(np.float32) transformed = c * np.log(1 + img_float) return np.uint8(255 * transformed / transformed.max()) ``` #### 3. 幂次变换 (伽马校正) **公式**: ``` s = c × r^γ ``` - γ < 1:扩展低灰度值(图像变亮) - γ > 1:扩展高灰度值(图像变暗) - γ = 1:恒等变换 **应用**:显示器校正、对比度调整 ```python def gamma_transform(img, gamma=1.0): invGamma = 1.0 / gamma img_float = img.astype(np.float32) / 255.0 corrected = np.power(img_float, invGamma) return np.uint8(corrected * 255) # 示例 brighter = gamma_transform(img, gamma=0.5) # 变亮 darker = gamma_transform(img, gamma=2.0) # 变暗 ``` #### 4. 对比度拉伸 (Contrast Stretching) **目的**:扩展灰度动态范围 **分段线性变换**: ``` s = T(r) = { (r1/r2) × r, 0 ≤ r < r1 { [(s2-s1)/(r2-r1)]×(r-r1)+s1, r1 ≤ r < r2 { [(L-1-s2)/(L-1-r2)]×(r-r2)+s2, r2 ≤ r < L-1 ``` ```python def contrast_stretch(img, r1=50, r2=200, s1=0, s2=255): img_float = img.astype(np.float32) # 分段线性变换 transformed = np.piecewise( img_float, [img_float < r1, (img_float >= r1) & (img_float < r2), img_float >= r2], [lambda x: (s1/r1) * x, lambda x: ((s2-s1)/(r2-r1)) * (x-r1) + s1, lambda x: ((255-s2)/(255-r2)) * (x-r2) + s2] ) return np.uint8(np.clip(transformed, 0, 255)) ``` --- ## 3.3 直方图处理 ⭐⭐⭐ ### 📊 什么是直方图? **定义**:统计图像中每个灰度级出现的频率 **直方图函数**: ``` h(rₖ) = nₖ ``` - rₖ:第 k 个灰度级 - nₖ:灰度级为 rₖ 的像素数量 **归一化直方图**: ``` p(rₖ) = nₖ / N ``` - N:图像总像素数 - p(rₖ):灰度级 rₖ 出现的概率 ### 💻 绘制直方图 ```python import cv2 import matplotlib.pyplot as plt def plot_histogram(img): plt.figure(figsize=(10, 5)) # 计算直方图 hist = cv2.calcHist([img], [0], None, [256], [0, 256]) # 绘制 plt.plot(hist, color='black') plt.xlabel('灰度级') plt.ylabel('像素数量') plt.title('图像直方图') plt.xlim([0, 256]) plt.grid(True, alpha=0.3) plt.show() # 使用 plot_histogram(img) ``` ### 🎯 直方图均衡化 (Histogram Equalization) **目的**:自动增强对比度,使直方图均匀分布 **原理**: ``` sₖ = T(rₖ) = (L-1) × Σ(p(rⱼ)), j=0 to k ``` **步骤**: 1. 计算原始直方图 2. 计算累积分布函数 (CDF) 3. 映射到新的灰度值 4. 四舍五入取整 ```python def histogram_equalization(img): # 方法 1:使用 OpenCV equalized = cv2.equalizeHist(img) # 方法 2:手动实现(帮助理解) hist, bins = np.histogram(img.flatten(), 256, [0, 256]) cdf = hist.cumsum() cdf_normalized = (cdf - cdf.min()) * 255 / (cdf.max() - cdf.min()) equalized_manual = cdf_normalized[img].astype(np.uint8) return equalized # 使用 enhanced = cv2.equalizeHist(img) ``` **适用场景**: - ✅ X 光片、卫星图像 - ✅ 背景过亮或过暗的图像 - ❌ 已经对比度良好的图像(可能过增强) ### 🎨 自适应直方图均衡化 (CLAHE) **问题**:全局均衡化可能过度增强噪声 **解决**:局部自适应均衡化 (CLAHE - Contrast Limited AHE) ```python def clahe_equalization(img, clipLimit=2.0, tileGridSize=(8, 8)): clahe = cv2.createCLAHE( clipLimit=clipLimit, # 对比度限制(防止噪声放大) tileGridSize=tileGridSize # 局部区域大小 ) return clahe.apply(img) # 使用 enhanced = clahe_equalization(img, clipLimit=3.0, tileGridSize=(8, 8)) ``` **参数说明**: - `clipLimit`:越大对比度越强,但噪声也越多(推荐 2-4) - `tileGridSize`:越小局部性越强,计算量越大 --- ## 3.4 空间滤波基础 ⭐⭐⭐ ### 🔍 什么是空间滤波? **定义**:在图像空间域中,用像素邻域的值来计算新像素值 **滤波过程**: ``` 1. 滤波器模板在图像上滑动 2. 对每个位置,计算模板覆盖区域内像素的加权和 3. 将结果赋给中心像素 ``` ### 📐 卷积 (Convolution) **公式**: ``` g(x, y) = Σ Σ w(s, t) × f(x+s, y+t) s t ``` - w:滤波器模板(核/掩模) - f:输入图像 - g:输出图像 **卷积步骤**: 1. 翻转滤波器(180°旋转) 2. 滑动窗口 3. 对应位置相乘后求和 ```python import numpy as np import cv2 def convolve2d(img, kernel): """手动实现 2D 卷积(帮助理解)""" kh, kw = kernel.shape pad_h, pad_w = kh // 2, kw // 2 # 填充边界 padded = cv2.copyMakeBorder( img, pad_h, pad_h, pad_w, pad_w, cv2.BORDER_REFLECT ) h, w = img.shape output = np.zeros_like(img, dtype=np.float32) # 滑动窗口 for y in range(h): for x in range(w): region = padded[y:y+kh, x:x+kw] output[y, x] = np.sum(region * kernel) return np.uint8(np.clip(output, 0, 255)) ``` ### 🎯 相关 (Correlation) 与卷积类似,但**不翻转**滤波器 **OpenCV 中的滤波函数**: ```python # 方框滤波 result = cv2.boxFilter(img, -1, (3, 3)) # 归一化滤波 result = cv2.blur(img, (3, 3)) # 自定义滤波 kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]]) result = cv2.filter2D(img, -1, kernel) ``` --- ## 3.5 平滑空间滤波器 ### 🎯 用途 - 模糊(去除细节) - 降噪(平滑噪声) ### 1. 均值滤波 (Mean Filter) **模板**:所有系数相等 ``` 1 1 1 1 1 1 → 除以 9(归一化) 1 1 1 ``` **公式**: ``` g(x,y) = (1/M) × Σ f(x+i, y+j) ``` - M:模板像素总数 ```python # 3×3 均值滤波 blurred = cv2.blur(img, (3, 3)) # 5×5 均值滤波 blurred = cv2.blur(img, (5, 5)) ``` **特点**: - ✅ 简单快速 - ❌ 模糊边缘,噪声去除不彻底 ### 2. 高斯滤波 (Gaussian Filter) **模板**:高斯分布权重(中心大,边缘小) **3×3 高斯核示例**: ``` 1 2 1 2 4 2 → 除以 16 1 2 1 ``` **5×5 高斯核**: ``` 1 4 6 4 1 4 16 24 16 4 6 24 36 24 6 4 16 24 16 4 1 4 6 4 1 ``` ```python # 高斯滤波 blurred = cv2.GaussianBlur(img, (5, 5), sigmaX=0) # 参数说明: # (5, 5): 核大小(必须是奇数) # sigmaX: 标准差(0 表示自动计算) ``` **特点**: - ✅ 保留边缘优于均值滤波 - ✅ 符合人眼视觉特性 - ❌ 计算量稍大 ### 3. 中值滤波 (Median Filter) **原理**:取邻域像素的中值 ```python # 中值滤波 denoised = cv2.medianBlur(img, 5) # 参数:邻域大小(必须是奇数) ``` **特点**: - ✅ 去除椒盐噪声效果极佳 - ✅ 保护边缘不模糊 - ❌ 对高斯噪声效果一般 - ❌ 非线性滤波(不能频域分析) ### 📊 滤波器对比 ```python import cv2 import numpy as np import matplotlib.pyplot as plt # 添加椒盐噪声 def add_salt_pepper_noise(img, prob=0.05): noisy = img.copy() h, w = img.shape num_noise = int(h * w * prob) # 盐噪声(白色) for _ in range(num_noise): y, x = np.random.randint(0, h), np.random.randint(0, w) noisy[y, x] = 255 # 椒噪声(黑色) for _ in range(num_noise): y, x = np.random.randint(0, h), np.random.randint(0, w) noisy[y, x] = 0 return noisy # 测试 noisy = add_salt_pepper_noise(img, prob=0.05) mean_filtered = cv2.blur(noisy, (5, 5)) gauss_filtered = cv2.GaussianBlur(noisy, (5, 5), 0) median_filtered = cv2.medianBlur(noisy, 5) fig, axes = plt.subplots(2, 2, figsize=(12, 12)) axes[0, 0].imshow(noisy, cmap='gray') axes[0, 0].set_title('含椒盐噪声') axes[0, 1].imshow(mean_filtered, cmap='gray') axes[0, 1].set_title('均值滤波') axes[1, 0].imshow(gauss_filtered, cmap='gray') axes[1, 0].set_title('高斯滤波') axes[1, 1].imshow(median_filtered, cmap='gray') axes[1, 1].set_title('中值滤波') plt.tight_layout() plt.show() ``` --- ## 3.6 锐化空间滤波器 ### 🎯 用途 - 突出边缘和细节 - 补偿图像模糊 ### 1. 拉普拉斯算子 (Laplacian) **二阶微分算子**,各向同性 **模板**: ``` 0 -1 0 -1 4 -1 或 -1 -1 -1 0 -1 0 -1 8 -1 -1 -1 -1 ``` **锐化公式**: ``` g(x,y) = f(x,y) - ∇²f(x,y) ``` ```python # 拉普拉斯锐化 laplacian = cv2.Laplacian(img, cv2.CV_64F) sharpened = img - laplacian sharpened = np.uint8(np.clip(sharpened, 0, 255)) # 或使用 filter2D kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]]) sharpened = cv2.filter2D(img, -1, kernel) ``` ### 2. 非锐化掩蔽 (Unsharp Masking) **步骤**: 1. 模糊原图 2. 原图减模糊图得到掩蔽 3. 原图加掩蔽得到锐化图 **公式**: ``` g(x,y) = f(x,y) + k × [f(x,y) - f_blur(x,y)] ``` - k:增强强度(k>1 为高提升滤波) ```python def unsharp_mask(img, k=1.5, kernel_size=(5, 5)): blurred = cv2.GaussianBlur(img, kernel_size, 0) mask = img - blurred sharpened = img + k * mask return np.uint8(np.clip(sharpened, 0, 255)) # 使用 sharpened = unsharp_mask(img, k=1.5) ``` ### 3. 梯度算子(边缘检测) #### Sobel 算子 **x 方向模板**: ``` -1 0 1 -2 0 2 -1 0 1 ``` **y 方向模板**: ``` -1 -2 -1 0 0 0 1 2 1 ``` ```python # Sobel 边缘检测 sobel_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3) sobel_y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3) # 梯度幅值 gradient_magnitude = np.sqrt(sobel_x**2 + sobel_y**2) gradient_magnitude = np.uint8(255 * gradient_magnitude / gradient_magnitude.max()) # 或使用 edges = cv2.Sobel(img, cv2.CV_8U, 1, 1, ksize=3) ``` #### Prewitt 算子 ```python # Prewitt 算子(OpenCV 无内置,需自定义核) prewitt_x = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]]) prewitt_y = np.array([[-1, -1, -1], [ 0, 0, 0], [ 1, 1, 1]]) edges_x = cv2.filter2D(img, -1, prewitt_x) edges_y = cv2.filter2D(img, -1, prewitt_y) ``` --- ## 💻 第 3 章综合实践 ### 项目:图像增强工具箱 ```python import cv2 import numpy as np import matplotlib.pyplot as plt class ImageEnhancer: """图像增强工具箱""" def __init__(self, img_path): self.img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) self.results = {} def gamma_correction(self, gamma=1.0): """伽马校正""" invGamma = 1.0 / gamma img_float = self.img.astype(np.float32) / 255.0 corrected = np.power(img_float, invGamma) self.results['gamma'] = np.uint8(corrected * 255) return self.results['gamma'] def histogram_equalize(self): """直方图均衡化""" self.results['hist_eq'] = cv2.equalizeHist(self.img) return self.results['hist_eq'] def clahe(self, clipLimit=3.0): """自适应直方图均衡化""" clahe = cv2.createCLAHE(clipLimit=clipLimit, tileGridSize=(8, 8)) self.results['clahe'] = clahe.apply(self.img) return self.results['clahe'] def gaussian_blur(self, kernel_size=(5, 5)): """高斯模糊""" self.results['gauss_blur'] = cv2.GaussianBlur( self.img, kernel_size, 0 ) return self.results['gauss_blur'] def median_filter(self, kernel_size=5): """中值滤波""" self.results['median'] = cv2.medianBlur(self.img, kernel_size) return self.results['median'] def sharpen(self, k=1.5): """锐化""" blurred = cv2.GaussianBlur(self.img, (5, 5), 0) mask = self.img - blurred self.results['sharpen'] = np.uint8( np.clip(self.img + k * mask, 0, 255) ) return self.results['sharpen'] def edge_detect(self): """边缘检测""" sobel_x = cv2.Sobel(self.img, cv2.CV_64F, 1, 0, ksize=3) sobel_y = cv2.Sobel(self.img, cv2.CV_64F, 0, 1, ksize=3) magnitude = np.sqrt(sobel_x**2 + sobel_y**2) self.results['edges'] = np.uint8( 255 * magnitude / magnitude.max() ) return self.results['edges'] def show_all(self): """显示所有结果""" n = len(self.results) + 1 cols = 3 rows = (n + cols - 1) // cols fig, axes = plt.subplots(rows, cols, figsize=(15, 5*rows)) axes = axes.flatten() axes[0].imshow(self.img, cmap='gray') axes[0].set_title('原始图像') for i, (name, result) in enumerate(self.results.items(), 1): axes[i].imshow(result, cmap='gray') axes[i].set_title(name.replace('_', ' ').title()) axes[i].axis('off') for j in range(i+1, len(axes)): axes[j].axis('off') plt.tight_layout() plt.show() # 使用示例 enhancer = ImageEnhancer('test.jpg') enhancer.gamma_correction(gamma=0.8) enhancer.histogram_equalize() enhancer.clahe(clipLimit=3.0) enhancer.sharpen(k=1.5) enhancer.show_all() ``` --- ## 📝 本章小结 ### ✅ 你应该掌握 - [ ] 理解灰度变换的四种基本类型 - [ ] 掌握直方图均衡化的原理和实现 - [ ] 理解空间滤波和卷积的概念 - [ ] 能够区分均值、高斯、中值滤波的特点 - [ ] 掌握拉普拉斯和非锐化掩蔽锐化方法 - [ ] 能够使用 Sobel 算子进行边缘检测 - [ ] 完成图像增强工具箱项目 ### 🤔 思考题 1. 为什么对数变换能增强暗部细节? 2. 直方图均衡化为什么能增强对比度? 3. 中值滤波为什么能去除椒盐噪声但保留边缘? 4. 拉普拉斯算子为什么是二阶微分? 5. 高斯滤波的σ参数对滤波效果有什么影响? ### 📖 关键公式总结 ``` 图像反转:s = L - 1 - r 对数变换:s = c × log(1 + r) 伽马校正:s = c × r^γ 卷积:g(x,y) = ΣΣ w(s,t) × f(x+s, y+t) 锐化:g(x,y) = f(x,y) - ∇²f(x,y) 非锐化掩蔽:g = f + k × (f - f_blur) ``` --- _第 3 章完 | 下一章:第 4 章 频率域滤波_ # 第 4-12 章 知识点详解 --- ## 第 4 章 频率域滤波 (Filtering in the Frequency Domain) ⭐⭐⭐ ### 📌 核心知识点 #### 4.1 傅里叶变换基础 **一维傅里叶变换**: ``` F(u) = Σ f(x) × e^(-j2πux), x=0 to N-1 ``` **二维傅里叶变换**: ``` F(u,v) = ΣΣ f(x,y) × e^(-j2π(ux/M + vy/N)) ``` **物理意义**:将图像从**空间域**转换到**频率域** - 低频分量:图像的平滑区域、整体轮廓 - 高频分量:图像的边缘、细节、噪声 **逆变换**: ``` f(x,y) = (1/MN) × ΣΣ F(u,v) × e^(j2π(ux/M + vy/N)) ``` #### 4.2 频域滤波步骤 ``` 1. 计算图像的傅里叶变换 F(u,v) 2. 乘以滤波器 H(u,v) 3. 计算逆傅里叶变换得到结果 ``` **公式**: ``` g(x,y) = F⁻¹[H(u,v) × F(u,v)] ``` #### 4.3 常见频域滤波器 | 滤波器 | 公式 | 特点 | |--------|------|------| | **理想低通** | H(u,v)=1 if D≤D₀, else 0 | 截止频率外完全阻断,产生振铃 | | **高斯低通** | H(u,v)=e^(-D²/2D₀²) | 平滑过渡,无振铃 | | **巴特沃斯低通** | H(u,v)=1/[1+(D/D₀)^2n] | 可调节陡峭程度 | | **理想高通** | H(u,v)=0 if D≤D₀, else 1 | 边缘检测 | | **高斯高通** | H(u,v)=1-e^(-D²/2D₀²) | 锐化 | #### 4.4 代码实现 ```python import cv2 import numpy as np import matplotlib.pyplot as plt def gaussian_lowpass_filter(shape, cutoff): """生成高斯低通滤波器""" rows, cols = shape crow, ccol = rows // 2, cols // 2 u, v = np.meshgrid(np.arange(cols), np.arange(rows)) D = np.sqrt((u - ccol)**2 + (v - crow)**2) H = np.exp(-(D**2) / (2 * cutoff**2)) return H def frequency_domain_filter(img, cutoff, filter_type='lowpass'): """频域滤波""" # FFT 变换 f = np.fft.fft2(img) fshift = np.fft.fftshift(f) # 零频移到中心 # 生成滤波器 H = gaussian_lowpass_filter(img.shape, cutoff) if filter_type == 'highpass': H = 1 - H # 高通 = 1 - 低通 # 滤波 fshift_filtered = fshift * H # 逆变换 f_ishift = np.fft.ifftshift(fshift_filtered) img_back = np.fft.ifft2(f_ishift) img_back = np.abs(img_back) return img_back # 使用 img = cv2.imread('test.jpg', cv2.IMREAD_GRAYSCALE) filtered = frequency_domain_filter(img, cutoff=50, filter_type='lowpass') ``` --- ## 第 5 章 图像复原与重建 (Image Restoration and Reconstruction) ### 📌 核心知识点 #### 5.1 噪声模型 | 噪声类型 | 概率密度函数 | 特点 | 适用场景 | |----------|--------------|------|----------| | **高斯噪声** | p(z) = (1/σ√2π)e^(-(z-μ)²/2σ²) | 对称分布 | 电子电路噪声 | | **椒盐噪声** | 双峰分布 | 随机黑白点 | 传输错误 | | **瑞利噪声** | p(z) = (2/b²)(z-a)e^(-(z-a)²/b²) | 偏态分布 | 距离成像 | | **伽马噪声** | p(z) = (b^a/Γ(a))z^(a-1)e^(-bz) | 正偏态 | 激光成像 | | **泊松噪声** | p(z) = e^(-λ)λ^z/z! | 离散分布 | 光子计数 | #### 5.2 退化模型 ``` g(x,y) = h(x,y) * f(x,y) + η(x,y) ``` - g:退化图像 - f:原始图像 - h:退化函数(点扩散函数 PSF) - η:加性噪声 - *:卷积 #### 5.3 复原方法 **1. 逆滤波**: ``` F̂(u,v) = G(u,v) / H(u,v) ``` - 问题:H(u,v)接近 0 时放大噪声 **2. 维纳滤波**(最小均方误差): ``` F̂(u,v) = [H*(u,v) / (|H(u,v)|² + Sη(u,v)/Sf(u,v))] × G(u,v) ``` - Sη:噪声功率谱 - Sf:原始图像功率谱 **3. 约束最小二乘滤波**: ``` 引入拉格朗日乘子,平衡复原和平滑 ``` #### 5.4 代码实现 ```python from scipy.signal import wiener # 维纳滤波(简化版) def wiener_filter(img, noise_var=0.01): return wiener(img, mysiz=(5,5), noise=noise_var) # 使用 OpenCV 进行去卷积 def richardson_lucy_deconvolution(blurred, psf, iterations=10): """Richardson-Lucy 盲去卷积""" img = blurred.astype(np.float32) psf = psf.astype(np.float32) for _ in range(iterations): convolved = cv2.filter2D(img, -1, psf) ratio = blurred / (convolved + 1e-10) img *= cv2.filter2D(ratio, -1, psf[::-1, ::-1]) return img.astype(np.uint8) ``` --- ## 第 6 章 彩色图像处理 (Color Image Processing) ### 📌 核心知识点 #### 6.1 颜色模型 | 模型 | 分量 | 特点 | 应用 | |------|------|------|------| | **RGB** | 红、绿、蓝 | 加色模型,设备相关 | 显示器、相机 | | **CMY/CMYK** | 青、品红、黄、黑 | 减色模型 | 打印 | | **HSV/HSI** | 色调、饱和度、亮度/强度 | 符合人眼感知 | 图像分析 | | **YUV/YCbCr** | 亮度、色度 | 分离亮度和色度 | 视频压缩 | | **Lab** | 亮度、a 轴、b 轴 | 设备无关,均匀色空间 | 颜色匹配 | #### 6.2 RGB 到 HSV 转换 ```python # OpenCV 颜色空间转换 hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb) ``` #### 6.3 彩色图像滤波 **策略**: 1. 转换到 HSV/Lab 空间 2. 只对亮度通道滤波(避免颜色失真) 3. 转换回 RGB ```python def color_preserving_blur(img): """保持颜色的模糊""" hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) h, s, v = cv2.split(hsv) # 只对 V 通道模糊 v_blur = cv2.GaussianBlur(v, (5, 5), 0) hsv_blur = cv2.merge([h, s, v_blur]) return cv2.cvtColor(hsv_blur, cv2.COLOR_HSV2BGR) ``` #### 6.4 基于颜色的分割 ```python def segment_by_color(img, lower_color, upper_color): """基于颜色范围分割""" hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) lower = np.array(lower_color) upper = np.array(upper_color) mask = cv2.inRange(hsv, lower, upper) result = cv2.bitwise_and(img, img, mask=mask) return mask, result # 提取红色物体 mask, red_objects = segment_by_color(img, lower_color=[0, 100, 100], upper_color=[10, 255, 255] ) ``` --- ## 第 7 章 小波和多分辨率处理 (Wavelets and Multiresolution Processing) ### 📌 核心知识点 #### 7.1 多分辨率分析 **思想**:在不同尺度下观察图像 - 粗尺度:整体结构 - 细尺度:局部细节 #### 7.2 图像金字塔 **高斯金字塔**: ```python def gaussian_pyramid(img, levels=4): """构建高斯金字塔""" pyramid = [img] for _ in range(levels-1): img = cv2.pyrDown(img) pyramid.append(img) return pyramid # 使用 pyramid = gaussian_pyramid(img, levels=4) ``` **拉普拉斯金字塔**: ```python def laplacian_pyramid(img, levels=4): """构建拉普拉斯金字塔""" gaussian = gaussian_pyramid(img, levels) laplacian = [] for i in range(levels-1): expanded = cv2.pyrUp(gaussian[i+1]) diff = cv2.subtract(gaussian[i], expanded) laplacian.append(diff) laplacian.append(gaussian[-1]) # 最顶层 return laplacian ``` #### 7.3 小波变换 **Haar 小波**(最简单): - 尺度函数:φ(x) = 1 (0≤x<1), else 0 - 小波函数:ψ(x) = 1 (0≤x<0.5), -1 (0.5≤x<1) **二维小波分解**: ``` 原始图像 → LL(近似) LH(水平细节) HL(垂直细节) HH(对角细节) ``` ```python import pywt # 二维小波分解 coeffs = pywt.dwt2(img, 'haar') LL, (LH, HL, HH) = coeffs # 重构 img_reconstructed = pywt.idwt2((LL, (LH, HL, HH)), 'haar') ``` --- ## 第 8 章 图像压缩 (Image Compression) ### 📌 核心知识点 #### 8.1 压缩原理 **冗余类型**: 1. **编码冗余**:码字长度未优化 2. **像素间冗余**:相邻像素相关 3. **心理视觉冗余**:人眼不敏感的信息 **压缩比**: ``` CR = n₁ / n₂ ``` - n₁:原始数据量 - n₂:压缩后数据量 #### 8.2 编码方法 **1. 霍夫曼编码**(变长编码): ```python import heapq from collections import Counter def huffman_encode(data): freq = Counter(data) heap = [[weight, [symbol, ""]] for symbol, weight in freq.items()] heapq.heapify(heap) while len(heap) > 1: lo = heapq.heappop(heap) hi = heapq.heappop(heap) for pair in lo[1:]: pair[1] = '0' + pair[1] for pair in hi[1:]: pair[1] = '1' + pair[1] heapq.heappush(heap, [lo[0] + hi[0]] + lo[1:] + hi[1:]) return sorted(heapq.heappop(heap)[1:], key=lambda p: len(p[0])) ``` **2. 游程编码 (RLE)**: ``` AAAAA BBB CC → (A,5) (B,3) (C,2) ``` **3. 算术编码**:比霍夫曼更高效,接近熵极限 #### 8.3 JPEG 压缩标准 **步骤**: ``` 1. 颜色空间转换 (RGB → YCbCr) 2. 下采样色度通道 3. 分块 (8×8) 4. DCT 变换 5. 量化 6. 熵编码 (Zigzag + 霍夫曼) ``` ```python from skimage.transform import rescale import numpy as np def dct2(block): """二维离散余弦变换""" return scipy.fftpack.dct(scipy.fftpack.dct(block.T, norm='ortho').T, norm='ortho') def idct2(block): """二维逆 DCT""" return scipy.fftpack.idct(scipy.fftpack.idct(block.T, norm='ortho').T, norm='ortho') ``` --- ## 第 9 章 形态学图像处理 (Morphological Image Processing) ⭐⭐ ### 📌 核心知识点 #### 9.1 基本形态学操作 **1. 腐蚀 (Erosion)**: ``` A ⊖ B = {z | B_z ⊆ A} ``` - 效果:物体缩小,去除小物体 - 应用:分离粘连物体、去除噪声 ```python eroded = cv2.erode(img, kernel, iterations=1) ``` **2. 膨胀 (Dilation)**: ``` A ⊕ B = {z | B_z ∩ A ≠ ∅} ``` - 效果:物体扩大,填充孔洞 - 应用:填补裂缝、连接断裂 ```python dilated = cv2.dilate(img, kernel, iterations=1) ``` **3. 开运算 (Opening)**: ``` A ○ B = (A ⊖ B) ⊕ B ``` - 先腐蚀后膨胀 - 应用:去除小物体、平滑边界 ```python opened = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) ``` **4. 闭运算 (Closing)**: ``` A • B = (A ⊕ B) ⊖ B ``` - 先膨胀后腐蚀 - 应用:填充孔洞、连接邻近物体 ```python closed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) ``` #### 9.2 形态学应用 **1. 边界提取**: ```python def extract_boundary(img, kernel): eroded = cv2.erode(img, kernel) boundary = cv2.subtract(img, eroded) return boundary ``` **2. 孔洞填充**: ```python def fill_holes(img): # 创建标记(边界为 1,内部为 0) marker = np.zeros_like(img) marker[1:-1, 1:-1] = img[1:-1, 1:-1] marker = cv2.bitwise_not(marker) # 形态学重建 filled = marker.copy() kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3)) while True: prev = filled.copy() filled = cv2.dilate(filled, kernel) filled = cv2.bitwise_and(filled, marker) if np.array_equal(filled, prev): break return cv2.bitwise_not(filled) ``` **3. 骨架提取**: ```python def skeletonize(img): """形态学骨架化""" skeleton = np.zeros_like(img) element = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3)) while True: eroded = cv2.erode(img, element) temp = cv2.dilate(eroded, element) temp = cv2.subtract(img, temp) skeleton = cv2.bitwise_or(skeleton, temp) img = eroded.copy() if cv2.countNonZero(img) == 0: break return skeleton ``` --- ## 第 10 章 图像分割 (Image Segmentation) ⭐⭐⭐ ### 📌 核心知识点 #### 10.1 阈值分割 **1. 全局阈值**: ```python # 简单阈值 _, thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) # Otsu 自动阈值 _, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) ``` **2. 自适应阈值**: ```python # 局部自适应阈值 adaptive = cv2.adaptiveThreshold( img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, # 或 MEAN_C cv2.THRESH_BINARY, blockSize=11, # 邻域大小 C=2 # 常数偏移 ) ``` #### 10.2 边缘分割 **Canny 边缘检测**: ```python edges = cv2.Canny( img, threshold1=100, # 低阈值(连接弱边缘) threshold2=200, # 高阈值(强边缘) apertureSize=3, L2gradient=False ) ``` #### 10.3 区域分割 **1. 区域生长**: ```python def region_growing(img, seed_point, threshold=10): """区域生长算法""" h, w = img.shape segmented = np.zeros_like(img, dtype=np.uint8) y, x = seed_point seed_value = img[y, x] stack = [(y, x)] while stack: y, x = stack.pop() if segmented[y, x]: continue if abs(int(img[y, x]) - int(seed_value)) < threshold: segmented[y, x] = 255 # 添加 4-邻域 for dy, dx in [(-1,0), (1,0), (0,-1), (0,1)]: ny, nx = y + dy, x + dx if 0 <= ny < h and 0 <= nx < w and not segmented[ny, nx]: stack.append((ny, nx)) return segmented ``` **2. 分水岭算法**: ```python def watershed_segmentation(img): """分水岭分割""" # 二值化 _, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 形态学操作获取确定背景 kernel = np.ones((3,3), np.uint8) sure_bg = cv2.dilate(thresh, kernel, iterations=3) # 距离变换获取确定前景 dist = cv2.distanceTransform(thresh, cv2.DIST_L2, 5) _, sure_fg = cv2.threshold(dist, 0.7*dist.max(), 255, 0) sure_fg = np.uint8(sure_fg) # 未知区域 unknown = cv2.subtract(sure_bg, sure_fg) # 标记 _, markers = cv2.connectedComponents(sure_fg) markers = markers + 1 markers[unknown==255] = 0 # 分水岭 markers = cv2.watershed(cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), markers) return markers ``` --- ## 第 11 章 表示与描述 (Representation and Description) ### 📌 核心知识点 #### 11.1 边界表示 **1. 链码 (Chain Code)**: - Freeman 链码:8 方向编码 - 差分链码:对旋转不敏感 **2. 多边形近似**: - 最小周长多边形 - 合并分裂算法 #### 11.2 区域描述子 **1. 纹理描述**: - 灰度共生矩阵 (GLCM) - 局部二值模式 (LBP) ```python from skimage.feature import graycomatrix, graycoprops # GLCM 特征 glcm = graycomatrix(img, distances=[1], angles=[0], levels=256) contrast = graycoprops(glcm, 'contrast') correlation = graycoprops(glcm, 'correlation') energy = graycoprops(glcm, 'energy') homogeneity = graycoprops(glcm, 'homogeneity') ``` **2. 矩描述子**: ```python # Hu 矩(平移、缩放、旋转不变) moments = cv2.moments(img) huMoments = cv2.HuMoments(moments) ``` --- ## 第 12 章 目标识别 (Object Recognition) ### 📌 核心知识点 #### 12.1 模式识别基础 **识别流程**: ``` 图像 → 预处理 → 特征提取 → 特征选择 → 分类 → 识别结果 ``` #### 12.2 分类器 **1. 最小距离分类器**: ```python def min_distance_classifier(features, class_means): """最小距离分类""" distances = [np.linalg.norm(features - mean) for mean in class_means] return np.argmin(distances) ``` **2. 相关匹配**: ```python # 模板匹配 result = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED) _, max_val, _, max_loc = cv2.minMaxLoc(result) ``` #### 12.3 深度学习简介 **CNN 基本结构**: ``` 输入 → 卷积层 → 激活 (ReLU) → 池化 → ... → 全连接 → Softmax → 输出 ``` **使用预训练模型**: ```python import tensorflow as tf # 加载预训练模型 model = tf.keras.applications.ResNet50(weights='imagenet') # 预测 from tensorflow.keras.applications.resnet50 import preprocess_input, decode_predictions img = cv2.resize(img, (224, 224)) img = preprocess_input(img) predictions = model.predict(np.expand_dims(img, axis=0)) label = decode_predictions(predictions, top=1)[0][0] ``` --- ## 📖 学习建议 ### 难度分级 - ⭐⭐⭐ 核心必学:第 2、3、4、10 章 - ⭐⭐ 重要:第 5、6、9、11 章 - ⭐ 选读:第 7、8、12 章(根据兴趣) ### 实践建议 1. **每章至少完成 1 个代码项目** 2. **建立自己的图像处理工具库** 3. **用实际数据集测试算法** 4. **参与 Kaggle 图像相关竞赛** ### 进阶方向 - **医学图像分析**:重点学习第 5、10 章 - **工业检测**:重点学习第 3、9、10 章 - **计算机视觉**:重点学习第 10、11、12 章 + 深度学习 - **图像压缩**:重点学习第 7、8 章 --- _教程完 | 祝学习顺利!🌱_