I am writing an iPhone application and need to essentially implement something equivalent to the 'eyedropper' tool in photoshop, where you can touch a point on the image and capture the RGB values for the pixel in question to determine and match its color. Getting the UIImage is the easy part, but is there a way to convert the UIImage data into a bitmap representation in which I could extract this information for a given pixel? A working code sample would be most appreciated, and note that I am not concerned with the alpha value.
我正在编写一个iPhone应用程序,需要在photoshop中实现类似于“eyedropper”工具的功能,在这个工具中,你可以触摸到图像上的一个点,并捕捉到像素的RGB值,以确定和匹配它的颜色。获取UIImage是很简单的一部分,但是有没有一种方法可以将UIImage数据转换成位图表示,在位图表示中,我可以提取给定像素的信息?非常感谢一个可用的代码示例,并注意我不关心alpha值。
8 个解决方案
#1
38
A little more detail...
I posted earlier this evening with a consolidation and small addition to what had been said on this page - that can be found at the bottom of this post. I am editing the post at this point, however, to post what I propose is (at least for my requirements, which include modifying pixel data) a better method, as it provides writable data (whereas, as I understand it, the method provided by previous posts and at the bottom of this post provides a read-only reference to data).
我在今晚早些时候发表了一篇文章,在这篇文章的底部,我对文章的内容进行了一些整理和补充。我编辑这个职位在这一点上,然而,把我提出的是(至少对于我的要求,包括修改像素数据)一个更好的方法,因为它提供了可写数据(然而,据我所知,所提供的方法之前的文章,这篇文章的底部提供了一个只读引用数据)。
Method 1: Writable Pixel Information
方法1:可写像素信息。
-
I defined constants
我定义的常量
#define RGBA 4 #define RGBA_8_BIT 8
-
In my UIImage subclass I declared instance variables:
在我的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;
-
The pixel struct (with alpha in this version)
像素结构(在这个版本中带有alpha)
typedef struct RGBAPixel { byte red; byte green; byte blue; byte alpha; } RGBAPixel;
-
Bitmap function (returns pre-calculated RGBA; divide RGB by A to get unmodified RGB):
位图函数(返回预计算的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; }
Read-Only Data (Previous information) - method 2:
Step 1. I declared a type for byte:
步骤1。我声明了一个字节类型:
typedef unsigned char byte;
Step 2. I declared a struct to correspond to a pixel:
步骤2。我声明了一个与像素对应的结构体:
typedef struct RGBPixel{
byte red;
byte green;
byte blue;
}
RGBPixel;
Step 3. I subclassed UIImageView and declared (with corresponding synthesized properties):
步骤3。I子类化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;
Step 4. Subclass code I put in a method named bitmap (to return the bitmap pixel data):
步骤4。我放入一个名为位图的方法(返回位图像素数据):
//Get the bitmap data from the receiver's 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's 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;
Step 5. I made an accessor method:
第5步。我做了一个accessor方法:
-(RGBPixel*)pixelDataForRow:(int)row column:(int)column{
//Return a pointer to the pixel data
return &pixelData[row * column];
}
#2
20
Here is my solution for sampling color of an UIImage.
这是我对UIImage颜色采样的解决方案。
This approach renders the requested pixel into a 1px large RGBA buffer and returns the resulting color values as an UIColor object. This is much faster than most other approaches I've seen and uses only very little memory.
这种方法将请求的像素呈现到1px的RGBA大缓冲区中,并将结果的颜色值作为UIColor对象返回。这比我见过的大多数其他方法都要快得多,而且只使用很少的内存。
This should work pretty well for something like a color picker, where you typically only need the value of one specific pixel at a any given time.
这对于颜色选择器之类的东西来说应该非常有效,在这种情况下,您通常只需要在任何给定时间内指定一个像素的值。
Uiimage+Picker.h
用户界面图像+ Picker.h
#import <UIKit/UIKit.h>
@interface UIImage (Picker)
- (UIColor *)colorAtPosition:(CGPoint)position;
@end
Uiimage+Picker.m
用户界面图像+ 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
11
You can't access the bitmap data of a UIImage directly.
不能直接访问UIImage的位图数据。
You need to get the CGImage representation of the UIImage. Then get the CGImage's data provider, from that a CFData representation of the bitmap. Make sure to release the CFData when done.
你需要得到UIImage的CGImage表示。然后从位图的CFData表示中获取CGImage的数据提供程序。确保在完成时释放CFData。
CGImageRef cgImage = [image CGImage];
CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef bitmapData = CGDataProviderCopyData(provider);
You will probably want to look at the bitmap info of the CGImage to get pixel order, image dimensions, etc.
您可能想要查看CGImage的位图信息以获得像素顺序、图像尺寸等等。
#4
5
Lajos's answer worked for me. To get the pixel data as an array of bytes, I did this:
拉霍斯的回答对我起了作用。为了将像素数据作为字节数组获取,我这样做了:
UInt8* data = CFDataGetBytePtr(bitmapData);
UInt8 *数据= CFDataGetBytePtr(bitmapData);
More info: CFDataRef documentation.
更多信息:CFDataRef文档。
Also, remember to include CoreGraphics.framework
另外,记住要包含CoreGraphics.framework
#5
3
Thanks everyone! Putting a few of these answers together I get:
谢谢大家!把这些答案放在一起,我得到:
- (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;
}
This just takes a point range 0.0-1.0 for both x and y. Example:
对于x和y都取0-1.0的点范围。
UIColor* sampledColor = [self colorFromImage:image
sampledAtPoint:CGPointMake(p.x/imageView.frame.size.width,
p.y/imageView.frame.size.height)];
This works great for me. I am making a couple assumptions like bits per pixel and RGBA colorspace, but this should work for most cases.
这对我很有用。我做了一些假设,比如每个像素的比特数和RGBA颜色空间,但这应该适用于大多数情况。
Another note - it is working on both Simulator and device for me - I have had problems with that in the past because of the PNG optimization that happened when it went on the device.
另一个注意事项——它正在为我的模拟器和设备工作——我在过去有问题,因为PNG优化发生在设备上。
#6
1
To do something similar in my application, I created a small off-screen CGImageContext, and then rendered the UIImage into it. This allowed me a fast way to extract a number of pixels at once. This means that you can set up the target bitmap in a format you find easy to parse, and let CoreGraphics do the hard work of converting between color models or bitmap formats.
为了在我的应用程序中做类似的事情,我创建了一个小的屏幕外的CGImageContext,然后将UIImage呈现到其中。这使我可以快速地一次提取多个像素。这意味着您可以以一种易于解析的格式设置目标位图,并让CoreGraphics完成在颜色模型或位图格式之间进行转换的艰苦工作。
#7
1
I dont know how to index into image data correctly based on given X,Y cordination. Does anyone know?
我不知道如何根据给定的X,Y关系来正确索引图像数据。有人知道吗?
pixelPosition = (x+(y*((imagewidth)*BytesPerPixel)));
pixelPosition =(x + y *((imagewidth)* BytesPerPixel)));
// pitch isn't an issue with this device as far as I know and can be let zero... // ( or pulled out of the math ).
//据我所知,这款设备没有问题,可以让它归零……//(或退出数学)。
#8
#1
38
A little more detail...
I posted earlier this evening with a consolidation and small addition to what had been said on this page - that can be found at the bottom of this post. I am editing the post at this point, however, to post what I propose is (at least for my requirements, which include modifying pixel data) a better method, as it provides writable data (whereas, as I understand it, the method provided by previous posts and at the bottom of this post provides a read-only reference to data).
我在今晚早些时候发表了一篇文章,在这篇文章的底部,我对文章的内容进行了一些整理和补充。我编辑这个职位在这一点上,然而,把我提出的是(至少对于我的要求,包括修改像素数据)一个更好的方法,因为它提供了可写数据(然而,据我所知,所提供的方法之前的文章,这篇文章的底部提供了一个只读引用数据)。
Method 1: Writable Pixel Information
方法1:可写像素信息。
-
I defined constants
我定义的常量
#define RGBA 4 #define RGBA_8_BIT 8
-
In my UIImage subclass I declared instance variables:
在我的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;
-
The pixel struct (with alpha in this version)
像素结构(在这个版本中带有alpha)
typedef struct RGBAPixel { byte red; byte green; byte blue; byte alpha; } RGBAPixel;
-
Bitmap function (returns pre-calculated RGBA; divide RGB by A to get unmodified RGB):
位图函数(返回预计算的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; }
Read-Only Data (Previous information) - method 2:
Step 1. I declared a type for byte:
步骤1。我声明了一个字节类型:
typedef unsigned char byte;
Step 2. I declared a struct to correspond to a pixel:
步骤2。我声明了一个与像素对应的结构体:
typedef struct RGBPixel{
byte red;
byte green;
byte blue;
}
RGBPixel;
Step 3. I subclassed UIImageView and declared (with corresponding synthesized properties):
步骤3。I子类化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;
Step 4. Subclass code I put in a method named bitmap (to return the bitmap pixel data):
步骤4。我放入一个名为位图的方法(返回位图像素数据):
//Get the bitmap data from the receiver's 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's 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;
Step 5. I made an accessor method:
第5步。我做了一个accessor方法:
-(RGBPixel*)pixelDataForRow:(int)row column:(int)column{
//Return a pointer to the pixel data
return &pixelData[row * column];
}
#2
20
Here is my solution for sampling color of an UIImage.
这是我对UIImage颜色采样的解决方案。
This approach renders the requested pixel into a 1px large RGBA buffer and returns the resulting color values as an UIColor object. This is much faster than most other approaches I've seen and uses only very little memory.
这种方法将请求的像素呈现到1px的RGBA大缓冲区中,并将结果的颜色值作为UIColor对象返回。这比我见过的大多数其他方法都要快得多,而且只使用很少的内存。
This should work pretty well for something like a color picker, where you typically only need the value of one specific pixel at a any given time.
这对于颜色选择器之类的东西来说应该非常有效,在这种情况下,您通常只需要在任何给定时间内指定一个像素的值。
Uiimage+Picker.h
用户界面图像+ Picker.h
#import <UIKit/UIKit.h>
@interface UIImage (Picker)
- (UIColor *)colorAtPosition:(CGPoint)position;
@end
Uiimage+Picker.m
用户界面图像+ 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
11
You can't access the bitmap data of a UIImage directly.
不能直接访问UIImage的位图数据。
You need to get the CGImage representation of the UIImage. Then get the CGImage's data provider, from that a CFData representation of the bitmap. Make sure to release the CFData when done.
你需要得到UIImage的CGImage表示。然后从位图的CFData表示中获取CGImage的数据提供程序。确保在完成时释放CFData。
CGImageRef cgImage = [image CGImage];
CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef bitmapData = CGDataProviderCopyData(provider);
You will probably want to look at the bitmap info of the CGImage to get pixel order, image dimensions, etc.
您可能想要查看CGImage的位图信息以获得像素顺序、图像尺寸等等。
#4
5
Lajos's answer worked for me. To get the pixel data as an array of bytes, I did this:
拉霍斯的回答对我起了作用。为了将像素数据作为字节数组获取,我这样做了:
UInt8* data = CFDataGetBytePtr(bitmapData);
UInt8 *数据= CFDataGetBytePtr(bitmapData);
More info: CFDataRef documentation.
更多信息:CFDataRef文档。
Also, remember to include CoreGraphics.framework
另外,记住要包含CoreGraphics.framework
#5
3
Thanks everyone! Putting a few of these answers together I get:
谢谢大家!把这些答案放在一起,我得到:
- (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;
}
This just takes a point range 0.0-1.0 for both x and y. Example:
对于x和y都取0-1.0的点范围。
UIColor* sampledColor = [self colorFromImage:image
sampledAtPoint:CGPointMake(p.x/imageView.frame.size.width,
p.y/imageView.frame.size.height)];
This works great for me. I am making a couple assumptions like bits per pixel and RGBA colorspace, but this should work for most cases.
这对我很有用。我做了一些假设,比如每个像素的比特数和RGBA颜色空间,但这应该适用于大多数情况。
Another note - it is working on both Simulator and device for me - I have had problems with that in the past because of the PNG optimization that happened when it went on the device.
另一个注意事项——它正在为我的模拟器和设备工作——我在过去有问题,因为PNG优化发生在设备上。
#6
1
To do something similar in my application, I created a small off-screen CGImageContext, and then rendered the UIImage into it. This allowed me a fast way to extract a number of pixels at once. This means that you can set up the target bitmap in a format you find easy to parse, and let CoreGraphics do the hard work of converting between color models or bitmap formats.
为了在我的应用程序中做类似的事情,我创建了一个小的屏幕外的CGImageContext,然后将UIImage呈现到其中。这使我可以快速地一次提取多个像素。这意味着您可以以一种易于解析的格式设置目标位图,并让CoreGraphics完成在颜色模型或位图格式之间进行转换的艰苦工作。
#7
1
I dont know how to index into image data correctly based on given X,Y cordination. Does anyone know?
我不知道如何根据给定的X,Y关系来正确索引图像数据。有人知道吗?
pixelPosition = (x+(y*((imagewidth)*BytesPerPixel)));
pixelPosition =(x + y *((imagewidth)* BytesPerPixel)));
// pitch isn't an issue with this device as far as I know and can be let zero... // ( or pulled out of the math ).
//据我所知,这款设备没有问题,可以让它归零……//(或退出数学)。