接触图像配准是从去年十月份开始的,老师要求我尽快重现一遍整个流程,这样对课题可以有一个整体的把握,而后再仔细推敲细节,甚至提出自己的想法,老师的这个思路现在觉得非常不错。自己当时选取的是SURF方法,因为是对SIFT的改进,改进的当然好一点吧,但后来发现要想深刻地理解算法原理,还是要研究更具有里程碑意义的SIFT,在这个阶段从调用opencv库转为研究Rob Hess的源码,第一次仔细看源码,教科书搬实现了David G.Lowe的论文,从尺度空间的构建到kd树的建立,不由惊呼这才叫写代码,我这一天净是写bug了。后来开题答辩,在知乎上了解到还有KAZE的算法,于是又简单研究了一下非线性尺度空间的构建,据说这种算法保留了图像更多的细节。再后来有文章要投稿,搞不了里程碑式的原创只能在前人基础上修修补补,那么在哪个基础上修补也是个问题,虽然当时含糊选择了SIFT进行改进,但还是对现存算法的性能没有确切的认识,为了调研清楚(这其实是开题阶段应该做的),对现存主流算法的横向比较变得势在必行。
这一工作其实已经被邹宇华做完了,但问题在于重现他的代码时出现了许多问题,下面我仔细讲一下遇到的问题和对应的解决办法。
虽然OpenCV版本号已经符合2.4以上的标准,但是头文件找不到utinity.hpp。在网上查了好久,这个文件在OpenCV3以上才有,但实验证明简单地把OpenCV3的这个文件放到目录中是行不通的。最后改来改去也只是实现了SIFT、SURF等方法的比较。不知道如何把KAZE加入到Feature2D类。编译安装OpenCV3.2之后实现了KAZE、AKAZE等的比较,但却无法实现与SIFT、SURF的比较,查资料才发现在3.0以上是版本中把一些算法放在了xfeature中,而这一模块要手动添加扩展模块opencv_contrib。编译过程要选择和自己的OpenCV版本对应的模块。环境变量、库目录、包含库、附加依赖项都配置好之后,终于可以加入算法比较了,但还要注意SIFT调用方法的变化。原始代码中有一些注释掉的地方其实是有用的,我简单进行了修改进行了使用,否则得到的txt文件数据部分都是初始值0.此外,我还对FrameMatchingStatistics类加入了峰值信噪比PSNR作为评价标准,衡量配准后的图像与参考图像的相似性;在printPerformanceStatistics函数中增添了计算平均每幅图检测到的特征点的数目。最后通过matlab把性能曲线画出来。每一项的改进过程中都失败多次,也收货满满,比如PSNR函数只能求灰度图像的信噪比,无穷大范数比较两个矩阵,perspectiveTransform对特征点集的变换它也只能对坐标进行变换,这些都写在代码的注释部分。实验图像、c++代码、实验生成的txt文件和matlab代码都挂在了github上,欢迎fork、star。https://github.com/zcg1942/Opencv3_FeatCompare
(1)homographyError
(2)correctMatchesPercent
(3)percentOfMatches
(4)matchingRatio
(5)Meandistance
(6)PSNR(7)performance
下面是我对实验结果的分析:
| 亮度 source + cv::Scalar(t,t,t,t) | 高斯模糊 cv::GaussianBlur()实现,高斯核是参数边长为t*2+1大小的矩阵 | 旋转 warpAffine函数实现旋转。getRotationMatrix2D函数得到旋转中心,旋转角度 cv::INTER_CUBIC内插 | 尺度缩放 Resize()实现 source.cols * t + 0.5f (source.rows * t + 0.5f)) cv::INTER_AREA内插 |
homographyError矩阵误差。配准求得的变换矩阵与真实的矩阵之积应该是单位阵。矩阵之积与单位阵之差,求其无穷范数(只取小于1的部分) | 表现最好的是SIFT、SURF、BRISK。ORB在亮度-100的部分最差 | 趋势都是模糊程度越高,效果也差。表现最好的依次是KAZE、AKAZE、SIFT、SURF。但KAZE在核大于7后有一个跃变 | 表现最好的依次是KAZE、AKAZE、BRISK。 SIFT的性能曲线较平滑,在参数180度取极小值,左右对称分布,大体呈上凸的抛物线 | SIFT性能最好,且最稳定。其次是SURF和KAZE,KAZE波动更大。 |
correctMatchesPercent =correctMatches / matchesCount 正确匹配对与初始匹配对之比。距离误差小于3认为是正确匹配 | SIFT、BRISK、KAZE、AKAZE效果最好, | 表现最好的依次是AKAZE、KAZE,在80%以上。ORB性能次之,但性能下降最快 | 表现最好的是AKAZE、SIFT、ORB。最差的是SURF。所以算法都在90 度倍数附近有较大极值 | 表现最好的是AKAZE、KAZE、ORB、SIFT |
percentOfMatches=matchesCount / (s.totalKeypoints)初匹配对数与变换后的待配准图像提取出的特征点的数目之比 | 表现最好的依次是AKZE、SIFT、KAZE,都在90%以上 | AKAZE、BRISK、KAZE表现最好,且能维持在90%以上。SIFT在高斯核变大后反而性能变好,也可达90% | AKAZE表现最好,ORB、SIFT、KAZE其次。都有几个明显的极值,分别是90、180、270度左右 | AKAZE表现最好,其余除了ORB都差不多。但尺度大于1时除了BRISK和ORB,其余都急剧下降到低水平 |
matchingRatio() = correctMatchesPercent * percentOfMatches * 100.0f; | 表现最好的是AKAZE。性能曲线走势和correctMatchesPercent相近 | 同左 | 同左 | 同左 |
Meandistance 将待配准图像按照求出的矩阵的逆变换回去后的特征点与原图的距离的均值 | SURF、KAZE距离的平均值几乎为0,KAZE稍高一些也只有0.2左右。其次是SIFT
| SURF、KAZE距离的平均值几乎为0,KAZE稍高一些也只有0.2左右。其次是AKAZE、ORB
| SURF、KAZE距离的平均值几乎为0,KAZE稍高一些也只有0.2左右。其次依次是ORB、AKAZE。所有算法都有明显的极小值
| SURF、KAZE距离的平均值几乎为0,KAZE稍高一些也只有0.2左右 其次是ORB、AKAZE |
PSNR峰值信噪比 | 表现几乎一样,ORB在低亮度的时候稍差。曲线在明暗两部分几乎对称分布 | 表现几乎一样,ORB在高斯模糊算子大于5的部分稍差一点。高斯算子小于3时PSNR都在30~34平缓分布 | 表现几乎一样,曲线几乎重合。在旋转角度10度时就从350dB骤降到15,曲线在180度左右对称分布 | 尺度缩放时不同算法表现差异最大。KAZE、SIFT、SURF在尺度放大时平稳增大,缩小时平稳降低。AKAZE、BRISK、ORB性能稍差,且波动较大 |
每帧图像提取特征点到匹配的耗时,平均每个特征点的耗时,平均每帧变换图的总特征点数 | 每个特征点耗时和每幅图像耗时最高的都是KAZE和SIFT,最少的是BRISK和ORB。检测出的特征点数最多的是SURF,最少的是ORB有几百个,其他的都在1000个以上 |
剩下的几点疑惑和值得注意的地方,在旋转变换中,匹配率与旋转角度近似成周期为90度的关系;当以峰值信噪比为评价标准时,PSNR与旋转角度没有这种周期关系,且在旋转角度刚达到10度就急剧下降到15dB,只在180度时可以达到30dB左右,目前猜测应该是变换模型太简单,因为只是利用密集透视变换函数warpPerspective()做了透视变换,没有充分利用好匹配得到的匹配对;在meandistance的标准下,SURF表现明显好过其他算法,不管在哪个变换,变换参数多大,都稳定保持在接近0的状态,这值得关注。
这就是目前的一些进展。虽然已经涵盖了各种变换,但是原始图像的选择还是非常重要,我这里选取的是University of Oxford的特征点检测数据集中的一幅图片,在这个网站上其实也有很多特征点、角点、区域的检测、描述的比较。不同种类的图像有自己独特的特征,如何针对具体的图像做优化应该可以结合机器学习进一步改进。