【问题标题】:Manipulating raw png data处理原始 png 数据
【发布时间】:2011-11-05 10:01:20
【问题描述】:

我想读取一个 PNG 文件,这样我可以:

a) 访问文件的原始位图数据,没有色彩空间调整或 alpha 预乘。

b) 基于该位图,在窗口中的图像中显示位切片(R、G、B 或 A 的任何单个 )。如果我有位图,我可以找到正确的位,但是我可以把它们塞进什么东西让它们出现在屏幕上?

c) 对位平面进行一些修改后,编写一个新的 PNG 文件,同样不做任何调整。

这仅适用于某些特定图像。除了简单的 RGBA-32 之外,PNG 预计不会包含任何数据。

通过在这里阅读一些类似的问题,我怀疑 NSBitmapImageRep 用于文件读/写,并在 NSView 中绘制屏幕部分。这听起来对吗?

【问题讨论】:

    标签: objective-c cocoa image-processing png raw-data


    【解决方案1】:

    1.) 您可以使用 NSBitmapImageRep 的 -bitmapData 来获取原始像素数据。不幸的是,CG(NSBitmapImageRep 的后端)不支持原生解预乘,所以你必须自己解预乘。此处使用的色彩空间与文件中的色彩空间相同。以下是如何取消预乘图像数据:

    NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:data];
        NSInteger width = [imageRep pixelsWide];
        NSInteger height = [imageRep pixelsHigh];
    
        unsigned char *bytes = [imageRep bitmapData];
        for (NSUInteger y = 0; y < width * height * 4; y += 4) { // bgra little endian + alpha first
            uint8_t a, r, g, b;
    
            if (imageRep.bitmapFormat & NSAlphaFirstBitmapFormat) {
                a = bytes[y];
                r = bytes[y+1];
                g = bytes[y+2];
                b = bytes[y+3];
            } else {
                r = bytes[y+0];
                g = bytes[y+1];
                b = bytes[y+2];
                a = bytes[y+3];
            }
    
            // unpremultiply alpha if there is any
            if (a > 0) {
                if (!(imageRep.bitmapFormat & NSAlphaNonpremultipliedBitmapFormat)) {
                    float factor = 255.0f/a;
                    b *= factor;
                    g *= factor;
                    r *= factor;
                }
            } else {
                b = 0;
                g = 0;
                r = 0;
            }
            bytes[y]=a; // for argb
            bytes[y+1]=r;
            bytes[y+2]=g;
            bytes[y+3]=b;
        }
    

    2.) 我想不出一个简单的方法来做到这一点。您可以制作自己的图像绘制方法,循环遍历原始图像数据并根据这些值生成新图像。请参阅上文以了解如何开始。

    3.) 这是一种从原始数据位置获取 CGImage 的方法(您可以使用原生 CG 函数将 png 写入文件,如果 CG 让您感到不舒服,可以将其转换为 NSBitmapImageRep)

    static CGImageRef cgImageFrom(NSData *data, uint16_t width, uint16_t height) {
    CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGImageAlphaFirst;
    
    CGImageRef cgImage = CGImageCreate(width, height, 8, 32, 4 * width, colorSpace, bitmapInfo, provider, NULL, NO, kCGRenderingIntentDefault);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);
    return cgImage;
    }
    

    您可以使用 +dataWithBytes:length: 从原始数据对象中创建 NSData 对象

    【讨论】:

    • 哎哟。删除预乘将产生严重的舍入误差,尤其是在 alpha 较小的情况下。这将破坏访问位图的意义。我需要加载和保存文件中未预乘的 exact 位图值。 CGImageCreateWithPNGDataProvider 似乎会支持这一点,因为 CGImage 有一个访问器来判断它是否被预乘。 CGImageDestinationCreateWithURL 听起来它会写PNG。
    • 是的,如果原始图像中的数据已经未预乘,那么 CGImage/NSBitmapImageRep 将照此处理。绘制到上下文中然后在内存中初始化为新图像的图像将不会被取消预乘,因为它不受支持。此外,在非预乘过程中,损失是不可避免的。
    • 问题是,PNG文件中的原始数据是预乘的。这就是我要加载、修改和保存的数据。但是,如果我正确阅读了引用,则 NSBitmapImageRep(我可以看到如何从中访问位图的唯一类)将在使用文件中的数据初始化时进行预乘。如果我无法在 Cocoa 中找到获取该数据的简单方法,我认为 Python 的 PyPNG 模块是我最好的选择。
    • 是的,我也考虑过。实际上,我在短短几个小时内就设法从 Python 中得到了我想要的东西。 :)
    【解决方案2】:

    我从未在这方面工作过,但你可以使用Image IO 来做这件事。

    【讨论】:

    • Image IO 处理文件和 CGI​​mageRef 之间的图像传输,显然没有预乘,因此将涵盖 #1 和 #3。我可以将 CGImage 提供给各种过滤器,或将其发送到屏幕,但看不到访问位图数据的方法。 :(
    猜你喜欢
    • 2016-10-01
    • 2013-07-20
    • 2020-11-27
    • 2012-10-06
    • 1970-01-01
    • 1970-01-01
    • 2014-06-14
    • 2012-07-06
    • 2010-09-25
    相关资源
    最近更新 更多