The 来自 m3tho 的回答 https://stackoverflow.com/a/47275099/52738正确地解决你会如何apply您想要的转换:使用fitgeotrans https://www.mathworks.com/help/images/ref/fitgeotrans.html with a 'projective'转换 https://www.mathworks.com/help/images/ref/fitgeotrans.html#bvonaug,因此要求您指定 4 个控制点(即输入和输出图像中的 4 对对应点)。然后您可以使用以下命令应用此转换imwarp https://www.mathworks.com/help/images/ref/imwarp.html.
那么问题来了how您选择这些点对来创建所需的变换,在本例中是创建一个透视投影 https://www.mathworks.com/help/matlab/visualize/understanding-view-projections.html。如下所示,透视投影考虑到观看位置(即“相机”)将具有定义圆锥形视场的给定视角。通过获取该圆锥体内的所有 3D 点并将它们投影到观看平面上来渲染场景,该平面是位于摄像机目标处的平面,垂直于摄像机与其目标的连线。
首先,我们假设您的图像位于观察平面中,并且角点由标准化参考系描述,使得它们跨越[-1 1]
在每个方向。我们需要首先通过选择视角来选择我们想要的透视度,然后计算相机和观察平面之间的距离。 45度左右的视角可以模拟正常人眼的透视感,因此利用视平面的角点来定义圆锥视场的边缘,我们可以计算出相机距离如下:
camDist = sqrt(2)./tand(viewAngle./2);
现在我们可以使用它来生成一组用于转换的控制点。我们首先应用一个3-D 旋转 https://en.wikipedia.org/wiki/Rotation_matrix#In_three_dimensions到观察平面的角点,绕 y 轴旋转一定量theta
。这会将它们旋转出平面,因此我们现在通过定义一条从相机到每个旋转角点的线将角点投影回观察平面上,找到与平面相交的点 https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection。我将省去您的数学推导(您可以根据上述链接中的公式自行实现它们),但在这种情况下,一切都简化为以下一组计算:
term1 = camDist.*cosd(theta);
term2 = camDist-sind(theta);
term3 = camDist+sind(theta);
outP = [-term1./term2 camDist./term2; ...
term1./term3 camDist./term3; ...
term1./term3 -camDist./term3; ...
-term1./term2 -camDist./term2];
And outP
现在包含输出图像中标准化的控制点集。给定尺寸的图像s
,我们可以创建一组输入和输出控制点,如下所示:
scaledInP = [1 s(1); s(2) s(1); s(2) 1; 1 1];
scaledOutP = bsxfun(@times, outP+1, s([2 1])-1)./2+1;
您可以像这样应用转换:
tform = fitgeotrans(scaledInP, scaledOutP, 'projective');
outputView = imref2d(s);
newImage = imwarp(oldImage, tform, 'OutputView', outputView);
您可能遇到的唯一问题是旋转 90 度(即从图像平面的一端看)会创建一组共线点,这会导致fitgeotrans
出错。在这种情况下,从技术上讲,您只需要一个空白图像,因为从侧面查看二维对象时您看不到它。
下面是一些通过动画旋转图像来说明上述转换的代码:
img = imread('peppers.png');
s = size(img);
outputView = imref2d(s);
scaledInP = [1 s(1); s(2) s(1); s(2) 1; 1 1];
viewAngle = 45;
camDist = sqrt(2)./tand(viewAngle./2);
for theta = linspace(0, 360, 360)
term1 = camDist.*cosd(theta);
term2 = camDist-sind(theta);
term3 = camDist+sind(theta);
outP = [-term1./term2 camDist./term2; ...
term1./term3 camDist./term3; ...
term1./term3 -camDist./term3; ...
-term1./term2 -camDist./term2];
scaledOutP = bsxfun(@times, outP+1, s([2 1])-1)./2+1;
tform = fitgeotrans(scaledInP, scaledOutP, 'projective');
spinImage = imwarp(img, tform, 'OutputView', outputView);
if (theta == 0)
hImage = image(spinImage);
set(gca, 'Visible', 'off');
else
set(hImage, 'CData', spinImage);
end
drawnow;
end
这是动画: