[Android]-图片JNI(C++\Java)高斯模糊的实现与比较

来源:互联网 发布:淘宝奇葩 编辑:程序博客网 时间:2024/05/29 11:20

新文章:[Android] 图片JNI(C++\Java)高斯模糊 多线程

前几天一直在弄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=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[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+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();}

同时找到一个借鉴这个所改进后成为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:QiujuerDate:2014-04-18Description:实现图片模糊处理**************************************************/#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:          mallocTable Accessed: NULLTable Updated:NULLInput:          像素点集合,图片宽,图片高,模糊半径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);}
在改为这个的过程中还遇到 了一个很喜剧的问题,我发现我使用这个来进行调用后结果程序内存一直增大,直到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);

对应的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");    }}

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

此时我做了3种测试,一种是直接在Java层实现,一种是传递像素点集合模糊,还有就是直接传递图片进行模糊,结果如下:




通过上面的比较我们可以得出这样的结论:

1.Java的确最慢,但是其实也慢不了多少,虚拟机优化好了一样猛。

2.C中直接传递像素集合的速度最快(第一次启动)

3.在我多次切换界面后发现,直接传递像素点集合的耗时会增加,从60多到120多。

4.多次切换后发现,其实直接传递像素点的速度与传递图片过去的速度几乎一样。

5.多次操作后发现传递文件的波动较小,在100~138之间,其次是传递像素点集合的波动较大,java的波动最大!

以上就是我的结论,可能有些不正确,但是在我的机器上的确是这样!

注:勾选选择框“Downscale before blur”会先压缩图片后模糊然后放大图片,这样的情况下,模糊效果会稍微损失一些效果,但是其速度确实无法比拟的。

其耗时在:1~10ms内可运算完成。当然与你要模糊的大小有关系!


最后:项目地址:GitHub

原创作品,转载请注明出处!

    QIUJUER(qiujuer@live.cn)


29 0