关于ios:iOS对图片进行像素读取

4次阅读

共计 4551 个字符,预计需要花费 12 分钟才能阅读完成。

图片解码

常见的图片压缩格局次要是 PNG 和 JPEG,在 iOS 的程序开发中,个别不须要获取一张图片解码后的数据,但如果须要对像素进行操作,可能就须要理解怎么获取相干的像素值。
位图图像(bitmap),是由多个像素点排列组成的。当咱们对 PNG 和 JPG 进行解码后,应该获取到一组像素点数据,这样一组数据就组成了解码后的位图。图片解码能够看做一次解压缩,所以位图占用的空间会更大,位图所占的空间很好计算,图片的面积 * 每个像素占用的字节。
+ (nullable UIImage *)imageWithContentsOfFile:(NSString *)path;

通常会应用 imageWithContentsOfFile 来加载一张图片,这样创立 UIImage 时并不会在这里进行解码,当 UIImage 被绘制时才会解码。

CGImage

CGImage 的解释是位图或者图片蒙版,也就是说能够通过 CGImage 来读取像素值。当间接对 CGImage 的像素读取时能够应用上面的形式。

NSData *imageData = [NSData dataWithContentsOfFile:imagePath];
CFDataRef dataRef = (__bridge CFDataRef)imageData;
CGImageSourceRef source = CGImageSourceCreateWithData(dataRef, nil);
CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, nil);
width = (int)CGImageGetWidth(cgImage);
height = (int)CGImageGetHeight(cgImage);
size_t pixelCount = width * height;
CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef data = CGDataProviderCopyData(provider);

CFRelease(data);
CGImageRelease(cgImage);             
CFRelease(source);

这种读取像素的形式无奈指定色彩格局,而是读取图片原有的色彩空间所组成像素的组合。须要留神的是,所有创立进去的数据都须要调用相应的 release 来进行开释。这里创立 CGImage 的办法也能够对 CGImage 的缓存形式进行设置。而且当咱们从 CGImage 的 dataProvider 中获取数据时,目前只能拷贝一份到 CFDataRef 中,而无奈间接读取。

CGBitmapContext

以 RGB 色彩空间为例,色彩相干的数据可能有以上这么多,CGImage 有相干的办法能够读取相应的数据,但有些办法 iOS12 之后才反对。

typedef CF_ENUM(uint32_t, CGImageAlphaInfo) {
    kCGImageAlphaNone,               /* For example, RGB. */
    kCGImageAlphaPremultipliedLast,  /* For example, premultiplied RGBA */
    kCGImageAlphaPremultipliedFirst, /* For example, premultiplied ARGB */
    kCGImageAlphaLast,               /* For example, non-premultiplied RGBA */
    kCGImageAlphaFirst,              /* For example, non-premultiplied ARGB */
    kCGImageAlphaNoneSkipLast,       /* For example, RBGX. */
    kCGImageAlphaNoneSkipFirst,      /* For example, XRGB. */
    kCGImageAlphaOnly                /* No color data, alpha data only */
};

typedef CF_ENUM(uint32_t, CGImageByteOrderInfo) {
    kCGImageByteOrderMask     = 0x7000,
    kCGImageByteOrderDefault  = (0 << 12),
    kCGImageByteOrder16Little = (1 << 12),
    kCGImageByteOrder32Little = (2 << 12),
    kCGImageByteOrder16Big    = (3 << 12),
    kCGImageByteOrder32Big    = (4 << 12)
} CG_AVAILABLE_STARTING(10.0, 2.0);

typedef CF_ENUM(uint32_t, CGImagePixelFormatInfo) {
    kCGImagePixelFormatMask      = 0xF0000,
    kCGImagePixelFormatPacked    = (0 << 16),
    kCGImagePixelFormatRGB555    = (1 << 16), /* Only for RGB 16 bits per pixel */
    kCGImagePixelFormatRGB565    = (2 << 16), /* Only for RGB 16 bits per pixel */
    kCGImagePixelFormatRGB101010 = (3 << 16), /* Only for RGB 32 bits per pixel */
    kCGImagePixelFormatRGBCIF10  = (4 << 16), /* Only for RGB 32 bits per pixel */
} CG_AVAILABLE_STARTING(10.14, 12.0);

