iPhoneの画像上のピクセルのRGB値を取得する方法
-
02-07-2019 - |
質問
iPhoneアプリケーションを作成しているので、Photoshopの「スポイト」ツールに相当するものを本質的に実装する必要があります。 。 UIImageを取得するのは簡単ですが、UIImageデータをビットマップ表現に変換して、特定のピクセルのこの情報を抽出する方法はありますか?実際に動作するコードサンプルをいただければ幸いです。アルファ値は関係ないことに注意してください。
解決
もう少し詳しく...
このページで述べたことに統合して少し追加して、今晩初めに投稿しました。これはこの投稿の下部にあります。この時点で投稿を編集していますが、書き込み可能なデータを提供するので、提案するものを投稿するために(少なくともピクセルデータの変更を含む私の要件のために)より良い方法です(私が理解すると、提供されている方法以前の投稿によって、この投稿の下部にあるデータへの読み取り専用の参照を提供しています。
方法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.ビットマップという名前のメソッドにビットマップピクセルデータを返すためのサブクラスコード:
//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;
ステップ5.アクセサーメソッドを作成しました:
-(RGBPixel*)pixelDataForRow:(int)row column:(int)column{
//Return a pointer to the pixel data
return &pixelData[row * column];
}
他のヒント
UIImageの色をサンプリングするための私のソリューションです。
このアプローチは、要求されたピクセルを1pxの大きなRGBAバッファーにレンダリングし、結果の色の値をUIColorオブジェクトとして返します。これは私が見た他のほとんどのアプローチよりもはるかに高速であり、メモリをほとんど使用しません。
これは、カラーピッカーなど、通常は常に1つの特定のピクセルの値のみが必要なものに対して、かなりうまく機能するはずです。
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
UIImageのビットマップデータに直接アクセスすることはできません。
UIImageのCGImage表現を取得する必要があります。次に、ビットマップのCFData表現からCGImageのデータプロバイダーを取得します。完了したら、CFDataを必ずリリースしてください。
CGImageRef cgImage = [image CGImage];
CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef bitmapData = CGDataProviderCopyData(provider);
おそらく、CGImageのビットマップ情報を見て、ピクセルの順序、画像の寸法などを取得する必要があります。
ラジョスの答えは私にとってはうまくいきました。ピクセルデータをバイトの配列として取得するには、次のようにしました。
UInt8 * data = CFDataGetBytePtr(bitmapData);
詳細:CFDataRef ドキュメント。
また、 CoreGraphics.framework
みんなありがとう!これらの答えのいくつかをまとめると:
- (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;
}
これは、xとyの両方に対して0.0〜1.0のポイント範囲を取ります。例:
UIColor* sampledColor = [self colorFromImage:image
sampledAtPoint:CGPointMake(p.x/imageView.frame.size.width,
p.y/imageView.frame.size.height)];
これは私にとってはうまくいきます。私はピクセルあたりのビット数やRGBA色空間のようないくつかの仮定をしていますが、これはほとんどの場合に機能するはずです。
別の注意-シミュレータとデバイスの両方で動作しています-デバイスで実行されたときにPNG最適化が発生したため、過去に問題が発生しました。
アプリケーションで同様のことを行うために、小さなオフスクリーンCGImageContextを作成し、UIImageをその中にレンダリングしました。これにより、一度に複数のピクセルをすばやく抽出することができました。これは、解析しやすい形式でターゲットビットマップを設定できることを意味し、CoreGraphicsにカラーモデルまたはビットマップ形式間の変換のハードワークを任せることができます。
指定されたX、Yコード化に基づいて画像データに正しくインデックスを付ける方法がわかりません。誰でも知っていますか?
pixelPosition =(x +(y *((imagewidth)* BytesPerPixel)));
//私の知る限り、ピッチはこのデバイスの問題ではなく、ゼロにすることができます... //(または数学から引き出された)。
ANImageBitmapRep を使用して、ピクセルレベルのアクセス(読み取り/書き込み)を許可します。