1. 程式人生 > >Android之高斯模糊的記錄

Android之高斯模糊的記錄

最近在開發專案的時候遇到一個UI提出的效果就是PS裡面的高斯模糊效果,上圖

                                                          

下面其實是一段文字內容,但是當用戶沒有獲取到某種許可權的時候,是不能檢視具體的文字內容的(但是又給使用者一種下面有文字內容的模糊的感覺)。當用戶點選偷瞄一下的時候需要獲取某種許可權,使這個模糊(遮罩)效果消失,顯露出真正的文字內容。UI說在PS裡面這叫高斯模糊。自己網上看了看,其實在Android裡面也有高斯模糊效果的API,在API 11的時候RenderScript,用來進行高效的圖片處理。其實所謂的模糊,就是在以我們真實的圖片的基礎上,對這張照片來進行處理,然後將處理後的照片,放在真實圖片上面,給人一種放否能看清又不能看清的感覺,當年的微信發紅包看圖片應該也是這樣實現的吧,只是將我這裡的偷瞄一眼改成了需要發紅包,其實是異曲同工之妙吧。先來看看RenderScript對高斯模糊的操作方法吧

	public static Bitmap blurBitmap(Bitmap bitmap,Context context){  
        
        //Let's create an empty bitmap with the same size of the bitmap we want to blur  
        Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);  
          
        //Instantiate a new Renderscript  
        RenderScript rs = RenderScript.create(context);  
          
        //Create an Intrinsic Blur Script using the Renderscript  
        ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));  
          
        //Create the Allocations (in/out) with the Renderscript and the in/out bitmaps  
        Allocation allIn = Allocation.createFromBitmap(rs, bitmap);  
        Allocation allOut = Allocation.createFromBitmap(rs, outBitmap);  
          
        //Set the radius of the blur  
        blurScript.setRadius(1.f);   //其實這裡就是設定的模糊程度 值越大模糊越厲害 最後可能一團黑 哈哈
          
        //Perform the Renderscript  
        blurScript.setInput(allIn);  
        blurScript.forEach(allOut);  
          
        //Copy the final bitmap created by the out Allocation to the outBitmap  
        allOut.copyTo(outBitmap);  
          
        //recycle the original bitmap  
        bitmap.recycle();  
          
        //After finishing everything, we destroy the Renderscript.  
        rs.destroy();  
          
        return outBitmap;    
    }  
這裡高斯模糊的邏輯其實就是 ,根據你所傳入的bitmap,在setRadius(int)設定模糊程度,用RenderScript呼叫之後 模糊之後 返回模糊之後的圖片,再將我們模糊之後的bitmap作為一張照片"蓋"在我們所需要模糊(保護)的內容這塊區域之上,這樣就給人一種神祕的感覺。這是google 給我們提供的api裡面有的,在效能更方面應該是比較優的,但是當時沒有選擇用這種方法,而是選擇了github上面的FastBlur方法,至於原因後續再說。但是正如前面所說,RenderScript是api11之後才能使用的,但是低版本怎麼辦呢,這就需要考慮到相容性了,還好google也提供了一套方案。下面就相容低版本的高斯模糊做一些配置問題。其實低版本可以將supportV8中的包拷貝進來。

1.首先去你的sdk的目錄下build-tool目錄下找到所需要的jar包,我的路徑是 H:\android-sdk-windows\build-tools\23.0.3\renderscript\lib,將renderscript-v8.jar拷貝到我們的工程當中libs下面,以後所有的用到renderScript的相關操作的類,都引用這裡面的,不要引用系統預設的。例如import android.renderscript.RenderScript;  改為 import android.support.v8.renderscript.RenderScript;

2.使用RenderScript庫,在某些手機或Android版本奔潰的問題 ,錯誤資訊:

H:/AndroidRuntime(4476): android.support.v8.renderscript.RSRuntimeException: Error loading RS

jni library: java.lang.UnsatisfiedLinkError: Couldn't load RSSupport: findLibrary returned null

匯入官方jar renderscript-v8.jar 報這個錯誤 android.support.v8.renderscript.RSRuntimeException: Error loadin 或者 java.lang.UnsatisfiedLinkError: Couldn't load RSSupport from loader dalvik.system.PathClassLoader

