Cambiar el tamaño de una imagen con interpolación bilineal sin tamaño

He encontrado algunos métodos para agrandar una imagen, pero no hay solución para reducir una imagen. Actualmente estoy usando el método vecino más cercano. ¿Cómo podría hacer esto con la interpolación bilineal sin usar la función de imresize en MATLAB?

En sus comentarios, mencionó que quería cambiar el tamaño de una imagen mediante la interpolación bilineal. Tenga en cuenta que el algoritmo de interpolación bilineal es independiente del tamaño. Puede usar el mismo algoritmo para agrandar una imagen y reducirla. Los factores de escala correctos para muestrear las ubicaciones de los píxeles dependen de las dimensiones de salida que especifique. Esto no cambia el algoritmo central por cierto.

Antes de comenzar con cualquier código, lo referiré a las diapositivas de procesamiento de imágenes digitales II de Richard Alan Peters sobre interpolación , específicamente la diapositiva Nº 59. Tiene una gran ilustración, así como pseudocódigo sobre cómo hacer la interpolación bilineal que es amigable MATLAB. Para ser autónomo, voy a incluir su diapositiva aquí para que podamos seguir y codificarla:

enter image description here

Escribamos una función que haga esto por nosotros. Esta función imread una imagen (que se lee a través de imread ) que puede ser en color o en escala de grises, así como una matriz de dos elementos: la imagen que desea cambiar de tamaño y las dimensiones de salida en una matriz de dos elementos del imagen de tamaño final que desea. El primer elemento de esta matriz serán las filas y el segundo elemento de esta matriz serán las columnas. Simplemente revisaremos este algoritmo y calcularemos los valores de píxel de salida / escala de grises usando este pseudocódigo:

 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 

Tome el código anterior, cópielo y péguelo en un archivo llamado bilinearInterpolation.m y guárdelo. Asegúrese de cambiar su directorio de trabajo donde guardó este archivo.


Excepto por sub2ind y tal vez meshgrid , todo parece estar de acuerdo con el algoritmo. meshgrid es muy fácil de explicar. Todo lo que hace es especificar una cuadrícula 2D de coordenadas (x,y) , donde cada ubicación en su imagen tiene un par de coordenadas (x,y) o columna y fila. La creación de una cuadrícula a través de meshgrid evita cualquier bucle for ya que habrá generado todas las ubicaciones de píxeles correctas del algoritmo que necesitamos antes de continuar.

Cómo funciona sub2ind es que toma una ubicación de fila y columna en una matriz 2D (bueno … realmente puede ser cualquier cantidad de dimensiones que desee) y genera un único índice lineal. Si no conoce cómo MATLAB indexa en matrices, hay dos formas de acceder a un elemento en una matriz. Puede usar la fila y la columna para obtener lo que desea, o puede usar un índice de columnas principales . Echa un vistazo a este ejemplo de matriz que tengo a continuación:

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

Si queremos acceder al número 9, podemos hacer A(2,4) que es lo que la mayoría de la gente tiende a hacer por defecto. Hay otra forma de acceder al número 9 usando un solo número, que es A(11) … ¿cómo es ese el caso? MATLAB presenta la memoria de sus matrices en formato columna-mayor . Esto significa que si tuvieras que tomar esta matriz y juntar todas sus columnas en una sola matriz, se vería así:

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

Ahora, si desea acceder al elemento número 9, deberá acceder al undécimo elemento de este conjunto. Volviendo al bit de interpolación, sub2ind es crucial si desea vectorizar el acceso a los elementos en su imagen para hacer la interpolación sin hacer ningún bucle for . Como tal, si nos fijamos en la última línea del pseudocódigo, queremos acceder a los elementos en r , c , r+1 y c+1 . Tenga en cuenta que todas estas son matrices en 2D , donde cada elemento en cada una de las ubicaciones coincidentes en todas estas matrices nos dice los cuatro píxeles de los que necesitamos muestrear para producir el píxel de salida final. La salida de sub2ind también será matrices 2D del mismo tamaño que la imagen de salida. La clave aquí es que cada elemento de las matrices en 2D de r , c , r+1 c+1 nos dará los índices de columnas principales en la imagen a la que queremos acceder, y al arrojar esto como entrada en la imagen para la indexación, obtendremos exactamente las ubicaciones de píxeles que queremos.


Hay algunas sutilezas importantes que me gustaría agregar al implementar el algoritmo:

  1. Debe asegurarse de que los índices para acceder a la imagen al interpolar fuera de la imagen estén configurados en 1 o en el número de filas o columnas para asegurarse de no salirse de los límites. En realidad, si se extiende hacia la derecha o debajo de la imagen, debe establecerla en una debajo del máximo ya que la interpolación requiere que acceda a los píxeles a uno hacia la derecha o hacia abajo. Esto asegurará que aún estés dentro de los límites.

  2. También debe asegurarse de que la imagen de salida se convierte en la misma clase que la imagen de entrada.

  3. Corrí a través de un bucle for para interpolar cada canal por sí mismo. Podrías hacerlo de forma inteligente utilizando bsxfun , pero decidí usar un bucle for para simplificar, y para que puedas seguir el algoritmo.


Como ejemplo para mostrar esto funciona, usemos la imagen onion.png que es parte de la ruta del sistema de MATLAB. Las dimensiones originales de esta imagen son 135 x 198 . Vamos a interpolar esta imagen haciéndola más grande, yendo a 270 x 396 que es dos veces el tamaño de la imagen original:

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

El código anterior interpolará la imagen aumentando cada dimensión en dos veces, luego mostrará una figura con la imagen original y otra con la imagen ampliada. Esto es lo que obtengo para ambos:

enter image description here

enter image description here


Del mismo modo, reduzcamos la imagen a la mitad:

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

Tenga en cuenta que la mitad de 135 es 67.5 para las filas, pero redondeé a 68. Esto es lo que obtengo:

enter image description here

enter image description here


Una cosa que noté en la práctica es que el upsampling con bilinear tiene un rendimiento decente en comparación con otros esquemas como el bicúbico … o incluso Lanczos . Sin embargo, cuando está reduciendo una imagen, porque está eliminando detalles, el vecino más cercano es muy suficiente. Encuentro bilineal o bicúbico excesivo. No estoy seguro de cuál es tu aplicación, pero juego con los diferentes algoritmos de interpolación y veo lo que te gusta de los resultados. Bicubic es otra historia, y se lo dejo como ejercicio. Esas diapositivas que te remití tienen material sobre interpolación bicúbica si estás interesado.


¡Buena suerte!