3、图像采集与对齐:手持拍摄的挑战、全局对齐与局部对齐、基于特征点的对齐算法(SIFT/ORB)、光流法对齐
各位同学,欢迎来到第三章。
前面我们聊了HDR和多帧降噪的基本原理。但有个现实问题摆在那——你拍的照片,手会抖。哪怕你觉得自己稳如磐石,在像素级看来,那都是地震。
这一章,我们就来搞定这个「地震」。说白了,就是图像对齐。
3.1 手持拍摄的挑战:为什么对齐这么难?
你想想看,手持拍三张包围曝光照片,从按下快门到抬起手指,至少得半秒钟。这半秒里,你的呼吸、心跳、甚至风吹过来,都会让画面产生位移。
我遇到过最夸张的一次,是在一个阴天的傍晚拍夜景HDR。三张照片拍完,回放一看,路灯都重影了。当时我就意识到,没有对齐的HDR,就是一场灾难。
手持拍摄的主要挑战,我归纳为三类:
- 全局平移与旋转:整个画面整体偏移或旋转。这是最常见的,因为手在抖。
- 局部视差:近处的物体和远处的物体,因为相机位置的微小变化,产生的相对位移不同。你想想看,你拍一个近处的花和远处的山,手稍微动一下,花的位置变化很大,山几乎没变。
- 卷帘快门效应:现在的手机大多是CMOS传感器,逐行扫描。如果拍摄过程中有快速移动的物体,或者手抖得厉害,画面会出现「果冻效应」——直线变弯,物体变形。
3.2 全局对齐 vs 局部对齐
对齐算法,按作用范围分两大类:全局对齐和局部对齐。
| 特性 | 全局对齐 | 局部对齐 |
|---|---|---|
| 模型 | 仿射变换、透视变换(单应性矩阵) | 光流场、网格变形 |
| 适用场景 | 相机纯旋转、小范围平移、静态场景 | 存在视差、运动物体、复杂场景 |
| 计算量 | 低 | 高 |
| 对齐精度 | 整体一致,局部可能错位 | 局部精细,但可能过拟合 |
| 典型算法 | 基于特征点(SIFT/ORB) + RANSAC | 光流法(Farneback/LK)、网格流 |
我个人习惯是:先用全局对齐做粗配准,再用局部对齐做精修。这样既保证了效率,又兼顾了精度。
💡 核心思路:全局对齐解决「大方向」的问题,局部对齐解决「小细节」的问题。两者结合,才是王道。
3.3 基于特征点的对齐算法(SIFT / ORB)
这是目前最主流的全局对齐方法。核心思路很简单:找到两张图像中相同的「地标」,然后根据这些地标的位置关系,计算出变换矩阵。
3.3.1 SIFT(尺度不变特征变换)
SIFT是经典中的经典。它提取的特征点,对旋转、尺度缩放、亮度变化都有很强的鲁棒性。
它的流程大致是:
- 尺度空间极值检测:在不同尺度下,用高斯差分(DoG)寻找潜在的关键点。
- 关键点定位:剔除低对比度和边缘响应的点,精确定位。
- 方向分配:为每个关键点分配一个主方向,实现旋转不变性。
- 特征描述:在关键点周围计算梯度直方图,生成128维的特征向量。
嗯,这里要注意,SIFT虽然精度高,但计算量巨大。在手机上做实时HDR,基本跑不动。我一般在PC端做离线处理时才会用SIFT。
3.3.2 ORB(定向FAST和旋转BRIEF)
ORB是SIFT的轻量级替代品。它结合了FAST角点检测和BRIEF描述子,并加入了旋转不变性。
它的优势很明显:
- 速度快:比SIFT快两个数量级,适合移动端。
- 精度尚可:在大多数场景下,足够满足HDR对齐的需求。
- 免费:SIFT有专利限制,ORB是开源的。
我在项目中用过ORB做实时HDR预览对齐。在骁龙8系芯片上,处理1080p图像,能做到30fps。效果嘛,只要场景不是太极端,基本够用。
🔧 实战技巧:使用ORB时,建议开启 nfeatures 参数,控制特征点数量。我一般设为1000-2000个。太多了,匹配耗时;太少了,匹配精度不够。
3.3.3 特征匹配与RANSAC
提取特征点后,需要做匹配。常用的方法是暴力匹配(Brute-Force)或快速最近邻(FLANN)。
匹配完成后,会有很多误匹配。这时候就需要RANSAC(随机抽样一致性)算法登场了。
RANSAC的核心思想是:随机选取一小部分匹配点,计算变换矩阵,然后统计有多少其他点符合这个矩阵。重复多次,找到支持点数最多的那个矩阵。
// 伪代码示例:RANSAC求解单应性矩阵
cv::Mat findHomographyRANSAC(
const std::vector<cv::Point2f>& srcPoints,
const std::vector<cv::Point2f>& dstPoints,
double threshold = 3.0, // 内点阈值(像素)
int maxIters = 2000 // 最大迭代次数
) {
cv::Mat mask; // 内点标记
cv::Mat H = cv::findHomography(srcPoints, dstPoints, cv::RANSAC, threshold, mask, maxIters);
return H;
}
⚠️ 避坑指南:我曾经在纹理极少的场景(比如一面白墙)使用特征点对齐,结果RANSAC找不到足够的内点,直接崩了。后来我加了一个回退策略:如果内点比例低于30%,就改用全局平移模型(只计算x/y偏移)。
3.4 光流法对齐
特征点对齐只能得到稀疏的匹配点,然后插值出全局变换。但有些场景,比如画面中有运动的树叶、飘动的旗帜,局部运动非常复杂。这时候,光流法就派上用场了。
光流法,说白了就是计算每个像素的运动矢量。它假设:同一个物体在相邻帧之间,亮度保持不变,且运动是连续的。
3.4.1 稀疏光流 vs 稠密光流
| 类型 | 代表算法 | 输出 | 计算量 | 适用场景 |
|---|---|---|---|---|
| 稀疏光流 | Lucas-Kanade (LK) | 指定特征点的运动矢量 | 低 | 跟踪特定物体、辅助特征点匹配 |
| 稠密光流 | Farneback、DeepFlow | 每个像素的运动矢量 | 高 | 精细对齐、运动补偿、视频插帧 |
在HDR合成中,我通常使用稠密光流来做局部对齐。特别是处理有运动物体的场景时,光流能很好地补偿局部运动,避免鬼影。
3.4.2 Farneback光流实战
OpenCV里集成了Farneback光流算法。它的原理是基于多项式展开,把每个像素的邻域拟合成一个二次多项式,然后通过比较多项式的系数来估计运动。
// C++示例:Farneback稠密光流
cv::Mat flow; // 输出的光流场(双通道:x方向位移,y方向位移)
cv::calcOpticalFlowFarneback(
prevGray, // 前一帧灰度图
currGray, // 当前帧灰度图
flow, // 输出光流
0.5, // 金字塔缩放比例
3, // 金字塔层数
15, // 窗口大小
3, // 迭代次数
5, // 多项式邻域大小
1.1, // 标准差
0 // 标志位
);
得到光流场后,就可以对图像进行warp(重映射),实现像素级的对齐。
🔧 实战技巧:光流法对噪声很敏感。我建议在计算光流前,先对图像做高斯滤波(核大小3x3或5x5)。另外,如果场景中有大面积的纯色区域(比如天空),光流会失效。这时候可以结合特征点对齐的结果,做混合处理。
3.5 知识结构总览
下面这张图,概括了本章的核心知识体系。你可以把它当作一个「对齐算法选型指南」。
3.6 本章小结
图像对齐,是HDR和多帧降噪的基石。没有对齐,后面的合成就是空中楼阁。
这一章我们讲了:
- 手持拍摄的三大挑战:全局运动、局部视差、卷帘快门。
- 全局对齐与局部对齐的区别和适用场景。
- 基于特征点的SIFT和ORB算法,以及RANSAC如何剔除误匹配。
- 光流法的原理和实战,特别是稠密光流在局部对齐中的应用。