[Android]-圖片JNI(C++\Java)高斯模糊的實現與比較

xiaoasdf 8年前發布 | 10K 次閱讀 Android開發 移動開發

轉自:http://blog.csdn.net/qiujuer/article/details/24282047

前幾天一直在弄android上的圖片模糊效果的實現!

一直找不到方法,看別人說都是調用JNI,但是JNI這個東西我還真不熟悉啊!

只好從零開始了!這里不講JNI的平臺搭建,只講JNI的關鍵代碼,具體的項目我會共享出來給大家!

對于JNI下使用C++來模糊圖片這個我真的沒找到,只好自己寫C++的來實現了。

在國外的一個項目中找到了一個”堆棧模糊效果“,原型如下:

// Stack Blur v1.0
//
// Author: Mario Klingemann <mario@quasimondo.com>
// http://incubator.quasimondo.com
// created Feburary 29, 2004
// This is a compromise between Gaussian Blur and Box blur
// It creates much better looking blurs than Box Blur, but is
// 7x faster than my Gaussian Blur implementation.
//
// I called it Stack Blur because this describes best how this
// filter works internally: it creates a kind of moving stack
// of colors whilst scanning through the image. Thereby it
// just has to add one new block of color to the right side
// of the stack and remove the leftmost color. The remaining
// colors on the topmost layer of the stack are either added on
// or reduced by one, depending on if they are on the right or
// on the left side of the stack.
//
// If you are using this algorithm in your code please add
// the following line:
//
// Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
PImage a;
PImage b;
void setup()
{
  a=loadImage("dog.jpg");
  size(a.width, a.height);
  b=new PImage(a.width, a.height);
  fill(255);
  noStroke();
  frameRate(25);
}
void draw()
{
  System.arraycopy(a.pixels,0,b.pixels,0,a.pixels.length);
  fastblur(b,mouseY/4);
  image(b, 0, 0);
}
void fastblur(PImage img,int radius){
  if (radius<1){
    return;
  }
  int[] pix=img.pixels;
  int w=img.width;
  int h=img.height;
  int wm=w-1;
  int hm=h-1;
  int wh=wh;
  int div=radius+radius+1;
  int r[]=new int[wh];
  int g[]=new int[wh];
  int b[]=new int[wh];
  int rsum,gsum,bsum,x,y,i,p,yp,yi,yw;
  int vmin[] = new int[max(w,h)];
  int divsum=(div+1)>>1;
  divsum=divsum;
  int dv[]=new int[256divsum];
  for (i=0;i<256divsum;i++){
    dv[i]=(i/divsum);
  }
  yw=yi=0;
  int[][] stack=new int[div][3];
  int stackpointer;
  int stackstart;
  int[] sir;
  int rbs;
  int r1=radius+1;
  int routsum,goutsum,boutsum;
  int rinsum,ginsum,binsum;
  for (y=0;y<h;y++){
    rinsum=ginsum=binsum=routsum=goutsum=boutsum=rsum=gsum=bsum=0;
    for(i=-radius;i<=radius;i++){
      p=pix[yi+min(wm,max(i,0))];
      sir=stack[i+radius];
      sir[0]=(p & 0xff0000)>>16;
      sir[1]=(p & 0x00ff00)>>8;
      sir[2]=(p & 0x0000ff);
      rbs=r1-abs(i);
      rsum+=sir[0]rbs;
      gsum+=sir[1]rbs;
      bsum+=sir[2]*rbs;
      if (i>0){
        rinsum+=sir[0];
        ginsum+=sir[1];
        binsum+=sir[2];
      } else {
        routsum+=sir[0];
        goutsum+=sir[1];
        boutsum+=sir[2];
      }
    }
    stackpointer=radius;
    for (x=0;x<w;x++){
      r[yi]=dv[rsum];
      g[yi]=dv[gsum];
      b[yi]=dv[bsum];

  rsum-=routsum;
  gsum-=goutsum;
  bsum-=boutsum;
  stackstart=stackpointer-radius+div;
  sir=stack[stackstart%div];

  routsum-=sir[0];
  goutsum-=sir[1];
  boutsum-=sir[2];

  if(y==0){
    vmin[x]=min(x+radius+1,wm);
  }
  p=pix[yw+vmin[x]];

  sir[0]=(p & 0xff0000)>>16;
  sir[1]=(p & 0x00ff00)>>8;
  sir[2]=(p & 0x0000ff);
  rinsum+=sir[0];
  ginsum+=sir[1];
  binsum+=sir[2];
  rsum+=rinsum;
  gsum+=ginsum;
  bsum+=binsum;

  stackpointer=(stackpointer+1)%div;
  sir=stack[(stackpointer)%div];

  routsum+=sir[0];
  goutsum+=sir[1];
  boutsum+=sir[2];

   rinsum-=sir[0];
  ginsum-=sir[1];
  binsum-=sir[2];

   yi++;
}
yw+=w;

} for (x=0;x<w;x++){ rinsum=ginsum=binsum=routsum=goutsum=boutsum=rsum=gsum=bsum=0; yp=-radius*w; for(i=-radius;i<=radius;i++){ yi=max(0,yp)+x;

   sir=stack[i+radius];

  sir[0]=r[yi];
  sir[1]=g[yi];
  sir[2]=b[yi];

  rbs=r1-abs(i);

  rsum+=r[yi]*rbs;
  gsum+=g[yi]*rbs;
  bsum+=b[yi]*rbs;

  if (i>0){
    rinsum+=sir[0];
    ginsum+=sir[1];
    binsum+=sir[2];
  } else {
    routsum+=sir[0];
    goutsum+=sir[1];
    boutsum+=sir[2];
  }

  if(i<hm){
    yp+=w;
  }
}
yi=x;
stackpointer=radius;
for (y=0;y<h;y++){
  pix[yi]=0xff000000 | (dv[rsum]<<16) | (dv[gsum]<<8) | dv[bsum];
  rsum-=routsum;
  gsum-=goutsum;
  bsum-=boutsum;
  stackstart=stackpointer-radius+div;
  sir=stack[stackstart%div];

  routsum-=sir[0];
  goutsum-=sir[1];
  boutsum-=sir[2];

   if(x==0){
    vmin[y]=min(y+r1,hm)*w;
  }
  p=x+vmin[y];

  sir[0]=r[p];
  sir[1]=g[p];
  sir[2]=b[p];

  rinsum+=sir[0];
  ginsum+=sir[1];
  binsum+=sir[2];
  rsum+=rinsum;
  gsum+=ginsum;
  bsum+=binsum;
  stackpointer=(stackpointer+1)%div;
  sir=stack[stackpointer];

  routsum+=sir[0];
  goutsum+=sir[1];
  boutsum+=sir[2];

  rinsum-=sir[0];
  ginsum-=sir[1];
  binsum-=sir[2];
  yi+=w;
}

}

img.updatePixels(); }</pre>