這個錯誤原因是因為在4.4以上的手機上自帶 librsjni.so和libRSSupport.so 而在4.0以下,或者某些奇葩手機是沒有這兩個jni 的.所以我將我H:\android-sdk-windows\build-tools\23.0.3\renderscript\lib\packaged 目錄下的檔案全部拷貝到lib下面對應的資料夾下面 沒有對應的資料夾就建立,如果有對應的資料夾就將資料夾中的內容拷貝進去arm64-v8a,armeabi-v7a,mips,x86。

這2步進行了之後 ,我們就可以完全相容低版本的問題了額。

在進行高斯模糊的時候 ,我們有時候會遇到這樣一種需求,就是當我們的介面(Activity)顯示完成之後,我們希望我們模糊效果就能出來,我上面的渲染bitmap的函式中因為函式中用到bitmap.getWidth(),getHeight()獲得其寬高,那我們應該在什麼時候去呼叫我們的這個方法 使其能夠正常執行呢?在onStart()方法中?還是在onResume()回撥方法中?呵呵,其實都不是。實際中做實驗的時候,你可去嘗試,這個兩個函式中是獲取不到其寬高的呢,那麼下面有幾種方法可以使用 ,都是我經過嘗試使用之後,能夠執行的。

1,如果你在Activity啟動完成,介面顯示出來的時候需要顯示我們對某個控制元件的模糊效果,我們可以使用Activity的回撥方法。

  @Override
	    public void onWindowFocusChanged(boolean hasFocus) {
	    	// TODO Auto-generated method stub
	    	super.onWindowFocusChanged(hasFocus);
	    	if(hasFocus){  //介面完全渲染完成
	    		//我們應該在這裡面執行我們的模糊圖片的效果
	    	}else{  //介面焦點失去
	    		
	    	}
	    }
為什麼在這個方法之中執行呢?不在上面所說的onResume()方法中呢?其實我也說不了很清楚,說說自己的理解吧,可能在onResume()回撥方法當中,所有介面並沒有完全渲染完成,所有有些控制元件我們是不能得到他的寬高等屬性的,而在onWindowFcusChanged回撥方法中,我們所有的UI都渲染完成,能夠獲得我們的寬高了,上面的blurBitmap也不會報錯了。

2,view.post(runnable),將我們的模糊執行的邏輯放在runnalbe中執行,這裡我的理解是,通過post將一個runnable投遞到訊息佇列的隊尾,然後等待Looper呼叫此runnable的時候,view已經初始化完成了。

3。ViewTreeObserver:使用ViewTreeObserver的眾多回調可以完成這個功能,比如使用addOnPreDrawListener這個介面,不知道為什麼這裡獲取寬高也不會出錯。

hover_view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
			@Override
			public boolean onPreDraw() {
				hover_view.getViewTreeObserver().removeOnPreDrawListener(this);  //以免重複執行 所以先移除
				textview.buildDrawingCache();
				Bitmap bmp = textview.getDrawingCache();
				FastBlur.blur(bmp, textview, hover_view, context);
				return true;
			}
		});

 我的具體需求是在listView中去給每一個Item擁有模糊效果,由於這個模糊涉及到bitmap,所以當資料Item很多的時候,滑動會存在卡頓現象。因為在blurBitmap函式的27行有一句程式碼bitmap.recycle(); 當我們listView的item 複用時候 由於bitmap已經被回收了,會造成程式崩潰的bug。所以當時我就考慮用github上面的FastBlur去實現(後來才發現其實RenderScript也可以實現)這種模糊效果。

看看gitHub上面的FastBulr的模糊效果的具體實現:

public static Bitmap doBlur(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;  
    }  
