web-dev-qa-db-fra.com

Redimensionner une image avec interpolation bilinéaire sans agrandissement

J'ai trouvé quelques méthodes pour agrandir une image mais il n'y a pas de solution pour réduire une image. J'utilise actuellement la méthode du plus proche voisin. Comment pourrais-je faire cela avec une interpolation bilinéaire sans utiliser la fonction imresize dans MATLAB?

13
teardrop

Dans vos commentaires, vous avez mentionné que vous vouliez redimensionner une image à l'aide d'une interpolation bilinéaire. Gardez à l'esprit que l'algorithme d'interpolation bilinéaire est indépendant de la taille. Vous pouvez très bien utiliser le même algorithme pour agrandir ou réduire une image. Les bons facteurs d'échelle pour échantillonner les emplacements des pixels dépendent des dimensions de sortie que vous spécifiez. Cela ne change pas l'algorithme de base en passant.

Avant de commencer avec un code, je vais vous renvoyer à diapositives de traitement d'image numérique de Richard Alan Peters II sur interpolation , en particulier la diapositive # 59. Il a une grande illustration ainsi qu'un pseudocode sur la façon de faire une interpolation bilinéaire compatible MATLAB. Pour être autonome, je vais inclure sa diapositive ici afin que nous puissions suivre et coder:

enter image description here

Écrivons une fonction qui fera cela pour nous. Cette fonction prendra une image (qui est lue à travers imread ) qui peut être en couleur ou en niveaux de gris, ainsi qu'un tableau de deux éléments - L'image que vous souhaitez redimensionner et les dimensions de sortie dans un tableau à deux éléments de l'image redimensionnée finale que vous souhaitez. Le premier élément de ce tableau sera les lignes et le deuxième élément de ce tableau sera les colonnes. Nous allons simplement parcourir cet algorithme et calculer les valeurs de couleurs/niveaux de gris des pixels de sortie en utilisant ce pseudocode:

function [out] = bilinearInterpolation(im, out_dims)

    %// Get some necessary variables first
    in_rows = size(im,1);
    in_cols = size(im,2);
    out_rows = out_dims(1);
    out_cols = out_dims(2);

    %// Let S_R = R / R'        
    S_R = in_rows / out_rows;
    %// Let S_C = C / C'
    S_C = in_cols / out_cols;

    %// Define grid of co-ordinates in our image
    %// Generate (x,y) pairs for each point in our image
    [cf, rf] = meshgrid(1 : out_cols, 1 : out_rows);

    %// Let r_f = r'*S_R for r = 1,...,R'
    %// Let c_f = c'*S_C for c = 1,...,C'
    rf = rf * S_R;
    cf = cf * S_C;

    %// Let r = floor(rf) and c = floor(cf)
    r = floor(rf);
    c = floor(cf);

    %// Any values out of range, cap
    r(r < 1) = 1;
    c(c < 1) = 1;
    r(r > in_rows - 1) = in_rows - 1;
    c(c > in_cols - 1) = in_cols - 1;

    %// Let delta_R = rf - r and delta_C = cf - c
    delta_R = rf - r;
    delta_C = cf - c;

    %// Final line of algorithm
    %// Get column major indices for each point we wish
    %// to access
    in1_ind = sub2ind([in_rows, in_cols], r, c);
    in2_ind = sub2ind([in_rows, in_cols], r+1,c);
    in3_ind = sub2ind([in_rows, in_cols], r, c+1);
    in4_ind = sub2ind([in_rows, in_cols], r+1, c+1);       

    %// Now interpolate
    %// Go through each channel for the case of colour
    %// Create output image that is the same class as input
    out = zeros(out_rows, out_cols, size(im, 3));
    out = cast(out, class(im));

    for idx = 1 : size(im, 3)
        chan = double(im(:,:,idx)); %// Get i'th channel
        %// Interpolate the channel
        tmp = chan(in1_ind).*(1 - delta_R).*(1 - delta_C) + ...
                       chan(in2_ind).*(delta_R).*(1 - delta_C) + ...
                       chan(in3_ind).*(1 - delta_R).*(delta_C) + ...
                       chan(in4_ind).*(delta_R).*(delta_C);
        out(:,:,idx) = cast(tmp, class(im));
    end

Prenez le code ci-dessus, copiez-le et collez-le dans un fichier appelé bilinearInterpolation.m Et enregistrez-le. Assurez-vous de modifier votre répertoire de travail dans lequel vous avez enregistré ce fichier.


À l'exception de sub2ind Et peut-être de meshgrid, tout semble conforme à l'algorithme. meshgrid est très facile à expliquer. Tout ce que vous faites est de spécifier une grille 2D de coordonnées (x,y), Où chaque emplacement de votre image a une paire de coordonnées (x,y) Ou de colonnes et de lignes. La création d'une grille via meshgrid évite toute boucle for car nous aurons généré tous les bons emplacements de pixels à partir de l'algorithme dont nous avons besoin avant de continuer.

