乐虎游戏|乐虎国际登录|欢迎你

Android实现动态高斯模糊效果

日期:2020-03-21编辑作者:计算机资讯

我司产品说要在我司APP里搞一个高斯模糊的效果,之前没做过,就只能先查资料了。 先是查到一个很nubility的三方类GPUImage,但是看了之后感觉太过繁琐,我只是想简单的实现一个高斯模糊。后听我们组长说直接使用系统方法就可以实现。再一查,果然,使用CIImage的话,九行代码就解决了。

高斯模糊是什么?

前言

iOS 7 开始 Apple 从 拟物化 过渡到了 扁平化 的设计风格,同时也搭配使用了 毛玻璃风格 当做背景效果,不得不说十分惊艳,颇有当时pc上 Windows VistaOS X Yosemite��� 的味道,在那之后,Google 也从 Android L(5.0)开始使用了 原质化设计(Material Design) 设计语言,与 Microsoft 的 Metro 那种纯扁平化风格看似很相像,但实则因为引用了 Z轴 的概念,使其有了阴影和立体感,传达了 响应式交互 的设计理念。说到这里有一些跑题,因为笔者对设计美学很感兴趣,所以对这些平台都稍微了解一些皮毛。今天就来研究一下如何在 Android 上实现高斯模糊效果。

CIContext *context = [CIContext contextWithOptions:nil];CIImage *inputImage = [[CIImage alloc] initWithImage:[UIImage imageNamed:@"xiaoke"]];// create gaussian blur filterCIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"];[filter setValue:inputImage forKey:kCIInputImageKey];[filter setValue:[NSNumber numberWithFloat:4] forKey:@"inputRadius"];//设置模糊度// blur imageCIImage *result = [filter valueForKey:kCIOutputImageKey];CGImageRef cgImage = [context createCGImage:result fromRect:inputImage.extent];UIImage *blurImage = [UIImage imageWithCGImage:cgImage]; CGImageRelease;

高斯模糊(英语:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop、GIMP以及Paint.NET等图像处理软件中广泛使用的处理效果,通常用它来减少图像噪声以及降低细节层次。这种模糊技术生成的图像,其视觉效果就像是经过一个半透明屏幕在观察图像,这与镜头焦外成像效果散景以及普通照明阴影中的效果都明显不同。

目前 Android 上实现高斯模糊效果的方式有:

  • Java : FastBlur.java ,应用非常广泛的 StackBlur 模糊算法实现代码,效率最低

  • C++ :两种实现,1:标准高斯模糊算法 2:均值模糊,效率中等

  • Android : RenderScript ,用来在 Android 上编写高性能代码的一种语言(使用C99标准,运行时机器再次优化编译, 可以均衡的运行在多个CPU 和 GPU上,有一个半径限制小于25的限制),效率最高

效果如下:

什么?看不明白?没关系,我也看不明白,维基百科复制回来的嘛。我们直接放一些图片来了解以下这个高斯模糊是怎么样的。因为高斯模糊在iOS中最常见,这里抓了几张iOS网易云的图片:

简单聊聊 FastBlur

因为效果的实现是基于 Java 的,所以有必要先来了解一下方法如何使用。

public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap)

可以看出,使用方法非常简单,传入待虚化的bitmap、虚化程度(一般为8)、是否重用flag,最后返回模糊后的bitmap。

但如果直接把一张大图传入进行虚化,很容易就会产生OOM内存溢出,那就意味着我只能虚化小图,这样才能防止内存溢出。但是我并不想换其他图,那么,我们就应该把这张图缩小。

平时我们对图片缩小,必然会带来很明显的清晰度的损失,但高斯模糊本身的目的就是要实现模糊的效果,因此实际上的效果差别不大,几乎可以忽略。

同时由于图片缩小后再进行模糊处理,需要处理的像素点和半径都变小,从而使得模糊处理速度加快。

图片 1IMG_2018.PNG

图片 2

ReScale

public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) {}

我们可以利用Bitmap的 createScaledBitmap() 方法来进行bitmap的缩放。其中前三个参数很明显,其中宽高我们可以选择为原图尺寸的1/5;第四个filter是指缩放的效果,filter为true则会得到一个边缘平滑的bitmap,反之,则会得到边缘锯齿、pixelrelated的bitmap。这里我们要对缩放的图片进行虚化,所以无所谓边缘效果,filter=false。

所以,我们要使用

int scaleRatio = 5;// 缩放比例 此处代表1/5
int blurRadius = 8;// 虚化程度
Bitmap scaledBitmap = Bitmap.createScaledBitmap(originBitmap,
    originBitmap.getWidth() / scaleRatio,
    originBitmap.getHeight() / scaleRatio,
    false);
Bitmap blurBitmap = FastBlur.doBlur(scaledBitmap, blurRadius, true);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageBitmap(blurBitmap);

可以得到如下效果:

图片 3

图片地址

