一、图像表示基础

1.1 数字图像模型

  • 灰度图: I(x,y) ∈ [0, 255], 单通道
  • 彩色图: RGB ∈ [0,255]³, 三通道
  • 二值图: I(x,y) ∈ {0, 1}
  • 多光谱: 高光谱 > 3通道
  • 深度图: 每个像素存储距离值
  • Indexed Image: 索引+调色板

1.2 颜色空间

颜色空间 描述 CV用途
RGB 红绿蓝三原色 显示/存储
Lab* 感知均匀 颜色分析
HSV/HSI 色相/饱和度/明度 颜色分割/检测
YCbCr 亮度+色度 视频压缩
Grayscale 单通道亮度 特征提取基础
CMYK 印刷四色 打印

转换原因: RGB 对光照/阴影敏感,HSV/Lab 更鲁棒

1.3 图像坐标系

      u (列/x)
     ────────→
   │ ┌────────
 v │ │(u,v)
 │ │ 像素
 ↓ │
(行/y)

二、基础图像操作

2.1 像素操作

import cv2
import numpy as np

# 读取/写入
img = cv2.imread('image.jpg')
cv2.imwrite('output.jpg', img)

# 颜色空间转换
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 像素访问
pixel = img[100, 200]  # (b,g,r)
img[100:200, 200:300] = [255, 0, 0]  # ROI操作

2.2 几何变换(投影模型&单应矩阵)

变换 自由度 矩阵形式 不变性质
平移 2 [I|t] 方向
刚体(旋转+平移) 3 [R|t] 长度+角度
相似(缩放+刚体) 4 s[R|t] 角度
仿射 6 A=[[a11,a12,a13],[a21,a22,a23]] 平行线
透视/投影 8 H (3×3) 直线(交比)
# 图像缩放
resized = cv2.resize(img, (width, height))
resized = cv2.resize(img, None, fx=0.5, fy=0.5)

# 旋转
(h, w) = img.shape[:2]
M = cv2.getRotationMatrix2D((w/2, h/2), 45, 1.0)
rotated = cv2.warpAffine(img, M, (w, h))

# 透视变换
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
warped = cv2.warpPerspective(img, M, (out_w, out_h))

2.3 直方图操作

# 直方图计算
hist = cv2.calcHist([img], [0], None, [256], [0, 256])

# 直方图均衡化(增强对比度)
equ = cv2.equalizeHist(gray)

# CLAHE(自适应直方图均衡化,避免噪声放大)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
enhanced = clahe.apply(gray)

# 直方图匹配
# 将图像直方图映射到目标分布

三、图像滤波(核心)

3.1 线性滤波

滤波器 效果 公式
均值滤波 1/k² * ones(k,k) 平滑去噪,模糊 G(i) = 1/N Σ I(j)
高斯滤波 G(x,y) = exp(-(x²+y²)/2σ²) 加权平滑,保留边缘 G*I
Sobel [-1,0,1; -2,0,2; -1,0,1] 水平和垂直边缘 ∇I
Scharr 优化Sobel权重 更精确梯度 ∇I
Laplacian [0,1,0; 1,-4,1; 0,1,0] 二阶边缘检测 ∇²I
LoG Gauss + Laplacian 高斯拉普拉斯 σ · (∇²G) * I

3.2 非线性滤波

滤波器 原理 优点
中值滤波 邻域内像素取中值 去椒盐噪声,保边缘
双边滤波 空间域+像素值域加权 保边去噪,美颜
非局部均值 全图相似块加权 去噪效果最好
导向滤波 利用引导图滤波 O(1)复杂度,保边
# 线性滤波
blur = cv2.blur(img, (5,5))          # 均值滤波
gauss = cv2.GaussianBlur(img, (5,5), 1.5)  # 高斯滤波
sobel = cv2.Sobel(img, cv2.CV_64F, 1, 0)    # Sobel X方向
lap = cv2.Laplacian(img, cv2.CV_64F)        # Laplacian

# 非线性滤波
median = cv2.medianBlur(img, 5)       # 中值滤波
bilateral = cv2.bilateralFilter(img, 9, 75, 75)  # 双边滤波

3.3 图像金字塔

