当前位置:  开发笔记 > IOS > 正文

如何从UIImage(Cocoa Touch)或CGImage(Core Graphics)获取像素数据?

如何解决《如何从UIImage(CocoaTouch)或CGImage(CoreGraphics)获取像素数据?》经验,为你挑选了8个好方法。

我有一个UIImage(Cocoa Touch).从那以后,我很高兴得到一个CGImage或其他你想要的东西.我想写这个函数:

- (int)getRGBAFromImage:(UIImage *)image atX:(int)xx andY:(int)yy {
  // [...]
  // What do I want to read about to help
  // me fill in this bit, here?
  // [...]

  int result = (red << 24) | (green << 16) | (blue << 8) | alpha;
  return result;
}

谢谢!



1> Olie..:

仅供参考,我将Keremk的答案与原始大纲相结合,清理了拼写错误,将其归结为返回一系列颜色并完成整个编译.结果如下:

+ (NSArray*)getRGBAsFromImage:(UIImage*)image atX:(int)x andY:(int)y count:(int)count
{
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];

    // First get the image into your data buffer
    CGImageRef imageRef = [image CGImage];
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
    NSUInteger bytesPerPixel = 4;
    NSUInteger 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), imageRef);
    CGContextRelease(context);

    // Now your rawData contains the image data in the RGBA8888 pixel format.
    NSUInteger byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
    for (int i = 0 ; i < count ; ++i)
    {
        CGFloat alpha = ((CGFloat) rawData[byteIndex + 3] ) / 255.0f;
        CGFloat red   = ((CGFloat) rawData[byteIndex]     ) / alpha;
        CGFloat green = ((CGFloat) rawData[byteIndex + 1] ) / alpha;
        CGFloat blue  = ((CGFloat) rawData[byteIndex + 2] ) / alpha;
        byteIndex += bytesPerPixel;

        UIColor *acolor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
        [result addObject:acolor];
    }

  free(rawData);

  return result;
}


使用CALLOC INSTEAD OF MALLOC !!!! 我正在使用此代码来测试某些像素是否透明,并且我正在获取虚假信息,其中像素是透明的,因为内存未先被清除.使用calloc(width*height,4)而不是malloc就可以了.其余代码很棒,谢谢!
这很棒.谢谢.关于用法的注意事项:x和y是图像中开始获取RGBAs的坐标,'count'是从该点获取的像素数,从左到右,然后逐行.示例用法:获取整个图像的RGBAs:`getRGBAsFromImage:image atX:0和Y:0 count:image.size.width*image.size.height`获取图像最后一行的RGBAs:`getRGBAsFromImage:image atX:0和Y:image.size.height-1 count:image.size.width`
不要忘记对颜色进行多次选择.而且,这些乘以1不会做任何事情.
方式过于繁琐的malloc大空间只能读取一个像素.
即使它是正确的.当count是一个很大的数字时(比如一行图像的长度或整个图像的大小!)我不认为这个方法的性能会很好,因为太多的UIColor对象真的很重.在现实生活中,当我需要一个像素时,UIColor就可以了,(UIColors的NSArray对我来说太过分了).当你需要一堆颜色我不会使用UIColors,因为我可能会使用OpenGL等.在我看来,这种方法没有现实生活用途,但非常适合教育目的.
请注意,使用相机垂直拍摄的图像会在`UIImage`对象中设置`imageOrientation`标志,而不是旋转图像.这可能会导致一些令人讨厌的错误,因为转换为`CGImage`会忽略这一点.因此,如果`imageOrientation`是'UIImageOrientationUp`(用于手机横向拍摄的图像),那么这将正常工作,但如果它是'UIImageOrientationRight`(它是用相机垂直拍摄的图像)那么整个图像将在阵列中旋转90度.谨防!
在为"红色","绿色","蓝色"赋值时,数字不在0和1之间.因此,使用这些值创建的"UIColor"几乎总是白色.每个这些代码值必须分为255.0f,如:`UIColor*acolor = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha];
感谢您的分享。对于复杂的,半成品的,换句话说,糟糕的API,Apple确实做了出色的工作。

2> keremk..:

一种方法是将图像绘制到由给定缓冲区支持的位图上下文(对于给定的颜色空间)(在这种情况下它是RGB):(请注意,这会将图像数据复制到该缓冲区,因此您可以想要缓存它而不是每次需要获取像素值时执行此操作)

见下面的样本:

// First get the image into your data buffer
CGImageRef image = [myUIImage CGImage];
NSUInteger width = CGImageGetWidth(image);
NSUInteger height = CGImageGetHeight(image);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *rawData = malloc(height * width * 4);
NSUInteger bytesPerPixel = 4;
NSUInteger 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));
CGContextRelease(context);

// Now your rawData contains the image data in the RGBA8888 pixel format.
int byteIndex = (bytesPerRow * yy) + xx * bytesPerPixel;
red = rawData[byteIndex];
green = rawData[byteIndex + 1];
blue = rawData[byteIndex + 2];
alpha = rawData[byteIndex + 3];


这会泄漏内存.发布rawData:free(rawData);
`CGContextDrawImage`需要三个参数,上面只有两个...我认为它应该是`CGContextDrawImage(context,CGRectMake(0,0,width,height),image)`

