简体   繁体   English

Android,立即模糊Bitmap?

[英]Android, Blur Bitmap instantly?

So I'm trying to blur an image as fast as possible(instant feel like), as the activity needs to be updated as I press the Blur button. 所以我试图尽可能快地模糊图像(即时感觉),因为当我按下模糊按钮时需要更新活动。

The problem I am having is that, I cannot find a Blur that works quick enough... Note: The blur, preferably a Gaussian blur, doesn't need to be the best quality at all.. 我遇到的问题是,我找不到一个足够快的模糊 ... 注意:模糊,最好是高斯模糊,根本不需要是最好的质量..

I tried out the following, but it takes a few seconds, is there anyway this code could be made to run quicker in sacrifice of quality ? 我尝试了以下内容,但需要几秒钟,无论如何,这些代码可以在牺牲质量时更快地运行吗? Or are there any other alternatives? 还是有其他选择吗? I would look into GPU stuff, but this blur is really just an effect related to the UI and only happens when I press open a transparent activity sized as a small box... 我会研究GPU的东西,但这种模糊实际上只是一个与UI有关的效果,只有当我按下打开一个大小为一个小盒子的透明活动时才会发生...

Any Ideas? 有任何想法吗?

static Bitmap fastblur(Bitmap sentBitmap, int radius, int fromX, int fromY,
    int width, int height) {

// 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 = sentBitmap.copy(sentBitmap.getConfig(), true);

if (radius < 1) {
    return (null);
}

int w = width;
int h = height;

int[] pix = new int[w * h];

bitmap.getPixels(pix, 0, w, fromX, fromY, 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;

int originRadius = radius;
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;
}

radius = originRadius;

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++) {
        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] = 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, fromX, fromY, w, h);

return (bitmap);

} }

Try to scale down the image 2, 4, 8, ... times and then scale it up again. 尝试缩小图像2,4,8,...次,然后再将其放大。 That is fast. 那很快。 Otherwise implement it in renderscript. 否则在renderscript中实现它。

If you want more than the scaling you can look at this code snippet in renderscript. 如果您想要比缩放更多,可以在renderscript中查看此代码段。 It does the same kind of bluring as given in another answer. 它具有与另一个答案中给出的相同的模糊效果。 The same algorithm can be implemented in Java and is an optimization of the other answer. 可以用Java实现相同的算法,并且是另一个答案的优化。 This code blurs one line. 这段代码模糊了一行。 To blur a bitmap you should invoke this for all lines and then the same for all columns (you need to reimplement it to handle columns). 要模糊位图,您应该为所有行调用此方法,然后对所有列调用相同的方法(您需要重新实现它以处理列)。 To get a quick blur just do this once. 为了快速模糊,只需执行一次。 If you want a better looking blur do it several times. 如果你想要一个更好看的模糊,做几次。 I usually only do it twice. 我通常只做两次。

The reason for doing one line is that I tried to parallelize the algorithm, that gave some improvement and is really simple in renderscript. 做一行的原因是我试图并行化算法,这给了一些改进,并且在renderscript中非常简单。 I invoked the below code for all lines in parallel, and then the same for all columns. 我为所有行并行调用了下面的代码,然后对所有列调用相同的代码。

int W = 8;
uchar4 *in;
uchar4 *out;    

int N;
float invN;    

uint32_t nx;
uint32_t ny;    

void init_calc() {
  N = 2*W+1;
  invN = 1.0f/N;    

  nx = rsAllocationGetDimX(rsGetAllocation(in));
  ny = rsAllocationGetDimY(rsGetAllocation(in));
}    

void root(const ushort *v_in) {
  float4 sum = 0;    

  uchar4 *head = in + *v_in * nx;
  uchar4 *tail = head;
  uchar4 *p = out + *v_in * nx;    

  uchar4 *hpw = head + W;
  uchar4 *hpn = head + N;
  uchar4 *hpx = head + nx;
  uchar4 *hpxmw = head + nx - W - 1;    

  while (head < hpw) {
      sum += rsUnpackColor8888(*head++);
  }    

  while (head < hpn) {
      sum += rsUnpackColor8888(*head++);
      *p++ = rsPackColorTo8888(sum*invN);
  }    

  while (head < hpx) {
    sum += rsUnpackColor8888(*head++);
    sum -= rsUnpackColor8888(*tail++);
    *p++ = rsPackColorTo8888(sum*invN);
  }    

  while (tail < hpxmw) {
      sum -= rsUnpackColor8888(*tail++);
      *p++ = rsPackColorTo8888(sum*invN);
  }
}

Here is for the vertical bluring: 这是垂直模糊:

int W = 8;
uchar4 *in;
uchar4 *out;    

int N;
float invN;    

uint32_t nx;
uint32_t ny;    

void init_calc() {
  N = 2*W+1;
  invN = 1.0f/N;    

  nx = rsAllocationGetDimX(rsGetAllocation(in));
  ny = rsAllocationGetDimY(rsGetAllocation(in));
}    

