Я пишу приложение для iPhone и должен существенно реализовать что-то, что эквивалентно инструменту "eyedropper" в Photoshop, где вы можете коснуться точки на изображении и захватить значения RGB для рассматриваемого пикселя, чтобы определить и сопоставить его цвет, Получение UIImage - легкая часть, но есть ли способ конвертировать данные UIImage в растровое представление, в котором я мог бы извлечь эту информацию для заданного пикселя? Образец рабочего кода был бы наиболее оценен, и обратите внимание, что меня не интересует альфа-значение.
Как получить значения RGB для пикселя на изображении на iphone
Ответ 1
Немного подробнее...
Я опубликовал ранее этот вечер с консолидацией и небольшим дополнением к тому, что было сказано на этой странице, - это можно найти в нижней части этой публикации. Тем не менее, я редактирую сообщение в этот момент, чтобы опубликовать то, что я предлагаю (по крайней мере, для моих требований, которые включают в себя изменение данных пикселей), лучший метод, поскольку он предоставляет записываемые данные (тогда как, как я понимаю, предложенный метод по предыдущим сообщениям и внизу этого сообщения предоставляет ссылку на чтение только для чтения).
Способ 1: записываемая информация о пикселях
-
Я определил константы
#define RGBA 4 #define RGBA_8_BIT 8
-
В моем подклассе UIImage я объявил переменные экземпляра:
size_t bytesPerRow; size_t byteCount; size_t pixelCount; CGContextRef context; CGColorSpaceRef colorSpace; UInt8 *pixelByteData; // A pointer to an array of RGBA bytes in memory RPVW_RGBAPixel *pixelData;
-
Структура пикселя (с альфой в этой версии)
typedef struct RGBAPixel { byte red; byte green; byte blue; byte alpha; } RGBAPixel;
-
Функция растрового изображения (возвращает предварительно вычисленный RGBA, делит RGB на A, чтобы получить немодифицированный RGB):
-(RGBAPixel*) bitmap { NSLog( @"Returning bitmap representation of UIImage." ); // 8 bits each of red, green, blue, and alpha. [self setBytesPerRow:self.size.width * RGBA]; [self setByteCount:bytesPerRow * self.size.height]; [self setPixelCount:self.size.width * self.size.height]; // Create RGB color space [self setColorSpace:CGColorSpaceCreateDeviceRGB()]; if (!colorSpace) { NSLog(@"Error allocating color space."); return nil; } [self setPixelData:malloc(byteCount)]; if (!pixelData) { NSLog(@"Error allocating bitmap memory. Releasing color space."); CGColorSpaceRelease(colorSpace); return nil; } // Create the bitmap context. // Pre-multiplied RGBA, 8-bits per component. // The source image format will be converted to the format specified here by CGBitmapContextCreate. [self setContext:CGBitmapContextCreate( (void*)pixelData, self.size.width, self.size.height, RGBA_8_BIT, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast )]; // Make sure we have our context if (!context) { free(pixelData); NSLog(@"Context not created!"); } // Draw the image to the bitmap context. // The memory allocated for the context for rendering will then contain the raw image pixelData in the specified color space. CGRect rect = { { 0 , 0 }, { self.size.width, self.size.height } }; CGContextDrawImage( context, rect, self.CGImage ); // Now we can get a pointer to the image pixelData associated with the bitmap context. pixelData = (RGBAPixel*) CGBitmapContextGetData(context); return pixelData; }
Данные только для чтения (предыдущая информация) - метод 2:
Шаг 1. Я объявил тип для байта:
typedef unsigned char byte;
Шаг 2. Я объявил структуру, соответствующую пикселю:
typedef struct RGBPixel{
byte red;
byte green;
byte blue;
}
RGBPixel;
Шаг 3. Я подклассифицировал UIImageView и объявил (с соответствующими синтезированными свойствами):
// Reference to Quartz CGImage for receiver (self)
CFDataRef bitmapData;
// Buffer holding raw pixel data copied from Quartz CGImage held in receiver (self)
UInt8* pixelByteData;
// A pointer to the first pixel element in an array
RGBPixel* pixelData;
Шаг 4. Код подкласса Я положил метод с именем bitmap (чтобы вернуть данные изображения растрового изображения):
//Get the bitmap data from the receiver CGImage (see UIImage docs)
[self setBitmapData: CGDataProviderCopyData(CGImageGetDataProvider([self CGImage]))];
//Create a buffer to store bitmap data (unitialized memory as long as the data)
[self setPixelBitData:malloc(CFDataGetLength(bitmapData))];
//Copy image data into allocated buffer
CFDataGetBytes(bitmapData,CFRangeMake(0,CFDataGetLength(bitmapData)),pixelByteData);
//Cast a pointer to the first element of pixelByteData
//Essentially what we're doing is making a second pointer that divides the byteData units differently - instead of dividing each unit as 1 byte we will divide each unit as 3 bytes (1 pixel).
pixelData = (RGBPixel*) pixelByteData;
//Now you can access pixels by index: pixelData[ index ]
NSLog(@"Pixel data one red (%i), green (%i), blue (%i).", pixelData[0].red, pixelData[0].green, pixelData[0].blue);
//You can determine the desired index by multiplying row * column.
return pixelData;
Шаг 5. Я сделал метод доступа:
-(RGBPixel*)pixelDataForRow:(int)row column:(int)column{
//Return a pointer to the pixel data
return &pixelData[row * column];
}
Ответ 2
Вот мое решение для цвета выборки UIImage.
Этот подход отображает запрошенный пиксель в 1px большой буфер RGBA и возвращает результирующие значения цвета как объект UIColor. Это намного быстрее, чем большинство других подходов, которые я видел, и использует только очень мало памяти.
Это должно работать очень хорошо для чего-то вроде выбора цвета, где вам обычно требуется только значение одного конкретного пикселя в любой момент времени.
UIImage + Picker.h
#import <UIKit/UIKit.h>
@interface UIImage (Picker)
- (UIColor *)colorAtPosition:(CGPoint)position;
@end
UIImage + Picker.m
#import "UIImage+Picker.h"
@implementation UIImage (Picker)
- (UIColor *)colorAtPosition:(CGPoint)position {
CGRect sourceRect = CGRectMake(position.x, position.y, 1.f, 1.f);
CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, sourceRect);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *buffer = malloc(4);
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
CGContextRef context = CGBitmapContextCreate(buffer, 1, 1, 8, 4, colorSpace, bitmapInfo);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0.f, 0.f, 1.f, 1.f), imageRef);
CGImageRelease(imageRef);
CGContextRelease(context);
CGFloat r = buffer[0] / 255.f;
CGFloat g = buffer[1] / 255.f;
CGFloat b = buffer[2] / 255.f;
CGFloat a = buffer[3] / 255.f;
free(buffer);
return [UIColor colorWithRed:r green:g blue:b alpha:a];
}
@end
Ответ 3
Вы не можете напрямую получить доступ к данным растрового изображения UIImage.
Вам нужно получить представление CGImage UIImage. Затем получите поставщика данных CGImage, из которого представлено представление CFData растрового изображения. Обязательно отпустите CFData, когда закончите.
CGImageRef cgImage = [image CGImage];
CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef bitmapData = CGDataProviderCopyData(provider);
Вы, вероятно, захотите посмотреть информацию о растровом изображении CGImage, чтобы получить порядок пикселей, размеры изображения и т.д.
Ответ 4
Ответ Lajos работал у меня. Чтобы получить пиксельные данные в виде массива байтов, я сделал следующее:
UInt8* data = CFDataGetBytePtr(bitmapData);
Дополнительная информация: CFDataRef documentation.
Кроме того, не забудьте включить CoreGraphics.framework
Ответ 5
Спасибо всем! Объединяя несколько ответов, я получаю:
- (UIColor*)colorFromImage:(UIImage*)image sampledAtPoint:(CGPoint)p {
CGImageRef cgImage = [image CGImage];
CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef bitmapData = CGDataProviderCopyData(provider);
const UInt8* data = CFDataGetBytePtr(bitmapData);
size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
size_t width = CGImageGetWidth(cgImage);
size_t height = CGImageGetHeight(cgImage);
int col = p.x*(width-1);
int row = p.y*(height-1);
const UInt8* pixel = data + row*bytesPerRow+col*4;
UIColor* returnColor = [UIColor colorWithRed:pixel[0]/255. green:pixel[1]/255. blue:pixel[2]/255. alpha:1.0];
CFRelease(bitmapData);
return returnColor;
}
Это просто занимает диапазон тонов 0.0-1.0 для x и y. Пример:
UIColor* sampledColor = [self colorFromImage:image
sampledAtPoint:CGPointMake(p.x/imageView.frame.size.width,
p.y/imageView.frame.size.height)];
Это отлично работает для меня. Я делаю пару предположений, таких как бит на пиксель и цветовое пространство RGBA, но это должно работать в большинстве случаев.
Еще одно замечание - он работает как для симулятора, так и для устройства - у меня были проблемы с этим в прошлом из-за оптимизации PNG, которая произошла, когда она продолжалась на устройстве.
Ответ 6
Чтобы сделать что-то подобное в моем приложении, я создал небольшой экран CGImageContext, а затем отобразил UIImage. Это позволило мне быстро извлечь несколько пикселей одновременно. Это означает, что вы можете настроить целевое растровое изображение в формате, который легко найти для анализа, и позволить CoreGraphics выполнять тяжелую работу по преобразованию между цветовыми моделями или форматами растровых изображений.
Ответ 7
Я не знаю, как правильно индексировать данные изображения на основе заданного корня X, Y. Кто-нибудь знает?
pixelPosition = (x + (y * ((ширина изображения) * BytesPerPixel)));
//Шаг не является проблемой с этим устройством, насколько я знаю, и может быть нулевым... // (или вытащил из математики).
Ответ 8
Используйте ANImageBitmapRep, который дает доступ на уровне пикселей (чтение/запись).