我 imrotate() 一个图像,绘制两条线,旋转回线并在原始图像中绘制它们,但在 MATLAB 中没有得到预期的结果?

I imrotate() an image, draw two lines, rotate back the lines and draw them in orginal image, but don't get the expected result in MATLAB?

我想做什么:

假设我有一个图像 I,我使用 imrotate 旋转 -45°(我得到I_R)。然后我画两条线 ABCD (平行线)。最后,我将这两条线向后旋转(45°)并绘制在原图中I.

我是怎么做到的##

我使用 MATLAB 函数 imrotate() 旋转 I

I_R = imrotate(I,-45);

从 Matlab 的帮助中,我得到:B = imrotate(A,angle) 围绕其中心点沿逆时针方向将图像 A 旋转 angle 度数。

But it seems that imrotate add a translation to the image ! I have read the code of built-in matlab function, it seems that it uses a function called getOutputBound to check if the rotated image will fit in the figure. This translation is what I am looking for !!

四个点A,B,C,D组成两条平行线AB & CD

A = [x_A; u];
B = [x_B; u];

C = [x_A; d];
D = [x_B; d];

所以现在,我旋转两条线,我使用我的函数 rotateTwoPoints(),只需调用以下两行:

[Af,Bf] = rotateTwoPoints(A,B,-45,O,true);
[Cf,Df] = rotateTwoPoints(C,D,-45,O,true);

其中 Oorigin 旋转的位置。

但是当我像这样在图像 I 中绘制结果线 AfBf & CfDf 时:

plot([Af(1) Bf(1)],[Af(2) Bf(2)],'k');
plot([Cf(1) Df(1)],[Cf(2) Df(2)],'k'); 

我得到的结果不正确!

问题:在I_R中,AB & CD 包含我所说的 BlueZone(见图 3)。但是旋转的背线 AfBf & CfDf 没有覆盖它!

图像中的结果

这里是旋转后的图片I_R和绘制的两条线(中间两条红线分别对应ABCD) :

然后我在原始图像中绘制旋转线 AfBf & CfDf I(黑色粗体点对应于我完成的中心旋转):

图像已更新

问题:如您所见,BlueZone 位于 ABCD 两条线内。但是旋转回来就变成外面了,如下图(红色箭头指向BlueZone):


更新添加了一个片段

由于我的问题尚未解决,我选择了导致问题的代码并将其添加为以下片段(文件中存储了一个变量,您可以下载 here):

function Question()

% load image in I, the image is available online in the below link  
load I ;

% rotate I with -45° using imrotate
I_R = imrotate(I,-45);

% some data
x_A = 3 ;
x_B = 79;

u = 24;
d = 44;

% some meaningful Points : A,B,C and D that form two lines AB and CD
% parallels
A = [x_A; u];
B = [x_B; u];

C = [x_A; d];
D = [x_B; d];

% figure 1 contain two subplots 
figure(1);
% draw rotated image I_R
subplot(1,2,1), axis image, imagesc(I_R), hold on;
% draw two lines AB and CD in red in rotated image 
plot([A(1) B(1)],[A(2) B(2)],'r');
plot([C(1) D(1)],[C(2) D(2)],'r');
title('I_R the rotated image with the two lines AB and CD');

% draw original image I
subplot(1,2,2), axis image, imagesc(I)  , hold on;

% compute the middle of image I
axises=axis;
center = [mean(axises(1:2)),mean(axises(3:4))]';
% draw the center in red and as a point
plot(center(1),center(2),'ro');

% rotate the two lines, the result is the two lines AfBf and CfDf
[Af,Bf] = rotateTwoPoints(A,B,-45,center,true);
[Cf,Df] = rotateTwoPoints(C,D,-45,center,true);

% draw the rotated back lines in original image I
figure(1);
subplot(1,2,2);
plot([Af(1) Bf(1)],[Af(2) Bf(2)],'k');
plot([Cf(1) Df(1)],[Cf(2) Df(2)],'k');
title('the original image I with the two lines AfBf and CfDf');

function [Af,Bf] = rotateTwoPoints (A,B,t,Origin,isPlot)

% Definition of the rotation matrix (rotation around origin)
R=[ ...
    cosd(t) -sind(t)
    sind(t) cosd(t)
    ];

% translation 
At = A - Origin;
Bt = B - Origin;

% rotation of the points A and B
Ar = R*At;
Br = R*Bt;

