Cómo optimizar una clase de procesamiento de imágenes

Tengo la clase siguiente que procesa un mapa de bits para colocar una distorsión del ojo de pez en él.

He ejecutado mi aplicación a través de TraceView y encontró que prácticamente todo el tiempo de procesamiento se pasa el bucle a través del mapa de bits.
Un desarrollador ha sugerido no usar float, ya que esto ralentizará las cosas en lo que se refiere a los gráficos. ¿También no es necesario usar math.pow () y ceil ()?
En el momento de colocar el efecto haciendo un bucle en todo el mapa de bits toma alrededor de 42 segundos, sí segundos 🙂
He intentado reemplazar los flotadores con ints y eso ha reducido el tiempo a 37 segundos, pero el efecto ya no está presente en el mapa de bits.
El arg k es originalmente un flotador y establece el nivel de distorsión, por ejemplo, 0,0002F, si paso un int el efecto no funciona.

¿Puede alguien señalarme en la dirección correcta sobre cómo optimizar este proceso? Una vez que lo haya optimizado me gustaría mirar en tal vez no looping a través de todo el mapa de bits y tal vez poner un cuadro delimitador alrededor del efecto o utilizando el algoritmo de abajo que determina si un píxel está dentro del círculo con radio 150.

class Filters{ float xscale; float yscale; float xshift; float yshift; int [] s; private String TAG = "Filters"; long getRadXStart = 0; long getRadXEnd = 0; long startSample = 0; long endSample = 0; public Filters(){ Log.e(TAG, "***********inside filter constructor"); } public Bitmap barrel (Bitmap input, float k){ //Log.e(TAG, "***********INSIDE BARREL METHOD "); float centerX=input.getWidth()/2; //center of distortion float centerY=input.getHeight()/2; int width = input.getWidth(); //image bounds int height = input.getHeight(); Bitmap dst = Bitmap.createBitmap(width, height,input.getConfig() ); //output pic // Log.e(TAG, "***********dst bitmap created "); xshift = calc_shift(0,centerX-1,centerX,k); float newcenterX = width-centerX; float xshift_2 = calc_shift(0,newcenterX-1,newcenterX,k); yshift = calc_shift(0,centerY-1,centerY,k); float newcenterY = height-centerY; float yshift_2 = calc_shift(0,newcenterY-1,newcenterY,k); xscale = (width-xshift-xshift_2)/width; // Log.e(TAG, "***********xscale ="+xscale); yscale = (height-yshift-yshift_2)/height; // Log.e(TAG, "***********yscale ="+yscale); // Log.e(TAG, "***********filter.barrel() about to loop through bm"); /*for(int j=0;j<dst.getHeight();j++){ for(int i=0;i<dst.getWidth();i++){ float x = getRadialX((float)i,(float)j,centerX,centerY,k); float y = getRadialY((float)i,(float)j,centerX,centerY,k); sampleImage(input,x,y); int color = ((s[1]&0x0ff)<<16)|((s[2]&0x0ff)<<8)|(s[3]&0x0ff); // System.out.print(i+" "+j+" \\"); dst.setPixel(i, j, color); } }*/ int origPixel; long startLoop = System.currentTimeMillis(); for(int j=0;j<dst.getHeight();j++){ for(int i=0;i<dst.getWidth();i++){ origPixel= input.getPixel(i,j); getRadXStart = System.currentTimeMillis(); float x = getRadialX((float)j,(float)i,centerX,centerY,k); getRadXEnd= System.currentTimeMillis(); float y = getRadialY((float)j,(float)i,centerX,centerY,k); sampleImage(input,x,y); int color = ((s[1]&0x0ff)<<16)|((s[2]&0x0ff)<<8)|(s[3]&0x0ff); // System.out.print(i+" "+j+" \\"); if( Math.sqrt( Math.pow(i - centerX, 2) + ( Math.pow(j - centerY, 2) ) ) <= 150 ){ dst.setPixel(i, j, color); }else{ dst.setPixel(i,j,origPixel); } } } long endLoop = System.currentTimeMillis(); long loopDuration = endLoop - startLoop; long radXDuration = getRadXEnd - getRadXStart; long sampleDur = endSample - startSample; Log.e(TAG, "sample method took "+sampleDur+"ms"); Log.e(TAG, "getRadialX took "+radXDuration+"ms"); Log.e(TAG, "loop took "+loopDuration+"ms"); // Log.e(TAG, "***********filter.barrel() looped through bm about to return dst bm"); return dst; } void sampleImage(Bitmap arr, float idx0, float idx1) { startSample = System.currentTimeMillis(); s = new int [4]; if(idx0<0 || idx1<0 || idx0>(arr.getHeight()-1) || idx1>(arr.getWidth()-1)){ s[0]=0; s[1]=0; s[2]=0; s[3]=0; return; } float idx0_fl=(float) Math.floor(idx0); float idx0_cl=(float) Math.ceil(idx0); float idx1_fl=(float) Math.floor(idx1); float idx1_cl=(float) Math.ceil(idx1); int [] s1 = getARGB(arr,(int)idx0_fl,(int)idx1_fl); int [] s2 = getARGB(arr,(int)idx0_fl,(int)idx1_cl); int [] s3 = getARGB(arr,(int)idx0_cl,(int)idx1_cl); int [] s4 = getARGB(arr,(int)idx0_cl,(int)idx1_fl); float x = idx0 - idx0_fl; float y = idx1 - idx1_fl; s[0]= (int) (s1[0]*(1-x)*(1-y) + s2[0]*(1-x)*y + s3[0]*x*y + s4[0]*x*(1-y)); s[1]= (int) (s1[1]*(1-x)*(1-y) + s2[1]*(1-x)*y + s3[1]*x*y + s4[1]*x*(1-y)); s[2]= (int) (s1[2]*(1-x)*(1-y) + s2[2]*(1-x)*y + s3[2]*x*y + s4[2]*x*(1-y)); s[3]= (int) (s1[3]*(1-x)*(1-y) + s2[3]*(1-x)*y + s3[3]*x*y + s4[3]*x*(1-y)); endSample = System.currentTimeMillis(); } int [] getARGB(Bitmap buf,int x, int y){ int rgb = buf.getPixel(y, x); // Returns by default ARGB. int [] scalar = new int[4]; scalar[0] = (rgb >>> 24) & 0xFF; scalar[1] = (rgb >>> 16) & 0xFF; scalar[2] = (rgb >>> 8) & 0xFF; scalar[3] = (rgb >>> 0) & 0xFF; return scalar; } float getRadialX(float x,float y,float cx,float cy,float k){ x = (x*xscale+xshift); y = (y*yscale+yshift); float res = x+((x-cx)*k*((x-cx)*(x-cx)+(y-cy)*(y-cy))); return res; } float getRadialY(float x,float y,float cx,float cy,float k){ x = (x*xscale+xshift); y = (y*yscale+yshift); float res = y+((y-cy)*k*((x-cx)*(x-cx)+(y-cy)*(y-cy))); return res; } float thresh = 1; float calc_shift(float x1,float x2,float cx,float k){ float x3 = (float)(x1+(x2-x1)*0.5); float res1 = x1+((x1-cx)*k*((x1-cx)*(x1-cx))); float res3 = x3+((x3-cx)*k*((x3-cx)*(x3-cx))); if(res1>-thresh && res1 < thresh) return x1; if(res3<0){ return calc_shift(x3,x2,cx,k); } else{ return calc_shift(x1,x3,cx,k); } } }// end of filters class 

[Update] He creado los arrays como variable de instancia y los instancié en el constructor Filter (). ¿Es esto lo que querías decir? La aplicación se ejecutaba en 84 segundos (error), pero ahora se ejecuta en 69 segundos. Parece que no hay ningún GC desconectado.

