Как получить данные о пикселях из UIImage (Cocoa Touch) или CGImage (Core Graphics)?

У меня есть 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; } 

Благодаря!

9 Solutions collect form web for “Как получить данные о пикселях из UIImage (Cocoa Touch) или CGImage (Core Graphics)?”

FYI, я объединил ответ Керемка с моим оригинальным контуром, очистил опечатки, обобщил его, чтобы вернуть массив цветов и получил все, что нужно скомпилировать. Вот результат:

 + (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; } 

Один из способов сделать это – сделать изображение в растровом контексте, поддерживаемом данным буфером для данного цветового пространства (в данном случае это 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]; 

Технические вопросы Q & A QA1509 от Apple показывают следующий простой подход:

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

Используйте CFDataGetBytePtr чтобы получить фактические байты (и различные методы CGImageGet* чтобы понять, как их интерпретировать).

Вот stream SO, где @Matt отображает только нужный пиксель в 1×1-контекст, смещая изображение так, чтобы нужный пиксель выравнивался с одним пикселем в контексте.

 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); 

UIImage – обертка, байты – CGImage или CIImage

Согласно Apple Reference на UIImage объект неизменен, и у вас нет доступа к байтам поддержки. Хотя верно, что вы можете получить доступ к данным CGImage, если вы заполнили UIImage CGImage (явно или неявно), он вернет NULL если UIImage будет поддерживаться CIImage и наоборот.

Объекты изображения не обеспечивают прямой доступ к их базовым данным изображения. Однако вы можете получить данные изображения в других форматах для использования в вашем приложении. В частности, вы можете использовать свойства cgImage и ciImage для получения версий образа, совместимых с Core Graphics и Core Image, соответственно. Вы также можете использовать функции UIImagePNGRepresentation ( 🙂 и UIImageJPEGRepresentation ( : _ 🙂 для создания объекта NSData, содержащего данные изображения в формате PNG или JPEG.

Общие трюки, чтобы обойти эту проблему

Как указано, ваши варианты

  • Представление UIImagePNGR или JPEG
  • Определите, есть ли у изображения CGImage или CIImage резервные данные и получите их там

Ни один из них не является особенно хорошими трюками, если вы хотите, чтобы данные, которые не являются данными ARGB, PNG или JPEG, и данные еще не поддерживаются CIImage.

Моя рекомендация, попробуйте CIImage

При разработке вашего проекта у вас может возникнуть больше смысла избегать UIImage и выбирать что-то еще. UIImage, как обертка изображений Obj-C, часто поддерживается CGImage до такой степени, что мы принимаем это как должное. CIImage имеет тенденцию быть лучшим форматом обертки, поскольку вы можете использовать CIContext, чтобы получить желаемый формат, не зная, как он был создан. В вашем случае получение растрового изображения будет вопросом вызова

– render: toBitmap: rowBytes: bounds: format: colorSpace:

В качестве дополнительного бонуса вы можете начать делать приятные манипуляции с изображением, привязывая фильтры к изображению. Это решает многие проблемы, когда изображение перевернуто или должно быть повернуто / масштабировано и т. Д.

Основываясь на ответе Оли и Алгала, вот обновленный ответ для 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.. 

быстрый

Не мог поверить, что здесь нет ни одного правильного ответа . Нет необходимости выделять указатели, и неумножимые значения по-прежнему необходимо нормализовать. Чтобы перерезать погоню, вот верная версия для 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(py) + bytesPerPixel * Int(px) 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) } } } 

Основываясь на разных ответах, но главным образом на этом , это работает для того, что мне нужно:

 UIImage *image1 = ...; // The image from where you want a pixel data int pixelX = ...; // The X coordinate of the pixel you want to retrieve int pixelY = ...; // The Y coordinate of the pixel you want to retrieve uint32_t pixel1; // Where the pixel data is to be stored CGContextRef context1 = CGBitmapContextCreate(&pixel1, 1, 1, 8, 4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst); CGContextDrawImage(context1, CGRectMake(-pixelX, -pixelY, CGImageGetWidth(image1.CGImage), CGImageGetHeight(image1.CGImage)), image1.CGImage); CGContextRelease(context1); 

В результате этих строк у вас будет пиксель в формате AARRGGBB с альфой, всегда установленным в FF в 4- pixel1 беззнаковом целочисленном pixel1 .

Давайте будем гением компьютера.