从图中可以看出,首先可以确定思路是对的;然后,可以看出毛玻璃效果还不是特别的明显。为了得到如iOS那样的虚化效果,我们有两种方法:

  • 增大scaleRatio缩放比,使用更小的bitmap去虚化可以得到更好的模糊效果,而且有利于占用内存的减小;
  • 增大blurRadius,可以得到更高程度的虚化,不过会导致CPU更加intensive

这里笔者通过增大缩放比来实验。

  • scaleRatio = 10

图片 4

图片地址

  • scaleRatio = 20

图片 5

图片地址

通过上面对比图我们可以找出最适合自己的虚化效果。

可以看到这个界面中的背景,其实就是通过图1中间那个小图片模糊得到的,这样做的好处就是整体性很好,并且不会因为图片过渡突兀而影响界面内容的阅读。

再来聊聊 RenderScript

RenderScript 主要 在Android中的对图形进行处理,RenderScript 采用C99语法进行编写,主要优势在于性能较高。在 API 11 的时候被加入到 Android 中。同时,Google提供了android.support.v8.renderscript兼容包,能够实现更低版本的兼容。

RenderScript 提供了一个用于实现高斯模糊的封装类 ScriptIntrinsicBlur ,因为在 API 17 后才正式适配到 Android ,所以在不使用兼容包的情况下只能兼容到4.2的设备。但是,我们有兼容包啊向下兼容不是梦。

那么,究竟在Android上怎么去实现这个效果呢?这里推荐使用官方提供在Support Library中的一个工具来做,就是RenderScript。这个RenderScript的功能其实不止有这一个,而其他的一些功能,可以在官方文档中阅读学习,这里不给出。

准备阶段

使用这个工具的原因其实很简单,就是性能。因为涉及到绘图,所以如果性能不行,那么无论对于高质量图片或者是变化较多的需求都是很吃力的,而这个工具则会充分发挥设备的计算能力(CPU和GPU)来进行计算,并且是使用C99衍生语言进行脚本编写的,相较于Java性能是大大的提升。

引入兼容包

方法很简单,只需在build.gradle中加入:

defaultConfig {
        ....
        // 就是这么简单
        renderscriptTargetApi 19
        renderscriptSupportModeEnabled true
    }

另外由于一些厂商会深度定制Android系统,所以一些必要的依赖文件会被他们直接去掉,这导致一些型号的设备上调用RenderScriptd的部分方法时会报错。遇到这种兼容问题的话,需要加上这些可能丢失的文件。
其实也简单,打开android_sdk/build-tools/选择19以上版本/renderscript/lib/packaged我们可以看见3个包含.so文件的文件夹。

图片 6

so文件

直接复制这三个文件加到项目工程的 jniLibs 包下,没有的话去建一个。

图片 7

jniLibs文件夹

如果首次创建 jniLibs 文件夹,还需要在 build.gradle 的 android{} 中加入:

sourceSets {
        main {
            jniLibs.srcDirs = ['jniLibs']
        }
    }

针对使用的混淆的同学,需要在混淆中加入:

-keep class android.support.v8.renderscript.** { *; }

说到这里,有些同学就开始方了,C99衍生?What?这个不需要担心,对于高斯模糊这个实现,Google官方已经给出了对应的解决方案,我们并不需要编写对应的脚本就可以使用了,所以无需担心。

实现高斯模糊

  • 将核心实现方法 ScriptIntrinsicBlur 封装成工具类。
import android.support.v8.renderscript.*;  // 需要导入v8包,否则无法向下兼容

public class BlurBitmapUtil {

    /***
     * 图片缩放比例 (例如 1/10)
     */
    private static int scaleRatio = 10;

    /**
     * 对图片进行高斯模糊
     *
     * @param context    上下文对象
     * @param image      需要模糊的图片
     * @param blurRadius 模糊半径,由于性能限制,这个值的取值区间为(0至25f)
     * @return 模糊处理后的图片
     */
    public static Bitmap blurBitmap(Context context, Bitmap image, @FloatRange(from = 1, to = 25)
            float blurRadius) {
        // 计算图片缩小后的长宽
        int width = Math.round(image.getWidth() / scaleRatio);
        int height = Math.round(image.getHeight() / scaleRatio);

        // 创建一张缩小后的图片做为渲染的图片
        Bitmap bitmap = Bitmap.createScaledBitmap(image, width, height, false);

        // 创建RenderScript内核对象
        RenderScript rs = RenderScript.create(context);
        // 创建一个模糊效果的RenderScript的工具对象,第二个参数Element相当于一种像素处理的算法,高斯模糊的话用这个就好
        ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));

        // 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间
        // 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去
        Allocation input = Allocation.createFromBitmap(rs, bitmap);
        // 创建相同类型的Allocation对象用来输出
        Type type = input.getType();
        Allocation output = Allocation.createTyped(rs, type);

        // 设置渲染的模糊程度, 25f是最大模糊度
        blurScript.setRadius(blurRadius);
        // 设置blurScript对象的输入内存
        blurScript.setInput(input);
        // 将输出数据保存到输出内存中
        blurScript.forEach(output);
        // 将数据填充到bitmap中
        output.copyTo(bitmap);

        // 销毁它们释放内存
        input.destroy();
        output.destroy();
        blurScript.destroy();
        rs.destroy();
        type.destroy();

        return bitmap;
    }

