I imrotate() una imagen, dibujar dos líneas, rotar las líneas hacia atrás y dibujarlas en una imagen original, pero ¿no obtienes el resultado esperado en MATLAB?
rotation translation (5)
Puede que no lo arregle, pero intente definir su matriz como:
R=[ ...
cosd(t) -sind(t)
-sind(t) -cosd(t)
];
Al trazar una imagen, debe cuidar el eje Y, que pasa automáticamente de arriba hacia abajo.
Lo que quiero hacer :
Supongamos que tengo una imagen I
que giro -45 ° usando imrotate
(obtengo I_R
). Luego dibujo dos líneas AB
y CD
(paralelos). Finalmente, giro las dos líneas hacia atrás ( 45 ° ) y las dibujo en la imagen original I
Cómo hago eso ##
imrotate()
I
usando la función MATLAB imrotate()
:
I_R = imrotate(I,-45);
De la ayuda de Matlab, obtengo: B = imrotate (A, ángulo) gira la imagen A en grados en sentido antihorario alrededor de su punto central.
¡Pero parece que
imrotate
agrega una traducción a la imagen! He leído el código de la función incorporada de matlab, parece que usa una función llamadagetOutputBound
para comprobar si la imagen girada cabe en la figura. ¡Esta traducción es lo que estoy buscando!
Los cuatro puntos A,B,C,D
forman dos líneas paralelas AB
y CD
.
A = [x_A; u];
B = [x_B; u];
C = [x_A; d];
D = [x_B; d];
Así que ahora, giro las dos líneas, utilizo mi función rotateTwoPoints()
, simplemente llamando a las dos líneas siguientes:
[Af,Bf] = rotateTwoPoints(A,B,-45,O,true);
[Cf,Df] = rotateTwoPoints(C,D,-45,O,true);
Donde O
es el origen alrededor del cual la rotación será.
- He intentado
O = [0;0]
Quiero decir que es el origen de la trama. Sin éxito ! - Entonces elijo
O
el centroide de la imagenI
usandoregionprops(I,"Centroid")
. Estaba mal porque el centroide no es un centro. - Ahora, uso el centro de la imagen
O = floor(size(I)/2+0.5)''
o usandoceil
!
Pero cuando dibujo las líneas resultantes AfBf
y CfDf
en la imagen I
gusta esto:
plot([Af(1) Bf(1)],[Af(2) Bf(2)],''k'');
plot([Cf(1) Df(1)],[Cf(2) Df(2)],''k'');
¡Obtuve un resultado que no es correcto!
Problema : En I_R , AB
& CD
contiene lo que yo llamo BlueZone (ver imagen 3). ¡Pero las líneas posteriores giradas AfBf
y CfDf
no lo cubren!
Los resultados en Imágenes
Aquí está la imagen girada I_R y las dos líneas dibujadas (las dos líneas rojas medias corresponden a AB
y CD
):
Luego dibujo las líneas giradas AfBf
y CfDf
en la imagen original I (el punto negro en negrita corresponde al centro en el que hice la rotación):
IMAGEN ACTUALIZADA
Problema : Como puede ver, BlueZone estaba dentro de las dos líneas AB
y CD
. Pero cuando se gira hacia atrás, queda afuera, como se muestra en la siguiente imagen (las flechas rojas apuntan a BlueZone ):
ACTUALIZACIÓN AGREGADA un SNIPPET
Como mi problema aún no se resolvió, seleccioné el código que causa el problema y lo agregué como el siguiente fragmento (hay una variable almacenada en un archivo que puede descargar aquí ):
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
PD: estoy usando MATLAB R2012b
Como dices, la rotación se calcula correctamente, como se muestra en el primer gráfico. El problema entonces es solo con la visualización final de los resultados. Cuando haces esto
plot([Af(1) Bf(1)],[Af(2) Bf(2)],''k'');
plot([Cf(1) Df(1)],[Cf(2) Bf(2)],''k'');
tiene un error tipográfico en la segunda línea (segundo elemento del segundo argumento) - está trazando Bf(2)
como el final de la segunda línea en lugar de Df(2)
. Cuando reemplacé esto con Df(2)
trazó líneas paralelas como se esperaba.
ACTUALIZAR:
En los comentarios sugerí refactorizar este código para usar matrices de transformación para todo, de modo que el mismo conjunto de transformaciones se pueda aplicar tanto a la imagen como a las superposiciones. Aquí hay un resumen genérico de cómo puede configurarlo.
Hay dos puntos principales a tener en cuenta.
La convención de Matlab con imágenes es que y = 0 es la parte superior.
imshow
y related then pon y = 0 en la parte superior yy aumentando en la gráfica. Esta es la convención opuesta a laplot
, por lo que cuando superpone tramas e imágenes, una de ellas debe invertirse. Esto tiene consecuencias para el uso de matrices de transformación, la principal es que el ángulo de rotación debe invertirse.Las imágenes en matlab se pueden considerar como los datos z para una cuadrícula regular de píxeles. Sin embargo, esas coordenadas de píxeles no se almacenan, solo se deducen cuando muestra la imagen. En consecuencia, cuando
imshow
una imagen y luego laimshow
aimshow
, la traducción no es evidente, ya que vuelve a infundir un nuevo conjunto de coordenadas de píxeles que son las mismas que las de la imagen no transformada. Para evitar esto, debemos arreglar referencias espaciales a una imagen.
Además de esto, las transformaciones 2d se llevan a cabo utilizando matrices de 3 espacios, por lo que necesitamos definir puntos de salida en 3space (pero luego truncarlos para visualizarlos en diagramas 2d).
Así que vamos a ponerlo todo junto.
% 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)
En el segundo gráfico, tracé la línea transormada en verde y la línea original en amarillo, para comparar.
Puntos a tener en cuenta:
La función
imwarp
no usa la matriz de rotación directamente, primero tiene que construir un objetotform
y luego suministrar eso. Por supuesto, puede hacer todo esto en una sola línea.Tener dos matrices es un poco poco elegante. Aquí está bien, donde solo tenemos que preocuparnos por una sola rotación, pero el objetivo de usar matrices de transformación es que puedes encadenar una secuencia de transformaciones simplemente multiplicando las matrices. Si tiene que hacer esto dos veces, estropearía una pieza de código que de otro modo sería elegante, y eso nunca funcionaría, así que probablemente la forma más limpia de hacerlo sería voltear sus imágenes al inicio de todo el proceso, y luego voltearlas de nuevo al al final si es necesario (para exportar, por ejemplo).
La contabilidad para los datos de puntos es algo tediosa. Hay muchas maneras de hacerlo, dependiendo de las convenciones que elija adoptar con respecto a qué coordenada se mantiene en qué posición en los vectores de columna. Nunca he encontrado un conjunto que funcione bien con las transformaciones y el
plot
3space, y lo que funciona mejor cambia según la aplicación. Las funciones de ayuda pueden ahorrarle algunos dolores de cabeza, si no es el próximo mantenedor, y envolverlo todo en una clase es lo mejor, si puede justificar el tiempo.
ACTUALIZACIÓN2:
Para minimizar el uso de las coordenadas de 3 espacios, simplemente usaría vectores de 2 espacios para describir todos sus puntos, como es normal, y luego agregaría la coordenada z ficticia solo cuando necesite realizar la transformación.
Entonces podrías tener
testpoint = [1; 5]; % x and y xoordinates only
trpoint = Rpoints*[testpoint; 1];
trpoint = trpoint(1:2);
Puedes poner esto en un contenedor, pero no hay forma de imwarp
el hecho de que necesitas usar una matriz de 3x3 para la sección imwarp
, lo que significa que necesitas especificar tus coordenadas en 3space también.
Alternativamente, podría truncar la matriz de rotación para la transformación de coordenadas:
trpoint = Rpoints(1:2, 1:2)*testpoint;
pero de una manera u otra tendrás que hacer cierta contabilidad de índices.
ACTUALIZACIÓN3:
El OP no tiene acceso a imref2d
, por lo que aquí hay una forma estrafalaria de lograr el mismo resultado. La línea importante es esta
imshow(Ir, Rr);
donde Rr
es el objeto de referencia spstial emitido por la función imwarp
. Si no tiene esto, puede proporcionar referencias espaciales para imshow
manualmente con los ''XData''
e ''YData''
. Primero debe calcular las extensiones y decidir una convención. imref2d
utiliza la convención de (0,0) como superior izquierda y cuando se gira la imagen, esta esquina de la imagen original permanece como coordenada (0,0), de modo que la imagen girada ahora se extiende desde y = [- 181: 181] y x = [0: 363]. Para obtener estos valores, debe transformar las cuatro esquinas de la imagen y luego calcular los máximos y mínimos.
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,:));
a continuación, puede reemplazar la línea imshow
con este
imshow(Ir, ''xdata'', xlims, ''ydata'', ylims);
todo lo demás debería ser igual.
Tenga en cuenta que, cuando hice todo esto, hubo una ligera diferencia entre los valores que obtuve arriba y los producidos por imwarp
en el objeto imref2d
. Esa función regresó
XWorldLimits: [0.2264 363.2264]
YWorldLimits: [-181.5000 181.5000]
Mientras que el mío da
xlims: [0.7071 362.7458];
ylims: [-181.0193 181.0193];
No puedo explicar esto sin buscar en la fuente de imwarp
e, incluso si no es MEX en ese nivel, no estoy seguro de qué tan lejos podrías llegar antes de que puedas ser acusado de infracción de la propiedad intelectual.
Estos números representan la ubicación de la imagen en los ejes, por lo que si usa la imagen para elegir puntos, puede que estén desactivados unas décimas de píxel. Si solo está usando la imagen como referencia, entonces debería estar lo suficientemente cerca.
Puede que esté malinterpretando algo, pero lo que quiere hacer es girar una imagen, dibujar dos líneas y luego volver a girar todo, incluidas las dos líneas, ¿verdad?
Si es así, supongo que la forma más fácil de convertir las dos líneas sería traducirlas a coordenadas polares usando car2pol. http://se.mathworks.com/help/matlab/ref/cart2pol.html
Entonces debería ser fácil girar las líneas. Avísame si malinterpreté algo.
editar: Intenté hacer un código de ejemplo, aparentemente estoy demasiado cansado para pensar correctamente y hay algún error, pero creo que la idea es clara. Además, por favor, no use estructuras como lo hice, solo pensé que sería la manera más fácil de nombrar las variables.
%% 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'')
¡Encontré la solución!
Bueno, lo que sucede es que la función imrotate
no solo rota por defecto la imagen, sino que también hace una traducción para que la imagen girada encaje en la figura.
la solución es usar:
I_R = imrotate(I,-45,''nearest'',''crop'');
el parámetro importante aquí es ''crop'' (bbox):
B = imrotate (A, ángulo, método, bbox) gira la imagen A, donde bbox especifica el tamaño de la imagen devuelta. bbox es una cadena de texto que puede tener uno de los siguientes valores. El valor predeterminado está entre llaves ({}).
''recortar'' Hace que la imagen de salida B tenga el mismo tamaño que la imagen de entrada A, recortando la imagen girada para que quepa
{''loose''} Hace que la imagen de salida B sea lo suficientemente grande como para contener toda la imagen girada. B es generalmente más grande que A.
de la ayuda de la función imrotate aquí .
1) tienes imagen I
2) rotó la imagen I sobre su centro (-45 grados) => I_R está girando la imagen
3) dibujas dos líneas paralelas en I_R (línea 1 y línea 2)
4) rotar líneas con respecto al centro de imge I
El paso número 4 es incorrecto, debes elegir el centro de la imagen I_R para el centro de rotación .
Desde Matlab Docs: por defecto, imrotate crea una imagen de salida lo suficientemente grande como para incluir toda la imagen original. los píxeles que quedan fuera del límite de la imagen original se establecen en 0 y aparecen como fondo negro en la imagen de salida.
>
de los comentarios: 1) rotó la imagen sobre el punto central de "I" (-45degree)
2) cuando haces esto usando imrotate, la función gira y traduce la imagen principal y genera "I_R",
3) ¡ entonces el punto central de I_R es el punto central de "I" + alguna traducción! .
4) entonces el principal problema es calcular la cantidad de traducción ,
5) para hacer esto puede usar esto: Cálculo del valor de traducción y el ángulo de rotación de una imagen 2D rotada o esta: http://angeljohnsy.blogspot.com/2011/06/image-rotation.html o
6) simplemente puede usar algunas marcas en su imagen original (por ejemplo, marcar en el centro de la imagen original) para ver cuánto se tradujo (o para ver el centro de I_R directamente) y utilizar este centro para la rotación hacia atrás de las líneas.
Espero que encuentres una buena forma de calcular la traducción, incluso si puedes escribir tu propio script "image_rotation", no es tan difícil.