私は iPhone アプリケーションを作成していますが、Photoshop の「スポイト」ツールに相当するものを基本的に実装する必要があります。このツールでは、画像上のポイントに触れて、問題のピクセルの RGB 値をキャプチャして、その色を決定して一致させることができます。UIImage を取得するのは簡単ですが、UIImage データをビットマップ表現に変換して、特定のピクセルの情報を抽出する方法はありますか? 動作するコード サンプルがあれば非常にありがたいのですが、私はアルファ値には関心がないことに注意してください。
8 に答える
もう少し詳しく...
私は今晩早く、このページで述べられたことに統合と小さな追加を加えて投稿しました - それはこの投稿の一番下にあります. この時点で投稿を編集していますが、私が提案するものを投稿することは(少なくともピクセルデータの変更を含む私の要件では)書き込み可能なデータを提供するため、より良い方法です(一方、私が理解しているように、提供される方法は以前の投稿によって、この投稿の下部にあるデータへの読み取り専用参照を提供します)。
方法 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. byte の型を宣言しました。
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'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の色をサンプリングするための私の解決策です。
このアプローチは、要求されたピクセルを 1 ピクセルの大きな 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のビットマップ情報を調べて、ピクセルの順序や画像のサイズなどを取得することをお勧めします。
Lajosの答えは私にとってはうまくいきました。ピクセル データをバイト配列として取得するには、次のようにしました。
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 の最適化が発生したため、過去に問題がありました。
与えられたX、Yコーディネーションに基づいて画像データに正しくインデックスを付ける方法がわかりません。誰か知っている?
pixelPosition =(x +(y *((imagewidth)* BytesPerPixel)));
//ピッチは、私が知る限り、このデバイスでは問題ではなく、ゼロにすることができます... //(または数学から引き出されます)。
私のアプリケーションで同様のことを行うために、小さな画面外のCGImageContextを作成し、それにUIImageをレンダリングしました。これにより、一度に多数のピクセルをすばやく抽出することができました。これは、解析しやすい形式でターゲットビットマップを設定し、CoreGraphicsにカラーモデルまたはビットマップ形式間の変換のハードワークを任せることができることを意味します。
ピクセル レベルのアクセス (読み取り/書き込み) を提供するANImageBitmapRepを使用します。