Pregunta

He estado tratando de hacer mi propio operador de convolución en lugar de usar el incorporado que viene con Java. Apliqué el operador de convolución incorporado en esta imagenEnlace

Usando el operador de convolución incorporado con filtro gaussiano, obtuve esta imagen.Enlace

Ahora ejecuto la misma imagen usando mi código

public static int convolve(BufferedImage a,int x,int y){
 int red=0,green=0,blue=0;
      float[] matrix = { 
 0.1710991401561097f, 0.2196956447338621f, 0.1710991401561097f, 
 0.2196956447338621f, 0.28209479177387814f, 0.2196956447338621f, 
 0.1710991401561097f, 0.2196956447338621f, 0.1710991401561097f, 
 };
      for(int i = x;i<x+3;i++){
          for(int j = y;j<y+3;j++){
              int color = a.getRGB(i,j);
              red += Math.round(((color >> 16) & 0xff)*matrix[(i-x)*3+j-y]);
              green += Math.round(((color >> 8) & 0xff)*matrix[(i-x)*3+j-y]);
              blue += Math.round(((color >> 0) & 0xff)*matrix[(i-x)*3+j-y]);

          }
      }

    return (a.getRGB(x, y)&0xFF000000) | (red << 16) | (green << 8) | (blue);
}

Y el resultado que obtuve es este.Enlace

Además, ¿cómo optimizo el código que escribí? ¡El operador de convolución incorporado toma 1 ~ 2 segundos, mientras que mi código incluso si no está cumpliendo el propósito exacto como se supone que está tomando 5 ~ 7 segundos!

Roté accidentalmente mi imagen fuente mientras cargaba. Así que por favor ignora eso.

¿Fue útil?

Solución

En primer lugar, está convirtiendo innecesar (e injustamente) su resultado de Float a INT en cada ciclo del bucle. Su red, green y blue debe ser de tipo flotante y debe devolverse a entero solo después de la convolución (cuando se convierte en RGB):

  float red=0.0f, green = 0.0f, blue = 0.0f
  for(int i = x;i<x+3;i++){
      for(int j = y;j<y+3;j++){
          int color = a.getRGB(i,j);
          red += ((color >> 16) & 0xff)*matrix[(i-x)*3+j-y];
          green += ((color >> 8) & 0xff)*matrix[(i-x)*3+j-y];
          blue += ((color >> 0) & 0xff)*matrix[(i-x)*3+j-y];

      }
  }

return (a.getRGB(x, y)&0xFF000000) | (((int)red) << 16) | (((int)green) << 8) | ((int)blue);

El sangrado de los colores en su resultado es causado porque sus coeficientes en matrix estan equivocados:

0.1710991401561097f + 0.2196956447338621f + 0.1710991401561097f + 
0.2196956447338621f + 0.28209479177387814f + 0.2196956447338621f +
0.1710991401561097f + 0.2196956447338621f + 0.1710991401561097f =

1.8452741

La suma de los coeficientes en una matriz de convolución borrosa debe ser 1.0. Cuando aplica esta matriz a una imagen, puede obtener colores superiores a 255. Cuando eso sucede, los canales "sangran" en el siguiente canal (azul a verde, etc.). Una imagen completamente verde con esta matriz daría como resultado:

 green = 255 * 1.8452741 ~= 471 = 0x01D7;
 rgb = 0xFF01D700;

Que es un verde menos intenso con un toque de rojo.

Puedes arreglar eso dividiendo los coeficientes por 1.8452741, pero quieres asegurarte de que:

 (int)(255.0f * (sum of coefficients)) = 255

Si no, necesita agregar un cheque que limita el tamaño de los canales a 255 y no permita que se envuelvan. P.ej:

if (red > 255.0f)
   red = 255.0f;

Con respecto a la eficiencia/optimización:
Podría ser que la diferencia en la velocidad puede explicarse por este innecesario casting y llamado Math.round, pero un candidato más probable es la forma en que está accediendo a la imagen. No estoy lo suficientemente familiarizado con BufferedImage y Raster para aconsejarle sobre la forma más eficiente de acceder al búfer de imagen subyacente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top