一些常用滤镜的实现思路

一些常用滤镜的实现思路

1.什么是颜色?

​ 早在17世纪,颜色的本质是牛顿最早通过三棱镜的试验发现的。牛顿通过三棱镜研究对白光的折射就已经发现了白光可以分解成一系列从紫色到红色的一系列连续光谱。从而证明了白光是由不同的颜色的光线混合而成的。

“ 正确得讲,光线并不是彩色的。” --牛顿

  • 颜色是人的视觉系统对可见光的感知结果,感知到的颜 色由光波的波长决定
  • 视觉系统能感觉的波长范围为380~780nm。

颜色,其实本身不是一个物理意义的概念,其实是人的感知,因为彩色仅仅是存在于人的眼镜和大脑中。
一些常用滤镜的实现思路_第1张图片一些常用滤镜的实现思路_第2张图片

可见光的光谱:400nm-700nm

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oZWq6ibt-1623408706908)(/Users/william/Library/Application Support/typora-user-images/page49image9436816.png)]

S-Cone(锥状) :短波长(B)

M-Cone :中波长(G)

L-Cone :长波长(R)
一些常用滤镜的实现思路_第3张图片

2.简单的图像分类

  • Binary images (0 or 1)二值图像
  • Gray images(0-255 or 8 bits/pixel)灰度图
  • color images 色彩图像
    • 举例:Full color images(24 bits/pixel, rgb) 真彩色图像

一些常用滤镜的实现思路_第4张图片
​ 这张图可以看出,我们定义灰度图,需要256个灰度级,灰度级太多了会浪费内存,灰度级太少了会产生马赫带效应,因此,我们在分配图像内存的时候通常一个像素是分配3个字节(24个bit),也就是我们常说的真彩色(三通道)

图片滤镜思路 — 对顶点坐标,纹理坐标进行各种变换

一些常用滤镜的实现思路_第5张图片

几个例子

1.分屏滤镜:

分2格:

思路:判断纹理的y坐标,在纹理的(0.0, 0.5)和(0.5, 1.0)坐标填充为(0.25, 0.75)区间的内容。

  vec2 uv = TextureCoords.xy;
  float y;
  if (uv.y >= 0.0 && uv.y <= 0.5) {
      y = uv.y + 0.25;
  } else {
      y = uv.y - 0.25;
  }

其余类似

2.灰度

基础知识:

亮度:亮度指的颜色的综合明亮程度,并且与颜色的色调无关。

在sRGB规范中,亮度可以被定义为红,黄,蓝三种元素的现行组合,具体的权重如下:

const highp vec3 w = vec3(0.2125, 0.7154, 0.0721);

权值向量w中的三个数值的和为1.0,因此,该向量与有效的RGB之间的点积的结果将生成位于0-1之间的亮度值。

ps:sRGB色彩空间(标准红绿蓝色彩空间)是惠普与微软于1996年一起开发的用于显示器、打印机以及因特网的一种标准RGB色彩空间。

思路:灰度滤镜的实现有很多方法可以得到灰度滤镜,这边列举了一下几种方法

  • 权重算法: Gray = R * 0.3 + G * 0.59 + B * 0.11(比较逼真)
  • 平均值法: Gray = (R + G + B) / 3;(比较柔和)
  • 仅取绿色: Gray = G //人的眼睛对绿色比较敏感。
  • 亮度替代颜色值

GPUImage:中使用的是权重提取;

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
const highp vec3 w = vec3(0.2125, 0.7154, 0.0721);

void main (void) {
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    float luminance = dot(mask.rgb, w);
    gl_FragColor = vec4(vec3(luminance), 1.0);
}

由RGB到HSV的转换:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xln1euOY-1623408706912)(/Users/william/Library/Application Support/typora-user-images/image-20200724233809425.png)]

3.lookup table: 颜色查找表

目前我已知的有四种lookup 颜色查找表

1* 256 有局限,但是是无损的(rgb三个通道分开查询)

在这里插入图片描述

32 * 1024 选择性有损查询

在这里插入图片描述

256 * 256 精度损失较大