% translation 
Af = Ar + Origin;
Bf = Br + Origin;

if isPlot == true

    figure(100)

    % Plot of the original line
    plot(A(1),A(2),'k*', B(1),B(2),'b*');
    line([A(1) B(1)],[A(2) B(2)], 'Color','r');

    grid on
    hold on

    % Plot the Origin around which the rotation will be
    plot(Origin(1),Origin(2),'k*','LineWidth',3);

    % Plot of the rotated line
    plot(Af(1),Af(2),'g*', Bf(1),Bf(2),'r*');
    line([Af(1) Bf(1)],[Af(2) Bf(2)], 'Color','b');
    legend('A','B','line AB','Origin','Af','Bf','line AfBf',['angle: ',num2str(t)],'Location','northeastoutside');
    daspect([1 1 1])

end

PS: 我正在使用 MATLAB R2012b

可能无法修复它,但请尝试将矩阵定义为:

R=[ ...
    cosd(t) -sind(t)
    -sind(t) -cosd(t)
    ];

在图像上绘图时,您需要注意 y 轴,它会自动从上到下。

如您所说,旋转计算正确,如第一个图所示。那么问题就在于结果的最终显示。当你这样做时

plot([Af(1) Bf(1)],[Af(2) Bf(2)],'k');
plot([Cf(1) Df(1)],[Cf(2) Bf(2)],'k'); 

你在第二行(第二个参数的第二个元素)中有错字 - 你将 Bf(2) 绘制为第二行的结尾而不是 Df(2)。当我用 Df(2) 替换它时,它按预期绘制了平行线。

更新:

在评论中,我建议重构此代码以对所有内容使用变换矩阵,以便可以将同一组变换应用于图像和叠加层。以下是您如何进行设置的一般概述。

有两个要点需要注意。

  1. Matlab 对图像的约定是 y=0 是顶部。 imshow 和相关的然后将 y=0 放在顶部,y 增加。这与 plot 的约定相反,因此当您叠加绘图和图像时,其中之一必须反转。这对变换矩阵的使用有影响,主要是旋转角度必须反转。

  2. matlab 中的图像可以被认为是规则像素网格的 z 数据。然而,这些像素坐标不会被存储,它们只是在您显示图像时推断出来的。因此,当您翻译图像然后再次 imshow 时,翻译并不明显,因为它重新推断了一组新的像素坐标,这些像素坐标恰好与未转换图像的像素坐标相同。为了解决这个问题,我们必须修复图像的空间参考。

除此之外,2d 变换是使用 3space 矩阵执行的,因此我们需要在 3space 中定义输出点(但然后截断它们以在 2d 图中显示)。

所以让我们把它们放在一起。

% get an image
I = imread('cameraman.tif');
% get spatial referencing information
Rcb = imref2d(size(I));

% define some test points in 3space
pta = [10; 10; 0];
ptb = [ 50;  10; 0];
% construct a test line in 2space from our test points
testline = [pta(1:2) ptb(1:2)];

% overlay line on plot
figure(90); clf
imshow(I,Rcb); truesize; hold on;
plot(testline(1, :), testline(2, :), 'y', 'linewidth', 4)

% define our transforms
% rotation angle (deg)
t = 45;

% a transform suitable for images
Rimage=[ ...
    cosd(t) -sind(t) 0
    sind(t) cosd(t) 0
    0 0 1
    ];
% the same transform suitable for points
Rpoints=[ ...
    cosd(-t) -sind(-t) 0
    sind(-t) cosd(-t) 0
    0 0 1
    ];

% make tform object suitable for imwarp
tform = affine2d(Rimage);

% transform image and spatial referencing with tform
[Ir, Rr] = imwarp(I, tform);

% transform points directly using matrix multplication
ptar = Rpoints*pta;
ptbr = Rpoints*ptb;

% construct the rotated line for plotting
newline = [ptar(1:2) ptbr(1:2)];

% the results
figure(91); clf
imshow(Ir, Rr); truesize; 
hold on;
plot(testline(1, :), testline(2, :), 'y', 'linewidth', 4)
plot(newline(1, :), newline(2, :), 'g', 'linewidth', 4)

在第二个图中,我用绿色绘制了变形线,用黄色绘制了原始线,以供比较。

