J'essaie de faire pivoter une image avec Matlab sans utiliser la fonction imrotate. Je l'ai fait en utilisant une matrice de transformation.Mais ce n'est pas assez bon.Le problème est que l'image pivotée "glisse". Laissez-moi vous dire avec des images.
Voici mon image que je veux faire pivoter:
Mais quand je le fais pivoter, par exemple à 45 degrés, cela devient ceci:
Je demande pourquoi cela se produit. Voici mon code, y a-t-il des erreurs mathématiques ou de programmation à ce sujet?
image=torso;
%image padding
[Rows, Cols] = size(image);
Diagonal = sqrt(Rows^2 + Cols^2);
RowPad = ceil(Diagonal - Rows) + 2;
ColPad = ceil(Diagonal - Cols) + 2;
imagepad = zeros(Rows+RowPad, Cols+ColPad);
imagepad(ceil(RowPad/2):(ceil(RowPad/2)+Rows-1),ceil(ColPad/2):(ceil(ColPad/2)+Cols-1)) = image;
degree=45;
%midpoints
midx=ceil((size(imagepad,1)+1)/2);
midy=ceil((size(imagepad,2)+1)/2);
imagerot=zeros(size(imagepad));
%rotation
for i=1:size(imagepad,1)
for j=1:size(imagepad,2)
x=(i-midx)*cos(degree)-(j-midy)*sin(degree);
y=(i-midx)*sin(degree)+(j-midy)*cos(degree);
x=round(x)+midx;
y=round(y)+midy;
if (x>=1 && y>=1)
imagerot(x,y)=imagepad(i,j); % k degrees rotated image
end
end
end
figure,imagesc(imagerot);
colormap(gray(256));
La raison pour laquelle vous avez des trous dans votre image est que vous calculez l'emplacement dans imagerot
de chaque pixel dans imagepad
. Vous devez faire le calcul dans l'autre sens. Autrement dit, pour chaque pixel dans imagerot
interpoler dans imagepad
. Pour ce faire, il vous suffit d'appliquer la transformation inverse, qui dans le cas d'une matrice de rotation n'est que la transposition de la matrice (il suffit de changer le signe sur chaque sin
et de traduire dans l'autre sens).
Boucle sur les pixels dans imagerot
:
imagerot=zeros(size(imagepad)); % midx and midy same for both
for i=1:size(imagerot,1)
for j=1:size(imagerot,2)
x= (i-midx)*cos(rads)+(j-midy)*sin(rads);
y=-(i-midx)*sin(rads)+(j-midy)*cos(rads);
x=round(x)+midx;
y=round(y)+midy;
if (x>=1 && y>=1 && x<=size(imagepad,2) && y<=size(imagepad,1))
imagerot(i,j)=imagepad(x,y); % k degrees rotated image
end
end
end
Notez également que vos midx
et midy
doivent être calculés avec size(imagepad,2)
et size(imagepad,1)
respectivement, car la première dimension fait référence au nombre de lignes ( hauteur) et le second à la largeur.
REMARQUE: la même approche s'applique lorsque vous décidez d'adopter un schéma d'interpolation autre que le plus proche voisin, comme dans l'exemple de Rody avec interpolation linéaire.
EDIT : Je suppose que vous utilisez une boucle à des fins de démonstration, mais en pratique, il n'y a pas besoin de boucles. Voici un exemple d'interpolation du plus proche voisin (ce que vous utilisez), en conservant la même taille d'image, mais vous pouvez la modifier pour produire une image plus grande qui inclut l'image source entière:
imagepad = imread('peppers.png');
[nrows ncols nslices] = size(imagepad);
midx=ceil((ncols+1)/2);
midy=ceil((nrows+1)/2);
Mr = [cos(pi/4) sin(pi/4); -sin(pi/4) cos(pi/4)]; % e.g. 45 degree rotation
% rotate about center
[X Y] = meshgrid(1:ncols,1:nrows);
XYt = [X(:)-midx Y(:)-midy]*Mr;
XYt = bsxfun(@plus,XYt,[midx midy]);
xout = round(XYt(:,1)); yout = round(XYt(:,2)); % nearest neighbor!
outbound = yout<1 | yout>nrows | xout<1 | xout>ncols;
zout=repmat(cat(3,1,2,3),nrows,ncols,1); zout=zout(:);
xout(xout<1) = 1; xout(xout>ncols) = ncols;
yout(yout<1) = 1; yout(yout>nrows) = nrows;
xout = repmat(xout,[3 1]); yout = repmat(yout,[3 1]);
imagerot = imagepad(sub2ind(size(imagepad),yout,xout,zout(:))); % lookup
imagerot = reshape(imagerot,size(imagepad));
imagerot(repmat(outbound,[1 1 3])) = 0; % set background value to [0 0 0] (black)
Pour modifier l'interpolation linéaire ci-dessus, calculez les 4 pixels voisins de chaque coordonnée dans XYt
et effectuez une somme pondérée en utilisant le produit des composants fractionnaires comme poids. Je laisse cela comme un exercice, car cela ne ferait que gonfler ma réponse au-delà de la portée de votre question. :)
La méthode que vous utilisez (rotation par échantillonnage) est la plus rapide et la plus simple, mais aussi la moins précise.
La rotation par cartographie de zone, comme indiqué ci-dessous ( this est une bonne référence), est bien meilleure pour préserver la couleur.
Mais: notez que cela ne fonctionnera que sur les images en niveaux de gris/RVB, mais PAS sur les images en couleur comme celle que vous semblez utiliser.
image = imread('peppers.png');
figure(1), clf, hold on
subplot(1,2,1)
imshow(image);
degree = 45;
switch mod(degree, 360)
% Special cases
case 0
imagerot = image;
case 90
imagerot = rot90(image);
case 180
imagerot = image(end:-1:1, end:-1:1);
case 270
imagerot = rot90(image(end:-1:1, end:-1:1));
% General rotations
otherwise
% Convert to radians and create transformation matrix
a = degree*pi/180;
R = [+cos(a) +sin(a); -sin(a) +cos(a)];
% Figure out the size of the transformed image
[m,n,p] = size(image);
dest = round( [1 1; 1 n; m 1; m n]*R );
dest = bsxfun(@minus, dest, min(dest)) + 1;
imagerot = zeros([max(dest) p],class(image));
% Map all pixels of the transformed image to the original image
for ii = 1:size(imagerot,1)
for jj = 1:size(imagerot,2)
source = ([ii jj]-dest(1,:))*R.';
if all(source >= 1) && all(source <= [m n])
% Get all 4 surrounding pixels
C = ceil(source);
F = floor(source);
% Compute the relative areas
A = [...
((C(2)-source(2))*(C(1)-source(1))),...
((source(2)-F(2))*(source(1)-F(1)));
((C(2)-source(2))*(source(1)-F(1))),...
((source(2)-F(2))*(C(1)-source(1)))];
% Extract colors and re-scale them relative to area
cols = bsxfun(@times, A, double(image(F(1):C(1),F(2):C(2),:)));
% Assign
imagerot(ii,jj,:) = sum(sum(cols),2);
end
end
end
end
subplot(1,2,2)
imshow(imagerot);
Sortie:
Fait pivoter l'image colorée selon l'angle donné par l'utilisateur sans recadrage de l'image dans matlab.
La sortie de ce programme est similaire à la sortie de la commande intégrée "imrotate". Ce programme crée dynamiquement un arrière-plan en fonction de l'angle saisi par l'utilisateur.En utilisant la matrice de rotation et le décalage d'origine, nous obtenons une relation entre les coordonnées de l'image initiale et finale.Utilisant la relation entre coordonnées de l'image initiale et finale, nous cartographions maintenant les valeurs d'intensité pour chaque pixel.
img=imread('img.jpg');
[rowsi,colsi,z]= size(img);
angle=45;
rads=2*pi*angle/360;
%calculating array dimesions such that rotated image gets fit in it exactly.
% we are using absolute so that we get positve value in any case ie.,any quadrant.
rowsf=ceil(rowsi*abs(cos(rads))+colsi*abs(sin(rads)));
colsf=ceil(rowsi*abs(sin(rads))+colsi*abs(cos(rads)));
% define an array withcalculated dimensionsand fill the array with zeros ie.,black
C=uint8(zeros([rowsf colsf 3 ]));
%calculating center of original and final image
xo=ceil(rowsi/2);
yo=ceil(colsi/2);
midx=ceil((size(C,1))/2);
midy=ceil((size(C,2))/2);
% in this loop we calculate corresponding coordinates of pixel of A
% for each pixel of C, and its intensity will be assigned after checking
% weather it lie in the bound of A (original image)
for i=1:size(C,1)
for j=1:size(C,2)
x= (i-midx)*cos(rads)+(j-midy)*sin(rads);
y= -(i-midx)*sin(rads)+(j-midy)*cos(rads);
x=round(x)+xo;
y=round(y)+yo;
if (x>=1 && y>=1 && x<=size(img,1) && y<=size(img,2) )
C(i,j,:)=img(x,y,:);
end
end
end
imshow(C);
Regarde ça.
c'est le moyen le plus rapide que vous pouvez faire.
img = imread('Koala.jpg');
theta = pi/10;
rmat = [
cos(theta) sin(theta) 0
-sin(theta) cos(theta) 0
0 0 1];
mx = size(img,2);
my = size(img,1);
corners = [
0 0 1
mx 0 1
0 my 1
mx my 1];
new_c = corners*rmat;
T = maketform('affine', rmat); %# represents translation
img2 = imtransform(img, T, ...
'XData',[min(new_c(:,1)) max(new_c(:,1))],...
'YData',[min(new_c(:,2)) max(new_c(:,2))]);
subplot(121), imshow(img);
subplot(122), imshow(img2);