预乘Alpha的作用

转载自:https://www.cnblogs.com/xiaonanxia/p/9448444.html

Premultiplied Alpha 这个概念做游戏开发的人都不会不知道。Xcode 的工程选项里有一项 Compress PNG Files,会对 PNG 进行 Premultiplied Alpha,Texture Packer 中也有Premultiplied Alpha 的选项。那么问题来了,Premultiplied Alpha 是什么呢?我被这个问题困惑了很久,之前搜到过 Nvidia的这篇文章,其实说的很清楚,只是当时有很多相关概念没搞清楚,所以没看懂。直到前几天读《Real Time Rendering》时终于搞懂了。

Alpha Blending

要搞清楚这个问题,先得理解Alpha通道的工作原理,如果你已经了解可以直接跳过。

最常见的像素表示格式是RGBA8888即 (r, g, b, a),每个通道8位,0-255。例如红色60%透明度就是 (255, 0, 0, 153),为了表示方便alpha通道一般记成正规化后的0-1的浮点数,也就是 (255, 0, 0, 0.6)。而 Premultiplied Alpha 则是把RGB通道乘以透明度也就是 (r * a, g * a, b * a, a),50%透明红色就变成了(153, 0, 0, 0.6)。

透明通道在渲染的时候通过 Alpha Blending 产生作用,如果一个透明度为 as 的颜色 Cs 渲染到颜色 Cd上,混合后的颜色通过以下公式计算,

 

Co=αsCs+(1−αs)CdCo=αsCs+(1−αs)Cd

 

以60%透明的红色渲染到白色背景为例:

Co=(255,0,0)⋅0.6+(255,255,255)⋅(1−0.6)=(255,102,102)Co=(255,0,0)⋅0.6+(255,255,255)⋅(1−0.6)=(255,102,102)


也就是说,从视觉上,(255, 0, 0, 0.6)渲染到白色背景上 和 (255, 102, 102) 是同一个颜色。如果颜色以 Premultiplied Alpha 形式存储,也就是Cs已经乘以透明度了,所以混合公式变成了:

Co=Cs′+(1−αs)CdCo=Cs′+(1−αs)Cd

 

为什么要 Premultiplied Alpha 呢?

Premultiplied Alpha 后的像素格式变得不直观,因为在画图的时候都是先从调色板中选出一个RGB颜色,再单独设置透明度,如果RGB乘以透明度就搞不清楚原色是什么了。从前面的 Alpha Blending 公式可以看出,Premultiplied Alpha 之后,混合的时候可以少一次乘法,这可以提高一些效率,但这并不是最主要的原因。最主要的原因是:

没有 Premultiplied Alpha 的纹理无法进行 Texture Filtering(除非使用最近邻插值)。

以最常见的 filtering 方式线性插值为例,一个宽2px高1px的图片,左边的像素是红色,右边是绿色10%透明度,如果把这个图片缩放到1x1的大小,那么缩放后1像素的颜色就是左右两个像素线性插值的结果,也就是把两个像素各个通道加起来除以2。如果使用没有 Premultiplied Alpha 的颜色进行插值,那么结果就是:

((255,0,0,1)+(0,255,0,0.1))⋅0.5=(127,127,0,0.55)((255,0,0,1)+(0,255,0,0.1))⋅0.5=(127,127,0,0.55)

 

如果绿色 Premultiplied Alpha,也就是 (0, 255 * 0.1, 0, 0.1),和红色混合后:

((255,0,0,1)+(0,25,0,0.1))⋅0.5=(127,25,0,0.55)

 

从上面的图里第三个颜色是没有 Premultiplied Alpha 的混合结果,对比第四个 Premultiplied Alpha 后颜色的结果,显然第四个颜色更符合直觉,第三个颜色太绿了,因为绿色通道没有乘以透明度,所以在线性插值的时候占了过大的权重。

所以 Premultiplied Alpha 最重要的意义是使得带透明度图片纹理可以正常的进行线性插值。这样旋转、缩放或者非整数的纹理坐标才能正常显示,否则就会像上面的例子一样,在透明像素边缘附近产生奇怪的颜色。

纹理处理

我们使用的PNG图片纹理,一般是不会 Premultiplied Alpha 的。游戏引擎在载入PNG纹理后回手动处理,然后再glTexImage2D传给GPU,比如 Cocos2D-x 中的 CCImage::premultipliedAlpha:

复制代码

