NMM视频技术(旧)

 找回密码
 成为会员
搜索
查看: 5541|回复: 1

[AU使用] 关于AviUtl内部颜色格式[草译稿]

[复制链接]
发表于 2009-12-27 21:41 | 显示全部楼层 |阅读模式
作者:MakKi
初版发布:2009年01月24日
原文:AviUtlの内部形式について

翻译:dgwxx
翻译与更新日期:
2009年12月27日 草译稿

==============================
本文目前为草译版。尚未进行统一的校对和修改,尚未向原作者申请许可。
严禁任何形式的转载!無断転載厳禁。
==============================


==============================
正文
==============================

目录
1.关于本文
2.AviUtl内部颜色格式的基础
    2.1. PIXEL_YC结构体的定义
    2.2. 特征
3.与YUY2, 24bit-RGB的相互转换
    3.1. YUY2与YC48的相互转换
    3.2. RGB与YC48的相互转换
4.AviUtl内部的处理流程
    4.1. 输入
    4.2. 输出
    4.3. 编辑画面的显示
    4.4. 剪贴板
致谢
参考文献

1.关于本文
本文意在说明AviUtl内部使用的图像表现形式。但作者亦不过是一名AviUtl的普通用户,只能凭借已经公开的资料和软件的实际行为来进行分析。所以请留意本文的描述未必和AviUtl的实际情况完全一致。执笔当时AviUtl的版本是0.99g3[0],文中若无特别说明,则所记事项与版本相关度不高。此外,本文不涉及0.99版本之后所支持的YUY2模式滤镜。
希望本文能够对AviUtl用户及开发者有所帮助。若您发现内容的错误之处,请联系MakKi。

[0]本文发布前0.99g4版本发布,但随后确认颜色转换公式一致。从更新记录看来也基本可以认为没有改变。


2.AviUtl内部颜色格式的基础
AviUtl内部的图像由PIXEL_YC结构体进行表达。这种形式从很久以前[1]就开始采用,同时也是AviUtl的特色之一。同时,输入输出时采用了“YC48”这一FourCC[2]。

2.1. PIXEL_YC结构体的定义
以下代码引用自AviUtl插件SDK filter.h:

  1.     //  YC结构体
  2.     typedef struct {
  3.         short   y;        //  像素(亮度      )数据 (     0 ~ 4096 )
  4.         short   cb;        //  像素(色差(蓝))数据 ( -2048 ~ 2048 )
  5.         short   cr;        //  像素(色差(红))数据 ( -2048 ~ 2048 )
  6.                     //  像素数据可以超出指定范围
  7.                     //  像素数据可以不必
  8.     } PIXEL_YC;
复制代码
2.2. 特征
YC48通过将YUV进行扩展映射,将Y以0~4096、UV以-2048~2048范围各16bit进行表达。具体来说,完全的白色(RGB=FFFFFF)表现为y=4096,cb=0,cr=0,完全的黑色(RGB=000000)表现为y=0,cb=0,cr=0。这就意味着相对广泛使用的YUV各8bit精度的格式,AviUtl拥有16bit的高精度,可以将滤镜处理过程中的舍位误差降到最低。这也是AviUtl获得高精度的评价的最大的理由之一。
常有人误认为AviUtl内部颜色格式是12bit精度的4096阶数据,但实际上如您所见其实是YUV各4097阶,比12bit的范围还多了1阶。将YC48转换至其他颜色空间的时候需要特别加以注意。
正如2.1中的注释所描述,亮度低于0或高于4097、色度小于-2049或大于2049都是被允许的。因此AviUtl中可以保存YUV取值范围之外的数据。因此,插件必须能够正确处理这部分范围外的数据。同时,将反为外的数值强行塞进0~4096范围内这种做法会缩小动态范围,所以不建议采用。笔者不时会遇见收到比较极端的范围外的数值就会出现错误的滤镜。在此虽不点名提出,但建议作者尽快进行修正。

[1]在一个0.96d以前的filter.h中就已经有所定义,同时在茂木氏的老版插件中附带的filter.h中也得到了确认。
[2]0.99d以后的SDK中有所描述。


3.与YUY2和24bit-RGB的互相转换
目前AviUtl中的颜色转换式是随着0.98版加入YUY2输入支持的同时确立起来的。在那之前RGB的转换式曾经多次发生过变化。本节中介绍的转换式效用与0.98以后的转换式相同。(通过整数运算表现)

3.1. YUY2和YC48的相互转换
YUY2虽然是各平面8bit的256阶数据,但一般来说Y是范围在16~235之间的220阶数据、UV是16~240的225阶数据。因此,若转换时只将YUY2各数值简单地扩大16倍,是无法与YC48的0~4096阶范围一致的。这就是AviSynth中warpsharp插件包中ConvertYUY2ToAviUtlYC这个滤镜的问题所在[4]。为了获得正确的结果,需要使用以下的转换式(结果与AviUtl内部计算完全相同,算式中的位操作是算术移位)。
  1. // YUY2->YC48
  2. y  = ((Y * 1197)>>6) - 299;
  3. cb = ((U - 128)*4681 + 164) >> 8;
  4. cr = ((V - 128)*4681 + 164) >> 8;
