Android開發學習之路-動態高斯模糊怎麼做

来源:http://www.cnblogs.com/Fndroid/archive/2016/08/03/5734696.html
-Advertisement-
Play Games

什麼是高斯模糊? 高斯模糊(英語:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop、GIMP以及Paint.NET等圖像處理軟體中廣泛使用的處理效果,通常用它來減少圖像雜訊以及降低細節層次。這種模糊技術生成的圖像,其視覺效果就像是經過一個半透明屏幕在觀察圖像,這與鏡頭焦 ...


什麼是高斯模糊?

高斯模糊(英語:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop、GIMP以及Paint.NET等圖像處理軟體中廣泛使用的處理效果,通常用它來減少圖像雜訊以及降低細節層次。這種模糊技術生成的圖像,其視覺效果就像是經過一個半透明屏幕在觀察圖像,這與鏡頭焦外成像效果散景以及普通照明陰影中的效果都明顯不同。

什麼?看不明白?沒關係,我也看不明白,維基百科複製回來的嘛。我們直接放一些圖片來瞭解以下這個高斯模糊是怎麼樣的。因為高斯模糊在iOS中最常見,這裡抓了幾張iOS網易雲的圖片:

可以看到這個界面中的背景,其實就是通過圖1中間那個小圖片模糊得到的,這樣做的好處就是整體性很好,並且不會因為圖片過渡突兀而影響界面內容的閱讀。

那麼,究竟在Android上怎麼去實現這個效果呢?這裡推薦使用官方提供在Support Library中的一個工具來做,就是RenderScript。這個RenderScript的功能其實不止有這一個,而其他的一些功能,可以在官方文檔中閱讀學習,這裡不給出。

使用這個工具的原因其實很簡單,就是性能。因為涉及到繪圖,所以如果性能不行,那麼無論對於高質量圖片或者是變化較多的需求都是很吃力的,而這個工具則會充分發揮設備的計算能力(CPU和GPU)來進行計算,並且是使用C99衍生語言進行腳本編寫的,相較於Java性能是大大的提升。

說到這裡,有些同學就開始方了,C99衍生?What?這個不需要擔心,對於高斯模糊這個實現,Google官方已經給出了對應的解決方案,我們並不需要編寫對應的腳本就可以使用了,所以無需擔心。

 

我們把整個問題分為兩個部分:①高斯模糊實現;②動態高斯模糊實現

 

① 高斯模糊實現

首先要說明,我們要使用Support Library,所以版本是有要求的:

  • Android SDK Tools 版本必須大於等於22.2
  • Android SDK Build-tools 版本必須大於等於18.1.0

如果沒有達到,請使用SDK Manager升級一下。

接著創建我們的工程,並且在對應Module(預設創建的是app)的build.gradle文件中加入如下代碼:

defaultConfig {
        ...
        renderscriptTargetApi 18
        renderscriptSupportModeEnabled true
    }

renderscriptTargetApi:這個一般和App支持的最低版本相同即可。

package com.fndroid.renderscriptdemo;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {
    private ImageView mImageView;
    private Bitmap sampleImg;
    private Bitmap gaussianBlurImg;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImageView = (ImageView) findViewById(R.id.iv);
        sampleImg = BitmapFactory.decodeResource(getResources(), R.drawable.icon); // 獲取原圖
        gaussianBlurImg = blur(sampleImg, 25f);
        mImageView.setImageBitmap(gaussianBlurImg);
    }

    private Bitmap blur(Bitmap bitmap,float radius) {
        Bitmap output = Bitmap.createBitmap(bitmap); // 創建輸出圖片
        RenderScript rs = RenderScript.create(this); // 構建一個RenderScript對象
        ScriptIntrinsicBlur gaussianBlue = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); // 創建高斯模糊腳本
        Allocation allIn = Allocation.createFromBitmap(rs, bitmap); // 創建用於輸入的腳本類型
        Allocation allOut = Allocation.createFromBitmap(rs, output); // 創建用於輸出的腳本類型
        gaussianBlue.setRadius(radius); // 設置模糊半徑,範圍0f<radius<=25f
        gaussianBlue.setInput(allIn); // 設置輸入腳本類型
        gaussianBlue.forEach(allOut); // 執行高斯模糊演算法,並將結果填入輸出腳本類型中
        allOut.copyTo(output); // 將輸出記憶體編碼為Bitmap,圖片大小必須註意
        rs.destroy(); // 關閉RenderScript對象,API>=23則使用rs.releaseAllContexts()
        return output;
    }
}

這裡說明都註釋在代碼中了。需要瞭解的是RenderScript有兩個版本,分別是:

  • android.renderscript
  • android.support.v8.renderscript

上面代碼使用了第一個,第二個的用法類似,可以自行嘗試。

我們來理一下思路,因為RenderScript是依賴於Script的,而上文也說到了,Script是由C99衍生語言編寫,而代碼中的ScriptIntrinsicBlur就是對應於高斯模糊演算法的腳本。而Allocation對象則是將Java中的對象轉換為Script腳本所需類型的幫手,代碼中創建了兩個Allocation對象分別用來充當輸入和輸出。接著設置了高斯模糊的半徑(radius)。當調用forEach時,腳本會被執行,並且將執行結果填入輸出對應的Allocation中,最後調用copyTo來轉換為Bitmap對象返回。

效果圖:

 

② 動態高斯模糊

很多時候,我們可能會需要一個圖片以不同的模糊程度展現出來。你可能已經註意到上面方法中的模糊半徑了,我們可以做一個實驗,就是通過一個SeekBar來動態改變這個值看看效果:

