普通视图

发现新文章,点击刷新页面。
今天 — 2025年8月23日微言 | wyanassert

[转载] 给世界上色——滤镜底层原理

作者 wyanassert
2025年8月23日 15:37

滤镜最早应用在电视影视业,对剧和电影作品后期进行调色效果。如今拍照、修图都离不开滤镜效果。我们在微博、朋友圈、电视、影院里看到的照片视频,无一没有滤镜的效果,滤镜已经深入我们生活的方方面面。

这里浅略地揭秘一下当前图像处理中滤镜底层的原理。

在这里插入图片描述
在这里插入图片描述

RGB颜色


RGB色彩模式是工业界的一种颜色标准,是通过对红®、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是运用最广的颜色系统之一。
在这里插入图片描述
对于一张图片的每一个像素,都可以由唯一的颜色(R,G,B)表示,其中RGB的范围是0~255。0代表最暗,255代表最亮。
在这里插入图片描述

颜色查找表


滤镜的本质就是颜色的变换。我们可以把一个像素的颜色(R,G,B)看成是一个三维空间上的一个坐标点。颜色变换相当于是三维空间的 [一个坐标点][另一个坐标点] 的映射关系。也就是:

1
2
3
4
5
6
7
8
9
10
11
12
         (old R,old G,old B)  --->   (new R,new G,new B)

例如:

(23,77,134) ---> (122,34,255)

(189,65,138) ---> (5,65,21)

(0,0,2) ---> (33,0,1)

………………

在这里插入图片描述

即每一个(R,G,B)都有一个一一对应的目标颜色,那么一个完整的RGB颜色查找表总共有 256×256×256 = 2^24 条。

显然 2^24 这个数字有点太大了,存储它需要至少16Mb的空间,加载到内存也至少需要吃掉16Mb的内存
(问题1:这里可以先思考一下为什么是16Mb?后面会给出解释)

因此我们在实际查找表应用中一般使用 64×64×64 的查找表,RGB每个通道都将 [连续的4个坐标] 映射成 [1个目标坐标]

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(0,X,X)    --->   (34,X,X)

(1,X,X) ---> (34,X,X)

(2,X,X) ---> (34,X,X)

(3,X,X) ---> (34,X,X)


(4,X,X) ---> (128,X,X)

(5,X,X) ---> (128,X,X)

(6,X,X) ---> (128,X,X)

(7,X,X) ---> (128,X,X)

但显然这样会导致原始图片颜色精度的丢失(例如上面的0~3的亮度映射后都变成了无差别的34),那么就需要想办法降低这种精度丢失的问题
(问题2:这里可以先思考一下有哪些补偿精度的方法?后面会给出解答)

LUT图


有了颜色映射表,接下来需要考虑如何去表达这些映射关系了。有一个笨办法是用文本去存储这 64×64×64 这么多条 (old R,old G,old B) —> (new R,new G,new B)这样的映射关系。聪明的你肯定意识到了这个文本的大小会是一个天文数字。那么有没有更好的表达方法呢?优秀的前辈们发现可以用一个图片去表示这个映射关系,这就是LUT图:
在这里插入图片描述
这个LUT图的尺寸是 512×512 ,正好等于 64×64×64(查找表的数量),也就是刚好每一个像素可以表示一条 (old R,old G,old B) —> (new R,new G,new B)这样的映射关系!

我们可以用 [图片的像素坐标] 表示(old R,old G,old B),用这个坐标所在的 [像素的颜色] 表示(new R,new G,new B),这样就实现了映射表的一一对应关系。

我们可以先从比较简单的开始看,为什么用这个坐标所在的 [像素的颜色] 表示(new R,new G,new B)?因为图片本身每个像素就是由(R,G,B)组成,并且都是0~255,刚好可以完美表示一个(new R,new G,new B)。

再来看看比较复杂一丁点的,如何用 [图片的像素坐标] 表示(old R,old G,old B)?因为一个图片的坐标是二维坐标(x,y),要如何表示一个三维的(old R,old G,old B)呢?

可以看到这个图片每一行有8个小正方形,每一列也有8个小正方形,总共有64个小正方形。每个小正方形都是一张 64×64的图片。

我们先从一个小正方形开始看。其实每一个小正方形都代表了完整的(old R,old G) —> (new R,new G)的映射。其中像素Target坐标(x,y)代表(old R,old G),像素Target的颜色的RG值就代表(new R,new G)。

例如下面这张图片:

1.假设(old R,old G) = (100,150),那么Target的坐标就是(100 / 4,150 / 4)= (25,37)

2.如果这个图片里坐标是(25,37)的Target的像素值是(213, 88),那么(new R,new G)=(213, 88)