 class Filters{ private float xscale; private float yscale; private float xshift; private float yshift; private int [] s; private int [] scalar; private int [] s1; private int [] s2; private int [] s3; private int [] s4; private String TAG = "Filters"; long getRadXStart = 0; long getRadXEnd = 0; long startSample = 0; long endSample = 0; public Filters(){ Log.e(TAG, "***********inside filter constructor"); s = new int[4]; scalar = new int[4]; s1 = new int[4]; s2 = new int[4]; s3 = new int[4]; s4 = new int[4]; } public Bitmap barrel (Bitmap input, float k){ //Log.e(TAG, "***********INSIDE BARREL METHOD "); Debug.startMethodTracing("barrel"); float centerX=input.getWidth()/2; //center of distortion float centerY=input.getHeight()/2; int width = input.getWidth(); //image bounds int height = input.getHeight(); Bitmap dst = Bitmap.createBitmap(width, height,input.getConfig() ); //output pic // Log.e(TAG, "***********dst bitmap created "); xshift = calc_shift(0,centerX-1,centerX,k); float newcenterX = width-centerX; float xshift_2 = calc_shift(0,newcenterX-1,newcenterX,k); yshift = calc_shift(0,centerY-1,centerY,k); float newcenterY = height-centerY; float yshift_2 = calc_shift(0,newcenterY-1,newcenterY,k); xscale = (width-xshift-xshift_2)/width; // Log.e(TAG, "***********xscale ="+xscale); yscale = (height-yshift-yshift_2)/height; // Log.e(TAG, "***********yscale ="+yscale); // Log.e(TAG, "***********filter.barrel() about to loop through bm"); /*for(int j=0;j<dst.getHeight();j++){ for(int i=0;i<dst.getWidth();i++){ float x = getRadialX((float)i,(float)j,centerX,centerY,k); float y = getRadialY((float)i,(float)j,centerX,centerY,k); sampleImage(input,x,y); int color = ((s[1]&0x0ff)<<16)|((s[2]&0x0ff)<<8)|(s[3]&0x0ff); // System.out.print(i+" "+j+" \\"); dst.setPixel(i, j, color); } }*/ int origPixel; long startLoop = System.currentTimeMillis(); for(int j=0;j<dst.getHeight();j++){ for(int i=0;i<dst.getWidth();i++){ origPixel= input.getPixel(i,j); getRadXStart = System.currentTimeMillis(); float x = getRadialX((float)j,(float)i,centerX,centerY,k); getRadXEnd= System.currentTimeMillis(); float y = getRadialY((float)j,(float)i,centerX,centerY,k); sampleImage(input,x,y); int color = ((s[1]&0x0ff)<<16)|((s[2]&0x0ff)<<8)|(s[3]&0x0ff); // System.out.print(i+" "+j+" \\"); if( Math.sqrt( Math.pow(i - centerX, 2) + ( Math.pow(j - centerY, 2) ) ) <= 150 ){ dst.setPixel(i, j, color); }else{ dst.setPixel(i,j,origPixel); } } } long endLoop = System.currentTimeMillis(); long loopDuration = endLoop - startLoop; long radXDuration = getRadXEnd - getRadXStart; long sampleDur = endSample - startSample; Log.e(TAG, "sample method took "+sampleDur+"ms"); Log.e(TAG, "getRadialX took "+radXDuration+"ms"); Log.e(TAG, "loop took "+loopDuration+"ms"); // Log.e(TAG, "***********filter.barrel() looped through bm about to return dst bm"); Debug.stopMethodTracing(); return dst; } void sampleImage(Bitmap arr, float idx0, float idx1) { startSample = System.currentTimeMillis(); // s = new int [4]; if(idx0<0 || idx1<0 || idx0>(arr.getHeight()-1) || idx1>(arr.getWidth()-1)){ s[0]=0; s[1]=0; s[2]=0; s[3]=0; return; } float idx0_fl=(float) Math.floor(idx0); float idx0_cl=(float) Math.ceil(idx0); float idx1_fl=(float) Math.floor(idx1); float idx1_cl=(float) Math.ceil(idx1); /* int [] s1 = getARGB(arr,(int)idx0_fl,(int)idx1_fl); int [] s2 = getARGB(arr,(int)idx0_fl,(int)idx1_cl); int [] s3 = getARGB(arr,(int)idx0_cl,(int)idx1_cl); int [] s4 = getARGB(arr,(int)idx0_cl,(int)idx1_fl);*/ s1 = getARGB(arr,(int)idx0_fl,(int)idx1_fl); s2 = getARGB(arr,(int)idx0_fl,(int)idx1_cl); s3 = getARGB(arr,(int)idx0_cl,(int)idx1_cl); s4 = getARGB(arr,(int)idx0_cl,(int)idx1_fl); float x = idx0 - idx0_fl; float y = idx1 - idx1_fl; s[0]= (int) (s1[0]*(1-x)*(1-y) + s2[0]*(1-x)*y + s3[0]*x*y + s4[0]*x*(1-y)); s[1]= (int) (s1[1]*(1-x)*(1-y) + s2[1]*(1-x)*y + s3[1]*x*y + s4[1]*x*(1-y)); s[2]= (int) (s1[2]*(1-x)*(1-y) + s2[2]*(1-x)*y + s3[2]*x*y + s4[2]*x*(1-y)); s[3]= (int) (s1[3]*(1-x)*(1-y) + s2[3]*(1-x)*y + s3[3]*x*y + s4[3]*x*(1-y)); endSample = System.currentTimeMillis(); } int [] getARGB(Bitmap buf,int x, int y){ int rgb = buf.getPixel(y, x); // Returns by default ARGB. // int [] scalar = new int[4]; scalar[0] = (rgb >>> 24) & 0xFF; scalar[1] = (rgb >>> 16) & 0xFF; scalar[2] = (rgb >>> 8) & 0xFF; scalar[3] = (rgb >>> 0) & 0xFF; return scalar; } float getRadialX(float x,float y,float cx,float cy,float k){ x = (x*xscale+xshift); y = (y*yscale+yshift); float res = x+((x-cx)*k*((x-cx)*(x-cx)+(y-cy)*(y-cy))); return res; } float getRadialY(float x,float y,float cx,float cy,float k){ x = (x*xscale+xshift); y = (y*yscale+yshift); float res = y+((y-cy)*k*((x-cx)*(x-cx)+(y-cy)*(y-cy))); return res; } float thresh = 1; float calc_shift(float x1,float x2,float cx,float k){ float x3 = (float)(x1+(x2-x1)*0.5); float res1 = x1+((x1-cx)*k*((x1-cx)*(x1-cx))); float res3 = x3+((x3-cx)*k*((x3-cx)*(x3-cx))); if(res1>-thresh && res1 < thresh) return x1; if(res3<0){ return calc_shift(x3,x2,cx,k); } else{ return calc_shift(x1,x3,cx,k); } } }// end of filters class 

Por lo que veo, su código está haciendo lo siguiente:

