人工智能深度学习火起来的时候我才知道有卷积这个东西,后面接触之后才知道卷积在图形学中也是相当有用的。
卷积主要作用就是特征识别,例如给你一张图片,图片划分为M*N的宫格,我们需要一个方法能够识别图片中某一个宫格,怎么办呢?就是信息数字化,将每一宫格图片数字化成横纵的数值矩阵,然后使用卷积核(此时的卷积核就是一个需要匹配的数值化矩阵),然后将卷积核与图片的每个宫格的矩阵进行点乘,得到的点乘数值能表明该卷积核与宫格的匹配程度。
这里我找到另一个博主的文章,描叙的很通熟易懂,链接指向:卷积提取特征,相关卷积滤波器的百度百科:卷积滤波器
简单来说就是使用卷积核进行点乘的区域矩阵得到的值越大说明特征越匹配。
但是特别指出一下,上面这个老鼠特征识别只是使用到了卷积核的cross-correlation(互相关)运算,而非卷积(convolution)运算,而卷积计算过程中需要提前进行卷积核逆时针180°旋转,所以我们把上面处理老鼠的过程叫做correlation或者滤波(filtering)而非convolution。
那么大家想象一下,对于同样一幅图和同一个卷积核,convolution操作首先逆时针旋转180°,那么convolution得到的结果和filtering得到的结果会不同,除非卷积核是翻转对称性的。
那么卷积核的convolution操作主要有什么作用呢?特别是图形学中,我们首先来看一个示例,这里我先说明一下卷积核与图形的convolution操作具体做法:
就是卷积核翻转后与对应图形宫格矩阵中的像素值进行点乘(也就是卷积核每个值*像素矩阵每个颜色值然后加权得到新的像素值置换像素矩阵中心像素),我们直接用百度上找到的翻转后的卷积核。
感觉在图形学中,convolution和filtering操作是等价的,无非就是提前使用特定的卷积核。
这里我找到了一个3*3的卷积核:
0.11 0.11 0.11
0.11 0.11 0.11
0.11 0.11 0.11
如果对数学比较敏感的同学,就能看得出来,这个卷积核与图片数值矩阵点乘后,会“均匀”的将中心像素周围像素的颜色(包含中心像素)加权替换到中心像素上。
同时如果对图形比较敏感的同学,就能依稀猜的出来,这种加权方式,会产生一种“模糊”的效果,打个比方就跟你用多彩沙盘画了一幅画,然后用手指轻轻浮动沙画,造成的效果就是类似这种。
先通过实现Cg shader来看下这个卷积核能达到什么效果,我们在fragment片段函数中定义的卷积核可以定义成已经进行180°翻转的,所以以后我们直接使用翻转后的卷积核,如下:
Shader "Unlit/ConvolutionShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Dim("Dim",Range(0,1)) = 0.11
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 conuvs[9] : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainTex_TexelSize; /*纹理属性*/
float _Dim;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
float2 uv = TRANSFORM_TEX(v.uv, _MainTex);
//从上到下从左到右依次计算卷积核对应的9个像素值
o.conuvs[0] = uv + _MainTex_TexelSize.xy*float2(-1,1);
o.conuvs[1] = uv + _MainTex_TexelSize.xy*float2(0,1);
o.conuvs[2] = uv + _MainTex_TexelSize.xy*float2(1,1);
o.conuvs[3] = uv + _MainTex_TexelSize.xy*float2(-1,0);
o.conuvs[4] = uv;
o.conuvs[5] = uv + _MainTex_TexelSize.xy*float2(1,0);
o.conuvs[6] = uv + _MainTex_TexelSize.xy*float2(-1,-1);
o.conuvs[7] = uv + _MainTex_TexelSize.xy*float2(0,-1);
o.conuvs[8] = uv + _MainTex_TexelSize.xy*float2(1,-1);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//不需要180°翻转,可以提前翻转卷积核
float conv[9] = {_Dim,_Dim,_Dim,_Dim,_Dim,_Dim,_Dim,_Dim,_Dim};
fixed4 col = fixed4(0,0,0,0);
for(int k = 0; k < 9; k++)
{
col += tex2D(_MainTex,i.conuvs[k]) * conv[k];
}
col.a = 1;
return col;
}
ENDCG
}
}
}
_MainTex_TexelSize为unityCG提供给我们的纹理属性,如下:
得到一个模糊效果,如下:
看得到一个模糊效果么?不太明显,但确实是模糊效果,就是将每个像素周围所有像素进行1/9的点乘加权,因为大部分情况下,连续的像素之间差别不大,所以加权之后产生一个模糊效果。
后面我们再找一些其他的卷积核实现一下其他的效果。