void Image::premultipliedAlpha() {
    unsigned int* fourBytes = (unsigned int*)_data;
    for (int i = 0; i < _width * _height; i++) {
        unsigned char* p = _data + i * 4;
        fourBytes[i] = CC_RGB_PREMULTIPLY_ALPHA(p[0], p[1], p[2], p[3]);
    }  
    _hasPremultipliedAlpha = true;
}

复制代码

 

 

而GPU专用的纹理格式,比如 PVR、ETC 一般在生成纹理都是默认 Premultiplied Alpha 的,这些格式一般是GPU硬解码,引擎用CPU处理会很慢。

总之 glTexImage2D 传给 GPU 的纹理数据最好都是 Multiplied Alpha 的,要么在生成纹理时由纹理工具 Pre-multiplied,要么载入纹理后由游戏引擎或UI框架 Post-multiplied。

<think>好的,用户想了解如何处理alpha通道和图像透明度的问题。首先,我需要确认用户的需求是什么。可能他们是在处理图像合成、去除背景或调整不透明度时需要这些技术。根据提供的引用内容,特别是引用3和4,用户可能在使用Python进行图像处理,需要向RGB图像添加alpha通道或者调整现有的alpha通道。 首先,我应该解释alpha通道的基本概念,比如RGBA模式中的A代表alpha,控制透明度。然后,用户可能需要具体的操作方法,比如如何用Python的PIL库或OpenCV来添加或修改alpha通道。根据引用3,有两种方法:一种是直接为每个像素设置固定的透明度,另一种是根据颜色值动态调整,比如将白色背景设为透明。 接下来,我需要考虑用户可能的进阶需求,比如处理带有alpha通道的图像合成,或者alpha的问题。引用2提到alpha,这可能涉及到图像处理中的混合技术,需要解释清楚。同时,引用4提到了两幅图像的合成,这可能涉及到使用alpha通道进行混合,比如使用cv2.addWeighted函数。 用户可能遇到的问题包括如何处理带有透明度的图片格式(如PNG),或者如何避免常见的错误,比如在不支持透明度的格式中保存图像。此外,性能优化或处理大图像时的注意事项也是需要考虑的。 最后,我需要生成相关问题,帮助用户深入理解,比如性能优化方法、不同场景的应用,或者处理多图层合成时的技巧。确保回答结构清晰,步骤明确,并提供代码示例,这样用户可以直接应用这些方法。同时,检查引用是否正确,确保每个引用标记对应正确的来源,比如引用3和4都涉及Python处理alpha通道的方法,而引用2解释了alpha的概念。</think>### Alpha通道与图像透明度处理方法 Alpha通道是图像中控制透明度的第四通道(RGBA模式),取值范围为$0$(完全透明)至$255$(完全不透明)[^2]。以下是具体处理技术: --- #### **1. 添加Alpha通道** - **方法1:使用PIL库** ```python from PIL import Image # 打开RGB图像并转换为RGBA模式 img = Image.open("input.jpg").convert("RGBA") # 创建新alpha通道(示例:全局半透明) alpha_data = [(pixel[3] if len(pixel)==4 else 128) for pixel in img.getdata()] img.putdata([(r,g,b,a) for (r,g,b,_),a in zip(img.getdata(), alpha_data)]) img.save("output.png") # 必须保存为支持透明的格式(如PNG) ``` 此方法通过`.convert("RGBA")`添加alpha通道,并自定义透明度值[^3]。 - **方法2:使用OpenCV** ```python import cv2 import numpy as np # 读取图像并添加全不透明alpha通道 bgr_img = cv2.imread("input.jpg") bgra_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2BGRA) # 设置特定颜色区域透明(示例:白色背景透明) white_mask = np.all(bgra_img[:,:,:3] == [255,255,255], axis=2) bgra_img[white_mask, 3] = 0 cv2.imwrite("output.png", bgra_img) ``` 该方法通过颜色阈值实现局部透明化[^4]。 --- #### **2. 透明度混合计算** alpha(Premultiplied Alpha)可优化混合效果: $$ C_{final} = (C_{src} \times \alpha_{src}) + (C_{dst} \times (1 - \alpha_{src})) $$ 其中$C$为颜色值,$\alpha$为透明度。 --- #### **3. 透明图像合成** 使用OpenCV实现图像叠加: ```python def blend_images(foreground, background, alpha): return cv2.addWeighted(foreground, alpha, background, 1-alpha, 0) ``` 此函数通过权重参数控制前景透明度[^4]。 --- #### **注意事项** - 格式兼容性:JPEG不支持透明度,必须使用PNG、WebP等格式[^1] - 性能优化:GPU加速适用于大规模图像处理(如视频流) - 边缘处理:使用羽化(feathering)消除透明边缘锯齿
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值