图像透视变换原理及实现

上篇博客讲解了图像的仿射变换原理及实现,这篇博客讲讲透视变换的原理和实现,透视变换也叫投影变换,仿射变换是透视变换的特例。主要是透视变换能保持“直线性”,即原图像里面的直线,经透视变换后仍为直线。下面给出数学推导:

透视变换矩阵变换公式为:

其中透视变换矩阵:

               

要移动的点,即源目标点为:

                          

另外定点,即移动到的目标点为:

                       

这是一个从二维空间变换到三维空间的转换,因为图像在二维平面,故除以Z,  (X';Y';Z')表示图像上的点:

,展开上面公式,得到一个点的情况:

                                    

4个点可以得到8个方程,即可解出A。

                            

下面代码是上面公式的具体实现,getPerspective()是根据4组点对获取的透视变换矩阵。

function perspective_mat = getPerspective(moving_points,fixed_points)
% GETPERSPECTIVE 根据点获取透视变换矩阵
% 输入:
%     moving_points:n*2点集坐标(x,y)
%     fixed_points:n*2点集坐标(x,y),点顺序要对应moving_points
% 输出:
%     perspective_mat:3*3透视变换矩阵
% 
%  perspective_mat矩阵形式为[a11,a12,a13; a21,a22,a23; a31,a32,1];
%   满足fixed_points = perspective_mat*moving_points
% author: cuixing158@foxmail.com
%

assert(size(moving_points,1) == size(fixed_points,1)&& ...
size(moving_points,1)>=4);

nums = size(moving_points,1);
coefficient_mat = zeros(2*nums,8);
b = zeros(2*nums,1);
for i = 1:nums
    currentPoint = moving_points(i,:);
    currentFixedPoint = fixed_points(i,:);
    coefficient_mat(2*i-1,:) = [currentPoint(1),currentPoint(2),1,...
        0,0,0,...
        -currentPoint(1)*currentFixedPoint(1),-currentFixedPoint(1)*currentPoint(2)];
    b(2*i-1) = currentFixedPoint(1);
    
    coefficient_mat(2*i,:) = [0,0,0,...
        currentPoint(1),currentPoint(2),1,...
        -currentPoint(1)*currentFixedPoint(2),-currentPoint(2)*currentFixedPoint(2)];
    b(2*i) = currentFixedPoint(2);
end
perspective_mat = coefficient_mat\b; % 大于4个点时候为最小二乘法计算
perspective_mat = reshape([perspective_mat;1],3,3)';

上面函数是通过4组点(大于等于)获取透视变换矩阵。下面给出测试代码,分别用手写的和系统的函数,对比时间加以说明。

%% prepare
img = rgb2gray(imread('book.jpg'));
width = size(img,2);
height = size(img,1);
figure;imshow(img)
% moving_points = [0,0;
%                 100,50;
%                 0,50;
%                 50,100];
moving_points = ginput(4);
hold on ; plot(moving_points(:,1),moving_points(:,2),'ro');
fixed_points = [0,0;
    100,0;
    0,200;
    100,200];

%% method 1,use matlab function
tfom = fitgeotrans(moving_points,fixed_points,'projective');
X = moving_points(:,1);
Y = moving_points(:,2);
[x,y] = transformPointsForward(tfom,X(:),Y(:));
figure;plot(x,y,'ro');title('验证坐标点对齐')
grid on
tic;
dst_img = imwarp(img,tfom);
t_sys = toc;
figure;imshow(dst_img);title(['图像仿射变换后(系统函数),耗时(s):',num2str(t_sys)])

%% method 2, get perspective matrix
perspective_mat = getPerspective(moving_points,fixed_points);
A = perspective_mat;
X = [1;width;1;width]; % 原图片的四个角点x坐标
Y = [1;1;height;height]; % 原图片的四个角点y坐标
moving_points_mat = [X(:)';Y(:)';ones(1,size(X(:),1))];
dst_points = A*moving_points_mat;
for i = 1:size(dst_points,2)
    dst_points(1:2,i) = dst_points(1:2,i)/dst_points(3,i);
end
figure;plot(dst_points(1,:),dst_points(2,:),'bo');title('原图像仿射变换后的坐标范围')
grid on;

%% 仿射变换后图像逐像素进行插值赋值
min_x = min(dst_points(1,:));
max_x = max(dst_points(1,:));
min_y = min(dst_points(2,:));
max_y = max(dst_points(2,:));
W = round(max_x - min_x);
H = round(max_y -min_y);
wrapImg = zeros(H,W);
tic;
for i = 1:H
    for j = 1:W
        x = min_x+j; % 使得x,y的范围在原坐标范围内
        y = min_y+i;
        moving_point = A\[x;y;1];
        temp_point = [moving_point(1);moving_point(2)]./moving_point(3);
        if temp_point(1)>=1&&temp_point(1)<width&& ...
                temp_point(2)>=1&&temp_point(2)<height
            wrapImg(i,j) = img(round(temp_point(2)),round(temp_point(1)));
        end
    end
end
t_manual = toc;
figure;
imshow(uint8(wrapImg));title(['图像仿射变换后(手写函数),耗时(s):',num2str(t_manual)])




红色圆圈为鼠标点选的点,顺序为书籍左上点、右上点、左下点、右下点。图中对比可以看出,效果是一样的,不过系统的函数效率高,耗时较少。

2022.12.9 开源地址(10行代码实现透视变换贴广告图):GitHub - cuixingxing150/perspective-homography-matlab

### 图像透视变换原理 图像透视变换是一种几何变换,它模拟了三维空间到二维平面的投影效果。这种变换不仅能够处理平移、旋转和缩放操作,还能够纠正由于拍摄角度引起的变形现象[^3]。 具体来说,透视变换通过定义源图像上的四个角点与目标图像上对应的四个角点之间的映射关系来完成。这八个点构成了一组对应关系,利用这些对应关系可以通过解线性方程组得到一个\(3 \times 3\) 的单应性矩阵 \(H\) 。该矩阵用于描述从原始坐标系到新坐标系下的转换过程: \[ H = \begin{bmatrix} h_{11}&h_{12}&h_{13}\\ h_{21}&h_{22}&h_{23}\\ h_{31}&h_{32}&1\end{bmatrix}\] 对于任意给定的一对匹配点 (x,y) 和其对应的变换后的点 (\(x'\),\(y'\)) ,满足如下关系: \[ \left[\begin{array}{c} x' \\ y'\\ w' \end{array}\right]=H*\left[\begin{array}{l} x \\ y\\ 1 \end{array}\right]\] 其中 w 是齐次坐标的尺度因子,在实际应用中通常被标准化为1。因此最终计算出来的像素位置应该是(x'/w', y'/w')。 ### MATLAB代码实现 下面是一个简单的MATLAB脚本例子,展示了如何读取一张图片并对其进行透视变换: ```matlab % 加载测试图像 img = imread('example.jpg'); % 替换为你自己的文件路径 grayImg = rgb2gray(img); % 将彩色图像转成灰度图以便简化处理 figure; imshow(grayImg); title('Original Image'); % 定义源图像和平面上的目标区域四边形顶点 srcPoints = [180, 70; 450, 150; 440, 400; 190, 400]; dstPoints = [180, 150; 450, 150; 450, 400; 180, 400]; % 计算透视变换矩阵 tform = fitgeotrans(srcPoints,dstPoints,'projective'); % 应用变换 outputImage = imwarp(grayImg,tform); % 显示结果 figure; imshow(outputImage); title('Transformed Image'); ``` 这段程序首先加载了一个示例图像,并将其转化为灰度模式以减少颜色通道的影响;接着指定了源图像中的四个特征点以及它们应该映射到的目的地矩形框内的相应位置;最后调用了`fitgeotrans()` 函数来估计所需的变换参数,并使用 `imwarp()` 对输入图像进行了扭曲调整[^4]。
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值