2.即完成了(old R,old G) = (100,150) —> (new R,new G)=(213, 88) 的映射关系!
在这里插入图片描述
至此已经完成了R通道和G通道的映射,那么如何确定B通道的映射关系呢?前面说到一个完整LUT图有8×8=64个小正方形,这64个小正方形就是用来确定B通道的映射关系的。

我们把这64个小正方形按从左到右从上到下排列,编号0~63,我们就可以把(old B)映射到其中的某个小正方形格子。

拿下面这个示例图比较能说明过程:

假如(old B) = (50),那么最终的颜色落在第(50 / 4)=(12)个格子上。

但注意,这里的(12)并不是(new B),(12)仍然只是图片的 [坐标],因此它代表的其实还是(old B),仅仅 / 4 了而已。

我们回到上面一步的步骤,确定了在是哪个小正方形,就可以在这个小正方形里根据(old R,old G)确定最终的Target。那么(new B)就等于 像素Target颜色的B值!
在这里插入图片描述

聪明的你也许已经意识到了,在这64个小正方形里,每个小正方形相同(x,y)坐标所对应Target像素颜色的(R,G)都是一样的,仅仅只有B不一样。这也就是为什么B颜色最终是根据计算 [在哪个小正方形里] 来确定的。

我们再来完整走一遍LUT图颜色映射的全过程。

1.假如一个像素点原始的颜色是 (old R,old G,old B)=(38,201,88)

2.根据(old B)确定在哪个小正方形:88 / 4 = 22

3.在第22个小正方形里,根据(old R,old G)确定最终Target的坐标:(38 / 4,201 / 4)=(9,50)

4.假如第22个小正方形中,(9,50)所对应的Target像素的颜色是(50,3,126)

5.那么最终的颜色(new R,new G,new B)=(50,3,126),至此完成一个像素点颜色的映射。

6.遍历原始图片的每一个像素,都走一遍1~5的过程,完成对整张图片的滤镜效果。

这里先解答一下上面 [问题1] 和 [问题2] 的解答:

问题1:为什么是16Mb?

因为映射关系都用LUT图表示,每个像素代表一条映射,那么64×64×64 = 2^18,一张 2^18 个像素的无损图片(一般是.png)大小至少是256Kb,而 256×256×256 = 2^24 个像素的无损图片大小至少是16Mb。

问题2:有哪些补偿精度的方法?

注意到上面精度的丢失是因为像素颜色从 256 –> 64,我们上面在做除法的时候丢失了小数点,例如(38 / 4,201 / 4)=(9,50),但其实应该是(38 / 4,201 / 4)=(9.5,50.25),在实际运用的时候我们并不会抛弃小数点。

在计算的时候,如果计算得到的坐标不是位于一个像素的正中心,那么在取色时,会对相邻的几个像素进行加权平均,更靠近的像素权重越大。直观地说就是,理谁越靠近,那么谁就对最终的颜色有更重要的影响。

例如下面这个图,在最终确定颜色时,会考虑相邻的4个像素点的颜色。这就是双线性插值法,除此之外也有单线性插值法,有兴趣的朋友欢迎交流。
在这里插入图片描述
双线性插值法示意图:
在这里插入图片描述

如何制作一个LUT图?


1.打开Photoshop,打开一个你想调色的图片

在这里插入图片描述

2.通过各种调节,达到你所期待的颜色

在这里插入图片描述

3.载入一张原始LUT图

在这里插入图片描述
什么是原始LUT图:就是经过这个LUT颜色变化之后,还是原来的颜色,也就是 [什么颜色都不变]

它的映射关系:

1
2
3
4
5
6
7
8
9
10
(0,0,0) ---> (0,0,0)

(0,0,1) ---> (0,0,1)

………………

(254,255,255) ---> (254,255,255)

(255,255,255) ---> (255,255,255)

4.对这张LUT图也应用上对刚才图片的调色效果

在这里插入图片描述
至此一张LUT滤镜图就做好了:
在这里插入图片描述

怎么理解这个过程?

我的理解是,我们对图片进行 [调色的一系列操作],再 [作用在原始LUT图上],就相当于让这张原始LUT图记录下了 [这一系列操作],记下来之后就可以拿去对任意的图片进行滤镜效果了。

最后附一个效果图

请添加图片描述

[转载] 美颜的奥秘——磨皮底层原理

作者 wyanassert
2025年8月23日 15:14

原文地址

据不完全统计,全世界每隔3秒就有一个人上传自己的自拍照,甚至不少人在P图上所花的时间都超过了化妆时间。

从十多年前“美图秀秀”的横空出世,再到近年来的实时美颜。到今天,美颜功能已经嵌入到各类手机系统当中,帮助大家实现完美自拍。有玩笑说,中国的P图术、韩国的整容术和日本的化妆术瓜三分天下。此秘术自诞生以来教众不断,但受用者,可瞬间变成天仙下凡,号称“传说中的3大东方神秘力量”。由此可见,随着朋友圈、微博等自拍社交越来越盛行,拍个美美的照片已经是人们的刚需了。

其实磨皮算法最底层的本质就是一种降噪算法,也可以说是模糊算法。即通过对脸部的模糊,把各种面部瑕疵模糊掉,以达到磨皮的效果。

本文很简单地介绍几种很基础的模糊算法以及磨皮后的边缘和细节还原。
在这里插入图片描述

一、磨皮核心——模糊算法


模糊算法也可以说是降噪算法,把清晰可见的东西变得模糊。磨皮的原理就是把脸部变“模糊”,把各种瑕疵、皱纹、痘印等变模糊,使得整个脸看起来很光滑平整。模糊算法就是这样,可以隐去很多细节,也可以说是可以用更少的图像信息量去表达一幅图,因此许多细节就在模糊的过程中被抹去。

如何使一张图片变模糊呢,我们不妨从微观看起。

我们来看一张3*3的图:

在这里插入图片描述

假设上面的数字都代表当前位置的像素值。

假如正中央的像素”9”代表一个瑕疵点,那么我们如何把这个”9”模糊掉呢?

1.中值滤波

对核心及周围的像素值排序,取中间值作为新的像素值。
在这里插入图片描述

2.均值滤波

将核心及周围的像素求和取平均值,作为新的像素值。
在这里插入图片描述

3.高斯滤波

在均值滤波的基础上,对每个像素加上一个权重。这个权重是一个高斯函数。概况地说,距离中心点越近,权重越大;距离中心点越远,权重越小。

一维高斯函数可以这样表示:
在这里插入图片描述
下图分别是一维高斯图像和二维高斯图像:
在这里插入图片描述
把二维高斯函数放到我们上面的3*3的区域中,并做归一化,就得到了权重:
在这里插入图片描述
那么最终的颜色这样计算:
在这里插入图片描述

4.双边滤波

在高斯滤波的基础上,再加上一个[像素差异]的权重:与中心颜色相差越大,权重越低;与中心颜色相差越小,权重越高。

这么做是为了能够在模糊的时候,较好地保护图像的边缘细节信息。这也是磨皮常用的模糊算法,因为磨皮就是需要保留人脸的一些纹路边缘细节,使得磨皮效果看起来更加自然。

可以这么理解:

高斯核是[空间域]上的权重:距离中心的空间距离越远,权重越小。

双边滤波多了一个[值域]上的权重:距离中心的像素差别越大,权重越小。

以下两个图片可以更好理解双边滤波:
在这里插入图片描述
在这里插入图片描述
还是拿上面3*3的区域应该这样算:
在这里插入图片描述
这里的值域核仅为了表达方便,实际应用中也需要做类似归一化的操作

我们可以看一下这几种模糊算法的效果:

原图:
在这里插入图片描述
中值滤波:
在这里插入图片描述
均值滤波:
在这里插入图片描述
高斯滤波:
在这里插入图片描述
双边滤波:
在这里插入图片描述

二、锐化——边缘和细节还原


锐化可以分成2步:第一步,提取边缘;第二步:边缘还原到原图上。第二步其实就是简单的把边缘图叠加到原图上,因此这里重点介绍边缘提取算法。

在这里插入图片描述

1.USM锐化

USM锐化是最常见的锐化,其主要利用了模糊图,原理如下:
在这里插入图片描述
上文说过的模糊算法其实就是把大部分细节抹去,用原图减去模糊图,就得到了这幅图像的边缘和细节了。得到细节之后,叠加回原图,就实现了锐化的效果。

如果想要更大的锐化怎么办呢?那就使用更模糊的图,以得到更大的差值:
在这里插入图片描述

2.拉普拉斯Laplace锐化

拉普拉斯锐化方式是通过对像素进行卷积遍历,以得到边缘。
在这里插入图片描述
以4领域卷积核为例:

如果当前像素与上下左右4个像素完全相同,那么计算得的结果为0,即代表当前像素并不是边缘;

反之如果计算结果不为0,说明当前像素与上下左右像素值存在差异,那么这个像素在一定程度上是边缘的一部分。

拉普拉斯锐化效果如下:
在这里插入图片描述

3.索贝尔sobel锐化

sobel锐化也是使用对像素的卷积遍历,不同的是它区分横纵卷积核。
在这里插入图片描述
以横向卷积核为例:

如果左边一列和右边一列像素完全相同,那么计算得的结果为0,即代表当前像素并不是边缘;

如果左边一列和右边一列像素值有所差别,那么计算结果不为0,代表当前像素正处在边缘部分。

sobel边缘提取效果如下:
在这里插入图片描述

效果图:
请添加图片描述

❌
❌