Level 0 (原始)  →  Level 1 (1/2)  →  Level 2 (1/4)
  W×H              W/2 × H/2          W/4 × H/4
  • 高斯金字塔: 平滑+下采样
  • 拉普拉斯金字塔: 高频残差(用于图像融合/压缩)
  • SIFT的关键: 在不同尺度(八度)上检测关键点
# 高斯金字塔
layer = img.copy()
gp = [layer]
for i in range(6):
    layer = cv2.pyrDown(layer)
    gp.append(layer)

# 拉普拉斯金字塔
lp = [gp[5]]
for i in range(5, 0, -1):
    expanded = cv2.pyrUp(gp[i])
    laplacian = cv2.subtract(gp[i-1], expanded)
    lp.append(laplacian)

四、边缘检测 ⭐

4.1 Canny边缘检测(经典算法)

步骤:

  1. 高斯平滑: 去噪声
  2. 计算梯度: Sobel → 幅值M + 方向θ
    • M = √(Gx² + Gy²)
    • θ = atan2(Gy, Gx)
  3. 非极大值抑制: 只保留局部的梯度最大值
  4. 双阈值: 高阈值>强边缘, 低阈值>弱边缘
  5. 边缘跟踪: 只保留与强边缘连接的弱边缘
edges = cv2.Canny(img, threshold1=50, threshold2=150)

4.2 其他边缘检测

算法 特点
Sobel 简单快速,对噪声敏感
Prewitt 类似Sobel,权重更简单
Roberts Cross 2×2核,对噪声极敏感
LoG (Marr-Hildreth) 零交叉检测
DoG LoG的高效近似
Canny 最经典,效果最好

4.3 边缘检测评估指标

  • RMSE (均方根误差)
  • PSNR (峰值信噪比)
  • SSIM (结构相似性)
  • Edge Precision / Recall
  • Pratt’s Figure of Merit (FOM)

五、形态学操作(二值图处理)

5.1 基本操作

操作 公式 效果
腐蚀 A⊖B = {z B_z ⊆ A}
膨胀 A⊕B = {z B̂_z ∩ A ≠ ∅}
开运算 (A⊖B)⊕B 先腐蚀后膨胀:去噪点
闭运算 (A⊕B)⊖B 先膨胀后腐蚀:填小孔
形态学梯度 膨胀-腐蚀 边缘提取
Top Hat 原图 - 开运算 提取亮特征
Black Hat 闭运算 - 原图 提取暗特征
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
eroded = cv2.erode(img, kernel, iterations=1)
dilated = cv2.dilate(img, kernel, iterations=1)
opened = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
closed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)

六、图像分割(经典方法)

方法 原理 优点 缺点
阈值分割 (Otsu) 最大化类间方差 自动/快速 仅适合双峰直方图
分水岭算法 洪水泛滥 粘连分割 易过分割
GrabCut 图割迭代 交互式分割 需初始框
Mean Shift 聚类+空间 自动分割 参数敏感
SLIC超像素 K-means + 空间约束 快速 需指定数量
GraphCut 最小割 能量优化分割 计算量大
# Otsu阈值
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# 自适应阈值
adaptive = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                  cv2.THRESH_BINARY, 11, 2)

# GrabCut
mask = np.zeros(img.shape[:2], np.uint8)
bgdModel = np.zeros((1,65), np.float64)
fgdModel = np.zeros((1,65), np.float64)
rect = (x, y, w, h)
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0), 0, 1).astype('uint8')
result = img * mask2[:,:,np.newaxis]

# SLIC超像素
from skimage.segmentation import slic
segments = slic(img, n_segments=250, compactness=10)

七、霍夫变换(Hough Transform)

7.1 原理

将图像空间的点映射到参数空间:

  • 直线检测: ρ = x·cosθ + y·sinθ
    • 一点 → 一条正弦曲线
    • N点共线 → 曲线交汇于一处
  • 圆检测: (x-a)² + (y-b)² = r²
# 直线检测
lines = cv2.HoughLinesP(edges, rho=1, theta=np.pi/180,
                         threshold=100, minLineLength=100, maxLineGap=10)

# 圆检测
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1, minDist=20,
                            param1=50, param2=30, minRadius=0, maxRadius=0)

八、图像增强与复原

8.1 增强技术

