java Android,立即模糊位图?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13435561/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 12:42:19  来源:igfitidea点击:

Android, Blur Bitmap instantly?

javaandroidalgorithmbitmapblur

提问by rennoDeniro

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 <[email protected]>

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);

}

}

回答by Tobias Ritzau

Try to scale down the image 2, 4, 8, ... times and then scale it up again. That is fast. Otherwise implement it in renderscript.

尝试将图像缩小 2、4、8、... 倍,然后再次放大。那很快。否则在渲染脚本中实现它。

If you want more than the scaling you can look at this code snippet in 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. 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.

如果您想要的不仅仅是缩放,您可以在渲染脚本中查看此代码片段。它执行与另一个答案中给出的相同类型的模糊。相同的算法可以在 Java 中实现,并且是另一个答案的优化。这段代码模糊了一行。要模糊位图,您应该对所有行调用它,然后对所有列调用它(您需要重新实现它以处理列)。要快速模糊,只需执行一次。如果您想要更好看的模糊效果,请多做几次。我通常只做两次。

The reason for doing one line is that I tried to parallelize the algorithm, that gave some improvement and is really simple in 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);
}

回答by gordy

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);
}