前言
图像拼接技术,将普通图像或视频图像进行无缝拼接,得到超宽视角甚至360度的全景图,这样就可以用普通数码相机实现场面宏大的景物拍摄。利用计算机进行匹配,将多幅具有重叠关系的图像拼合成为一幅具有更大视野范围的图像,这就是图像拼接的目的。
一、技术路线

图1 基于SIFT图像拼接技术路线图
二、实现过程
2.1 SIFT特征点提取
Sift算法的三大工序为:(1)提取关键点;(2)对关键点附加详细的信息(局部特征)也就是所谓的描述器;(3)通过两方特征点(附带上特征向量的关键点)的两两比较找出相互匹配的若干对特征点,也就建立了景物间的对应关系。即可以归结为在不同尺度空间上查找特征点(关键点)的问题。

图2 SIFT算法流程
提取关键点和对关键点附加详细的信息(局部特征)也就是所谓的描述器可以称做是Sift特征的生成,即从多幅图像中提取对尺度缩放、旋转、亮度变化无关的特征向量,Sift特征的生成一般包括以下几个步骤:
构建尺度空间,检测极值点,获得尺度不变性;
特征点过滤并进行精确定位;
为特征点分配方向值;
生成特征描述子。
以特征点为中心取16*16的邻域作为采样窗口,将采样点与特征点的相对方向通过高斯加权后归入包含8个bin的方向直方图,最后获得4*4*8的128维特征描述子。
当两幅图像的Sift特征向量生成以后,下一步就可以采用关键点特征向量的欧式距离来作为两幅图像中关键点的相似性判定度量。取图1的某个关键点,通过遍历找到图像2中的距离最近的两个关键点。在这两个关键点中,如果次近距离除以最近距离小于某个阙值,则判定为一对匹配点。
SIFT算法实现分为两个部分,第一个函数sift(img)函数输入1张图像,返回图像特征点;第二个函数siftMatch(img1,img2)输入两张图像,进行两张图像的匹配。
(1)函数1:返回图像的SIFT特征点
function [descriptors, locs] = sift(img) % 本函数返回图像的SIFT特征点 function [descriptors, locs] = sift(img) %输入彩色影像,转换为灰度图. img = rgb2gray(img); [rows, cols] = size(img); f = fopen('tmp.pgm', 'w'); if f == -1 error('Could not create file tmp.pgm.'); end fprintf(f, 'P5\n%d\n%d\n255\n', cols, rows); fwrite(f, img', 'uint8'); fclose(f); ifisunix command = '!./sift '; else command = '!siftWin32 '; end command = [command ' tmp.key']; eval(command); g = fopen('tmp.key', 'r'); if g == -1 error('Could not open file tmp.key.'); end [header, count] = fscanf(g, '%d %d', [1 2]); if count ~= 2 error('Invalid keypoint file beginning.'); end num = header(1); len = header(2); iflen ~= 128 error('Keypoint descriptor length invalid (should be 128).'); end % 输出矩阵 locs = double(zeros(num, 4)); descriptors = double(zeros(num, 128)); fori = 1:num [vector, count] = fscanf(g, '%f %f %f %f', [1 4]); %row col scale ori if count ~= 4 error('Invalid keypoint file format'); end locs(i, :) = vector(1, :); [descrip, count] = fscanf(g, '%d', [1 len]); if (count ~= 128) error('Invalid keypoint file value.'); end descrip = descrip / sqrt(sum(descrip.^2)); descriptors(i, :) = descrip(1, :); end fclose(g); delete('tmp.pgm');
(2)函数2:读入两幅图像,寻找它们的SIFT特征,显示相互匹配的特征点的连线
function [matchLoc1 matchLoc2] = siftMatch(img1, img2) % 本函数读入两幅图像,寻找它们的SIFT特征,显示相互匹配的特征点的连线 % 返回两幅图像相匹配的特征点 function [matchLoc1 matchLoc2] = siftMatch(img1, img2) % 寻找两幅图的特征点 [des1, loc1] = sift(img1); [des2, loc2] = sift(img2); distRatio = 0.6; %为第一幅的特征点寻找匹配的第二幅图中的特征点 des2t = des2'; matchTable = zeros(1,size(des1,1)); fori = 1 : size(des1,1) dotprods = des1(i,:) * des2t; [vals,indx] = sort(acos(dotprods)); if (vals(1) 0) line([loc1(i,2) loc2(matchTable(i),2)+cols1], ... [loc1(i,1) loc2(matchTable(i),1)], 'Color', 'c'); end end holdoff; num = sum(matchTable> 0); fprintf('Found %d matches.\n', num); idx1 = find(matchTable); idx2 = matchTable(idx1); x1 = loc1(idx1,2); x2 = loc2(idx2,2); y1 = loc1(idx1,1); y2 = loc2(idx2,1); matchLoc1 = [x1,y1]; matchLoc2 = [x2,y2]; end
2.2 RANSAC算法进行图像配准
在进行两幅或多幅图像间的镶嵌及配准时,通常情况下都会有一个参考图像或者基准图像作为处理的标准。其中就需要建立原始图像到参考图像的变换模型,而模型参数的稳健估计就成了关键技术之一。在实际问题中,得到的数据往往并不是完全准确的,常常伴随着许多异常数据,于是问题的关键在于如何处理不符合实际模型的异常数据。
一个简单的例子是从一组观测数据中找出合适的2维直线。假设观测数据中包含局内点和局外点,其中局内点近似的被直线所通过,而局外点远离于直线。简单的最小二乘法不能找到适应于局内点的直线,原因是最小二乘法尽量去适应包括局外点在内的所有点。相反,RANSAC能得出一个仅仅用局内点计算出模型,并且概率还足够高。但是,RANSAC并不能保证结果一定正确,为了保证算法有足够高的合理概率,我们必须小心的选择算法的参数。
图3 左图:包含很多局外点的数据集;右图:RANSAC找到的直线(局外点并不影响结果)
RANSAC只能从特定的数据集中估计出一个模型,如果存在两个(或多个)模型,RANSAC不能找到别的模型。
% RANSAC 实现函数 [H corrPtIdx] = ransac1(pts1,pts2,coef,@solveHomo,@calcDist); …… % ransac1函数返回单应矩阵H和满足距离阈值的点的下标 function [f inlierIdx] = ransac1( x,y,ransacCoef,funcFindF,funcDist ) minPtNum = ransacCoef.minPtNum; %用于拟合模型所需最少的样本点个数 iterNum = ransacCoef.iterNum; %迭代·次数 thInlrRatio = ransacCoef.thInlrRatio; %局内点数量阈值 thDist = ransacCoef.thDist; % 局内点到模型线之间距离阈值 ptNum = size(x,2); %特征点矩阵的列数 thInlr = round(thInlrRatio*ptNum);%局内点数量阈值,小于该值的模型舍弃 inlrNum = zeros(1,iterNum);%存放每次构建模型后,满足距离阈值的点个数 fLib = cell(1,iterNum);%存放所有构建的单应模型 %迭代计算 for p = 1:iterNum % 1.fit using random points sampleIdx = randIndex(ptNum,minPtNum);%从162个特征点中选择四个,用于构建单应矩阵 f1 = funcFindF(x(:,sampleIdx),y(:,sampleIdx));%构建单应矩阵,即某种模型 % 2.count the inliers, if more than thInlr, refit; else iterate dist = funcDist(f1,x,y);%计算所有特征点到模型的距离 inlier1 = find(dist