技术 方法
对比度拉伸 线性/非线性映射
直方图均衡化 HE / CLAHE
Gamma校正 I’ = I^γ
锐化 Laplacian + 原图: I’ = I - α∇²I
Retinex 光照分离
去雾 Dark Channel Prior (DCP)

8.2 去噪方法

方法 特点
滤波去噪 高斯/中值/双边
Non-Local Means 相似块平均
BM3D 3D协同滤波,SSIM最佳
DnCNN (DL) 深度学习去噪
Noise2Void (Self-supervised) 无需clean数据
# 锐化
kernel_sharpen = np.array([[0,-1,0], [-1,5,-1], [0,-1,0]])
sharpened = cv2.filter2D(img, -1, kernel_sharpen)

# Retinex
# ... 需实现多尺度Retinex (MSRCR)

九、视频处理基础

9.1 基本操作

cap = cv2.VideoCapture('video.mp4')
fps = int(cap.get(cv2.CAP_PROP_FPS))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

while True:
    ret, frame = cap.read()
    if not ret: break
    # 处理每一帧
    cv2.imshow('frame', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()

9.2 帧差法(运动检测)

diff = cv2.absdiff(frame1, frame2)
thresh = cv2.threshold(diff, 25, 255, cv2.THRESH_BINARY)[1]

9.3 背景减除

backSub = cv2.createBackgroundSubtractorMOG2()
# 或: backSub = cv2.createBackgroundSubtractorKNN()
fgMask = backSub.apply(frame)

📺 推荐视频

资源 链接
OpenCV 官方入门教程 https://docs.opencv.org/master/d9/df8/tutorial_root.html
Learn OpenCV (YouTube) https://www.youtube.com/@LearnOpenCV
图像处理基础 (MIT 6.819) MIT OCW
数字图像处理 (王伟, 中科大) B站

📚 推荐书籍

  • Digital Image Processing (4th Ed.) - Gonzalez & Woods ⭐
  • Computer Vision: Algorithms and Applications - Szeliski (第3章)
  • Practical Python and OpenCV - Adrian Rosebrock
  • Learning OpenCV - Bradski & Kaehler
  • Programming Computer Vision with Python - Jan Erik Solem

🔗 视频链接

  • OpenCV Tutorial (B站): 搜索 “OpenCV教程”
  • 中科大《数字图像分析》: B站搜索
  • CS231n 图像分类部分

附录:深层补充

1. 频率域图像处理详解

1.1 傅里叶变换在图像中的含义

二维离散傅里叶变换(DFT):
F ( u , v ) = ∑ x = 0 M − 1 ∑ y = 0 N − 1 f ( x , y ) e − j 2 π ( u x / M + v y / N ) F(u,v) = \sum_{x=0}^{M-1} \sum_{y=0}^{N-1} f(x,y) e^{-j2\pi(ux/M + vy/N)} F(u,v)=x=0M1y=0N1f(x,y)ej2π(ux/M+vy/N)

频率分量的物理含义

  • 低频 u , v u,v u,v 接近0):对应图像中灰度缓慢变化区域(平滑背景、大块物体)
  • 高频 u , v u,v u,v 较大):对应灰度急剧变化区域(边缘、角点、纹理、噪声)
  • 幅度谱 ∣ F ( u , v ) ∣ |F(u,v)| F(u,v):各频率成分的强度
  • 相位谱 ∠ F ( u , v ) \angle F(u,v) F(u,v):各频率成分的位置信息(相位对图像感知极其重要,即使幅度谱被破坏,仅用相位谱也能恢复出可识别的边缘信息)

性质

  • 中心化:将低频移到中心( F ( u − M / 2 , v − N / 2 ) ⋅ ( − 1 ) u + v F(u-M/2, v-N/2) \cdot (-1)^{u+v} F(uM/2,vN/2)(1)u+v
  • 旋转不变性:图像旋转 θ \theta θ → 幅度谱也旋转 θ \theta θ
  • 尺度变化:图像缩小 → 频谱扩展(反比关系)
1.2 滤波器传递函数

理想低通滤波器(ILPF)
H ( u , v ) = { 1 D ( u , v ) ≤ D 0 0 D ( u , v ) > D 0 H(u,v) = \begin{cases} 1 & D(u,v) \leq D_0 \\ 0 & D(u,v) > D_0 \end{cases} H(u,v)={10D(u,v)D0D(u,v)>D0

  • 优点:数学简单
  • 缺点:振铃效应(Gibbs现象),因频域突变导致空域振荡

Butterworth低通滤波器(BLPF)
H ( u , v ) = 1 1 + [ D ( u , v ) / D 0 ] 2 n H(u,v) = \frac{1}{1 + [D(u,v)/D_0]^{2n}} H(u,v)=1+[D(u,v)/D0]2n1

  • n n n 阶控制过渡带宽度
  • n n n 越大越接近理想滤波器(振铃越强)
  • n n n 小则平滑过渡,无振铃

高斯低通滤波器(GLPF)
H ( u , v ) = e − D 2 ( u , v ) / 2 σ 2 = e − D 2 ( u , v ) / 2 D 0 2 H(u,v) = e^{-D^2(u,v)/2\sigma^2} = e^{-D^2(u,v)/2D_0^2} H(u,v)=eD2(u,v)/2σ2=eD2(u,v)/2D02

  • 空域和频域都是高斯(无振铃)
  • 时频不确定性原理: σ x ⋅ σ u ≥ 1 / 4 π \sigma_x \cdot \sigma_u \geq 1/4\pi σxσu1/4π
import numpy as np
import cv2

def fft_filter(img, D0, filter_type='gaussian', n=2):
    """频域滤波通用函数"""
    # DFT
    f = np.fft.fft2(img.astype(np.float32))
    fshift = np.fft.fftshift(f)
    
    # 构建滤波器
    rows, cols = img.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)
    
    if filter_type == 'ideal':
        H = (D <= D0).astype(np.float32)
    elif filter_type == 'butterworth':
        H = 1 / (1 + (D / D0)**(2*n))
    elif filter_type == 'gaussian':
        H = np.exp(-D**2 / (2 * D0**2))
    
    # 应用滤波器并反变换
    f_filtered = fshift * H
    f_ishift = np.fft.ifftshift(f_filtered)
    img_back = np.fft.ifft2(f_ishift)
    return np.abs(img_back)
1.3 FFT加速卷积原理

空域卷积复杂度 O ( M N ⋅ k 2 ) O(MN \cdot k^2) O(MNk2),频域复杂度 O ( M N log ⁡ M N ) O(MN \log MN) O(MNlogMN)

卷积定理
f ∗ g = F − 1 { F { f } ⋅ F { g } } f * g = \mathcal{F}^{-1}\{\mathcal{F}\{f\} \cdot \mathcal{F}\{g\}\} fg=F1{F{f}F{g}}

加速条件:当卷积核 k × k k \times k k×k 满足 k k k 远大于 log ⁡ 2 N \log_2 N log2N 时,FFT加速显著。实践中,图像越大、核越大,FFT加速越明显。

实际例子:1000×1000图像,50×50卷积核

  • 空域: 10 6 × 2500 = 2.5 × 10 9 10^6 \times 2500 = 2.5 \times 10^9 106×2500=2.5×109 次操作
  • FFT: 2 × 10 6 × log ⁡ 2 ( 2048 ) ≈ 2.2 × 10 7 2 \times 10^6 \times \log_2(2048) \approx 2.2 \times 10^7 2×106×log2(2048)2.2×107 次操作
  • 加速比:约100倍

2. 边缘检测的深入对比

2.1 各算子的数学原理

Sobel算子
G x = [ − 1 0 1 − 2 0 2 − 1 0 1 ] , G y = [ − 1 − 2 − 1 0 0 0 1 2 1 ] G_x = \begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{bmatrix}, \quad G_y = \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ 1 & 2 & 1 \end{bmatrix} Gx= 121000121 ,Gy= 101202101
本质:水平方向平滑(3点均值)+ 垂直方向差分(中心差分的加权版)。

Prewitt算子
G x = [ − 1 0 1 − 1 0 1 − 1 0 1 ] , G y = [ − 1 − 1 − 1 0 0 0 1 1 1 ] G_x = \begin{bmatrix} -1 & 0 & 1 \\ -1 & 0 & 1 \\ -1 & 0 & 1 \end{bmatrix}, \quad G_y = \begin{bmatrix} -1 & -1 & -1 \\ 0 & 0 & 0 \\ 1 & 1 & 1 \end{bmatrix} Gx= 111000111 ,Gy= 101101101
比Sobel更简单的权重(无中心加强),对噪声更敏感。

Roberts Cross算子
G x = [ 1 0 0 − 1 ] , G y = [ 0 1 − 1 0 ] G_x = \begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix}, \quad G_y = \begin{bmatrix} 0 & 1 \\ -1 & 0 \end{bmatrix} Gx=[1001],Gy=[0110]
2×2核,方向对角,对噪声最敏感(感受野最小)。

Laplacian算子
∇ 2 f = ∂ 2 f ∂ x 2 + ∂ 2 f ∂ y 2 \nabla^2 f = \frac{\partial^2 f}{\partial x^2} + \frac{\partial^2 f}{\partial y^2} 2f=x22f+y22f
离散近似(4邻域): [ 0 1 0 1 − 4 1 0 1 0 ] \begin{bmatrix} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{bmatrix} 010141010
8邻域: [ 1 1 1 1 − 8 1 1 1 1 ] \begin{bmatrix} 1 & 1 & 1 \\ 1 & -8 & 1 \\ 1 & 1 & 1 \end{bmatrix} 111181111

特点:二阶导数为零交叉(zero-crossing)对应边缘。

LoG (Laplacian of Gaussian)
LoG ( x , y ) = 1 π σ 4 ( x 2 + y 2 2 σ 2 − 1 ) e − x 2 + y 2 2 σ 2 \text{LoG}(x,y) = \frac{1}{\pi\sigma^4}\left(\frac{x^2+y^2}{2\sigma^2} - 1\right) e^{-\frac{x^2+y^2}{2\sigma^2}} LoG(x,y)=πσ41(2σ2x2+y21)e2σ2x2+y2
先高斯平滑再Laplacian,等效于用LoG核对图像做一次卷积。

DoG (Difference of Gaussians)
DoG ( x , y ) = G σ 1 ( x , y ) − G σ 2 ( x , y ) ≈ LoG ( x , y ) \text{DoG}(x,y) = G_{\sigma_1}(x,y) - G_{\sigma_2}(x,y) \approx \text{LoG}(x,y) DoG(x,y)=Gσ1(x,y)Gσ2(x,y)LoG(x,y)
LoG的高效近似(SIFT中用于检测尺度空间极值)。

2.2 Canny完整流程详解
def canny_edge_detection(img, sigma=1.0, low_thresh=0.1, high_thresh=0.3):
    # Step 1: 高斯平滑
    # 核大小取 2*ceil(3*sigma)+1
    ksize = int(2 * np.ceil(3 * sigma) + 1)
    smoothed = cv2.GaussianBlur(img, (ksize, ksize), sigma)
    
    # Step 2: 计算梯度幅值和方向
    Gx = cv2.Sobel(smoothed, cv2.CV_64F, 1, 0, ksize=3)
    Gy = cv2.Sobel(smoothed, cv2.CV_64F, 0, 1, ksize=3)
    magnitude = np.sqrt(Gx**2 + Gy**2)
    angle = np.arctan2(Gy, Gx) * 180 / np.pi
    # 量化角度到4个方向: 0°, 45°, 90°, 135°
    angle[angle < 0] += 180
    angle = np.round(angle / 45) * 45 % 180
    
    # Step 3: 非极大值抑制(Non-Maximum Suppression)
    # 对每个像素,沿梯度方向比较相邻两像素的幅值
    # 若当前像素不是最大值则抑制
    nms = np.zeros_like(magnitude)
    h, w = magnitude.shape
    for i in range(1, h-1):
        for j in range(1, w-1):
            theta = angle[i, j]
            if theta == 0:    # 水平方向
                n1, n2 = magnitude[i, j-1], magnitude[i, j+1]
            elif theta == 45: # 对角线
                n1, n2 = magnitude[i+1, j-1], magnitude[i-1, j+1]
            elif theta == 90: # 垂直方向
                n1, n2 = magnitude[i-1, j], magnitude[i+1, j]
            else:             # 135°对角线
                n1, n2 = magnitude[i-1, j-1], magnitude[i+1, j+1]
            
            if magnitude[i, j] >= n1 and magnitude[i, j] >= n2:
                nms[i, j] = magnitude[i, j]
    
    # Step 4: 双阈值检测
    high = high_thresh * nms.max()
    low = low_thresh * high
    strong = (nms >= high).astype(np.uint8) * 255
    weak = ((nms >= low) & (nms < high)).astype(np.uint8) * 25
    
    # Step 5: 边缘跟踪(滞后的阈值化)
    # 只保留与强边缘相连的弱边缘
    edges = strong.copy()
    # 使用8连通检查
    for i in range(1, h-1):
        for j in range(1, w-1):
            if weak[i, j] > 0:  # 弱边缘
                if np.any(strong[i-1:i+2, j-1:j+2]):  # 邻域有强边缘
                    edges[i, j] = 255
    return edges

各算子噪声鲁棒性对比(从优到劣):
Canny > LoG > Sobel > Prewitt > Roberts

Canny通过三步保证鲁棒性:1) 高斯平滑(去噪)2) NMS(细化边缘)3) 双阈值滞后(剔除虚假响应)


