# Chapter03 / `src/chapter3` 说明

## 直方图均衡化（`Histogram_process.py`）

1. 计算每个灰度级的 PDF（Probability Density Function，归一化直方图即离散 PDF）。
2. 计算 CDF（Cumulative Distribution Function，累计分布函数）。
3. 映射关系（离散、256 级时）：

```text
s_k = round((L - 1) * cdf[k])   （L = 256 时输出落在 0～255）
```

有了映射表，即可把每个像素的原灰度换成新灰度，得到均衡后的图像。

---

## 空间滤波器（`Spatial_filter.py`）

空间滤波是在**图像平面本身**上对像素及其**邻域**做运算，不变换到其他域（区别于频域滤波）。邻域与一组**系数**（核、掩模、模板）做卷积或统计运算，得到新像素值。掩模里的数是**权重/系数**，不是图像灰度。

### 滤波具体是怎么工作的

可以把空域滤波理解成：**用一个固定形状的窗口（如 3×3）在整幅图上逐格滑动**，对**每一个像素位置**都算出一个新值，写入结果图里对应位置；原图一般**不被改写**，输出与输入**同尺寸**（彩色图则按通道或算子定义处理）。

按运算类型大致分两类：

1. **线性滤波（卷积型，如均值、高斯、Sobel/Prewitt 模板）**  
   在窗口内把**每个像素乘以核上对应系数再求和**（再除以归一化因子、取绝对值等）。本质是**邻域加权和**。核越大，参与运算的像素越多：平滑类核会让图像更糊；梯度类核则覆盖更大的差分范围。

2. **非线性滤波（如中值、最大、最小）**  
   不做固定系数的加权和，而是对邻域灰度做**排序或取极值**（中位数、最大值、最小值），因此一般**不能**写成“一个卷积核乘加”。

无论哪种，当前正在处理的那个坐标都是窗口的**中心**；窗口关于该中心对称放置（例如 3×3 以该像素为中心）。

### “边缘”分两种：图像边界 vs 灰度边缘

中文里说“边缘”容易混指两件事，滤波时的处理也完全不同。

**1）图像四周边界上的像素（最外几行、几列）**

算邻域时，窗口会向四周伸出；在**最靠外**的像素处，邻域会**超出图像范围**，外面没有真实灰度可用。实现上必须先做**边界填充（padding）**：在四周假想出一圈“虚拟像素”，再对原图有效区域内的每个点照常取邻域运算。

- 本项目中 **`mean_blur`、`box_blur`、`gaussian_blur`、`median_blur`**，以及 **`laplacian`、`sobel_gradient`、`prewitt_gradient`、`roberts_gradient`、`canny`** 等，均调用 OpenCV，边界行为遵循其**默认边界类型**（通常为 `BORDER_DEFAULT`，效果上接近**反射/镜像**，避免在边界处整块变成同一常数带来的生硬截断）。  
- **`max_filter` / `min_filter`** 在 `Spatial_filter.py` 里用手写循环实现，显式使用 **`copyMakeBorder(..., BORDER_CONSTANT, value=0)`**，即虚拟像素为 **0（黑）**。因此**图像最外圈附近**的邻域里会掺入“假黑”，与上面 OpenCV 内置滤波在边界上的结果**可能不一致**，对比实验时需要注意。

常见填充策略还有：**常数 0**、**复制边缘一行/列**、**对称/周期延拓** 等。不同策略只影响靠近边界、厚度约 **核半径** 的那一圈；**图像内部**、邻域完全落在图内的像素，结果与填充方式无关。

**2）图像内容里的灰度边缘（轮廓、明暗交界）**

这里不是“缺像素”，而是**灰度跳变**：

- **平滑滤波**：窗口同时盖住跳变两侧，输出往往是两侧灰度的平均或某种统计量，因此**真实的物体边缘会变钝、变宽**，细节被抹平——这是平滑的固有代价。  
- **锐化 / 梯度 / 拉普拉斯**：正是利用这种跳变，在边缘处产生**强响应**，从而**突出轮廓**；同时也会让噪声处的跳变变明显。

### 平滑滤波

- **目的**：抑制噪声、模糊细节；随机噪声常表现为灰度突变，平滑会削弱这种突变。
- **副作用**：真实边缘也是突变，会被一起模糊。

#### 线性平滑（均值 / 盒式 / 高斯）

- **均值滤波**：邻域内算术平均，模板越大越模糊、细节丢失越多。
- **盒式滤波**：与归一化均值类似；可不归一化做邻域求和实验。
- **高斯滤波**：中心权重大、边缘权重小，模糊更平滑，常作 Canny 等算法的预处理。

对应类方法：`mean_blur`、`box_blur`、`gaussian_blur`。

#### 非线性平滑（统计排序）

- **中值滤波**：邻域排序取中位数，对**椒盐噪声**效果好，比同尺寸均值更能保留阶跃边缘。
- **最大值 / 最小值滤波**：邻域取 max/min；本实现中彩色图会先转灰度（与 `3.4` 目录下 `maxminblur.py` 思路一致），便于教学用双重循环看清过程。

对应类方法：`median_blur`、`max_filter`、`min_filter`。

### 锐化滤波

- **目的**：增强边缘和细节，或补偿之前的模糊。
- **原理**：与灰度**变化率**有关；一阶导数（梯度）在边缘处幅度大，二阶导数（拉普拉斯）在边缘过零或呈强响应。

#### 拉普拉斯（二阶）

- 对图像做拉普拉斯近似，突出突变区域，也会放大噪声。
- **锐化叠加**：\(g = f + c \cdot \nabla^2 f\) 的离散形式，即原图加上（加权后的）拉普拉斯响应。

对应类方法：`laplacian`、`laplacian_sharpen`。

#### 梯度（一阶）：Sobel、Prewitt、Roberts

- **Sobel**：带平滑的 3×3 一阶差分，x/y 方向分别卷积再合成幅值；实现中用 `CV_16S` 避免 `uint8` 截断，再 `convertScaleAbs`。
- **Prewitt**：3×3 模板，性质与 Sobel 类似，教材中常对比讨论。
- **Roberts**：2×2 交叉模板，实现简单，对对角方向边缘较敏感。

对应类方法：`sobel_gradient`、`prewitt_gradient`、`roberts_gradient`。

### Canny 边缘检测

- 一般只处理**灰度图**；类中会自动将 BGR 转灰度。
- 典型流程：高斯平滑 → 梯度幅值与方向 → 非极大抑制 → **双阈值**滞后跟踪，得到细、连通的边缘。
- 参数 `threshold1`、`threshold2` 控制强弱边缘的取舍。

对应类方法：`canny`。

### 如何使用 `SpatialFilterProcessor`

```python
import cv2
from Spatial_filter import SpatialFilterProcessor

img = cv2.imread("xxx.png")  # BGR
sp = SpatialFilterProcessor()

blur = sp.mean_blur(img, (5, 5))
edges = sp.sobel_gradient(img, ksize=3)
sharp = sp.laplacian_sharpen(img, ksize=3, weight=1.0)
edge_map = sp.canny(img, 50, 150)

# 或字符串统一入口
out = sp.apply(img, "median", ksize=5)
```

更完整的示例仍可与 `Chapter03/3.4 空间滤波器` 下各脚本对照阅读；算法要点以本节与 `3.4` 目录中 `README.md` 为准。
