共计 3553 个字符,预计需要花费 9 分钟才能阅读完成。
PS
上一篇咱们说了一些简略的滤镜,明度、对比度、曝光度等等
那么本篇咱们来说一下略微简单一点的卷积,了解卷积对图片解决还是非常重要的
卷积
从数学上讲,卷积就是一种运算,与减加乘除没有实质的区别的一种运算。
就像咱们能够通过 A + B 的运算来计算 A 与 B 的和一样,简略的加减乘除运算符能够看成混合运算符两边元素的信息,咱们能够认为卷积运算也是一种混合信息的伎俩
图像卷积
咱们方才说了卷积能够看成一种混合信息的伎俩,咱们来看一下图像卷积
如果咱们有上面一幅图像
这幅图像不太清晰,存在很多噪点。
PS
噪点又称 图像噪声(image noise)是图像中一种亮度或色彩信息的随机变动(被拍摄物体自身并没有),通常是电子噪声的体现。它个别是由扫描仪或数码相机的传感器和电路产生的,也可能是受胶片颗粒或者现实光电探测器中不可避免的的散粒噪声影响产生的。图像噪声是图像拍摄过程中不心愿存在的副产品,给图像带来了谬误和额定的信息。
这些噪点,就如同高山矗立的山峰:
咱们要做的就是把山峰刨掉一些土,填到山峰四周去。用数学的话来说,就是把山峰四周的高度均匀一下。
那咱们该如何做到这样呢?
图像卷积运算
在计算机上咱们都晓得图像能够用一个二维矩阵来存储,其存储的内容 (像素) 又对应手机 / 电脑 硬件屏幕上的点。
有噪点的原图,咱们能够把它转为一个矩阵:
然咱们咱们用上面这个矩阵
来均匀一下下面的矩阵
如果我要均匀一下 a1,1 点,运算过程如下
如下图咱们应用上面这个卷积模板进行图像卷积运算,其后果就如动图那样,每个像素值都进行了加权均匀运算。
动态的图可能看起来没那么直观,咱们来个动图
假如咱们有个矩阵 与原图进行卷积运算
PS
不同的矩阵会产生不同的卷积成果,因而有些中央把这个矩阵称为卷积核
过程如下
卷积利用
最经典的卷积利用就是图像锐化和含糊的解决。在挪动设施上应用 GPU 做图像锐化,个别就是利用空域滤波器对图像做模板卷积解决。
锐化
拉普拉斯算法比拟适宜用于改善图像含糊,是比拟罕用的边缘增强解决算子。
顶点着色器 Vertex Shader
attribute vec4 position;
attribute vec4 inputTextureCoordinate;
uniform float imageWidthFactor; // 屏幕宽度步长因子
uniform float imageHeightFactor; // 屏幕高度步长因子
uniform float sharpness; // 锐化外围值,由外层用户输出
varying vec2 textureCoordinate; // 以后纹理坐标
varying vec2 leftTextureCoordinate;
varying vec2 rightTextureCoordinate;
varying vec2 topTextureCoordinate;
varying vec2 bottomTextureCoordinate;
varying float centerMultiplier; // Laplacian 算子核心值
varying float edgeMultiplier; // Laplacian 算子边缘值
void main()
{
gl_Position = position;
vec2 widthStep = vec2(imageWidthFactor, 0.0);
vec2 heightStep = vec2(0.0, imageHeightFactor);
textureCoordinate = inputTextureCoordinate.xy;
leftTextureCoordinate = inputTextureCoordinate.xy - widthStep;
rightTextureCoordinate = inputTextureCoordinate.xy + widthStep;
topTextureCoordinate = inputTextureCoordinate.xy + heightStep;
bottomTextureCoordinate = inputTextureCoordinate.xy - heightStep;
centerMultiplier = 1.0 + 4.0 * sharpness;
edgeMultiplier = sharpness;
}
这次的 GLSL 显著和前几次不太一样了,前几次都是应用默认的 Vertex Shader, 那么这次咱们怎么了解那四个 left/top/right/bottom 的纹理坐标?还有那两个屏幕步长因子,咱们再来看看下层代码的输出:
@Override
public void onInit() {super.onInit();
sharpnessLocation = GLES20.glGetUniformLocation(getProgram(), "sharpness");
imageWidthFactorLocation = GLES20.glGetUniformLocation(getProgram(), "imageWidthFactor");
imageHeightFactorLocation = GLES20.glGetUniformLocation(getProgram(), "imageHeightFactor");
}
@Override
public void onOutputSizeChanged(int width, int height) {super.onOutputSizeChanged(width, height);
setFloat(imageWidthFactorLocation, 1.0f / width);
setFloat(imageHeightFactorLocation, 1.0f / height);
}
屏幕步长是 以后屏幕宽高的倒数,也就是依照以后屏幕的像素个数宰割开来。以以后纹理坐标对应 laplacian 算子的核心核,左右偏移 1 /width= 1 个像素的地位,就得出 laplacian 算子外围的其余外围 8 格的像素点。如下图所示了解
因为我选用了疾速拉式变换,所以 L -T,R-T,L-B,R- B 的地位就没算进去,因为其 laplacian 的因子为 0 相乘后果也为 0,没必要节约顶点着色器的的输入变量。
片段着色器 Fragment Shader
precision highp float;
varying highp vec2 textureCoordinate;
varying highp vec2 leftTextureCoordinate;
varying highp vec2 rightTextureCoordinate;
varying highp vec2 topTextureCoordinate;
varying highp vec2 bottomTextureCoordinate;
varying highp float centerMultiplier;
varying highp float edgeMultiplier;
uniform sampler2D inputImageTexture;
void main()
{mediump vec3 textureColor = texture2D(inputImageTexture, textureCoordinate).rgb;
mediump vec3 leftTextureColor = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
mediump vec3 rightTextureColor = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
mediump vec3 topTextureColor = texture2D(inputImageTexture, topTextureCoordinate).rgb;
mediump vec3 bottomTextureColor = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
gl_FragColor = vec4((textureColor * centerMultiplier - (leftTextureColor * edgeMultiplier + rightTextureColor * edgeMultiplier + topTextureColor * edgeMultiplier + bottomTextureColor * edgeMultiplier)), texture2D(inputImageTexture, bottomTextureCoordinate).w);
};
上成果
小结
本篇咱们介绍了卷积,作为图片解决的基础知识,卷积在之后的图像处理中还是会常常用到的
Github 代码