Android高斯模糊,android高斯

來源:互聯網
上載者:User

Android高斯模糊,android高斯

傳送門
github地址:http://developer.android.com/guide/topics/renderscript/compute.html;
https://github.com/kikoso/android-stackblur;
csdn參考知識:http://blog.csdn.net/huli870715/article/details/39378349
感謝大神們的無私奉獻,讓我從小彩筆慢慢成長成大彩筆的夢想更近一步。

什麼是高斯模糊(根據百科描述)

高斯模糊能夠把某一點周圍的像素色值按高斯曲線統計起來,採用數學上加權平均的計算方法得到這條曲線的色值,最後能夠留下人物的輪廓,即曲線。所有的顏色不過都是數字,各種模糊不過都是演算法。把要模糊的像素色值統計,用數學上加權平均的計算方法(高斯函數)得到色值,對範圍、半徑等進行模糊,大致就是高斯模糊。

高斯函數、正太分布、權重矩陣……有興趣的人可以瞭解一下,我們主要還是調用【stackblur】開源項目中名為fastBlur的方法在java層直接進行高斯模糊處理。

根據網上的一些參考資料,以及自己項目實際的要求,一開始直接調用下述方法等到以及模糊後的

 private Bitmap blurImageAmeliorate(Bitmap sentBitmap,int radius, boolean canReuseInBitmap)     {         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);      } 

結果沒有問題,就想網上描述的一樣。
但有一個問題就是直接使用原圖進行高斯模糊處理的時間有點長,大概200毫秒左右,有明顯的停頓感,即使用Handler非同步處理,但圖片展示的延後,使用者體驗明顯下降。

stackOverflow對於程式員來說永遠是最大的寶藏。http://stackoverflow.com/questions/2067955/fast-bitmap-blur-for-android-sdk這篇提問帖終於提供了新的解決思路:
This is a shot in the dark, but you might try shrinking the image and then enlarging it again. This can be done with Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter). Make sure and set the filter parameter to true. It’ll run in native code so it might be faster.
它所表述的原理為先通過縮小圖片,使其丟失一些像素點,接著進行模糊化處理,然後再放大到原來尺寸。由於圖片縮小後再進行模糊處理,需要處理的像素點和半徑都變小,從而使得模糊處理速度加快。
於是在高斯模糊演算法之外我們套接一層,進行位元影像的縮小。

private void blur(Bitmap bkg, View view) {          float radius = 2;          float scaleFactor = 8;          Bitmap overlay = Bitmap.createBitmap((int)(view.getMeasuredWidth()/scaleFactor), (int)(view.getMeasuredHeight()/scaleFactor), Bitmap.Config.ARGB_8888);          Canvas canvas = new Canvas(overlay);          canvas.translate(-view.getLeft()/scaleFactor, -view.getTop()/scaleFactor);          canvas.scale(1 / scaleFactor, 1 / scaleFactor);          Paint paint = new Paint();          paint.setFlags(Paint.FILTER_BITMAP_FLAG);          canvas.drawBitmap(bkg, 0, 0, paint);          view.setBackground(new BitmapDrawable(getResources(), blurImageAmeliorate(overlay, (int)radius, true)));      }   

Bitmap不懂,不常用有沒有。。。
Canvas不懂,不常用有沒有。。。
Paint不懂,不常用有沒有。。。
然後直接拿來調用最終的效果不錯

那麼問題來了,為什麼背景圖沒有填充整個RelativeLayout呢,我們通過RelativeLayout.setBackground來設定背景。
各種測試後問題原因在於Canvas縮小的處理,對於Canvas實在不在行,知道問題所在,也解釋不了。
現在回到正題,我們要做得是在高斯模糊處理之前得到一個縮小的位元影像,在此再感謝一下李剛老師的【瘋狂Android講義】,平時還是可以翻一翻,加深一些相關的知識。通過調用Bitmap.createScaledBitmap方法,我們可以等到一個縮小後的位元影像,而且能順利填充到組件背景,在效率上對比第一種快上不少,問題就如此處理了。。。除了Canvas的問題!

要用到高斯模糊上網查了很久,其它的處理方式我這也從別人那copy一下,以備以後有用。(以下內容,我只是用過一下下,出現的bug暫時沒有處理)

RenderScript
RenderScript是API11之後才引入的,所以對版本有限制,而且RenderScript確實挺複雜的,雖然使用他的Blur功能很簡單,但是要真正搞懂,不是一天兩天的事。學習文檔:http://developer.android.com/guide/topics/renderscript/compute.html

private void blur(Bitmap bkg, View view) {      long startMs = System.currentTimeMillis();      float radius = 20;      Bitmap overlay = Bitmap.createBitmap((int)(view.getMeasuredWidth()), (int)(view.getMeasuredHeight()), Bitmap.Config.ARGB_8888);      Canvas canvas = new Canvas(overlay);      canvas.translate(-view.getLeft(), -view.getTop());      canvas.drawBitmap(bkg, 0, 0, null);      RenderScript rs = RenderScript.create(SecondActivity.this);      Allocation overlayAlloc = Allocation.createFromBitmap(rs, overlay);      ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, overlayAlloc.getElement());      blur.setInput(overlayAlloc);      blur.setRadius(radius);      blur.forEach(overlayAlloc);      overlayAlloc.copyTo(overlay);      view.setBackground(new BitmapDrawable(getResources(), overlay));      rs.destroy();      statusText.setText("cost " + (System.currentTimeMillis() - startMs) + "ms");  }  

布局代碼就不詳細的copy過來了,實現這個方法一個要求最低的sdk版本是11,對於硬體要求是17,也就是說如果手機Android版本是4.2以下的話,應該是用不了的。我測試機還是小米1,讓我呵呵一笑。

以上差不多是自己這次接觸高斯模糊所見所學,有不足之處,請大家諒解。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.