其實也就是對bitmap進行了一定的畫素處理。引數sentBitmap其實就是我們要處理的圖片,radius就是傳入的要處理達到的模糊度,canReuseInBitmap標識是否能夠重用這個sentBitmap. 載入bitmap和渲染的問題我是採用上面的addOnPreDrawListener來處理的。對於處理listview中載入並且模糊的卡頓效果我採用的方法是對bitmap採取壓縮之後再模糊處理 將處理之後的圖片在通過bitmap的縮放,放大到我需要的指定大小。
public static void blur(Bitmap bkg, TextView textview, ImageView view,
			Context context) {
		int radius = 2;
		float scaleFactor = 8;
		Bitmap overlay = Bitmap.createBitmap(
				(int) (textview.getMeasuredWidth() / scaleFactor),
				(int) (textview.getMeasuredHeight() / scaleFactor),
				Bitmap.Config.ARGB_8888);
		Canvas canvas = new Canvas(overlay);
		Paint paint = new Paint();
		paint.setFlags(Paint.FILTER_BITMAP_FLAG);
		canvas.translate(-textview.getLeft() / scaleFactor, -textview.getTop()
				/ scaleFactor);
		canvas.scale(1 / scaleFactor, 1 / scaleFactor);
		canvas.drawBitmap(bkg, 0, 0, paint);

	//	overlay = doBlur(overlay, radius, true);
		overlay = blurBitmap(overlay, context);

		view.setBackground(new BitmapDrawable(context.getResources(), zoomImg(
				overlay, textview.getMeasuredWidth(),
				textview.getMeasuredHeight())));
	}

	/**
	 * 處理圖片
	 * 
	 * @param bm
	 *            所要轉換的bitmap
	 * @param newWidth新的寬
	 * @param newHeight新的高
	 * @return 指定寬高的bitmap
	 */
	public static Bitmap zoomImg(Bitmap bm, int newWidth, int newHeight) {
		// 獲得圖片的寬高
		int width = bm.getWidth();
		int height = bm.getHeight();
		// 計算縮放比例
		float scaleWidth = ((float) newWidth) / width;
		float scaleHeight = ((float) newHeight) / height;
		// 取得想要縮放的matrix引數
		Matrix matrix = new Matrix();
		matrix.postScale(scaleWidth, scaleHeight);
		// 得到新的圖片
		Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix,
				true);
		return newbm;
	}

函式blur通過我們需要模糊的控制元件(此處為TextView)產生的bitmap結合textView的寬和高建立一個縮小了scaleFactor倍大小的已經有模糊效果的bitmap物件,最後再通過zoomImg方法放大scaleFactor倍數(這裡的通用性可能不好,應該將scaleFactor寫成輸入引數)放到我們定義好的以及懸浮在TextView(需要模糊效果)的上面的wrapContent大小的ImageView裡面,用setBackGround放大設定位背景,這樣就覆蓋住了TextView。由於開始ImageView沒有任何內容和背景顏色 ,所有他並不現實出來,之後設定背景之後才會現實出來了。下面是效果的對比。傳送門

           

相關推薦

Android模糊記錄

最近在開發專案的時候遇到一個UI提出的效果就是PS裡面的高斯模糊效果,上圖                                                            下面其實是一段文字內容,但是當用戶沒有獲取到某種許可權的時候,是不能檢視具體的文

Unity shader學習屏幕後期處理效果模糊

歸一化 length spl 學習 baidu 一個 one ogr stat 高斯模糊,見 百度百科。 也使用卷積來實現,每個卷積元素的公式為: 其中б是標準方差,一般取值為1。 x和y分別對應當前位置到卷積中心的整數距離。 由於需要對高斯核中的權重進行歸一化,即使所有權

OpenCV-Python模糊

1.高斯噪聲函式 //將範圍限制在0~255之間 def thresholdfn(pv): if pv > 255: pv = 255 elif pv < 0: pv = 0 else: return

Android 圖片模糊解決方案

Android 圖片高斯模糊解決方案 近年來,圖片高斯模糊備受設計師的青睞,在各大知名APP中,如微信、手機QQ、網易雲音樂等等都有對背景高斯圖模糊的設計,在Adnroid 中,現在常用的圖片高斯模糊技術有三種:RenderScript 、fastBlur、對RenderScript和

Android Glide模糊載入圖片

