37

我目前正在尝试获取 UIImageView 中像素的 alpha 值。我从 [UIImageView image] 获得了 CGImage 并由此创建了一个 RGBA 字节数组。Alpha 是预乘的。

CGImageRef image = uiImage.CGImage;
NSUInteger width = CGImageGetWidth(image);
NSUInteger height = CGImageGetHeight(image);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
rawData = malloc(height * width * 4);
bytesPerPixel = 4;
bytesPerRow = bytesPerPixel * width;

NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(
    rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace,
    kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big
);
CGColorSpaceRelease(colorSpace);

CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
CGContextRelease(context);

然后,我使用 UIImageView 中的坐标计算给定 alpha 通道的数组索引。

int byteIndex = (bytesPerRow * uiViewPoint.y) + uiViewPoint.x * bytesPerPixel;
unsigned char alpha = rawData[byteIndex + 3];

但是我没有得到我期望的值。对于图像的完全黑色透明区域,我得到 alpha 通道的非零值。我是否需要转换 UIKit 和 Core Graphics 之间的坐标 - 即:y 轴是否倒置?还是我误解了预乘的 alpha 值?

更新:

@Nikolai Ruhe 的建议是关键。我实际上不需要在 UIKit 坐标和 Core Graphics 坐标之间进行转换。但是,在设置混合模式后,我的 alpha 值符合我的预期:

CGContextSetBlendMode(context, kCGBlendModeCopy);
4

4 回答 4

37

如果您想要的只是单个点的 alpha 值,那么您只需要一个 alpha-only 单点缓冲区。我相信这应该足够了:

// assume im is a UIImage, point is the CGPoint to test
CGImageRef cgim = im.CGImage;
unsigned char pixel[1] = {0};
CGContextRef context = CGBitmapContextCreate(pixel, 
                                         1, 1, 8, 1, NULL,
                                         kCGImageAlphaOnly);
CGContextDrawImage(context, CGRectMake(-point.x, 
                                   -point.y, 
                                   CGImageGetWidth(cgim), 
                                   CGImageGetHeight(cgim)), 
               cgim);
CGContextRelease(context);
CGFloat alpha = pixel[0]/255.0;
BOOL transparent = alpha < 0.01;

如果不需要每次都重新创建 UIImage,这是非常有效的。

编辑 2011 年 12 月 8 日:

一位评论者指出,在某些情况下,图像可能会被翻转。一直在想这个,有点抱歉没有直接用UIImage写代码,像这样(我想原因是当时没看懂UIGraphicsPushContext):

// assume im is a UIImage, point is the CGPoint to test
unsigned char pixel[1] = {0};
CGContextRef context = CGBitmapContextCreate(pixel, 
                                             1, 1, 8, 1, NULL,
                                             kCGImageAlphaOnly);
UIGraphicsPushContext(context);
[im drawAtPoint:CGPointMake(-point.x, -point.y)];
UIGraphicsPopContext();
CGContextRelease(context);
CGFloat alpha = pixel[0]/255.0;
BOOL transparent = alpha < 0.01;

我认为这将解决翻转问题。

于 2010-09-21T18:31:09.077 回答
10

是的,CGContext 的 y 轴指向上方,而在 UIKit 中它指向下方。请参阅文档

阅读代码后编辑:

您还想在绘制图像之前设置要替换的混合模式,因为您想要图像的 alpha 值,而不是之前在上下文缓冲区中的值:

CGContextSetBlendMode(context, kCGBlendModeCopy);

思考后编辑:

您可以通过构建尽可能小的 CGBitmapContext(1x1 像素?也许是 8x8?试一试)并在绘制之前将上下文转换到您想要的位置来更有效地进行查找:

CGContextTranslateCTM(context, xOffset, yOffset);
于 2009-06-25T09:57:43.273 回答
3

我是否需要转换 UIKit 和 Core Graphics 之间的坐标 - 即:y 轴是否倒置?

这是可能的。在CGImage中,像素数据的英文阅读顺序是:从左到右,从上到下。因此,数组中的第一个像素是左上角;第二个像素是顶行左起一个;等等

假设你有这个权利,你还应该确保你正在查看像素内的正确组件。也许您期待 RGBA 但要求 ARGB,反之亦然。或者,也许你的字节顺序错误(我不知道 iPhone 的字节序是什么)。

还是我误解了预乘的 alpha 值?

听起来不像。

对于不知道的人:预乘意味着颜色分量被alpha预乘;无论颜色分量是否被它预乘,alpha 分量都是相同的。您可以通过将颜色分量除以 alpha 来反转(取消预乘)。

于 2009-06-25T09:58:11.650 回答
3

我在研究如何使用图像数据的 alpha 值而不是矩形边界框进行精灵之间的碰撞检测时发现了这个问题/答案。上下文是一个 iPhone 应用程序...我正在尝试执行上述建议的 1 像素绘制,但我仍然无法使其正常工作,但我找到了一种使用图像本身的数据创建 CGContextRef 的更简单方法,并且辅助功能在这里:

CGContextRef context = CGBitmapContextCreate(
                 rawData, 
                 CGImageGetWidth(cgiRef), 
                 CGImageGetHeight(cgiRef), 
                 CGImageGetBitsPerComponent(cgiRef), 
                 CGImageGetBytesPerRow(cgiRef), 
                 CGImageGetColorSpace(cgiRef),
                 kCGImageAlphaPremultipliedLast     
    );

这绕过了上面示例中所有丑陋的硬编码。最后一个值可以通过调用 CGImageGetBitmapInfo() 来检索,但在我的例子中,它从图像中返回一个值,导致 ContextCreate 函数出错。只有某些组合是有效的,如此处所述:http: //developer.apple.com/qa/qa2001/qa1037.html

希望这有帮助!

于 2009-07-12T23:46:26.057 回答