本文将会介绍一种基于特征的图像校准方式,在这种方式中,大量的特征将在第一张源图中被提取出来,这些特征将在目标图像中寻找匹配的特征信息。通过两幅图片中相匹配的特征信息,源图和目标图像之间的像素坐标转换关系将会被提取出来。借助这种转换关系可以实现将一幅图片与另一幅校准对齐。
在许多应用中,我们有两幅包含相同物体的图片或者文件图片,但两幅图片中的相同物体是没有对齐的。比如说你在第一幅图中选择一个角点,在另一幅图片中相同角点的坐标位置是不一样的。
图像配准可以将其中一幅图片进行变形处理,使得两幅图片中相同的特征完美对齐。
上图显示了在文件分析中的应用,我们用原始表格作为原始图像,手机拍摄的填写完成的表格图像作为目标图像,经过图像配准,目标图像已经与原始表格对齐,便于后期分析与信息提取等操作。
图像配准的应用领域广泛。
在许多文件处理的应用中,第一步一般都是将扫描的文件或拍摄的文件与模板对齐。如果你像编写一个自动的表格数据读取系统,第一步就需要将表格与模板对齐,然后对模板中特定位置的信息进行提取。
在医学处理上,在不同的时间对同一组织进行扫描的结果往往有轻微的偏差,利用配准技术可以将两幅图片进行对齐,便于病灶的对比分析。
另一个有意思的应用是在全景照片的生成,若两幅相邻图片是围绕光轴旋转摄像机所拍摄得来的,我们可以通过本文所说的技术对两幅图片进行配准。
在计算机视觉中,平面的单应性被定义为一个平面到另外一个平面的投影映射。图像配准的核心是一个3*3的单应矩阵。在满足以下两个条件下,两幅图片是单应相关的:
单应性简单来说就是一个3*3矩阵:
若(x1,y1)是第一幅图中的一个点的坐标,(x2,y2)是第二幅图片中相同物理点的坐标,单应矩阵可以通过以下方式将这两幅图片联系起来:
这样看来,如果我们能获取这个单应矩阵,那么应用这个单应矩阵对一幅图片所有像素的坐标进行变换,变换结果就能和第二图图片配准。
如果两幅图像中我们知道4对以上的对应点,那么我们就可以用OpenCV函数 findHomography 来找到这种单应关系,如上图所示红黄蓝橙四对点。在内部函数 findHomography 将会通过解决一个线性方程组来找到单应矩阵,数学解析细节在此不表。
看函数用法是怎么样的:
findHomography(point1, point2, h);
point1和point2是存储对应点的向量,h是计算出的单应矩阵。那我们该如何找到对应的点对呢?
在许多计算机视觉应用中,我们通常需要识别图像中感兴趣的稳定点。这些点被称为关键点或特征点,几种关键点检测技术已在OpenCV中实现。在此我们将会用ORB特征算子,因为SIFT和SURF已经被申请专利,如果要实际应用的话,必须要付一定的专利费,ORB免费,除此以外,ORB速度快,也比较精确。下图显示了ORB关键点的检测结果:
一个特征点检测器包含两部分:
两幅图片的单应性在我们知道两幅图片的对应特征的情况下可以被计算出来。所以匹配算法用于寻找在两幅图片中的那些对应特征点。为此,将一个图像中每个特征的描述符与第二个图像中每个特征的描述符进行比较,以找到良好的匹配。
//特征匹配的函数需要包含contirbe
#include
#include "opencv2/xfeatures2d.hpp"
#include "opencv2/features2d.hpp"
using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;
const int MAX_FEATURES = 500; //控制最多可以检测到的特征点对数
const float GOOD_MATCH_PERCENT = 0.15f;
void alignImages(Mat &im1, Mat &im2, Mat &im1Reg, Mat &h)
{
// Convert images to grayscale
Mat im1Gray, im2Gray;
cvtColor(im1, im1Gray, CV_BGR2GRAY);
cvtColor(im2, im2Gray, CV_BGR2GRAY);
// Variables to store keypoints and descriptors
std::vector keypoints1, keypoints2;
Mat descriptors1, descriptors2;
// Detect ORB features and compute descriptors.
Ptr orb = ORB::create(MAX_FEATURES);
orb->detectAndCompute(im1Gray, Mat(), keypoints1, descriptors1);
orb->detectAndCompute(im2Gray, Mat(), keypoints2, descriptors2);
// Match features.
std::vector matches;
Ptr matcher = DescriptorMatcher::create("BruteForce-Hamming");
matcher->match(descriptors1, descriptors2, matches, Mat());
// Sort matches by score
std::sort(matches.begin(), matches.end());
// Remove not so good matches
const int numGoodMatches = matches.size() * GOOD_MATCH_PERCENT;
matches.erase(matches.begin() + numGoodMatches, matches.end());
// Draw top matches
Mat imMatches;
drawMatches(im1, keypoints1, im2, keypoints2, matches, imMatches);
imwrite("matches.jpg", imMatches);
// Extract location of good matches
std::vector points1, points2;
for (size_t i = 0; i < matches.size(); i++)
{
points1.push_back(keypoints1[matches[i].queryIdx].pt);
points2.push_back(keypoints2[matches[i].trainIdx].pt);
}
// Find homography
h = findHomography(points1, points2, RANSAC);
// Use homography to warp image
warpPerspective(im1, im1Reg, h, im2.size());
}
int main(int argc, char **argv)
{
// Read reference image
string refFilename("src.jpg");
cout << "Reading reference image : " << refFilename << endl;
Mat imReference = imread(refFilename);
// Read image to be aligned
string imFilename("dst.jpg");
cout << "Reading image to align : " << imFilename << endl;
Mat im = imread(imFilename);
// Registered image will be resotred in imReg.
// The estimated homography will be stored in h.
Mat imReg, h;
// Align images
cout << "Aligning images ..." << endl;
alignImages(im, imReference, imReg, h);
// Write aligned image to disk.
string outFilename("aligned.jpg"
cout << "Saving aligned image : " << outFilename << endl;
imwrite(outFilename, imReg);
// Print estimated homography
cout << "Estimated homography : \n" << h << endl;
}
OpenCV内对特征点提取,描述子生成,特征匹配,计算单应矩阵,根据单应矩阵转换图像等都有对应函数,我们可以很方便的看算法效果。唯一需要注意的是头文件“xfeatures2d.hpp”和“features2d.hpp”并不在官方发行的opencv版本中包含,需要另外编译一个OpenCV_contrib库,该库包含一些匹配,深度神经网络,人脸识别等内容。OpenCV_contrib库的编译网上有许多详细的帖子介绍,在此给一个推荐https://blog.csdn.net/u012905422/article/details/74926256