如何访问和操作JPEG图像像素?

时间:2014-04-06 17:29:24

标签: objective-c image-processing uiimage bytearray jpeg

我有一个jpg文件。我需要将其转换为像素数据,然后更改某些像素的颜色。我是这样做的:

    NSString *string = [[NSBundle mainBundle] pathForResource:@"pic" ofType:@"jpg"];
    NSData *data = [NSData dataWithContentsOfFile:string];
    unsigned char *bytesArray = dataI.bytes;
    NSUInteger byteslenght = data.length;
   //--------pixel to array
    NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:byteslenght];
    for (int i = 0; i<byteslenght; i++) {
        [array addObject:[NSNumber numberWithUnsignedChar:bytesArray[i]]];
    }

这里我尝试改变从95到154的像素颜色。

NSNumber *number = [NSNumber numberWithInt:200];
    for (int i=95; i<155; i++) {
        [array replaceObjectAtIndex:i withObject:number];
    }

但是当我将数组转换为图像时,我得到的图像模糊了。我不明白为什么我不会对某些像素产生影响以及为什么我会对图片产生影响呢?

1 个答案:

答案 0 :(得分:1)

访问像素级数据的过程比你的问题所暗示的要复杂得多,因为正如Martin指出的那样,JPEG可以是压缩的图像格式。 Apple讨论了在Technical Q&A QA1509中获取像素数据的批准技术。

底线,为了得到UIImage的未压缩像素数据,你会:

  1. 获取CGImage的{​​{1}}。

  2. 通过UIImage获取CGImageRef的数据提供者。

  3. 通过CGImageGetDataProvider获取与该数据提供者相关联的二进制数据。

  4. 提取有关图像的一些信息,因此您知道如何解释该缓冲区。

  5. 因此:

    CGDataProviderCopyData

    鉴于你想要操纵它,你可能想要一些可变的像素缓冲区。最简单的方法是制作UIImage *image = ... CGImageRef imageRef = image.CGImage; // get the CGImageRef NSAssert(imageRef, @"Unable to get CGImageRef"); CGDataProviderRef provider = CGImageGetDataProvider(imageRef); // get the data provider NSAssert(provider, @"Unable to get provider"); NSData *data = CFBridgingRelease(CGDataProviderCopyData(provider)); // get copy of the data NSAssert(data, @"Unable to copy image data"); NSInteger bitsPerComponent = CGImageGetBitsPerComponent(imageRef); // some other interesting details about image NSInteger bitsPerComponent = CGImageGetBitsPerComponent(imageRef); NSInteger bitsPerPixel = CGImageGetBitsPerPixel(imageRef); CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); NSInteger bytesPerRow = CGImageGetBytesPerRow(imageRef); NSInteger width = CGImageGetWidth(imageRef); NSInteger height = CGImageGetHeight(imageRef); CGColorSpaceRef colorspace = CGImageGetColorSpace(imageRef); mutableCopy对象并在那里操纵它,但在这些情况下,我倾向于回退到C,创建一个NSData,其中我复制原始像素并使用传统的C阵列技术进行操作。

    创建缓冲区:

    void *outputBuffer

    关于如何操作它的确切细节,你必须看void *outputBuffer = malloc(width * height * bitsPerPixel / 8); NSAssert(outputBuffer, @"Unable to allocate buffer"); (它会告诉你它是RGBA还是ARGB;它是浮点还是整数)和bitmapInfo(这将是告诉你每个组件是8位还是16位,等等。例如,非常常见的JPEG格式是每个分量8比特,四个分量,RGBA(即,红色,绿色,蓝色和α,按此顺序)。但是你真的需要检查我们从bitsPerComponent中提取的各种属性以确保它们。有关详细信息,请参阅Quartz 2D Programming Guide - Bitmap Images and Image Masks中的讨论。我个人觉得“图11-2”特别有启发性。

    下一个逻辑问题是,当您完成操作像素数据时,如何为此创建CGImageRef。简而言之,您将颠倒上述过程,例如:创建数据提供者,创建UIImage,然后创建CGImageRef

    UIImage

    CGDataProviderRef outputProvider = CGDataProviderCreateWithData(NULL, outputBuffer, sizeof(outputBuffer), releaseData); CGImageRef outputImageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorspace, bitmapInfo, outputProvider, NULL, NO, kCGRenderingIntentDefault); UIImage *outputImage = [UIImage imageWithCGImage:outputImageRef]; CGImageRelease(outputImageRef); CGDataProviderRelease(outputProvider); 是一个只调用releaseData与数据提供者关联的像素缓冲区的C函数:

    free