本文为原创文章,未经本人允许,禁止转载。转载请注明出处。
1.cv::findHomography
该函数的目的是计算得到源平面和目标平面之间的透视变换$\mathbf{H}$:
\[s_i \begin{bmatrix} x'_i \\ y'_i \\ 1 \\ \end{bmatrix} \sim \mathbf{H} \begin{bmatrix} x_i \\ y_i \\ 1 \end{bmatrix}\]我们通过最小化以下反投影误差来求得$\mathbf{H}$:
\[\sum_i \left( x'_i - \frac{h_{11}x_i + h_{12}y_i + h_{13}}{h_{31}x_i + h_{32}y_i + h_{33}} \right)^2 + \left(y'_i - \frac{h_{21}x_i + h_{22}y_i + h_{23}}{h_{31}x_i + h_{32}y_i + h_{33}} \right)^2\]查看此篇博文帮助理解。
cv::findHomography
提供了4种最小化上式的求解方法:
- 最小二乘法
- RANSAC
- LMeDS
- RHO
1
2
3
4
5
6
7
8
9
Mat cv::findHomography (
InputArray srcPoints,
InputArray dstPoints,
int method = 0,
double ransacReprojThreshold = 3,
OutputArray mask = noArray(),
const int maxIters = 2000,
const double confidence = 0.995
)
参数详解:
srcPoints
:原始平面上的点。dstPoints
:目标平面上的点。method
:计算单应性矩阵的方法,默认是0
,即简单的最小二乘法。剩余三种可选的方法是RANSAC
、LMEDS
和RHO
。-
\[\left \| \text{dstPoints}_i - \text{convertPointsHomogeneous}(\mathbf{H} \cdot \text{srcPoints}_i) \right \|_2 > \text{ransacReprojThreshold}\]ransacReprojThreshold
:可将点对视为内点的最大允许反投影误差(仅用于RANSAC
和RHO
),即我们在第1.1部分RANSAC算法步骤第3步中提到的拟合误差阈值。如果有:则点$i$可被视为外点。如果srcPoints和dstPoints都是按像素算的话,那么ransacReprojThreshold设为1-10是比较合理的。
mask
:RANSAC或LMeDS可设置的输出mask。注意,输入mask会被忽略。maxIters
:RANSAC算法设置的最大迭代次数。confidence
:置信度,取值在0~1之间,解释见第1.1部分。
另一种重载形式:
1
2
3
4
5
6
7
Mat cv::findHomography (
InputArray srcPoints,
InputArray dstPoints,
OutputArray mask,
int method = 0,
double ransacReprojThreshold = 3
)
1.1.RANSAC
随机抽样一致算法(RANdom SAmple Consensus,RANSAC)。它采用迭代的方式从一组包含离群的被观测数据中估算出数学模型的参数。RANSAC是一个非确定性算法,在某种意义上说,它会产生一个在一定概率下合理的结果,而更多次的迭代会使这一概率增加。此RANSAC算法在1981年由Fischler和Bolles首次提出。
这里用一个简单的例子来说明,在一组数据点中找到一条最合适的线。RANSAC将这些数据点分为内点(inlier)和外点(outlier)。内点是可以被拟合到直线上的点,而外点则是无法被拟合的点,即异常值或离群点。如果我们使用简单的最小二乘法,因其会考虑所有点(包括离群点),所以会拟合出下图的红线。而RANSAC可以只由内点来计算出模型,所以可以拟合出下图的绿线。
RANSAC算法的步骤如下:
- 从原始数据点中随机选择一个子集,将这个子集称为假设内点集(hypothetical inliers)。
- 使用假设内点集拟合出一个模型。
- 计算所有原始数据点对这个模型的拟合程度,将所有拟合误差小于一定阈值的点放进一致点集(consensus set)。
- 使用一致点集重新拟合出一个新模型。
- 重复第3,4步,直至算法停止(满足精度要求或达到预设迭代次数)。
整个过程可参考下图:
从算法流程来看,初始点的选择、拟合误差阈值、迭代次数等参数都会对算法的结果产生影响。
接下来解释下cv::findHomography
中的参数confidence
。假设一个点属于真正内点集的概率为$w$,则:
即真正内点数在数据中的占比。如果我们每次计算模型使用$n$个点,那么这$n$个点至少有一个是外点的概率为:
\[1 - w^n\]如果我们迭代$k$次,每次用于计算模型的点中都至少有一个是外点,那么这种情况的概率为:
\[(1-w^n)^k\]相反,如果我们迭代$k$次,每次用于计算模型的点都是真正的内点,则其概率为:
\[p = 1- (1-w^n)^k\]这个$p$就是参数confidence
。根据上式,我们可得到:
其中,$w$是未知的,通常会预设一个估计值,比如0.5。$n$也是不确定的,比如对于单应性矩阵,至少需要4对匹配点,即$n$最少为4。$p$通常设为0.99或0.995。有了这些预设值,我们其实可以估算出大概所需的最大迭代次数:
\[k = \frac{\log(1-0.995)}{\log(1-0.5^4)} \approx 45.7\]在实际应用中,如果$p$设置的过高,则能确保高概率找到最优模型,但可能会增加迭代次数,消耗更多计算资源。但如果$p$设置的过低,虽然减少了计算资源,但模型可能不够准确。
1.2.LMeDS
LMeDS(Least Median of Squares,最小中值平方法)是一种鲁棒统计方法,用于估计模型参数,特别适合于处理包含离群值的数据集。它通过最小化残差平方的中值来估计模型参数,能够有效抑制异常点对估计结果的影响。步骤如下:
- 采样:从数据集中随机选择最少数量的样本点,足够拟合模型(例如,单应性矩阵需要4对点)。
- 模型拟合:使用选定的样本点拟合模型。
- 残差计算:计算所有数据点的残差。残差可以用欧氏距离来表示。
- 中值计算:计算残差平方的中值(Median of Squared Residuals)。
- 重复迭代:重复采样、拟合和计算中值若干次,保留中值最小的模型。
- 输出模型:最终输出残差平方中值最小的模型参数。
LMeDS的目标是找到使得残差平方的中值最小的模型参数,这样可以减少异常值的影响。
1.3.RHO
cv::RHO
指的是PROSAC(Progressive Sample Consensus),其是对RANSAC算法的一种改进。
PROSAC论文:Matching with PROSAC – Progressive Sample Consensus。
2.cv::perspectiveTransform
1
2
3
4
5
void cv::perspectiveTransform (
InputArray src,
OutputArray dst,
InputArray m
)
参数详解:
src
:二通道或三通道,每个元素被视为2D或3D向量进行转换。dst
:输出结果,和src的大小以及类型相同。m
:$3 \times 3$或$4 \times 4$的转化矩阵。
cv::perspectiveTransform
用于执行向量的透视矩阵变换,其通过将src的每个元素视为2D或3D向量来进行转换,方法如下:
其中,
\[(x',y',z',w') = \text{mat} \cdot \begin{bmatrix} x & y & z & 1 \\ \end{bmatrix}\]且,
\[w = \begin{cases} w' & \text{if} \ w' \neq 0 \\ \infty & \text{otherwise} \end{cases}\]上述公式适用于3D向量的转换,如果是2D向量,可以省略公式中的$z$。
该函数用于转换2D或3D向量的稀疏集合。如果要使用透视变换转换图像,可以使用cv::warpPerspective
。
3.平面对象识别
我们可以先用FLANN找到一组配对点,然后用cv::findHomography
算出转换矩阵,最后通过cv::perspectiveTransform
将原始图像的边界点投影到目标图像上。结果如下图所示,我们可以把原始图像中的书籍在目标图像中准确用红框圈出来。