typedef CF_OPTIONS(uint32_t, CGBitmapInfo) {
    kCGBitmapAlphaInfoMask = 0x1F,

    kCGBitmapFloatInfoMask = 0xF00,
    kCGBitmapFloatComponents = (1 << 8),

    kCGBitmapByteOrderMask     = kCGImageByteOrderMask,
    kCGBitmapByteOrderDefault  = kCGImageByteOrderDefault,
    kCGBitmapByteOrder16Little = kCGImageByteOrder16Little,
    kCGBitmapByteOrder32Little = kCGImageByteOrder32Little,
    kCGBitmapByteOrder16Big    = kCGImageByteOrder16Big,
    kCGBitmapByteOrder32Big    = kCGImageByteOrder32Big
} CG_AVAILABLE_STARTING(10.0, 2.0);

#ifdef __BIG_ENDIAN__
# define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Big
# define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Big
#else    /* Little endian. */
# define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Little
# define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Little
#endif

图片中单个像素的组成依据色彩空间,排列,以及占位有很多排列组合的形式,所以为了防止解决过多的状况,可能应用 CGBitmapContext 是一种对使用者来说比拟敌对的形式。
CGBitmapContext 能够用来把位图按位画进内存的画布。画布上每个像素点都是依照 CGBitmapContext 中指定的色彩空间来排布的。
在 CGBitmapContext 创立时,须要传入一些参数来指定咱们须要的画布是什么样的。

CGContextRef CGBitmapContextCreate(
// 画布中渲染的内存地址,这个尺寸至多须要 bytesPerRow * height,// 如果传入 NULL,这个办法会为位图开拓一段空间。void *data, 
// 位图的宽度
size_t width, 
// 位图的高度
size_t height, 
// 像素中每个元素所占用的位数,如一个 32 位的 RGB 格局,一个元素所占的位数为 8
size_t bitsPerComponent, 
// 位图每一行所占的字节数,如果 data 传了 NULL,这里传入 0 主动计算
size_t bytesPerRow, 
// 色彩空间
CGColorSpaceRef space, 
// 一些其余相干信息,比方是否蕴含 alpha 通道,alpha 通道在像素中的地位,像素中元素是整形还是浮点型等。uint32_t bitmapInfo);

在创立 CGBitmapContext 时,有些配置是不反对的,相应反对的格局能够查看文档 Graphics Contexts。这里举个例子,对于没有 alpha 通道的图片,想间接用 kCGImageAlphaNone 来生成不含 alpha 通道的画布,是不容许的。但应用 kCGImageAlphaNoneSkipLast 来疏忽 alpha 通道是能够的。当想间接获取非预乘的像素数据如 kCGImageAlphaLast 也是不容许的,只能应用预乘后的。
这里咱们将色彩空间管制成 BGRA,应用了如下的参数。

uint8_t* bitmapData = (uint8_t *)calloc(pixelCount * 4, sizeof(uint8_t));
int bytesPerRow = 4 * width;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(bitmapData, width, height, 8, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
if (!context) {return nullptr;}
CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);

CGColorSpaceRelease(colorSpace);             
CGContextRelease(context);

之后对 RGB 元素进行操作获取非预乘的像素数据。

auto iter = bitmapData;
int temp;
for (int i = 0; i < pixelCount; i++) {uint8_t alpha = *(iter + 3);
    if (alpha != 0) {for (int j = 0; j < 3; j++) {
            temp = *iter * 255;
            *iter++ = temp /alpha;
        }
        iter++;
    }else {iter += 4;}
}

应该还有很多空间能够优化,比方某些操作对于不带透明度的图片能够间接跳过。
标签: CGBitmapContext, CGImage, iOS

正文完
 0