3> yakovlev..:

Apple的技术问答QA1509展示了以下简单方法:

CFDataRef CopyImagePixels(CGImageRef inImage)
{
    return CGDataProviderCopyData(CGImageGetDataProvider(inImage));
}

使用CFDataGetBytePtr去实际字节(和各种CGImageGet*方法来了解如何对其进行解释).


这不是一个很好的方法,因为每个图像的像素格式往往变化很大.有几件事可以改变,包括1.图像的方向2. alpha分量的格式和3. RGB的字节顺序.我个人花了一些时间试图解码Apple的文档如何做到这一点,但我不确定它是否值得.如果你想快速完成,只需keremk的解决方案.

4> Erik Aigner..:

无法相信这里没有一个正确答案.无需分配指针,仍然需要对未经过相乘的值进行规范化.为了切入追逐,这里是Swift 4的正确版本.仅供UIImage使用.cgImage.

extension CGImage {
    func colors(at: [CGPoint]) -> [UIColor]? {
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bytesPerPixel = 4
        let bytesPerRow = bytesPerPixel * width
        let bitsPerComponent = 8
        let bitmapInfo: UInt32 = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue

        guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo),
            let ptr = context.data?.assumingMemoryBound(to: UInt8.self) else {
            return nil
        }

        context.draw(self, in: CGRect(x: 0, y: 0, width: width, height: height))

        return at.map { p in
            let i = bytesPerRow * Int(p.y) + bytesPerPixel * Int(p.x)

            let a = CGFloat(ptr[i + 3]) / 255.0
            let r = (CGFloat(ptr[i]) / a) / 255.0
            let g = (CGFloat(ptr[i + 1]) / a) / 255.0
            let b = (CGFloat(ptr[i + 2]) / a) / 255.0

            return UIColor(red: r, green: g, blue: b, alpha: a)
        }
    }
}



5> Nidal Fakhou..:
NSString * path = [[NSBundle mainBundle] pathForResource:@"filename" ofType:@"jpg"];
UIImage * img = [[UIImage alloc]initWithContentsOfFile:path];
CGImageRef image = [img CGImage];
CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(image));
const unsigned char * buffer =  CFDataGetBytePtr(data);


我刚刚找到这个链接,它的工作效果很棒:https://gist.github.com/739132

6> eddy..:

这是一个SO 线程,其中@Matt通过移动图像将所需像素仅渲染到1x1上下文中,以使所需像素与上下文中的一个像素对齐.



7> Cameron Lowe..:

UIImage是一个包装器,字节是CGImage或CIImage

根据UIImage上的Apple Reference,对象是不可变的,你无法访问后备字节.而如果你填充这是事实,你可以访问CGImage数据UIImageCGImage(显式或隐式),它将返回NULL如果UIImage通过支持CIImage,反之亦然.

图像对象不提供对其底层图像数据的直接访问.但是,您可以检索其他格式的图像数据,以便在您的应用中使用.具体来说,您可以使用cgImage和ciImage属性分别检索与Core Graphics和Core Image兼容的图像版本.您还可以使用UIImagePNGRepresentation(:)和UIImageJPEGRepresentation(:_ :)函数生成包含PNG或JPEG格式的图像数据的NSData对象.

解决这个问题的常用技巧

如上所述,您的选择是

UIImagePNGRepresentation或JPEG

确定图像是否具有CGImage或CIImage支持数据并将其获取

如果您希望输出不是ARGB,PNG或JPEG数据并且数据尚未由CIImage支持,则这些都不是特别好的技巧.

我的推荐,试试CIImage

在开发项目时,您可能更有意义地完全避免使用UIImage并选择其他内容.作为Obj-C图像包装器的UIImage通常由CGImage支持,我们认为它是理所当然的.CIImage往往是一种更好的包装器格式,因为您可以使用CIContext来获取所需的格式,而无需知道它是如何创建的.在您的情况下,获取位图将是一个调用的问题

- render:toBitmap:rowBytes:bounds:format:colorSpace:

作为额外的奖励,您可以通过将滤镜链接到图像上来开始对图像进行良好的操作.这解决了图像颠倒或需要旋转/缩放等许多问题.



8> swillsea..:

基于Olie和Algal的回答,这里是Swift 3的更新答案

public func getRGBAs(fromImage image: UIImage, x: Int, y: Int, count: Int) -> [UIColor] {

var result = [UIColor]()

// First get the image into your data buffer
guard let cgImage = image.cgImage else {
    print("CGContext creation failed")
    return []
}

let width = cgImage.width
let height = cgImage.height
let colorSpace = CGColorSpaceCreateDeviceRGB()
let rawdata = calloc(height*width*4, MemoryLayout.size)
let bytesPerPixel = 4
let bytesPerRow = bytesPerPixel * width
let bitsPerComponent = 8
let bitmapInfo: UInt32 = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue

guard let context = CGContext(data: rawdata, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {
    print("CGContext creation failed")
    return result
}

context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))

// Now your rawData contains the image data in the RGBA8888 pixel format.
var byteIndex = bytesPerRow * y + bytesPerPixel * x

for _ in 0..

迅速

推荐阅读
wurtjq
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有