复制代码
(算术移位>>6等于除以2^6=64,>>8等于除以2^8=256)
YUY2的结构为横向每2个像素共享一个色差数值,在AviUtl中,偶数的像素(设最左端像素为第0个像素)将直接使用YUY2中的色差数据,其右的像素(同上,奇数像素)的色差数据则使用左右两侧的色差数据的平均值进行插值。设左数第n个像素的色差数据为C[n],则插值处理的算式如下:
  1. C[2n+1] = ( C[2n] + C[2n+2] ) >> 1;
复制代码
(算术移位>>1等于除以2^1=2)
在AviUtl中,除2的操作被优化为1bit的算术移位。最右端的奇数像素的色差值与其左侧的像素相等。顺带说一句,AviUtl 0.99中出现的色差处理bug[5]就源于插值处理的错误。

  1. // YC48->YUY2
  2. Y = ((y*219 + 383)>>12) + 16;
  3. U = (((cb + 2048)*7 + 66)>>7) + 16;
  4. V = (((cr + 2048)*7 + 66)>>7) + 16;
复制代码
(算术移位>>12等于除以2^12=4096,>>7等于除以2^7128)
逆向转换时,色差数据将只使用左侧像素的数值,右侧像素的数值将直接舍弃。因为右侧像素的值已经是插值得来,若在此讲左右像素结果进行混合的话,有可能将其他像素的色差值也混入进来。
从转换式中也能够看出来,Y的16-235范围被映射至0~4096,UV的16-240范围被映射至-2048~2048,既进行了所谓的YC伸张、压缩转换[6]。此外,Y的0-15、236-255,UV的0-15、241-255部分数值会被当作范围外的数值在YC48中得到保留,YUV的动态范围不会被破坏。如果只进行YUY2和YC48的转换,则此过程是完全可逆的。

3.2. RGB与YC48间的转换
24bit RGB与YC48之间的转换可以通过AviUtl向插件提供的EXFUNC中的rgb2yc()和yc2rgb()两个函数进行。以下两组转换式与这两个函数的效果相同:
  1. // YC48->RGB
  2. R = ( 255*y + ( ((22881*cr>>16)+3) <<10) ) >>12;
  3. G = ( 255*y + ( ((-5616*cb>>16)+(-11655*cr>>16)+3) <<10) ) >>12;
  4. B = ( 255*y + ( ((28919*cb>>16)+3) <<10) ) >>12;

  5. // RGB->YC48
  6. y  = (( 4918*R+354)>>10) + (( 9655*G+585)>>10) + (( 1875*B+523)>>10);
  7. cb = ((-2775*R+240)>>10) + ((-5449*G+515)>>10) + (( 8224*B+256)>>10);
  8. cr = (( 8224*R+256)>>10) + ((-6887*G+110)>>10) + ((-1337*B+646)>>10);
复制代码
(算术移位>>16等于除以2^16=65536,<<10等于乘以2^10=1024,>>12等于除以2^12=4096)
这些转换式可以视为,按照ITU-R BT.601模拟RGB-YCbCr的转换式,由RGB各通道0~255的有效范围扩展至YC48有效范围而设计的。因此,若输入BT.709等Color Matrix的时候需要特别注意。此外,RGB和YC48之间的转换是完全可逆的,没有劣化。当然,如果中间夹杂了YC48->YUY2的转换,则会发生与RGB->YUY2相同的劣化。
综上所述,AviUtl会按照RGB 0~255范围、YUY2 16~235/16~240有效范围将数据映射至YC48的有效范围当中。如果通过AU进行了RGB和YUY2之间的转换,那么虽然经过了一次AviUtl内部格式,但作为结果来说存在YC伸张和压缩的过程。

[3]在所能确认的范围内,0.96h、0.97、0.97a、0.97c、0.98都发生过变化(含bug修正)。
[4]现在,seraphy氏的改良版warpsharp和拙作ConvertAviUtlYCFix已经解决了这个问题。
[5]这个Bug已在0.99a修正。
[6]话虽如此,但这个转换只是转换了数据范围,很难说算不算YC伸张、压缩。


4.AviUtl内部的处理流程
虽然AviUtl可以直接处理的颜色格式只有RGB/YUY2/YC48三种,但用户所见的大部分图像无外乎RGB和YUY2其中之一。特别是这两种形式能够表现的范围和阶数有所不同,如果不了解输入、输入的方式,很容易出现输入、输出颜色不同,或是输出后出现了banding等令人烦恼的现象。因此,本节就AviUtl处理的流程进行简明的说明。