使用 RenderScript 增加虚化程度的方法和 FastBlur 一样,有两种方法:

  • 增大scaleRatio缩放比,使用更小的bitmap去虚化可以得到更好的模糊效果,而且有利于占用内存的减小;

  • 增大blurRadius,可以得到更高程度的虚化,不过会导致虚化时间变长

但因为 RenderScript 的天然优势(低级语言, 运行时机器再次优化编译, 可以均衡的运行在多个CPU 和 GPU上),所以这里笔者通过增大虚化程度来实验,缩放比例为 1/10,实际运用时可以根据需求在对虚化程度和缩放比例上采取一个合适的数值。

  • blurRadius = 5

图片 8

  • blurRadius = 15

图片 9

  • blurRadius = 25

图片 10

通过上面对比图我们可以找出最适合自己的虚化效果。

目前来看,为何 Google 设置这个25的限制, 原因应该有两个 :

  1. 半径大于25的话耗时就成为了一个瓶颈;
  2. 如果想实现大于25的模糊效果,可以通过缩小原图,模糊,再放大来达到同样的效果

 我们把整个问题分为两个部分:

总结

以上就是如何用 FastBlur 和 RenderScript 在 Android 上实现和 iOS 一样的高斯模糊效果的简单介绍,虽然在性能上毋庸置疑是 RenderScript 上最好,但是在一些使用场景上 FastBlur 耗时会更短,所以我们各取所需,根据实际需求去选择使用。

    ①高斯模糊实现;

另一种可能性

上面说的2种解决方案都是从性能和效率出发的产物,但如果我的需求就是从网络上加载一张图片(比如头像),然后再高斯模糊化当背景,走一遍转换成bitma再将其转换成高斯模糊的流程或许会有一点点麻烦,这里我再提供一种简单快捷的解决方案 —— 基于Glid加载框架去实现一键 加载网络图片→高斯模糊化→展示

    ②动态高斯模糊实现 

引入兼容包

首先在build.gradle中加入图片框架需要的库和图片工具库:

defaultConfig {
        ....
        compile 'com.yutianran.maven:super-adapter:1.0.0'
        compile 'jp.wasabeef:glide-transformations:2.0.2'
}

① 高斯模糊实现

然后就开写,一行代码即可

Glide.with(this).load(url).bitmapTransform(new BlurTransformation(this,25)).into(imageView);

需要的参数很分别是

  • 上下文对象
  • 图片url
  • 上下文对象,虚化数值
  • imageView控件

图片 11

效果如上,可以看出 glide-transformations 库的虚化效果也是十分不错的,但对图片本身做的缩放应该不是很多,所以在加载速度上会弱于 FastBlur 和 RenderScript ,但作为轻量级图片而言足够了。

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

Code

相关代码已上传至Github:BlurView,欢迎Star,Fork。

     Android SDK Tools 版本必须大于等于22.2

参考文献

  1. [译] RenderScript:简单而快速的图像处理
  2. 图片高斯模糊效果简单优化
  3. 高斯模糊效果实现方案及性能对比
  4. Android:简单靠谱的动态高斯模糊效果
  5. 教你一分钟实现动态模糊效果
  6. 什么?IOS的专利?Android也能流畅实现毛玻璃效果(高斯模糊)效果
  7. Android 高斯模糊总结
  8. Android RenderScript 简单高效实现图片的高斯模糊效果
  9. [干货]一种快速毛玻璃虚化效果实现--Android

本文由乐虎游戏发布于计算机资讯,转载请注明出处:Android实现动态高斯模糊效果

关键词:

【LX豪彩】UIProgressView进程条的性格介绍

- viewDidLoad { - (void)viewDidLoad { [super viewDidLoad]; //实例化一个进度条,有两种样式,一种是UIProgressViewStyleBar一种是UIPro...

详细>>

浅谈常见的Loading进度条动效形式

在浏览网页、玩游戏、手提式有线电话机接受等气象,因为网速慢或是硬件差的关联,难免会遇上等候加载的场所,...

详细>>

iOS动漫之自定义转场动漫(push卡塔尔国

push.gif iOS7 开始苹果推出了自定义转场的 API 。从此,任何可以用 CoreAnimation实现的动画,都可以出现在两个 ViewContr...

详细>>

iOS[QuartzCore框架]CADisplayLink篇

在学习CoreAnimation的时候,发现好多地方都用到了CADisplayLink,现在就整理下自己的笔记吧,不足之处敬请指正。===...

详细>>