Comment sub2ind fonctionne, c'est qu'il faut un emplacement de ligne et de colonne dans une matrice 2D (enfin ... il peut vraiment être n'importe quel quantité de dimensions que vous voulez), et il génère un index linéaire unique. Si vous ne savez pas comment MATLAB indexe dans les matrices, il existe deux façons d'accéder à un élément dans une matrice. Vous pouvez utiliser la ligne et la colonne pour obtenir ce que vous voulez, ou vous pouvez utiliser un index column-major . Jetez un oeil à cet exemple de matrice que j'ai ci-dessous:

A = 

1  2  3  4  5
6  7  8  9  10
11 12 13 14 15

Si nous voulons accéder au nombre 9, nous pouvons faire A(2,4), ce que la plupart des gens ont tendance à utiliser par défaut. Il existe une autre façon d'accéder au numéro 9 en utilisant un seul numéro, qui est A(11)... maintenant comment est-ce le cas? MATLAB dispose la mémoire de ses matrices au format colonne-majeur . Cela signifie que si vous deviez prendre cette matrice et empiler toutes ses colonnes ensemble dans un seul tableau, cela ressemblerait à ceci:

A = 

1
6
11
2
7
12
3
8
13
4
9
14
5
10
15

Maintenant, si vous voulez accéder à l'élément numéro 9, vous devez accéder au 11e élément de ce tableau. Pour revenir au bit d'interpolation, sub2ind Est crucial si vous voulez vectoriser l'accès aux éléments de votre image pour faire l'interpolation sans faire de boucles for. Ainsi, si vous regardez la dernière ligne du pseudocode, nous voulons accéder aux éléments à r, c, r+1 Et c+1. Notez que tous ces éléments sont des tableaux 2D , où chaque élément dans chacun des emplacements correspondants dans tous ces tableaux nous indique les quatre pixels que nous devons échantillonner à partir de afin de produire le pixel de sortie final. La sortie de sub2ind Sera également des tableaux 2D de la même taille que l'image de sortie. La clé ici est que chaque élément des tableaux 2D de r, c, r+1 Et c+1 Nous donnera le colonne-major index dans l'image à laquelle nous voulons accéder, et en lançant cela comme entrée dans l'image pour l'indexation, nous obtiendrons exactement les emplacements de pixels que nous voulons.


Il y a quelques subtilités importantes que j'aimerais ajouter lors de la mise en œuvre de l'algorithme:

  1. Vous devez vous assurer que tous les indices pour accéder à l'image lors de l'interpolation en dehors de l'image sont définis sur 1 ou sur le nombre de lignes ou de colonnes pour vous assurer de ne pas sortir des limites. En fait, si vous vous étendez à droite ou en dessous de l'image, vous devez définir cela à un en dessous du maximum car l'interpolation nécessite que vous accédiez aux pixels à un à droite ou en dessous. Cela garantira que vous êtes toujours dans les limites.

  2. Vous devez également vous assurer que l'image de sortie est convertie dans la même classe que l'image d'entrée.

  3. J'ai parcouru une boucle for pour interpoler chaque canal par lui-même. Vous pouvez le faire intelligemment en utilisant bsxfun , mais j'ai décidé d'utiliser une boucle for pour plus de simplicité, et pour pouvoir suivre l'algorithme.


Comme exemple pour montrer que cela fonctionne, utilisons l'image onion.png Qui fait partie du chemin système de MATLAB. Les dimensions originales de cette image sont 135 x 198. Interpolons cette image en l'agrandissant, en allant à 270 x 396 Qui est deux fois la taille de l'image originale:

im = imread('onion.png');
out = bilinearInterpolation(im, [270 396]);
figure;
imshow(im);
figure;
imshow(out);

Le code ci-dessus va interpoler l'image en augmentant chaque dimension de deux fois plus, puis afficher une figure avec l'image d'origine et une autre figure avec l'image agrandie. C'est ce que j'obtiens pour les deux:

enter image description here

enter image description here


De même, réduisons l'image de moitié autant:

im = imread('onion.png');
out = bilinearInterpolation(im, [68 99]);
figure;
imshow(im);
figure;
imshow(out);

Notez que la moitié de 135 correspond à 67,5 pour les lignes, mais j'ai arrondi à 68. Voici ce que j'obtiens:

enter image description here

enter image description here


Une chose que j'ai remarquée dans la pratique est que le suréchantillonnage avec bilinéaire a des performances décentes par rapport à d'autres schémas comme bicubique ... ou même Lanczos . Cependant, lorsque vous réduisez une image, car vous supprimez des détails, le voisin le plus proche est largement suffisant. Je trouve bilinéaire ou bicubique exagéré. Je ne suis pas sûr de ce qu'est votre application, mais jouez avec les différents algorithmes d'interpolation et voyez ce que vous aimez dans les résultats. Bicubic est une autre histoire, et je vous laisse cela comme un exercice. Ces diapositives auxquelles je vous ai référé contiennent des informations sur l'interpolation bicubique si vous êtes intéressé.


Bonne chance!

57
rayryeng