注意事项:

  1. imwarp 函数不直接使用旋转矩阵,您必须先从中构造一个 tform 对象,然后再提供它。您当然可以在一行中完成所有这些操作。

  2. 有两个矩阵有点不雅。在这里我们只需要担心一个旋转是可以的,但是使用变换矩阵的全部要点是您可以通过乘以矩阵将一系列变换链接在一起。如果你必须这样做两次,它会破坏一段原本优雅的代码,而那永远不会,所以最干净的方法可能是在整个过程开始时翻转你的图像,然后在如有必要,最后(例如出口)。

  3. 点数据的记账有点繁琐。有很多方法可以做到这一点,具体取决于您选择采用的关于将哪个坐标保存在列向量中的哪个位置的约定。我从来没有找到一个集可以很好地与 3space 变换和 plot 配合使用,并且效果最好的变化取决于应用程序。辅助函数可以为您省去一些麻烦,如果不是下一个维护者的话,将它全部包装在 class 中是最简洁的,如果您能证明时间合理的话。

更新 2:

为了尽量减少 3 空间坐标的使用,我会像往常一样简单地使用 2 空间向量来描述所有点,然后仅在需要执行变换时才添加虚拟 z 坐标。

所以你可能有

testpoint = [1; 5]; % x and y xoordinates only
trpoint = Rpoints*[testpoint; 1];
trpoint = trpoint(1:2);

您可以将它放在一个包装器中,但无法回避这样一个事实,即您需要为 imwarp 部分使用 3x3 矩阵,这意味着您也需要在 3space 中指定坐标。

或者,您可以截断坐标变换的旋转矩阵:

trpoint = Rpoints(1:2, 1:2)*testpoint;

但无论怎样,您都必须进行一些索引簿记。

更新3:

OP 无权访问 imref2d,所以这里有一个 hacky 方法来实现相同的结果。重要的是这一行

imshow(Ir, Rr);

其中 Rrimwarp 函数输出的空间引用对象。如果您没有这个,您可以使用 'XData''YData' 参数手动提供对 imshow 的空间引用。你需要先计算出范围,然后决定一个约定。 imref2d 使用 (0,0) 作为左上角的约定,当图像旋转时,原始图像的这个角保持坐标 (0,0),这样旋转后的图像现在从 y=[- 181:181] 和 x=[0:363]。要获得这些值,您需要变换图像的所有四个角,然后计算出最大值和最小值。

xmax = size(I,2);
ymax = size(I,2);
corner1 = [xmax; 0; 0]+0.5;
corner2 = [xmax; ymax; 0]+0.5;
corner3 = [0; ymax; 0]+0.5;
corner4 = [0; 0; 0]+0.5;
tc1 = Rpoints*corner1;
tc2 = Rpoints*corner2;
tc3 = Rpoints*corner3;
tc4 = Rpoints*corner4;
corners = [tc1 tc2 tc3 tc4];
xlims = minmax(corners(1,:));
ylims = minmax(corners(2,:));

然后你可以用这一行替换 imshow

imshow(Ir, 'xdata', xlims, 'ydata', ylims);

其他都应该是一样的。

请注意,当我执行所有这些操作时,我在上面获得的值与 imwarpimref2d 对象中生成的值之间存在细微差别。该函数返回

XWorldLimits: [0.2264 363.2264]
YWorldLimits: [-181.5000 181.5000]

而我的给出

xlims: [0.7071  362.7458];
ylims: [-181.0193  181.0193];

如果不查看 imwarp 的来源,我无法解释这一点,即使它不是那个级别的 MEX,我也不确定你能走多远才能成为被指控侵犯知识产权。

这些数字说明了图像在轴上的位置,因此如果您使用图像选择点,它们可能会偏离零点几像素。如果您只是使用图像作为参考,那么它应该足够接近了。

我可能理解错了,你想做的是把一张图转过来,画两条线,然后把包括两条线在内的所有东西都转回去,对吧?

如果是这样的话,我想转动这两条线的最简单方法是使用 car2pol 将它们转换为极坐标。 http://se.mathworks.com/help/matlab/ref/cart2pol.html

那转线应该很容易吧。如果我有任何误解,请告诉我。

编辑: 我试着做了一些示例代码,显然我太累了,无法直接思考并且有一些错误,但我想这个想法很清楚。另外请不要像我一样使用结构体,我只是认为这是命名变量的最简单方法。

%% Load picture
close all
[pic] = dicomread('C0011866_00094.DCM');

%% Get four points
    figure, imshow(pic,[]); 
    title('Select four points'); hold on; 
    [x,y] = ginput;
    