3. 形态学操作的数学形态学

3.1 集合论定义

A A A 为二值图像中的像素集合, B B B 为结构元素。

腐蚀
A ⊖ B = { z ∣ B z ⊆ A } A \ominus B = \{z \mid B_z \subseteq A\} AB={zBzA}
// 结构元素完全包含在A中的所有中心点

膨胀
A ⊕ B = { z ∣ B ^ z ∩ A ≠ ∅ } A \oplus B = \{z \mid \hat{B}_z \cap A \neq \emptyset\} AB={zB^zA=}
// 结构元素(取反射后)在A中有交集的所有中心点

开运算
A ∘ B = ( A ⊖ B ) ⊕ B A \circ B = (A \ominus B) \oplus B AB=(AB)B
// 先腐蚀后膨胀:去除小凸起、断开细连接

闭运算
A ∙ B = ( A ⊕ B ) ⊖ B A \bullet B = (A \oplus B) \ominus B AB=(AB)B
// 先膨胀后腐蚀:填充小孔洞、连接断裂

对偶性
( A ⊖ B ) c = A c ⊕ B ^ (A \ominus B)^c = A^c \oplus \hat{B} (AB)c=AcB^
( A ∘ B ) c = A c ∙ B ^ (A \circ B)^c = A^c \bullet \hat{B} (AB)c=AcB^