同時找到一個借鑒這個所改進后成為Java的代碼,具體如下:

public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) {
    // Stack Blur v1.0 from
    // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
    //
    // Java Author: Mario Klingemann <mario at quasimondo.com>
    // http://incubator.quasimondo.com
    // created Feburary 29, 2004
    // Android port : Yahel Bouaziz <yahel at kayenko.com>
    // http://www.kayenko.com
    // ported april 5th, 2012
    // This is a compromise between Gaussian Blur and Box blur
    // It creates much better looking blurs than Box Blur, but is
    // 7x faster than my Gaussian Blur implementation.
    //
    // I called it Stack Blur because this describes best how this
    // filter works internally: it creates a kind of moving stack
    // of colors whilst scanning through the image. Thereby it
    // just has to add one new block of color to the right side
    // of the stack and remove the leftmost color. The remaining
    // colors on the topmost layer of the stack are either added on
    // or reduced by one, depending on if they are on the right or
    // on the left side of the stack.
    //
    // If you are using this algorithm in your code please add
    // the following line:
    //
    // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
    Bitmap bitmap;
    if (canReuseInBitmap) {
        bitmap = sentBitmap;
    } else {
        bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
    }
    if (radius < 1) {
        return (null);
    }
    int w = bitmap.getWidth();
    int h = bitmap.getHeight();
    int[] pix = new int[w * h];
    bitmap.getPixels(pix, 0, w, 0, 0, w, h);
    int wm = w - 1;
    int hm = h - 1;
    int wh = w * h;
    int div = radius + radius + 1;
    int r[] = new int[wh];
    int g[] = new int[wh];
    int b[] = new int[wh];
    int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
    int vmin[] = new int[Math.max(w, h)];
    int divsum = (div + 1) >> 1;
    divsum *= divsum;
    int dv[] = new int[256 * divsum];
    for (i = 0; i < 256 * divsum; i++) {
        dv[i] = (i / divsum);
    }
    yw = yi = 0;
    int[][] stack = new int[div][3];
    int stackpointer;
    int stackstart;
    int[] sir;
    int rbs;
    int r1 = radius + 1;
    int routsum, goutsum, boutsum;
    int rinsum, ginsum, binsum;
    for (y = 0; y < h; y++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        for (i = -radius; i <= radius; i++) {
            p = pix[yi + Math.min(wm, Math.max(i, 0))];
            sir = stack[i + radius];
            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);
            rbs = r1 - Math.abs(i);
            rsum += sir[0] * rbs;
            gsum += sir[1] * rbs;
            bsum += sir[2] * rbs;
            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
            }
        }
        stackpointer = radius;
        for (x = 0; x < w; x++) {
            r[yi] = dv[rsum];
            g[yi] = dv[gsum];
            b[yi] = dv[bsum];
            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;
            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];
            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];
            if (y == 0) {
                vmin[x] = Math.min(x + radius + 1, wm);
            }
            p = pix[yw + vmin[x]];
            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);
            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];
            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;
            stackpointer = (stackpointer + 1) % div;
            sir = stack[(stackpointer) % div];
            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];
            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];
            yi++;
        }
        yw += w;
    }
    for (x = 0; x < w; x++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        yp = -radius * w;
        for (i = -radius; i <= radius; i++) {
            yi = Math.max(0, yp) + x;
            sir = stack[i + radius];
            sir[0] = r[yi];
            sir[1] = g[yi];
            sir[2] = b[yi];
            rbs = r1 - Math.abs(i);
            rsum += r[yi] * rbs;
            gsum += g[yi] * rbs;
            bsum += b[yi] * rbs;
            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
            }
            if (i < hm) {
                yp += w;
            }
        }
        yi = x;
        stackpointer = radius;
        for (y = 0; y < h; y++) {
            // Preserve alpha channel: ( 0xff000000 & pix[yi] )
            pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;
            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];
            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];
            if (x == 0) {
                vmin[y] = Math.min(y + r1, hm) * w;
            }
            p = x + vmin[y];
            sir[0] = r[p];
            sir[1] = g[p];
            sir[2] = b[p];
            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];
            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;
            stackpointer = (stackpointer + 1) % div;
            sir = stack[stackpointer];
            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];
            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];
            yi += w;
        }
    }
    bitmap.setPixels(pix, 0, w, 0, 0, w, h);
    return (bitmap);
}

