2011-06-19 106 views
5

我有以下類來處理一個位圖放置魚眼失真。如何優化圖像處理類

我已經通過TraceView運行我的應用程序,發現幾乎所有的處理時間都花在循環位圖上。
一位開發人員建議不要使用浮點數,因爲這會降低圖形所關注的部分。還需要使用math.pow()和ceil()嗎?
目前通過遍歷整個位圖放置效果大約需要42秒,是秒:)
我試過用整數替換浮動,並已減少時間37秒,但效果是沒有在位圖上存在更長的時間。
arg k最初是一個浮點數,並設置失真的級別,例如0.0002F,如果我傳遞一個int,則該效果不起作用。

任何人都可以指出我如何優化這個過程的正確方向?一旦我優化了它,我想研究一下可能沒有循環遍歷整個位圖,也許會在效果周圍放置邊界框,或者使用下面的算法確定像素是否在半徑爲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 

[更新] 我創建了數組作爲實例變量,並在Filter()構造函數中實例化它們。這是你的意思嗎?該應用程序運行在84秒(錯誤),但現在運行在69秒。似乎也沒有註銷GC。

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 
+0

你可以嘗試代碼審查棧上張貼這樣的:http://codereview.stackexchange.com/ –

+0

@joe好,謝謝,我已經貼吧 – turtleboy

回答

1

從我所看到的,你的代碼執行以下操作:

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

getPixel()setPixel()函數調用相對昂貴。您可以嘗試使用getPixels()將所有像素放入數組,然後通過數組訪問每個像素,而不是在循環中反覆調用它們。請參閱此answer

如果仍然不夠,請嘗試使用C++編碼NDK

+0

@jim lim嗨,是的,我編碼應用程序最後一週做到這一點,然後使用「Bitmap.createBitmap(array,x,y,config);」方法。處理時間從50秒到6秒!謝謝 – turtleboy

+0

林嗨,我在想,ndk也許應該走的路。我是否需要手機的ms visual studio來將C++與android整合?或者我只需要任何C++編譯器來創建類文件,然後通過ndk調用它們?謝謝亞光 – turtleboy

+0

不,你不需要任何微軟技術來爲Android開發東西。下載NDK並通過一些[示例應用程序](http://developer.android.com/sdk/ndk/overview.html#samples)瞭解如何使用它。簡而言之,您編寫C/C++代碼並使用NDK提供的編譯器。我也寫了關於NDK的[這裏](http://blog.jh-lim.com/2011/07/compiling-open-source-libraries-for-android-part-2/)和[here](http ://blog.jh-lim.com/2011/06/compiling-open-source-libraries-for-android-part-1/)。 –

0

有一兩件事你可以嘗試是避免由兩個嵌套的循環外,一旦實例並將其傳遞到這些方法創建/重建在「sampleImage」和「getARGB」整型數組。從代碼可維護性的角度來看,這不是最佳實踐。但是,它會避免重複創建對象,數組初始化和垃圾收集開銷。這些代碼往往比其餘代碼中的算術運算要昂貴得多。

+0

@btreat是的,我會嘗試這個當我運行的應用程序似乎有很多GC在圖像顯示之前 – turtleboy

+0

@btreat我更新了帖子謝謝 – turtleboy

+0

@turtleboy - 是的,這是我的建議。看起來它提高了15-20%,這是一個好的開始。另一個優化機會是將局部變量中sampleImage結尾處的s [0-3]計算的一些值存儲起來。 – btreat

2

首先 - 衡量你的函數的一些部分,看看瓶頸在哪裏。不要試圖通過猜測來優化。

說了這麼多,我現在就嘗試說任務:)

做一個sqrt()每個像素是相當昂貴的 - 你是比較恆定的,所以不是,方恆和比較平方值即:

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

而且使用pow(x,2)方的東西可能是調用庫函數爲pow(),將您float s到double,但在進行通用動力提升算法和轉換回float秒。改爲使用x*x

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