一、配准流程总览
I1 ──→ 检测关键点(K) + 提取描述子(D) ──┐
I2 ──→ 检测关键点(K') + 提取描述子(D') ─┤
↓
最近邻匹配 (D ↔ D') + 双向比率检验
↓
RANSAC 剔除误匹配 → 估计单应矩阵 H
↓
warpAffine/warpPerspective(I2→I1) → 融合显示
二、直接用MATLAB内置SURF/SIFT
2.1 主程序:image_registration_surf_sift.m
matlab
%% ========== 基于 SURF/SIFT 的图像配准 ==========
clear; clc; close all;
%% 1. 读图(把路径换成你自己的)
I1 = imread('image1.jpg'); % 参考图(固定)
I2 = imread('image2.jpg'); % 待配准图(moving)
% 灰度化
if ndims(I1)==3, I1g = rgb2gray(I1); else I1g = I1; end
if ndims(I2)==3, I2g = rgb2gray(I2); else I2g = I2; end
I1g = im2single(I1g);
I2g = im2single(I2g);
fprintf('=== 基于特征的图像配准 ===\n');
%% 2. 选择特征:'SURF' 或 'SIFT'
feature_type = 'SURF'; % ← 切换为 'SIFT' 也行(R2021b+ 支持)
num_best = 200; % 取最强的关键点数目(降计算量)
switch lower(feature_type)
case 'surf'
pts1 = detectSURFFeatures(I1g, 'MetricThreshold', 200);
pts2 = detectSURFFeatures(I2g, 'MetricThreshold', 200);
[feat1, validPts1] = extractFeatures(I1g, pts1, 'FeatureSize', 64);
[feat2, validPts2] = extractFeatures(I2g, pts2, 'FeatureSize', 64);
pts1 = pts1(validPts1); pts2 = pts2(validPts2);
case 'sift'
% R2021b+ 内置SIFT
pts1 = detectSIFTFeatures(I1g, 'MetricThreshold', 0.004);
pts2 = detectSIFTFeatures(I2g, 'MetricThreshold', 0.004);
[feat1, validPts1] = extractFeatures(I1g, pts1);
[feat2, validPts2] = extractFeatures(I2g, pts2);
pts1 = pts1(validPts1); pts2 = pts2(validPts2);
end
fprintf('关键点: I1=%d, I2=%d\n', length(pts1), length(pts2));
%% 3. 匹配(最近邻 + ratio test Lowe 0.8)
indexPairs = matchFeatures(feat1, feat2, ...
'MatchThresh', 1.0, ... % 先用阈值1不过滤,后面做ratio test
'MaxRatio', 0.8, ... % Lowe ratio test
'Unique', true);
matchedPts1 = pts1(indexPairs(:,1));
matchedPts2 = pts2(indexPairs(:,2));
fprintf('初匹配对数: %d\n', length(matchedPts1));
%% 4. RANSAC 几何校验 → 估计单应矩阵 H(投影变换)
% 若你确定是纯平面旋转+平移,可用 affine2d;一般多视角用 projective2d
if length(matchedPts1) < 4
error('匹配点不足4对,无法估计变换!');
end
[tform, inlierIdx] = estimateGeometricTransform2D( ...
matchedPts2, matchedPts1, ...
'projective', ...
'MaxDistance', 4.0, ... % 内点阈值(像素)
'Confidence', 99, ...
'MaxNumTrials', 2000);
inlierPts2 = matchedPts2(inlierIdx);
inlierPts1 = matchedPts1(inlierIdx);
fprintf('RANSAC内点对数: %d / %d\n', sum(inlierIdx), length(matchedPts1));
%% 5. 配准翘曲(把 I2 对齐到 I1 坐标系)
Rout = imref2d(size(I1g));
I2_warped = imwarp(I2, tform, 'OutputView', Rout, 'Interp', 'bilinear', 'FillValues', 0);
%% 6. 显示结果
draw_results(I1, I2, I1g, I2g, matchedPts1, matchedPts2, inlierIdx, ...
tform, I2_warped, feature_type);
%% 7. 配准误差(可选:已知角点可算RMSE)
if exist('corner1_gt','var') && exist('corner2_gt','var')
corner2_warped = tform.transformPointsForward(corner2_gt);
rmse = sqrt(mean(sum((corner1_gt - corner2_warped).^2,2)));
fprintf('角点RMSE = %.2f px\n', rmse);
end
2.2 显示函数 draw_results.m
matlab
function draw_results(I1, I2, I1g, I2g, mp1, mp2, inlierIdx, tform, I2w, ftype)
figure('Position',[100 100 1400 900]);
% 1) 原图
subplot(2,3,1); imshow(I1); title('参考图 I1'); grid on;
subplot(2,3,2); imshow(I2); title('待配准图 I2'); grid on;
% 2) 初匹配(全匹配连线)
subplot(2,3,3);
showMatchedFeatures(I1g, I2g, mp1, mp2, 'PlotOptions', ...
{'ro','ys','--'}, 'Montage', false);
title(sprintf('%s 初匹配 (%d对)', ftype, length(mp1)));
% 3) RANSAC内点/外点
subplot(2,3,4);
showMatchedFeatures(I1g, I2g, mp1(inlierIdx), mp2(inlierIdx), ...
'PlotOptions',{'ro','ys','g-'}, 'Montage', false); hold on;
outlier = ~inlierIdx;
plot(mp2(outlier).Location(1,:), mp2(outlier).Location(2,:), ...
'rx', 'MarkerSize',8, 'HandleVisibility','off');
title(sprintf('RANSAC内点(%d) / 外点剔除数', sum(inlierIdx)));
% 4) 叠置对比(棋盘格)
subplot(2,3,5);
I1g8 = im2uint8(I1g);
I2w8 = im2uint8(im2double(I2w));
checker = uint8(zeros(size(I1g8,1),size(I1g8,2),3));
half = size(checker,2)/2;
checker(:,:,1) = I1g8;
checker(:,round(half)+1:end,:) = repmat(I2w8(:,round(half)+1:end,:),[1 1 1]);
imshow(checker); title('棋盘格拼合 I1 | I2_warped');
% 5) 差异图
subplot(2,3,6);
diff_img = abs(double(I1g) - double(im2gray(I2w)));
imshow(diff_img, []);
colormap hot; colorbar;
title('差异图 (越小配准越好)');
end
参考代码 基于sift和surf特征的图像配准 www.youwenfan.com/contentcsv/79416.html
三、SIFT核心骨架
3.1 SIFT四步走骨架
matlab
%% ========== 手写SIFT骨架(原理级)==========
% 注:这里是"可运行流程",描述子用简化方向直方图版(不是完整128维)
% 完整128维只需把方向bin×4×4展开即可
I = im2double(rgb2gray(imread('image1.jpg')));
I = imgaussfilt(I,0.8); % 预平滑
%% Step 1. 尺度空间(高斯金字塔)
octaves = 4; scales = 5;
k = 2^(1/(scales-3));
sigma0 = 1.6;
gauss_pyramid = cell(octaves, scales);
for o = 0:octaves-1
sigma = sigma0 * 2^o;
Img = imresize(I, 1/2^o, 'bilinear');
for s = 1:scales
gauss_pyramid{o+1,s} = imgaussfilt(Img, sigma);
sigma = sigma * k;
end
end
%% Step 2. DoG(差分)
dog = cell(octaves, scales-1);
for o = 1:octaves
for s = 1:scales-1
dog{o,s} = gauss_pyramid{o,s+1} - gauss_pyramid{o,s};
end
end
%% Step 3. 极值点检测(3×3×3邻域)
keypoints = [];
for o = 1:octaves
[h,w] = size(dog{o,1});
for s = 2:scales-2
D = dog{o,s};
for y = 2:h-1
for x = 2:w-1
val = D(y,x);
if abs(val) < 0.015, continue; end % 对比度阈值
cube = [dog{o,s-1}(y-1:y+1,x-1:x+1), ...
D(y-1:y+1,x-1:x+1), ...
dog{o,s+1}(y-1:y+1,x-1:x+1)];
if val==max(cube(:)) || val==min(cube(:))
keypoints(end+1,:) = [x*2^(o-1), y*2^(o-1), o, s, val];
end
end
end
end
end
fprintf('候选极值点: %d\n', size(keypoints,1));
%% Step 4. 方向分配 + 简化描述子(8方向直方图)
descriptors = [];
kp_out = [];
angle_bins = 8;
for kp_idx = 1:size(keypoints,1)
kx = round(keypoints(kp_idx,1));
ky = round(keypoints(kp_idx,2));
o = keypoints(kp_idx,3);
Iref = imresize(I, 1/2^(o-1), 'bilinear');
[Gy,Gx] = imgradientxy(Iref,'sobel');
mag = hypot(Gx,Gy);
ang = atan2(Gy,Gx);
% 选邻域
win = 8;
x1=max(1,kx-win); x2=min(size(Iref,2),kx+win);
y1=max(1,ky-win); y2=min(size(Iref,1),ky+win);
local_ang = ang(y1:y2,x1:x2);
% 方向直方图(找主方向)
hist_ang = histcounts(local_ang, angle_bins*2, [-pi pi]);
[~, main_bin] = max(hist_ang);
theta0 = -pi + (main_bin-0.5)*(2*pi/(angle_bins*2));
% 简化描述子:8维方向直方图(可扩为4×4×8=128)
desc = histcounts(mod(local_ang-theta0,2*pi), angle_bins*[0:angle_bins]/(angle_bins));
desc = desc / (norm(desc)+eps);
descriptors = [descriptors; desc];
kp_out = [kp_out; kx ky theta0];
end
fprintf('手写SIFT描述子: %d×%d\n', size(descriptors,1), size(descriptors,2));