由動圖可以看到,我們拖動SeekBar的時候,SeekBar已經跟不上我們的拖動了。這是為什麼?原因就是這個渲染工具雖然性能比較優異,但是如果圖片的質量和尺寸都較高的時候,我們直接進行修改模糊半徑重新渲染的做法往往時不可取的

這裡提出一個解決方案,這個Idea來自於郭大今天的推文作者"湫水長天"。

做法是,先創建一張模糊的圖片載入在ImageView中,接著在這個ImageView的上面再放置一個載入原圖ImageView,使用FrameLayout可以讓這兩個ImageView重疊再一起,接著當我們需要動態改變模糊程度的時候,改變上層的ImageView的BitmapAlpha就可以了。我們先看看效果圖:

使用了這個方法,滑動起來就會比較流暢了。(GIF載入完才是會是正常速度哦)

這裡給一下代碼參考吧:

public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener {
    private ImageView mImageView;
    private ImageView mImageViewCover;
    private Bitmap sampleImg;
    private Bitmap gaussianBlurImg;
    private SeekBar mSeekBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImageView = (ImageView) findViewById(R.id.iv);
        mSeekBar = (SeekBar) findViewById(R.id.sb);
        mImageViewCover = (ImageView) findViewById(R.id.iv_cover);
        sampleImg = BitmapFactory.decodeResource(getResources(), R.drawable.icon); // 獲取原圖
        gaussianBlurImg = blur(sampleImg, 25f);
        mImageView.setImageBitmap(gaussianBlurImg);
        mSeekBar.setOnSeekBarChangeListener(this);
    }

    private Bitmap blur(Bitmap bitmap, float radius) {
        Bitmap output = Bitmap.createBitmap(bitmap); // 創建輸出圖片
        RenderScript rs = RenderScript.create(this); // 構建一個RenderScript對象
        ScriptIntrinsicBlur gaussianBlue = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); //
        // 創建高斯模糊腳本
        Allocation allIn = Allocation.createFromBitmap(rs, bitmap); // 開闢輸入記憶體
        Allocation allOut = Allocation.createFromBitmap(rs, output); // 開闢輸出記憶體
        gaussianBlue.setRadius(radius); // 設置模糊半徑,範圍0f<radius<=25f
        gaussianBlue.setInput(allIn); // 設置輸入記憶體
        gaussianBlue.forEach(allOut); // 模糊編碼,並將記憶體填入輸出記憶體
        allOut.copyTo(output); // 將輸出記憶體編碼為Bitmap,圖片大小必須註意
        rs.destroy(); // 關閉RenderScript對象,API>=23則使用rs.releaseAllContexts()
        return output;
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        int alpha = 255 - progress;
        mImageViewCover.setImageAlpha(alpha);
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {

    }
}

 佈局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.fndroid.renderscriptdemo.MainActivity">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <ImageView
            android:id="@+id/iv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

        <ImageView
            android:id="@+id/iv_cover"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/icon"/>

    </FrameLayout>

    <SeekBar
        android:id="@+id/sb"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="255"/>

</LinearLayout>

 

如發現有誤,請指出,感謝。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、HTML和CSS 1、你做的頁面在哪些流覽器測試過?這些瀏覽器的內核分別是什麼? IE: trident內核 Firefox:gecko內核 Safari:webkit內核 Opera:以前是presto內核,Opera現已改用Google Chrome的Blink內核 Chrome:Blink ...
  • 利用sort()冒泡排序: 不聲明第三個變數冒泡排序: 第一層遍曆數組的個數(要遍歷多少次),第二次遍歷(共要迴圈幾次) a = 10; //第一個元素 b = 5; //下一個元素 if(a>b){ a = a+b; // a(15) = 10 +5; b = a-b; // b(10) = 15 ...
  • 方法一:使用border來設置邊框,元素有高度和寬度 效果: 利用transform屬性可以旋轉三角形,達到想要的效果。 方法二:利用border來撐起來三角形 效果: 應用場景:點擊234或者點擊選中的時候三角形指向對應的選項 小貼士: 1、學會經常使用偽元素例如after或者before來實現三 ...
  • 1.在項目中碰到了商品評價頁面,裡面有關於對商品的星星評價,當時的我只是把效果寫出來了(就是用戶點擊幾顆星星亮就顯現幾顆亮), 當我做好頁面交給後端同事的時候,他說我這樣做沒有意義他沒法做,那時我的腦子有些懵了。 後來後端同事說他來搗騰算了,作為一名21世紀的陽光好青年怎麼能夠把自己的任務都讓同事來 ...
  • 正則表達式蠻強大的哈,廢話不多說, 直接上整理內容 一:基本匹配符: \d 匹配數字 eg:'5\d0' >'580' \w 匹配字母或數字 eg:'\d\w\w' >'8zh' . 匹配除換行符外的任何一個字元任意字元 eg:'zh.' >'zh&' \s 空白符(tab)或空格 二:匹配字元長度 ...
  • × 目錄 [1]犯錯 [2]IIFE [3]let 前面的話 關於常見的一個迴圈和閉包的錯誤,很多資料對此都有文字解釋,但還是難以理解。本文將以執行環境圖示的方式來對此進行更直觀的解釋,以及對此類需求進行推衍,得到更合適的解決辦法 犯錯 以上代碼的運行結果是2,而不是預想的0。接下來用執行環境圖示的 ...
  • 1.創建phc文件![](http://images2015.cnblogs.com/blog/291840/201608/291840-20160803223710356-1796009336.jpg)2.在Build Setting/Prefix Header中添加 $(SRCROOT)/fi... ...
  • LinearLayout和RelativeLayout之間:共有屬性:java代碼中通過btn1關聯次控制項android:id="@+id/btn1"控制項寬度android:layout_width="80px" //"80dip"或"80dp"()android:layout_width =“wr ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...