【问题标题】:Getting pixel data from UIImageView -- works on simulator, not device从 UIImageView 获取像素数据——适用于模拟器,而不是设备
【发布时间】:2010-12-27 01:12:24
【问题描述】:

根据对a previous question 的回复,我在 UIImageView 上创建了一个类别,用于提取像素数据。这在模拟器中可以正常工作,但在部署到设备时却不行。我应该说不是总是——奇怪的是,如果 point.x == point.y; 它确实会获取正确的像素颜色;否则,它会为我提供该线另一侧像素的像素数据,就像镜像一样。 (因此,点击图像右下角的像素会给我左上角相应像素的像素数据,但点击左下角的像素会返回正确的像素颜色)。触摸坐标(CGPoint)正确。

我做错了什么?

这是我的代码:

@interface UIImageView (PixelColor)
- (UIColor*)getRGBPixelColorAtPoint:(CGPoint)point;
@end

@implementation UIImageView (PixelColor)

- (UIColor*)getRGBPixelColorAtPoint:(CGPoint)point
{
    UIColor* color = nil;

    CGImageRef cgImage = [self.image CGImage];
    size_t width = CGImageGetWidth(cgImage);
    size_t height = CGImageGetHeight(cgImage);
    NSUInteger x = (NSUInteger)floor(point.x);
    NSUInteger y = height - (NSUInteger)floor(point.y);

    if ((x < width) && (y < height))
    {
        CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
        CFDataRef bitmapData = CGDataProviderCopyData(provider);
        const UInt8* data = CFDataGetBytePtr(bitmapData);
        size_t offset = ((width * y) + x) * 4;
        UInt8 red = data[offset];
        UInt8 blue = data[offset+1];
        UInt8 green = data[offset+2];
        UInt8 alpha = data[offset+3];
        CFRelease(bitmapData);
        color = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f];
    }

    return color;
}

【问题讨论】:

  • 你能检查一下self.image.imageOrientation的值吗?您正在使用的图像可能在 UIImageOrientationLeftMirrored 中,这将是您看到的反射,但我不知道为什么它只会在设备上出现......
  • 模拟器和设备上都是 UIImageOrientationUp。我不确定“左镜像”是对正在发生的事情的正确解释,因为反射(似乎)沿着对角线 y=x 发生,而 UIImageOrientationLeftMirrored 是整个图像“逆时针 90 度”的简单旋转到 SDK。
  • 更多信息:如果我交换 x 和 y 的计算方式,则行为会相反——它适用于设备,但不适用于模拟器。 (虽然设备上的准确定位似乎逆时针偏移了几度,但这可能是我的手指与使用鼠标相比不精确)

标签: iphone uiimageview pixel


【解决方案1】:

您可以尝试以下替代方法:

  • 创建一个 CGBitmapContext
  • 将图像绘制到上下文中
  • 在上下文上调用 CGBitmapContextGetData 以获取底层数据
  • 计算原始数据的偏移量(基于您创建位图上下文的方式)
  • 提取值

这种方法在模拟器和设备上适用于我。

【讨论】:

  • 我试图避免这种情况,因为涉及的开销(这个函数每秒可以调用超过一个)
  • 您可以缓存原始数据,然后将开销减少为数组查找,除非图像不断变化。
  • 确实如此。但是,我仍然想知道我上面的代码中的根本问题是什么。
【解决方案2】:

我认为 R B G 是错误的。你有:

UInt8 red =   data[offset];     
UInt8 blue =  data[offset+1];
UInt8 green = data[offset+2];

但你真的不是指R G B吗? :

UInt8 red =   data[offset];     
UInt8 green = data[offset+1];
UInt8 blue =  data[offset+2];

但即使修复了这个问题,仍然存在问题,因为 Apple byte swaps(很棒的文章)在设备上时 R 和 B 值,但在模拟器上时不是。 p>

CFDataGetBytePtr 返回的 PNG 像素缓冲区存在类似的模拟器/设备问题。

这为我解决了问题:

#if TARGET_IPHONE_SIMULATOR
        UInt8 red =   data[offset];
        UInt8 green = data[offset + 1];
        UInt8 blue =  data[offset + 2];
#else
        //on device
        UInt8 blue =  data[offset];       //notice red and blue are swapped
        UInt8 green = data[offset + 1];
        UInt8 red =   data[offset + 2];
#endif

不确定这是否能解决您的问题,但您的行为异常的代码看起来与我修复之前的样子很接近。

最后一件事:我相信即使在调用CFRelease(bitmapData) 之后,模拟器也会让您访问像素缓冲区data[]。根据我的经验,在设备上不是。您的代码不应该受到影响,但如果这对其他人有帮助,我想我会提到它。

【讨论】:

  • 我花了一些时间才终于回到这一点,但您的代码非常棒。将 G 和 B 混合在一起,并且不知道 Xcode 在幕后所做的字节顺序更改,是一个致命的组合。
【解决方案3】:

在原始问题中发布的代码中看起来像这样,而不是:

NSUInteger x = (NSUInteger)floor(point.x);
NSUInteger y = height - (NSUInteger)floor(point.y);

应该是:

NSUInteger x = (NSUInteger)floor(point.x);
NSUInteger y = (NSUInteger)floor(point.y);

【讨论】:

    猜你喜欢
    • 2012-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-23
    • 1970-01-01
    • 2020-03-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多