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.
- Buscando algoritmos rápidos de distorsión de imagen
- Android, ¿Log reduce la velocidad de la aplicación?
- Crear pdf desde html utilizando Flying Saucer, renderer issue
- ¿Cómo puedo establecer un ancho mínimo (en caracteres) para un TextView?
- Resultado del procesamiento de la señal de audio con el algoritmo de Goertzel
¿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
- Android: escala y comprime un mapa de bits
- Cómo implementar efectos de distorsión de imagen en Android
- La vista personalizada de Android solo recibe onDraw una vez
- Rendimiento de procesamiento en android
- Enderezamiento de la imagen en Android
- Android - convertir el array byte rgb_565 en array argb o rgb
- Detección de datos periódicos desde el acelerómetro del teléfono
- ¿Cómo identificar la similitud (en%) entre 2 imágenes "sólo en blanco y negro"?
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.
- ¿Puedo usar ext2 o ext3 tarjeta externa SD formateada en Android 2.1
- Lo que onEndReachedThreshold realmente significa en reaccionar-nativo