4.1. 输入
AviUtl的输入,除了利用内置的AVI/BMP读取功能外,还可以经由输入插件和VFAPI两个途径。除了VFAPI输入必须经由RGB外,内置输入和输入插件都可以处理RGB/YUY2/YC48与安装在系统中的各种VCM Codec。接下来简单说明各个输入方式转换至AviUtl内部格式的过程。

RGB输入
经由RGB形式输入的数据之后,AviUtl会按3.2节所述方式转换至YC48。

YUY2输入
经由YUY2形式输入数据之后,AviUtl会按3.1节所述方式转换至YC48。

YC48输入
输入插件等将YC48数据输入AviUtl之后,AviUtl会直接接受YC48作为内部数据使用。

其他输入形式
若接收到上述形式以外的数据,AviUtl会尝试使用VCM将数据解码为RGB或YUY2颜色。若没有找到能够解码的Codec,则抛出输入失败的信息。至于具体解码为RGB还是YUY2,则取决于用户是否在“文件->环境设定->Codec设定(「ファイル→環境設定→コーデックの設定」)”选项中选择了“用YUY2展开(「YUY2で展開する」)”选项。
勾选了“用YUY2展开” 且 Codec可以解码为YUY2格式时
    从输入插件处得到的数据会被交给VCM,随后AviUtl会将解码为YUY2的图像转换为YC48。
未勾选 “用YUY2展开” 且 成功解码为RGB时
    由VCM解码为RGB,由AviUtl转换为YC48。
无法解码为指定颜色格式时
    自动尝试另一种解码方式。解码后过程同上。

通过“查看->文件信息(「表示→ファイルの情報」)”中的“视频展开形式(「ビデオ展開形式」)”可以查看是经由YUY2还是RGB打开文件。

4.2. 输出
AviUtl输出图像的方法,有AVI输出、输出插件和VFAPI三种。

AVI输出
内置的AVI输出功能,可以通过对话框中的“视频压缩(「ビデオ圧縮」)”按钮选择输出形式。在此选择“未压缩”则输出为24bit-RGB,选择“YUY2”则会将YC48转换为YUY2写入AVI文件。选择系统中安装的其他Codec时,AviUtl输出给Codec的数据由“文件->环境设定->Codec设定(「ファイル→環境設定→コーデックの設定」)”中的“通过YUY2压缩(「YUY2で圧縮する」)”选项来选择。

通过YUY2压缩
    YC48先转换成YUY2并输出给VCM,然后取得压缩后的数据写入文件。
不通过YUY2压缩
    YC48先转换成24bit-RGB输出给VCM,然后取得压缩后的数据写入文件。

如果遇到不接受YUY2的Codec,则会事先将“通过YUY2压缩”选项取消,通过24bit-RGB输出给Codec。

输出插件
输出插件向AviUtl请求数据的时候,可以选择RGB、YUY2、YC48中的任意一种。AviUtl会依据插件的请求,直接输出YC48,或将YC48转换为RGB/YUY2交给插件。

VFAPI
经由VFAPI只能输出24bit-RGB。aviutl.vfp会将YC48转换为RGB后输出给请求图像的程序。

4.3. 编辑画面的显示
“查看->Overlay显示(「表示→オーバーレイ表示」)”决定编辑画面的显示方式。

Overlay显示
    先将YC48转换为24bit-RGB[8],经由Windows GDI(Graphics Device Interface,图形设备接口)进行绘制。
非Overlay显示
    若可以使用YUY2 Overlay[9],AU会将YC48转换为YUY2写入缓存。若显卡[10]使用的YUV Scale与AU不同,则显示出来的颜色会与GDI(Overlay显示)的结果不同。

4.4. 剪贴板
由于经由剪贴板的复制和粘贴都通过DIB(Device Independent Bitmap)完成,因此操作必然是经由RGB进行的。复制时,先将YC48转换为RGB,然后交给剪贴板。从剪贴板粘贴过来的数据会原样保存在内部,转换成YC48使用。保存工程文件时,原始RGB数据会被写入aup文件进行保存。

[7]Video Compression Manager,是VFW (Video for Windows) 的一部分,是负责图像编/解码的界面(Interface)。
[8]GDI指定通过RGB输入数据。
[9]未测试YUY2 Overlay失败时AU会作何反应。
[10]比如多年前的GeForce系列。


致谢
向编写发布AviUtl这一如此优秀的程序的KENくん,向诸位开发插件、甚至公开源代码的开发者,以及本文的读者朋友致以衷心的感谢。

参考文献
发表于 2009-12-27 22:19 | 显示全部楼层
  1. C[2n+1] = ( C[2n] + C[2n+2] ) >> 1;
复制代码
令人惊讶的简单...甚至有种错误的预感
您需要登录后才可以回帖 登录 | 成为会员

本版积分规则

小黑屋|手机版|NMM视频技术

GMT+8, 2024-3-29 05:17 , Processed in 0.048820 second(s), 14 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表