# 色带处理

## 色带是如何产生的？

俗话说知己知彼，百战不殆，要对付色带这一号敌人，了解色带是如何产生的非常重要。

首先，色带往往出现在画面中的平面部分，大面积的色块往往是色带集中出现的区域，同时，一些渐变场景也常常会看到它。在现在的显示设备上，我们常常采用8bit每通道的RGB来量化色彩灰阶并显示画面。也就是说，RGB每个通道可以有256种灰阶表示，我们总共能显示的颜色种类就有256\*256\*256，也就是16777216种颜色。

你可能会说：好耶！有那么多颜色能表示，怎么会出现色带呢！

其实很简单，最简单粗暴的试想一下，屏幕上出现了一条从黑到白的线条，从最左到最右总长1920个像素，那每一个灰度区间如果从**线性的角度**讲，一个区间会长约7.5个像素，那区间和区间之间就会出现明显的一条痕迹，这就是色带的来历。所以，8bit下无论怎样都很有可能会出现色带，只是你能不能看得出而已。

当然了，上面特意提到了线性的角度，那么实际上，我们日常看的显示器内容，都是已经经过gamma校正了的，关于gamma校正这里不想细究，它的作用是通过重新分配灰度系数，让对黑暗不敏感，亮部敏感的人眼能够更清晰的看到画面中偏黑的区域。简单的来说，你看到的灰阶从最暗到最亮的分配并不是均匀的，越黑的部分分配的灰阶越多，这听起来可能有点怪，但是通过下图我们可以直观的感受到区别。

![这是原始的8bit黑白渐变图](https://i.v2ex.co/v795yRN9.jpeg)

如果我们用5bit（32位）来表示的话，那么必然会出现色彩断层，但是线性分布和gamma校正后的感觉会完全不同

![如果是线性分布的话，暗处的断层会很明显](https://i.v2ex.co/dl4oxx5T.png)

![但是gamma校正后，我们看到的亮度就非常均匀了](https://i.v2ex.co/9fQ3rkp1.png)

Gamma的出现虽然让人眼对画面的光暗得到了巨大的提升，但是它也打破了灰度的均匀分配，让色带更可能出现了。

## 那么，色带要如何解决呢？

其实方法总结下来就是两点，要么添加噪点，要么就将构成色带的像素替换为临近像素的中间值（其实就是一个字，抹！）

### 加噪：

很显然的，由于我们是人类，眼神十分的堪忧，没有经过特别的训练和熟悉一般很难看出色带，所以加点噪点可以更进一步的混淆人眼，让我们感觉不出色带的存在。同样的，你也很难在画面的纹理部分看出色带，只有什么都没有的纯色块/平面才会让色带看起来非常明显。

![人为制造的有色带的示例（注意墙壁上的色带）](https://i.v2ex.co/5633z3HVl.png)

![加噪之后，是不是就不明显多了](https://i.v2ex.co/U00w7zPvl.png)

当然，这样无脑的加噪会影响观感，而且效果也只能算凑合。我们接触的大部分BDMV都会为了防止色带打上一层不痛不痒的噪点\~\~（所以我们俗称SBMV）\~\~，这么做属于治标不治本的办法，但是保留噪点作为去色带操作后的保底是一个不错的选择，万一色带没抹干净好歹还有层噪点做遮羞布，其实也挺好的。

如果想要加噪，最直接的方法就是使用[AddGrain](https://github.com/HomeOfVapourSynthEvolution/VapourSynth-AddGrain)来进行：

`addgrain = core.grain.Add(res,1.0)`&#x20;

需要注意的是，加噪的时候，我们一般不推荐添加色度平面的噪点。毕竟彩色噪点看起来真的很不好看，还让本来就码率预算不充足的色度平面更加雪上加霜。所以我们推荐在加噪后，使用`std.ShufflePlanes`来仅将加噪后的Y平面与原Clip拼成一个新的Clip

```
src8   = core.lsmas.LWLibavSource(source=input)
src16  = mvf.Depth(src8,16,fulls=False,useZ=True)

noise = core.grain.Add(src16, 1.0)
# 使用ShufflePlanes提取出Y平面
noise = core.std.ShufflePlanes(noise, planes=0, colorfamily=vs.GRAY)
# 将Y平面合入原Clip
output = core.std.ShufflePlanes([noise,src16], planes=[0, 1, 2], colorfamily=vs.YUV)
```

当然了，全屏幕直接加噪，还是不够优雅，毕竟噪点也是占用大量码率的，我们希望能够好钢用在刀刃上——在需要加噪的地方加噪：

很显然的，大部分噪点其实出在比较暗的地方，较亮地方的噪点用打噪点也没办法很好的糊弄过去，较暗的地方打噪点效果就比较好，那么有没有一种办法，能够让噪点打在暗的地方呢？

{% hint style="info" %}
以下部分需要对：std.Expr()有一定程度的理解，对std.MaskedMerge有一定程度的理解
{% endhint %}

这时候就需要一些简单的数学知识了：

首先我们创建一个blankclip，所有像素的值都是32768，再对这个clip进行加噪：

```
nullclip = core.std.Expr(src16,["32768",""])
noise = core.grain.Add(nullclip,  1.0)
```

然后我们可以设计一个函数（例如二次函数）作为Mask，将0-255的8bit输入（x），转化为每个16bit下的像素应有的0-65535的权值（y）

如果y越接近与0，那么意味着噪点会被尽可能的添加回去

如果y越接近65535，那么意味着噪点会被尽可能的不添加回去

![一个二次函数：(x-48)^2\*5](https://i.v2ex.co/4AiB2D2Ml.png)

例如$$(x-48)^2\*5$$这个函数，在$$x=48$$的时候（8bit下这是相对暗的区域），$$y$$的取值是0，代表在这个区域的加噪强度是最高，而由于二次函数的特性，所以接下来的曲线会特别陡，在$$x=128$$时，$$y=32000$$，加噪强度已经降低到了50%，并且在$$x=162$$左右时，降噪强度已经变为0%，这样的函数可以说是比较理想的，当然如果你想调整零点或者曲线开口程度，那相信你应该知道该怎么改这个函数。所以，我们可以用VS中的Expr来基于clip创建这样一个mask:

`nrweight = core.std.Expr(src8, ["x 48 - dup * 5 *", ""], vs.YUV420P16)`

&#x20;然后我们使用`std.MaskedMerge()`来基于Mask合并片源与噪点层：

`out = core.std.MaskedMerge(noise,nullclip,nrweight,[0,1,2],True)`

&#x20;我们就获得了一个能够自适应添加噪点的clip了！

### 抹:

抹这个操作就字如其名了，检测色带，针对色带进行平滑就是抹的主要目的，我们常常使用f3kdb来进行去色带操作，但是如果遇到真的，非常难搞的色带时，GradFun3也是个不错的选择

#### 首先来讲讲f3kdb:

f3kdb的效果在deband滤镜中已经算是翘楚了，它的原理听起来也非常简单：将构成色带的像素替换为临近像素的中间值，并抖动加噪从而摆脱色带。很显然，这样的操作与降噪很像，并且会抹去画面中的细节。

f3kdb的所有参数的具体信息都可以查看滤镜百科中去色带滤镜部分，这里来讲讲f3kdb的常用写法：

由于BDMV里会默认添加一层噪点，它对于f3kdb滤镜去色带会有巨大的影响，所以一般我们需要在降噪之后再使用f3kdb，以保证最好的效果和最小的细节损伤，这个操作一般被称为：nr-deband

待续


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://guide.geeking.moe/cong-ru-men-dao-jing-tong/se-dai-chu-li.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