Glide.with(this) .load(R.mipmap.bg_default_cover) .crossFade(1000) .bitmapTransform(new B

封裝個 Android模糊元件

最近基於 Android StackBlur 開源庫,根據自己碰到的需求場景,封裝了個高斯模糊元件,順便記錄一下。 為什麼要自己重複造輪子? 其實也談不上重頭自己造輪子,畢竟是基於大神的開源庫,做了二次封裝。封裝的目的在於,方便外部使用。畢竟有著自己的程式設計習慣,大神的開源庫也只是提供了基礎的功能,現實程式

Android高效能模糊方案

簡述: 做直播類app的時候點選進入直播間接通的過程中首先顯示一張模糊的毛玻璃效果的圖片,那麼此時就要考慮使用高斯模糊的時候了。Android中提供了RenderScript來操作圖片,但是這個的使用版本要求是在API17以上,所以我們還可以考慮使用第三方可FastBlur

UnityShader例項14:螢幕特效模糊(Gaussian Blur)

Shader "PengLu/ImageEffect/Unlit/GaussianBlur" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } CGINCLUDE #include "UnityCG.cginc" s

Android實現模糊(也叫毛玻璃效果)

/*** 高斯模糊* @param bkg* @param view*/private void blur(Bitmap bkg, View view) {float radius = 25;Bitmap overlay = Bitmap.createBitmap((int) (view.getMeasure

Android快速模糊對話方塊

// MainActivity.java package com.example.blurdemo;import android.annotation.SuppressLint;import android.app.Activity;import android.conte

Android 圖片模糊處理

/** 水平方向模糊度 */ private static float hRadius = 10; /** 豎直方向模糊度 */ private static float vRadius = 10; /** 模糊迭代度 */ private static int iterations = 7;

Android開發 模糊的實現

同類參考文章:戳我 戳我 高斯模糊是什麼? 高斯模糊(英語:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop、GIMP以及Paint.NET等影象處理軟體中廣泛使用的處理效果,通常用它來減少影象噪聲以及降低細節層次。這種模糊技術生成的影象,其

opencv筆記--模糊處理

高斯噪點#!/usr/bin/env python # _*_ coding:utf-8 _*_ import cv2 as cv import numpy as np def clamp(pv): if pv > 255: return 2

android 實現模糊

方法一 (1)RenderScript RenderScript是Google在Android 3.0(API 11)中引入的一個高效能圖片處理框架。 使用RenderScriprt實現高斯模糊: 首先在在build.gradle的defaultConf

android實現模糊功能

最近專案要求android客戶端模仿IOS的一種模糊背景的效果,高斯模糊在PS裡邊常用。大體思路是兩步: 第一步獲取Activity的螢幕截圖。 第二步對截圖進行高斯模糊演算法。 總之感覺體驗不是很好,主要原因是對Bitmap進行操作,這種東西本來就是安卓的噩夢,搞不好還會

Android實現模糊

心血來潮再來一篇部落格,和大家分享一下高斯模糊技巧 核心程式碼如下: /** * bitmap 物件進行模糊處理 * @param sentBitmap 原圖bitmap

webgl智慧樓宇發光效果算法系列模糊

# webgl智慧樓宇發光效果算法系列之高斯模糊 如果使用過PS之類的影象處理軟體,相信對於模糊濾鏡不會陌生,影象處理軟體提供了眾多的模糊演算法。高斯模糊是其中的一種。 在我們的智慧樓宇的專案中,要求對樓宇實現樓宇發光的效果。 比如如下圖所示的簡單樓宇效果: ![](https://p3-jueji

少造輪子--android模糊,使用簡單,帶漸變!!!

簡單粗暴, 使用方法:BitmapBlurUtil.getInstance().setImageBlurBitmap(…); 完整程式碼: import android.content.Context; import android.graphics.Bi

Android使用RenderScript實現圖片的模糊效果

Android使用RenderScript實現圖片的高斯模糊效果 首先來看一下什麼是高斯模糊效果呢? 高斯模糊(英語:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop、GIMP以及Paint.NET等影象處理軟體中廣泛使用的處理效果,通常用它

Android 使用Glide4.7.1對圖片進行圓角處理和模糊

一、概述 在Glide V4版本之後,對於圓角處理和高斯模糊的方法都有了一些改變,下面主要簡單介紹下處理方法 二、正文 在開始之前,我們先來看看效果,看看是否符合你的需要,圖1:圓角處理(四個角可以隨意組合處理);圖2:高斯模糊處理;圖3:圓角處理和高斯模糊處理 (