如何在没有预乘alpha的情况下获得真实的RGBA或ARGB颜色值?

我使用CGBitmapContextCreatekCGImageAlphaPremultipliedFirst选项创build位图上下文。

我用一些主要颜色(纯红色,绿色,蓝色,白色,黑色),一些混合颜色(即紫色)和一些阿尔法变化做了5×5testing图像。 每当alpha分量不是255时,颜色值都是错误的。

我发现我可以重新计算颜色,当我做这样的事情:

 almostCorrectRed = wrongRed * (255 / alphaValue); almostCorrectGreen = wrongGreen * (255 / alphaValue); almostCorrectBlue = wrongBlue * (255 / alphaValue); 

但问题是,我的计算有时会减less3或甚至更多。 所以例如,我得到的价值为242,而不是绿色,我100%肯定它必须是正好245.阿尔法是128。

然后,对于与PNG位图中不同的alpha不透明度完全相同的颜色,我得到alpha = 255和green = 245,因为它应该是。

如果alpha为0,那么红色,绿色和蓝色也是0.这里所有的数据都丢失了,我无法弄清楚像素的颜色。

我怎样才能避免或撤销这个alpha预乘,以便我可以根据真实的RGB像素值修改图像中的像素,就像在Photoshop中创build图像时一样? 如何恢复R,G,B和A的原始值?


背景信息(可能不需要这个问题):

我正在做的是这样的:我把一个UIImage,绘制到一个位图的上下文,以执行一些简单的image processingalgorithm,移动每个像素的颜色取决于它以前的颜色。 没有什么特别的 但是我的代码需要真实的颜色。 当一个像素是透明的(意味着它的alpha值小于255)时,我的algorithm不应该关心这个,它应该只是修改R,G,B,而Alpha则保持不变。 有时候,它也会将阿尔法向上或向下移动。 但是我把它们看成是两件不同的事情。 Alpha控制透明度,而RGB控制颜色。

这是一个整数型预乘的基本问题:

  • 245 *(128/255)= 122.98
  • 122.98 截断为整数= 122
  • 122 *(255/128)= 243.046875

我不知道你为什么得到242而不是243,但是这个问题依然存在,而且α越低越差。

解决方法是使用浮点组件 。 Quartz 2D编程指南提供了您需要使用的格式的全部细节 。

重要的一点:你需要从创build原始图像时使用浮点(我不认为甚至可以将这样的图像保存为PNG;你可能需要使用TIFF)。 一个已经被整合在一个整体中的图像已经失去了精确性; 有没有得到它回来。

零阿尔法的情况是这个极端的版本,甚至浮点不能帮助你。 任何时间为零(alpha)为零,并且没有从该点恢复原始未预计的值。

用整数颜色types预乘alpha,是一种信息有损操作。 在量化过程中数据被破坏(舍入到8位)。

由于一些数据被破坏(四舍五入),没有办法恢复确切的原始像素颜色(除了一些幸运值)。 在将其绘制到位图上下文中之前,必须保存photoshop图像的颜色,并使用原始颜色数据,而不是来自位图的相乘颜色数据。

在尝试读取图像数据时,遇到了同样的问题,使用CoreGraphics将其渲染为其他图像,然后将结果保存为非预乘数据。 我发现我的解决scheme是保存一张表,其中包含CoreGraphics用于将非预乘数据映射到预乘数据的确切映射。 然后,用mult和floor()调用来估计最初的预估值是多less。 然后,如果估计值和表格查找结果不匹配,只需检查估计值以下的值和表格中估计值以上的值即可。

 // Execute premultiply logic on RGBA components split into componenets. // For example, a pixel RGB (128, 0, 0) with A = 128 // would return (255, 0, 0) with A = 128 static inline uint32_t premultiply_bgra_inline(uint32_t red, uint32_t green, uint32_t blue, uint32_t alpha) { const uint8_t* const restrict alphaTable = &extern_alphaTablesPtr[alpha * PREMULT_TABLEMAX]; uint32_t result = (alpha << 24) | (alphaTable[red] << 16) | (alphaTable[green] << 8) | alphaTable[blue]; return result; } static inline int unpremultiply(const uint32_t premultRGBComponent, const float alphaMult, const uint32_t alpha) { float multVal = premultRGBComponent * alphaMult; float floorVal = floor(multVal); uint32_t unpremultRGBComponent = (uint32_t)floorVal; assert(unpremultRGBComponent >= 0); if (unpremultRGBComponent > 255) { unpremultRGBComponent = 255; } // Pass the unpremultiplied estimated value through the // premultiply table again to verify that the result // maps back to the same rgb component value that was // passed in. It is possible that the result of the // multiplication is smaller or larger than the // original value, so this will either add or remove // one int value to the result rgb component to account // for the error possibility. uint32_t premultPixel = premultiply_bgra_inline(unpremultRGBComponent, 0, 0, alpha); uint32_t premultActualRGBComponent = (premultPixel >> 16) & 0xFF; if (premultRGBComponent != premultActualRGBComponent) { if ((premultActualRGBComponent < premultRGBComponent) && (unpremultRGBComponent < 255)) { unpremultRGBComponent += 1; } else if ((premultActualRGBComponent > premultRGBComponent) && (unpremultRGBComponent > 0)) { unpremultRGBComponent -= 1; } else { // This should never happen assert(0); } } return unpremultRGBComponent; } 

你可以在这个github链接中find完整的静态值表。

请注意,这种方法不会恢复原始未预乘像素预乘的“丢失”信息。 但是,它会返回最小的未乘的像素,一旦再次运行预乘逻辑,它将成为预乘像素。 当graphics子系统只接受预乘像素(如OSX上的CoreGraphics)时,这非常有用。 如果graphics子系统只接受预先倍率的像素,那么最好只存储预先倍率的像素,因为与未乘的像素相比,消耗的空间更less。

Interesting Posts