web-dev-qa-db-fra.com

Rotation d'image par Matlab sans utiliser d'imrotate

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:

enter image description here

Mais quand je le fais pivoter, par exemple à 45 degrés, cela devient ceci:

enter image description here

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));
20
Zapdos

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. :)

21
chappjc

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:

enter image description here

11
Rody Oldenhuis

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);
7
Goutham

Regarde ça.

c'est le moyen le plus rapide que vous pouvez faire. this is the output

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);
1
Sadegh Teimori