在这里插入图片描述

512 * 512 比较常用
一些常用滤镜的实现思路_第6张图片

通过LUT,你可以将一组RGB值输出为另一组RGB值,从而改变画面的曝光与色彩。我们可以看下一张颜色查找表长什么样,可以发现这边总共有8 * 8 = 64个格子.尺寸是512* 512 = 262,144。也就是说,一张基准图可以表示64 * 64 * 64 = 262,144种颜色。

​ 不妨我们可以先看下其中一个格子,对应的尺寸就是64* 64, 换句话说,也就是64 * 64 = 4,096个像素构成,可以表示 64 * 64种颜色。那么为啥需要64个格子呢,原因是在真彩色颜色空间中,是由rgb三个通道组成,一个格子只能用x和y构成的二维笛卡尔坐标来表示两个颜色通道的映射关系。原则上三通道的话,就需要x,y,z构成的三维笛卡尔坐标系来表示映射关系(也就是需要一张三维体纹理)。64个格子可以理解为把第三个维度(也就是z轴)一层一层抽出来,然后铺平,模拟表示一张三维纹理。

一些常用滤镜的实现思路_第7张图片

到这里,我们就可以明白,lookup table 的原理其实就是一个颜色区间的查询过程。举个例子0-4的灰度级都对应查找表的第一个像素的颜色。最后,得到了查询表的颜色值之后,再跟原图进行mix混合,就是叠加上滤镜的效果了。具体可以看看glsl的实现代码:

const float stepDis = 1.0 / 8.0;
const float perPixel = 1.0 / 512.0;
const float halfPixel = 0.5 / 512.0;

vec3 lookup(vec4 color, sampler2D lookupTexture) {
    float blue = color.b * 63.0;
    vec2 coord1;
    coord1.y = floor(blue / 8.0);
    coord1.x = floor(blue) - (coord1.y * 8.0);

    coord1 = coord1 * stepDis + halfPixel + (stepDis - perPixel) * color.xy;
    return texture2D(lookupTexture, coord1).rgb;
}

4.腐蚀/膨胀

https://www.bilibili.com/video/av15515818/

5.曝光:

快门打开时,光线透过镜头,经过光圈,进入暗室,最后照在成像材料上,这个过程称为曝光。

增加曝光,就是进入相加的光线更多了,可以通过把颜色值乘以一个2的指数次方来实现。

#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif // GL_FRAGMENT_PRECISION_HIGH

uniform sampler2D inputImageTexture;
varying vec2 textureCoord;
const float intensity = 1.2;

void main() {
    vec4 color = texture2D(inputImageTexture, textureCoord);
    color.rgb *= pow(2.0, intensity);
    gl_FragColor = color;
}

6.噪声

基础知识

随机:

一些常用滤镜的实现思路_第8张图片

​ y = fract(sin(x)*1.0)

一些常用滤镜的实现思路_第9张图片

​ y = fract(sin(x)*2.);

一些常用滤镜的实现思路_第10张图片

​ y = fract(sin(x)*4.);

sin(x)值域为-1.01.0 ,这里实际是指模1,返回0.01.0 间的正值,我们可以用这种效果通过把正弦函数打散成小片段来得到一些伪随机数。

一些常用滤镜的实现思路_第11张图片

​ y = fract(sin(x)*43758.5453123);

glsl业界里面求随机数的函数

float random (vec2 st) {
    return fract(sin(dot(st.xy,
                         vec2(12.9898,78.233)))*
        43758.5453123);
}

具体的数字可能是出自一篇论文

举个例子:

#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif // GL_FRAGMENT_PRECISION_HIGH

varying vec2 textureCoord;

float random (vec2 st) {
    return fract(sin(dot(st.xy,
                         vec2(12.9898,78.233)))*
        43758.5453123);
}

void main() {
    float rnd = random( textureCoord );

    gl_FragColor = vec4(rnd);
}

一些常用滤镜的实现思路_第12张图片

你可能感兴趣的:(算法,计算机图形学,OpenGL,计算机视觉,opengl,数字图像处理)