%% Plot the lines on top of the figure
hold on
plot(x(1:2),y(1:2),'b');
hold on
plot(x(3:4),y(3:4),'r');

%% Define the lines
%In cartesian coordinates
line1.start.cart = [x(1),y(1)];
line1.end.cart = [x(2),y(2)];

line2.start.cart = [x(3),y(3)];
line2.end.cart = [x(4),y(4)];

%Middle of picture
axises=axis;
center = [mean(axises(1:2)),mean(axises(3:4))];
plot(center(1),center(2),'ro')

%In polar coordinates from center [angle,radius]
[line1.start.pol(1),line1.start.pol(2)] = cart2pol(line1.start.cart(1)-center(1),line1.start.cart(2)-center(2));
[line1.end.pol(1),line1.end.pol(2)] = cart2pol(line1.end.cart(1)-center(1),line1.end.cart(2)-center(2));

[line2.start.pol(1),line2.start.pol(2)] = cart2pol(line2.start.cart(1)-center(1),line2.start.cart(2)-center(2));
[line2.end.pol(1),line2.end.pol(2)] = cart2pol(line2.end.cart(1)-center(1),line2.end.cart(2)-center(2));

%% Rotate the image a degrees
a = 10;
newpic = imrotate(pic,a);

%% Rotate the lines
line1.start.pol(1) = line1.start.pol(1) + a;
line1.end.pol(1) = line1.end.pol(1) + a;

line2.start.pol(1) = line2.start.pol(1) + a;
line2.end.pol(1) = line2.end.pol(1) + a;

%% Transform the coordinates back to cartesian
[line1.start.cart(1), line1.start.cart(2)] = pol2cart(line1.start.pol(1),line1.start.pol(2));
line1.start.cart = line1.start.cart+center;
line1.end.cart = line1.end.cart+center;
[line1.end.cart(1), line1.end.cart(2)] = pol2cart(line1.end.pol(1),line1.end.pol(2));
line2.start.cart = line2.start.cart+center;
line2.end.cart = line2.end.cart+center;

[line2.start.cart(1), line2.start.cart(2)] = pol2cart(line2.start.pol(1),line2.start.pol(2));
[line2.end.cart(1), line2.end.cart(2)] = pol2cart(line2.end.pol(1),line2.end.pol(2));

%% Plot it again
figure
imshow(newpic,[]);
hold on
plot([line1.start.cart(1),line1.end.cart(1)],[line1.start.cart(2),line1.end.cart(2)])
hold on
plot([line2.start.cart(1),line2.end.cart(1)],[line2.start.cart(2),line2.end.cart(2)])
hold on
plot(center(2),center(1),'ro')

1) 你有图像 I

2) 你旋转了图片 I 的中心(-45 度)=> I_R 旋转了图片

3)你画两条平行线在I_R (line1 & line2)

4) 相对于 imge I

的中心旋转线条

第 4 步错误您必须选择 图像中心 I_R 作为旋转中心

From Matlab Docs: By defaul imrotate creates an output image large enough to include the entire original image. pixels that fall outside the boundary of original image are set to 0 and apear as black background in the output image.

>

from comments: 1) you rotated the image about center point of "I" (-45degree)

2) when you do this using imrotate , the function rotate and translate the main picture and generates "I_R" ,

3) so the center point of I_R is the center point of "I" + some translation!.

4) so the main problem is calculating amount of translation,

5) to do this you can use this: Calculating translation value and rotation angle of a rotated 2D image or this: http://angeljohnsy.blogspot.com/2011/06/image-rotation.html or

6) simply you can use some markings on your original image (for example marking on the center of original image ) to see how much it translated ( or to see center of I_R directly) and the use this center for rotation back of lines.

希望你能找到一些计算翻译的好方法,甚至 您可以编写自己的 "image_rotation" 脚本,这并不难。

我找到了解决办法!

好吧,内置函数 imrotate 不仅会默认旋转图像,还会进行平移以使旋转后的图像适合图形。

解决方案是使用:

I_R = imrotate(I,-45,'nearest','crop');

这里重要的参数是'crop'(bbox):

B = imrotate(A,angle,method,bbox) rotates image A, where bbox specifies the size of the returned image. bbox is a text string that can have one of the following values. The default value is enclosed in braces ({}).

'crop' Make output image B the same size as the input image A, cropping the rotated image to fit

{'loose'} Make output image B large enough to contain the entire rotated image. B is generally larger than A.

借助函数 imrotate here