3.2 击中击不中变换(Hit-or-Miss)

定义 A ⊛ B = ( A ⊖ B 1 ) ∩ ( A c ⊖ B 2 ) A \circledast B = (A \ominus B_1) \cap (A^c \ominus B_2) AB=(AB1)(AcB2)

其中 B = ( B 1 , B 2 ) B = (B_1, B_2) B=(B1,B2) 是复合结构元素, B 1 B_1 B1 匹配前景, B 2 B_2 B2 匹配背景。

应用

  • 细化(Thinning): A ⊗ B = A − ( A ⊛ B ) A \otimes B = A - (A \circledast B) AB=A(AB)
  • 粗化(Thickening): A ⊙ B = A ∪ ( A ⊛ B ) A \odot B = A \cup (A \circledast B) AB=A(AB)
  • 骨架提取:通过反复细化
  • 角点检测:特定模式的击中击不中
3.3 形态学重建

地质学重建:基于标记图像(marker)和掩模图像(mask)的迭代膨胀。

R M ( F ) = 重复 [ ( F ⊕ B ) ∩ M ]  直到稳定 R_M(F) = \text{重复} [(F \oplus B) \cap M] \text{ 直到稳定} RM(F)=重复[(FB)M] 直到稳定

其中 F F F 是标记图像(marker), M M M 是掩模图像(mask), F ⊆ M F \subseteq M FM

应用

  • 重建开运算:先腐蚀再重建,比标准开运算更好地保留形状
  • 填充孔洞:标记为边界像素的补集,重建后反转
  • 边界清除:标记为边界像素,重建后移除连接边界的物体
def morphological_reconstruction(marker, mask, kernel_size=3):
    """形态学重建"""
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size))
    marker_prev = np.zeros_like(marker)
    while not np.array_equal(marker, marker_prev):
        marker_prev = marker.copy()
        # 膨胀加交会
        dilated = cv2.dilate(marker, kernel)
        marker = cv2.bitwise_and(dilated, mask)
    return marker

4. 图像金字塔与多尺度表示

4.1 Gaussian金字塔和Laplacian金字塔的构建数学

Gaussian金字塔(下采样):

G l + 1 ( i , j ) = ∑ m = − 2 2 ∑ n = − 2 2 w ( m , n ) ⋅ G l ( 2 i + m , 2 j + n ) G_{l+1}(i,j) = \sum_{m=-2}^{2} \sum_{n=-2}^{2} w(m,n) \cdot G_l(2i+m, 2j+n) Gl+1(i,j)=m=22n=22w(m,n)Gl(2i+m,2j+n)

其中 w ( m , n ) w(m,n) w(m,n) 是高斯权重,通常用5×5可分离高斯核:
w = 1 256 [ 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 ] w = \frac{1}{256} \begin{bmatrix} 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 \end{bmatrix} w=2561 1464141624164624362464162416414641

这一步相当于高斯平滑 + 2倍下采样

Laplacian金字塔(高频残差):

L l = G l − expand ( G l + 1 ) L_l = G_l - \text{expand}(G_{l+1}) Ll=Glexpand(Gl+1)

其中 expand 操作:将图像放大2倍后高斯插值。

expand ( G l + 1 ) ( i , j ) = 4 ∑ m = − 2 2 ∑ n = − 2 2 w ( m , n ) ⋅ G l + 1 ( i − m 2 , j − n 2 ) \text{expand}(G_{l+1})(i,j) = 4 \sum_{m=-2}^{2} \sum_{n=-2}^{2} w(m,n) \cdot G_{l+1}\left(\frac{i-m}{2}, \frac{j-n}{2}\right) expand(Gl+1)(i,j)=4m=22n=22w(m,n)Gl+1(2im,2jn)

L l L_l Ll 包含了 G l G_l Gl G l + 1 G_{l+1} Gl+1 所无法表示的高频信息(边缘、纹理细节)。

完全重建
G 0 = expand ( G 1 ) + L 0 = expand ( expand ( G 2 ) + L 1 ) + L 0 = . . . G_0 = \text{expand}(G_1) + L_0 = \text{expand}(\text{expand}(G_2) + L_1) + L_0 = ... G0=expand(G1)+L0=expand(expand(G2)+L1)+L0=...

4.2 多频段图像融合(Multi-band Blending)

用于全景拼接和无缝图像融合。

算法步骤

def multi_band_blending(img1, img2, mask, num_levels=5):
    """
    img1, img2: 输入图像
    mask: 融合权重掩模(0=img1区域, 1=img2区域, 中间=过渡)
    """
    # 1. 构建两幅图的Laplacian金字塔
    lap_pyramid1 = build_laplacian_pyramid(img1, num_levels)
    lap_pyramid2 = build_laplacian_pyramid(img2, num_levels)
    
    # 2. 构建掩模的Gaussian金字塔
    mask_pyramid = build_gaussian_pyramid(mask, num_levels)
    
    # 3. 逐层融合
    blended_pyramid = []
    for l in range(num_levels):
        blended = lap_pyramid1[l] * (1 - mask_pyramid[l]) + lap_pyramid2[l] * mask_pyramid[l]
        blended_pyramid.append(blended)
    
    # 4. 从金字塔重建图像
    result = reconstruct_from_laplacian_pyramid(blended_pyramid)
    return result

原理:高斯金字塔的层对应低通版本,Laplacian层对应带通版本。每层独立融合可以:

  • 低频区域(顶层):跨越拼接缝平滑过渡
  • 高频区域(底层):保留各自纹理细节,仅在接缝附近融合

相比于直接加权融合(出现突兀的重影和接缝),多频段融合可以同时实现平滑过渡和细节保留

选择融合层数的经验

  • 图像分辨率 W × H W \times H W×H → 最大层数 L = ⌊ log ⁡ 2 min ⁡ ( W , H ) ⌋ L = \lfloor \log_2 \min(W,H) \rfloor L=log2min(W,H)⌋
  • 通常取 4-6 层
  • 当两张图对齐非常精确时,可以减少层数(更多直接混合)
  • 当有视差或对齐误差时,增加层数让低频过渡更平滑
Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