借鑒于此我弄了一個C的代碼,基本上的整體過程都沒有變化,只是改變成了C(C++也可已)的而已:

文件名:ImageBlur.c

/*
Copyright:  Copyright QIUJUER 2013.
Author:     Qiujuer
Date:       2014-04-18
Description:實現圖片模糊處理
**/

include<malloc.h>

define ABS(a) ((a)<(0)?(-a):(a))

define MAX(a,b) ((a)>(b)?(a):(b))

define MIN(a,b) ((a)<(b)?(a):(b))

/* Function: StackBlur(堆棧模糊) Description: 使用堆棧方式進行圖片像素模糊處理 Calls: malloc Table Accessed: NULL Table Updated: NULL Input: 像素點集合,圖片寬,圖片高,模糊半徑 Output: 返回模糊后的像素點集合 Return: 返回模糊后的像素點集合 Others: NULL */ static int StackBlur(int pix, int w, int h, int radius) { int wm = w - 1; int hm = h - 1; int wh = w h; int div = radius + radius + 1; int r = (int )malloc(wh sizeof(int)); int g = (int )malloc(wh sizeof(int)); int b = (int )malloc(wh sizeof(int)); int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; int vmin = (int )malloc(MAX(w,h) sizeof(int)); int divsum = (div + 1) >> 1; divsum = divsum; int dv = (int )malloc(256 divsum sizeof(int)); for (i = 0; i < 256 divsum; i++) { dv[i] = (i / divsum); } yw = yi = 0; int(stack)[3] = (int()[3])malloc(div 3 sizeof(int)); int stackpointer; int stackstart; int sir; int rbs; int r1 = radius + 1; int routsum, goutsum, boutsum; int rinsum, ginsum, binsum; for (y = 0; y < h; y++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; for (i = -radius; i <= radius; i++) { p = pix[yi + (MIN(wm, MAX(i, 0)))]; sir = stack[i + radius]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rbs = r1 - ABS(i); rsum += sir[0] rbs; gsum += sir[1] rbs; bsum += sir[2] rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } } stackpointer = radius; for (x = 0; x < w; x++) { r[yi] = dv[rsum]; g[yi] = dv[gsum]; b[yi] = dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (y == 0) { vmin[x] = MIN(x + radius + 1, wm); } p = pix[yw + vmin[x]]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[(stackpointer) % div]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi++; } yw += w; } for (x = 0; x < w; x++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; yp = -radius w; for (i = -radius; i <= radius; i++) { yi = MAX(0, yp) + x; sir = stack[i + radius]; sir[0] = r[yi]; sir[1] = g[yi]; sir[2] = b[yi]; rbs = r1 - ABS(i); rsum += r[yi] rbs; gsum += g[yi] rbs; bsum += b[yi] rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } if (i < hm) { yp += w; } } yi = x; stackpointer = radius; for (y = 0; y < h; y++) { // Preserve alpha channel: ( 0xff000000 & pix[yi] ) pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (x == 0) { vmin[y] = MIN(y + r1, hm) w; } p = x + vmin[y]; sir[0] = r[p]; sir[1] = g[p]; sir[2] = b[p]; rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[stackpointer]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi += w; } } free(r); free(g); free(b); free(vmin); free(dv); free(stack); return(pix); }</pre>

在改為這個的過程中還遇到 了一個很喜劇的問題,我發現我使用這個來進行調用后結果程序內存一直增大,直到500多M,直接卡死。我知道是我寫的有內存泄漏了!

然后找了一下,發現果然是。只好進行free了。然后一下就好了,發現內存占用的確比Java的要少,速度也是要快一些!

在JNI中的實現我使用了兩種方案,一種是直接傳遞文件,一直是傳遞像素點集合進行模糊!分別如下:

/*

  • Class: com_accumulation_imageblurring_app_jni_ImageBlur
  • Method: blurIntArray
  • Signature: ([IIII)V / JNIEXPORT void JNICALL Java_com_accumulation_imageblurring_app_jni_ImageBlur_blurIntArray (JNIEnv , jclass, jintArray, jint, jint, jint); /*
  • Class: com_accumulation_imageblurring_app_jni_ImageBlur
  • Method: blurBitMap
  • Signature: (Landroid/graphics/Bitmap;I)V / JNIEXPORT void JNICALL Java_com_accumulation_imageblurring_app_jni_ImageBlur_blurBitMap (JNIEnv , jclass, jobject, jint);</pre>

    對應的Java調用:

    public class ImageBlur {
     public static native void blurIntArray(int[] pImg, int w, int h, int r);
     public static native void blurBitMap(Bitmap bitmap, int r);
     static {
    
     System.loadLibrary("JNI_ImageBlur");
    
    } }</pre>

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    此時我做了3種測試,一種是直接在Java層實現,一種是傳遞像素點集合模糊,還有就是直接傳遞圖片進行模糊,結果如下:



    通過上面的比較我們可以得出這樣的結論:

    1.Java的確最慢,但是其實也慢不了多少,虛擬機優化好了一樣猛。

    2.C中直接傳遞像素集合的速度最快(第一次啟動)

    3.在我多次切換界面后發現,直接傳遞像素點集合的耗時會增加,從60多到120多。

    4.多次切換后發現,其實直接傳遞像素點的速度與傳遞圖片過去的速度幾乎一樣。

    5.多次操作后發現傳遞文件的波動較小,在100~138之間,其次是傳遞像素點集合的波動較大,java的波動最大!

    以上就是我的結論,可能有些不正確,但是在我的機器上的確是這樣!

    注:勾選選擇框“Downscale before blur”會先壓縮圖片后模糊然后放大圖片,這樣的情況下,模糊效果會稍微損失一些效果,但是其速度確實無法比擬的。

    其耗時在:1~10ms內可運算完成。當然與你要模糊的大小有關系!

    最后:項目地址:GitHub

     

 本文由用戶 xiaoasdf 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!