void root(const ushort *v_in) {
  float4 sum = 0;    

  uchar4 *head = in + *v_in;
  uchar4 *tail = head;
  uchar4 *hpw = head + nx*W;
  uchar4 *hpn = head + nx*N;
  uchar4 *hpy = head + nx*ny;
  uchar4 *hpymw = head + nx*(ny-W-1);    

  uchar4 *p = out + *v_in;    

  while (head < hpw) {
      sum += rsUnpackColor8888(*head);
      head += nx;
  }    

  while (head < hpn) {
      sum += rsUnpackColor8888(*head);
      *p = rsPackColorTo8888(sum*invN);
      head += nx;
      p += nx;
  }    

  while (head < hpy) {
      sum += rsUnpackColor8888(*head);
      sum -= rsUnpackColor8888(*tail);
      *p = rsPackColorTo8888(sum*invN);
      head += nx;
      tail += nx;
      p += nx;
  }    

  while (tail < hpymw) {
      sum -= rsUnpackColor8888(*tail);
      *p = rsPackColorTo8888(sum*invN);
      tail += nx;
      p += nx;
  }
}

And here is the Java code that calls into the rs code: 这是调用rs代码的Java代码:

private RenderScript mRS;
private ScriptC_horzblur mHorizontalScript;
private ScriptC_vertblur mVerticalScript;
private ScriptC_blur mBlurScript;

private Allocation alloc1;
private Allocation alloc2;

private void hblur(int radius, Allocation index, Allocation in, Allocation out) {
    mHorizontalScript.set_W(radius);
    mHorizontalScript.bind_in(in);
    mHorizontalScript.bind_out(out);
    mHorizontalScript.invoke_init_calc();
    mHorizontalScript.forEach_root(index);
}

private void vblur(int radius, Allocation index, Allocation in, Allocation out) {
    mHorizontalScript.set_W(radius);
    mVerticalScript.bind_in(in);
    mVerticalScript.bind_out(out);
    mVerticalScript.invoke_init_calc();
    mVerticalScript.forEach_root(index);
}

Bitmap blur(Bitmap org, int radius) {
    Bitmap out = Bitmap.createBitmap(org.getWidth(), org.getHeight(), org.getConfig());

    blur(org, out, radius);

    return out;
}

private Allocation createIndex(int size) {
    Element element = Element.U16(mRS);
    Allocation allocation = Allocation.createSized(mRS, element, size);
    short[] rows = new short[size];
    for (int i = 0; i < rows.length; i++) rows[i] = (short)i;
    allocation.copyFrom(rows);

    return allocation;
}

private void blur(Bitmap src, Bitmap dst, int r) {
    Allocation alloc1 = Allocation.createFromBitmap(mRS, src);
    Allocation alloc2 = Allocation.createTyped(mRS, alloc1.getType());

    Allocation hIndexAllocation = createIndex(alloc1.getType().getY());
    Allocation vIndexAllocation = createIndex(alloc1.getType().getX());

    // Iteration 1
    hblur(r, hIndexAllocation, alloc1, alloc2);
    vblur(r, vIndexAllocation, alloc2, alloc1);
    // Iteration 2
    hblur(r, hIndexAllocation, alloc1, alloc2);
    vblur(r, vIndexAllocation, alloc2, alloc1);
    // Add more iterations if you like or simply make a loop
    alloc1.copyTo(dst);
}

Gaussian blur is expensive to do accurately. 准确地进行高斯模糊是很昂贵的。 A much faster approximation can be done by just iteratively averaging the pixels. 通过迭代平均像素可以实现更快的近似。 It's still expensive to blur the image a lot but you can redraw between each iteration to at least give instant feedback and a nice animation of the image blurring. 模糊图像仍然很昂贵但是你可以在每次迭代之间重绘,以至少给出即时反馈和图像模糊的漂亮动画。

static void blurfast(Bitmap bmp, int radius) {
  int w = bmp.getWidth();
  int h = bmp.getHeight();
  int[] pix = new int[w * h];
  bmp.getPixels(pix, 0, w, 0, 0, w, h);

  for(int r = radius; r >= 1; r /= 2) {
    for(int i = r; i < h - r; i++) {
      for(int j = r; j < w - r; j++) {
        int tl = pix[(i - r) * w + j - r];
        int tr = pix[(i - r) * w + j + r];
        int tc = pix[(i - r) * w + j];
        int bl = pix[(i + r) * w + j - r];
        int br = pix[(i + r) * w + j + r];
        int bc = pix[(i + r) * w + j];
        int cl = pix[i * w + j - r];
        int cr = pix[i * w + j + r];

        pix[(i * w) + j] = 0xFF000000 |
            (((tl & 0xFF) + (tr & 0xFF) + (tc & 0xFF) + (bl & 0xFF) + (br & 0xFF) + (bc & 0xFF) + (cl & 0xFF) + (cr & 0xFF)) >> 3) & 0xFF |
            (((tl & 0xFF00) + (tr & 0xFF00) + (tc & 0xFF00) + (bl & 0xFF00) + (br & 0xFF00) + (bc & 0xFF00) + (cl & 0xFF00) + (cr & 0xFF00)) >> 3) & 0xFF00 |
            (((tl & 0xFF0000) + (tr & 0xFF0000) + (tc & 0xFF0000) + (bl & 0xFF0000) + (br & 0xFF0000) + (bc & 0xFF0000) + (cl & 0xFF0000) + (cr & 0xFF0000)) >> 3) & 0xFF0000;
      }
    }
  }
  bmp.setPixels(pix, 0, w, 0, 0, w, h);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM