「干货」未来视觉7-Android高斯模糊方案

2019-08-07 14:10发布

这段时间工作真心比较忙,而空余时间,都在在研究补全一些opencv技术的基础,然后近来有个业务和图像有关,背景高斯模糊的方案了。

可知Android可以使用高斯模糊的方案也真的不少,但是最终还是要看效果和效率的。

至于高斯模糊有什么作用呢?

模糊阴影,其原理也是使用高斯模糊的。

在美颜磨皮这方面,使用高斯模糊是一个很好的方向。

要学习高斯模糊基础还是要先知道底层原理,其原理是基于数学的正态分布,越接近中心,权重就越大。

正态分布是一维的如何反映出正态分布?则需要使用高斯函数来实现。 上面的正态分布是一维的,而对于图像都是二维的,所以我们需要二维的正态分布。

正态分布的密度函数叫做"高斯函数"(Gaussian function)。它的一维形式是:

其中,μ是x的均值,σ是x的方差。因为计算平均值的时候,中心点就是原点,所以μ等于0。

根据一维高斯函数,可以推导得到二维高斯函数:

3*3的矩阵

为了计算权重矩阵,需要设定σ值。现假定σ=1.5,则模糊半径为1的权重矩阵如下:

这9个点的权重总和等于0.4787147,如果只计算这9个点的加权平均,还必须让它们的权重之和等于1,因此上面9个值还要分别除以0.4787147,得到最终的权重矩阵。

###目的是让滤镜的权重总值等于1。否则的话,使用总值大于1的滤镜会让图像偏亮,小于1的滤镜会让图像偏暗。

有了权重矩阵,就可以计算高斯模糊的值了。 假设现有9个像素点,灰度值(0-255)如下:

每个点乘以自己的权重值:

得到

将这9个值加起来,就是中心点的高斯模糊的值。 对所有点重复这个过程,就得到了高斯模糊后的图像。 ###对于彩色图片来说,则需要对RGB三个通道分别做高斯模糊。

如果到达边界的时候,还需要给边界侧补0处理

当图像处理的时候,高斯滤波会从左到右、从上到下的处理各个点的色值合成。

一.Glide

Glide可以使用一个扩展库glide-transformations,就是自己写Transform,然后在transform中添加高斯模糊的计算。具体代码如下

FastBlur.java, 里面变换的规则有点复杂,因为图像变化是基于java上层来完成,效率和速度是最低的。

二.Genius-blur

Genius-blur

这是使用jni的方案,大小只有20k左右。

内部blur_ARGB_8888的实现方法和Glide的FastBlur的算法方式是一样的,但是因为使用C++底层实现,速度肯定要快上很多。

三.RenderScript

使用RenderScript框架来加载渲染高斯模糊的渲染脚本,基础还是会使用底层的渲染Rs渲染库。 github.com/CameraKit/b… 兼容问题 官方说明 只能api17以上才能使用,如果低版本只能使用v8版本的。网上有简单的在build.gradle的defaultConifg添加

renderscriptTargetApi 26

renderscriptSupportModeEnabled true

添加v8的renderscript兼容包,容量加大一百多k。

但是我在AS3.3无法使用这种方法编译成功,提示我无法找到RenderScript的类,结果我只能手动引入了Sdkbuild-tools27.0.3renderscriptlibpackaged里面的so和jar包,才能编译通过,但是这样打出来的aar包2.2M,只是一个模糊兼容就增大这么大的容量,不怎么友好啊。 radius的范围是0~25。值越大越模糊。

public Bitmap blur(Bitmap src, int radius) {
final Allocation input = Allocation.createFromBitmap(rs, src);
final Allocation output = Allocation.createTyped(rs, input.getType());
final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
script.setRadius(radius);
script.setInput(input);
script.forEach(output);
output.copyTo(src);

return src;
}
复制代码

1.最终我采取了17以上使用renderScipt,以下的使用Genius-blur这个库,打包出来的aar大概是20k左右,足够兼容简单的高斯模糊要求。

2.正常的高斯模糊,是不包含色值,有些需要会要求带有特定色值的高斯模糊,这时候可以使用ImageView把高斯模糊的图设置为背景,然后把带有色值的透明图来设置到imageView的src图。这样看起来就会带有色值的高斯模糊了。

3.如果做到高斯模糊渐变消息,其实这里是使用障眼法,设置Bitmap的alpha值来达到渐变消失的。

四.使用Opencv来处理高斯图像

如果你框架中已经加入了Opencv的sdk,使用这种方法也是非常高效的。

Mat代表Opencv中的图像数据

Imgproc是Opencv的工具类

Imgproc.GaussianBlur是Opencv的高斯模糊处理


public class GaussianBlur {
tpublic static void main(String[] args) {
tttry{
tttSystem.loadLibrary(Core.NATIVE_LIBRARY_NAME);
ttt
tttMat src=Imgcodecs.imread("本地图片地址");
ttt//读取图像到矩阵中
tttif(src.empty()){
ttttthrow new Exception("no file");
ttt}
ttt
tttMat dst = src.clone();
ttt//复制矩阵进入dst
ttt
tttImgproc.GaussianBlur(src,dst,new Size(13,13),10,10);
ttt//图像模糊化处理11
tttImgcodecs.imwrite("写入地址", dst);
ttt
tttImgproc.GaussianBlur(src,dst,new Size(31,5),80,3);
ttt//图像模糊化处理33
tttImgcodecs.imwrite("写入地址", dst);
tt}catch(Exception e){
tttSystem.out.println("例外:" + e);
tt}
t}
}
复制代码

五.OpenGL

之前使用Opengl,来采集图像用于实时的美颜方案的。

使用高斯模糊是可以给人脸皮肤模糊,做到去磨皮等效果的。

可以看到RenderScript最高支持25范围的卷积核来做模糊,卷积核越大越模糊,计算量越大,速度越慢。计算太大就会造成卡顿,卷积核太小,模糊效果不够明细那。但是为了效率问题,Opengl这边只使用了统一几组采集点,就是在一定范围内只采集一些对应的点,例如周围点附近几组菱形顶点作为采集点。然后使用这种采集来做配置。

 /**模糊取值纹理坐标**/
blurCoordinates[0] = inputTextureCoordinate.xy + singleStepOffset * vec2(0.0, -10.0);
....
tblurCoordinates[19] = inputTextureCoordinate.xy + singleStepOffset * vec2(4.0, -4.0);

float sampleColor = centralColor.g * 20.0;
sampleColor += texture2D(inputImageTexture, blurCoordinates[0]).g;

/** 不同权重**/
sampleColor += texture2D(inputImageTexture, blurCoordinates[19]).g * 2.0;
/**最终模糊均值**/
sampleColor = sampleColor / 48.0;
/** .用原图绿色通道值减去sampleColor,加上0.5,整个步骤是PS中的高保留反差**/
float highPass = centralColor.g - sampleColor + 0.5;
复制代码

人脸是偏红色,所以红色通道的色值比较大,而人脸细节方面是保留在绿色通道上比较多。 如果需要美白,需要使用强光处理

/**5次强光处理**/
for(int i = 0; i < 5;i++)
{
highPass = hardLight(highPass);
}
float hardLight(float color)
{
if(color <= 0.5)
color = color * color * 2.0;
else
color = 1.0 - ((1.0 - color)*(1.0 - color) * 2.0);
return color;
}
复制代码

灰度图生成,公式为0.299R + 0.587G + 0.114*B

const highp vec3 W = vec3(0.299,0.587,0.114);
float luminance = dot(centralColor, W);
float alpha = pow(luminance, params);
复制代码

将灰度值作为阈值,用来排除非皮肤部分

 /** pow(x,y)是x的y次方**/
float alpha = pow(luminance, params);
/** 原图rgb值与高反差后的结果相比,噪声越大,两者相减后的结果越大,在原结果基础上加上一定值,来提高亮度,消除噪声。
pow函数中第二个参数可调(1/3~1),值越小,alpha越大,磨皮效果越明显,修改该值可作为美颜程度**/
vec3 smoothColor = centralColor + (centralColor-vec3(highPass))*alpha*0.1;
复制代码

以灰度值作为透明度将原图与混合后结果进行滤色、柔光等混合,并调节饱和度

gl_FragColor = vec4(mix(smoothColor.rgb, max(smoothColor, centralColor), alpha), 1.0);
复制代码

但是因为图片比例问题,高斯模糊的方式是没问题的,但是采集点的影响需要根据宽高比例调整 Cain_Huang对人脸磨皮的高斯模糊调整Android OpenGLES 实时美颜(磨皮)的优化

他在磨皮优化二的章节,提供了更高效的高斯模糊计算方式。 在顶点着色器,预先计算出周边点保存到数组,然后传递到片段着色器

uniform mat4 uMVPMatrix;
attribute vec4 aPosition;
attribute vec4 aTextureCoord;
// 高斯算子左右偏移值,当偏移值为5时,高斯算子为 11 x 11
const int SHIFT_SIZE = 5;
uniform highp float texelWidthOffset;
uniform highp float texelHeightOffset;
varying vec2 textureCoordinate;
varying vec4 blurShiftCoordinates[SHIFT_SIZE];
void main() {
gl_Position = uMVPMatrix * aPosition;
textureCoordinate = aTextureCoord.xy;
// 偏移步距
vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);
// 记录偏移坐标
for (int i = 0; i < SHIFT_SIZE; i++) {
blurShiftCoordinates[i] = vec4(textureCoordinate.xy - float(i + 1) * singleStepOffset,
textureCoordinate.xy + float(i + 1) * singleStepOffset);
}
}
复制代码

片段着色器,使用一个for循环来取得偏移坐标的色值总和,然后计算平均值。这里没有使用高斯核权重,所以并不算标准被高斯模糊计算。

precision mediump float;
varying vec2 textureCoordinate;
uniform sampler2D inputTexture;
const int SHIFT_SIZE = 5; // 高斯算子左右偏移值
varying vec4 blurShiftCoordinates[SHIFT_SIZE];
void main() {
// 计算当前坐标的颜色值
vec4 currentColor = texture2D(inputTexture, textureCoordinate);
mediump vec3 sum = currentColor.rgb;
// 计算偏移坐标的颜色值总和
for (int i = 0; i < SHIFT_SIZE; i++) {
sum += texture2D(inputTexture, blurShiftCoordinates[i].xy).rgb;
sum += texture2D(inputTexture, blurShiftCoordinates[i].zw).rgb;
}
// 求出平均值
gl_FragColor = vec4(sum * 1.0 / float(2 * SHIFT_SIZE + 1), currentColor.a);
}
复制代码

高斯模糊的应用和一些计算就分析到这里,五种高斯模糊的计算和分析,希望对大家有帮助。

文章来源: https://www.toutiao.com/group/6722238453322351117/