基于SIFT和SURF特征的图像配准(MATLAB)

一、配准流程总览

复制代码
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));
相关推荐
爱喝水的鱼丶1 小时前
SAP-ABAP:SAP 简单报表输出开发系列(共6篇)第三篇:SAP ALV 报表样式定制:字段布局与交互功能配置
服务器·开发语言·学习·交互·sap·abap
摇滚侠1 小时前
JDBC 基础到高级一套通关!基础篇 00-15
java·开发语言·数据库
foundbug9991 小时前
最优化问题的外点罚函数法与内点罚函数法-MATLAB实现
开发语言·matlab
弹简特1 小时前
【零基础学Python】05-Python函数完全指南:从初阶定义到进阶参数,一篇打通核心难点
开发语言·python
AugustRed1 小时前
A2UI 完整学习指南(含 Java 后端 + 前端实战示例)
java·开发语言·前端
jghhh012 小时前
MATLAB蒙特卡罗方法求解伊辛模型
数据库·人工智能·matlab
jingling5552 小时前
自建技术博客实战(三):工具专栏——地图定位、声音复刻与 rembg 抠图
android·开发语言·前端·ai·nextjs
basketball6162 小时前
C++进阶:1. 引用折叠规则
java·开发语言·c++
Deep-w2 小时前
【MATLAB】基于模型预测控制的自适应巡航车辆过渡工况安全控制研究
开发语言·人工智能·算法·机器学习·matlab