 for (every pixel in bitmap){ getPixel(); ...do something to pixel... setPixel(); } 

Las llamadas de función getPixel() y setPixel() son relativamente caras. En lugar de llamarlos una y otra vez en su bucle, podría intentar obtener todos los píxeles en una matriz mediante getPixels () , y luego acceder a cada píxel a través de la matriz. Refiérase a esta respuesta .

Si eso todavía no es suficiente, trate de codificar lo anterior en C + + a través del NDK .

En primer lugar, mida algunas partes de su función y vea dónde están los cuellos de botella. No intente optimizar por conjeturas.

Dicho esto, ahora voy a intentar dicha tarea 🙂

Hacer un sqrt() por píxel es bastante caro – usted está comparando con una constante, por lo que en su lugar, cuadrado de la constante y comparar el valor cuadrado con que:

 if( ( Math.pow(i - centerX, 2) + ( Math.pow(j - centerY, 2) ) ) <= 150*150 ){ 

También usar pow(x,2) para cuadrar algo es probablemente llamar a la función de biblioteca para pow() , convertir su float s a s double , hacer un algoritmo de potenciación de propósito general y convertir de nuevo a float . Solo usa x*x lugar.

 if(((i-centerX)*(i-centerX) + (j-centerY)*(j-centerY)) <= 150){ 

Una cosa que puedes intentar es evitar crear / recrear los arrays int en 'sampleImage' y 'getARGB' instanciándolos una vez fuera de los dos bucles anidados y pasándolos a esos métodos. Esto no sería la mejor práctica desde un punto de vista de mantenimiento de código. Sin embargo, evitaría la creación de objetos repetidos, la inicialización de la matriz y la recolección de basura. Estos tienden a ser mucho más costosos que las operaciones aritméticas en el resto del código.

  • Diferencia entre los formatos de imagen RGB888 y ARGB8888
  • Problema con el análisis Fecha cadena:
  • Extraño resultado de análisis de fecha y hora con SimpleDateFormat
  • Android convertir Imagen de ImageReader a YCbCr_420_SP (NV21) matriz de bytes utilizando script de render?
  • DSP biblioteca para hacer una aplicación de Android ..?
  • Procesamiento de imágenes en android con escala de estiramiento y efecto Twist
  • Objeto de fecha SimpleDateFormat no analiza correctamente la cadena de hora en el entorno Java (Android)
  • Bibliotecas de filtros de imágenes de Android
  • Cómo implementar efectos de distorsión de imagen en Android
  • ¿Qué código CSS utilizo para que mi página web llene la pantalla en un iPhone o Android?
  • Android Image Recognition API / library (no en la nube)
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.