web-dev-qa-db-ja.com

UIImageのピクセルアルファ値を取得する

現在、UIImageViewでピクセルのアルファ値を取得しようとしています。 [UIImageView image]からCGImageを取得し、これからRGBAバイト配列を作成しました。アルファは事前に乗算されています。

CGImageRef image = uiImage.CGImage;
NSUInteger width = CGImageGetWidth(image);
NSUInteger height = CGImageGetHeight(image);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
rawData = malloc(height * width * 4);
bytesPerPixel = 4;
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), image);
CGContextRelease(context);

次に、UIImageViewの座標を使用して、指定されたアルファチャネルの配列インデックスを計算します。

int byteIndex = (bytesPerRow * uiViewPoint.y) + uiViewPoint.x * bytesPerPixel;
unsigned char alpha = rawData[byteIndex + 3];

ただし、期待する値が得られません。画像の完全に黒い透明な領域の場合、アルファチャネルの値がゼロ以外になります。 UIKitとコアグラフィックスの間の座標を変換する必要がありますか?つまり、y軸は反転していますか?または、事前に乗算されたアルファ値を誤解しましたか?

更新:

@NikolaiRuheの提案がこれの鍵でした。実際、UIKit座標とCoreGraphics座標の間で変換する必要はありませんでした。ただし、ブレンドモードを設定した後、私のアルファ値は私が期待したものでした。

CGContextSetBlendMode(context, kCGBlendModeCopy);
35
teabot

はい、CGContextのy軸は上向きですが、UIKitでは下向きです。 ドキュメントを参照

コードを読んだ後に編集する:

また、以前にコンテキストのバッファにあったものではなく、画像のアルファ値が必要なため、画像を描画する前に置換するようにブレンドモードを設定する必要があります。

CGContextSetBlendMode(context, kCGBlendModeCopy);

考えてから編集:

可能な限り最小のCGBitmapContext(1x1ピクセル?多分8x8?試してみてください)を構築し、コンテキストを目的のコンテキストに変換することで、ルックアップをはるかに効率的に行うことができます描画前の位置:

CGContextTranslateCTM(context, xOffset, yOffset);
10
Nikolai Ruhe

必要なのが単一ポイントのアルファ値だけである場合、必要なのはアルファのみの単一ポイントバッファだけです。私はこれで十分だと信じています:

// assume im is a UIImage, point is the CGPoint to test
CGImageRef cgim = im.CGImage;
unsigned char pixel[1] = {0};
CGContextRef context = CGBitmapContextCreate(pixel, 
                                         1, 1, 8, 1, NULL,
                                         kCGImageAlphaOnly);
CGContextDrawImage(context, CGRectMake(-point.x, 
                                   -point.y, 
                                   CGImageGetWidth(cgim), 
                                   CGImageGetHeight(cgim)), 
               cgim);
CGContextRelease(context);
CGFloat alpha = pixel[0]/255.0;
BOOL transparent = alpha < 0.01;

UIImageを毎回再作成する必要がない場合、これは非常に効率的です。

2011年12月8日編集:

コメント提供者は、特定の状況下では画像が反転する可能性があると指摘しています。私はこれについて考えていましたが、このようにUIImageを直接使用してコードを記述しなかったことを少し残念に思います(理由は、当時私がUIGraphicsPushContext):

// assume im is a UIImage, point is the CGPoint to test
unsigned char pixel[1] = {0};
CGContextRef context = CGBitmapContextCreate(pixel, 
                                             1, 1, 8, 1, NULL,
                                             kCGImageAlphaOnly);
UIGraphicsPushContext(context);
[im drawAtPoint:CGPointMake(-point.x, -point.y)];
UIGraphicsPopContext();
CGContextRelease(context);
CGFloat alpha = pixel[0]/255.0;
BOOL transparent = alpha < 0.01;

それでフリッピングの問題は解決したと思います。

36
matt

長方形の境界ボックスではなく、画像データのアルファ値を使用してスプライト間の衝突検出を行う方法を調査しているときに、この質問/回答を見つけました。コンテキストはiPhoneアプリです...上記の提案された1ピクセルの描画を実行しようとしていますが、これを機能させるのにまだ問題がありますが、画像自体のデータを使用してCGContextRefを作成する簡単な方法を見つけました。ここでヘルパー関数:

CGContextRef context = CGBitmapContextCreate(
                 rawData, 
                 CGImageGetWidth(cgiRef), 
                 CGImageGetHeight(cgiRef), 
                 CGImageGetBitsPerComponent(cgiRef), 
                 CGImageGetBytesPerRow(cgiRef), 
                 CGImageGetColorSpace(cgiRef),
                 kCGImageAlphaPremultipliedLast     
    );

これにより、上記のサンプルのすべての醜いハードコーディングがバイパスされます。最後の値はCGImageGetBitmapInfo()を呼び出すことで取得できますが、私の場合は、ContextCreate関数でエラーが発生した画像から値を返します。ここに記載されているように、特定の組み合わせのみが有効です: http://developer.Apple.com/qa/qa2001/qa1037.html

これがお役に立てば幸いです。

3
Owyn

UIKitとコアグラフィックスの間の座標を変換する必要がありますか?つまり、y軸は反転していますか?

それが可能だ。 CGImageでは、ピクセルデータは英語の読み取り順序(左から右、上から下)です。したがって、配列の最初のピクセルは左上です。 2番目のピクセルは、一番上の行の左から1つです。等.

あなたがその権利を持っていると仮定すると、ピクセル内の正しいコンポーネントを見ていることも確認する必要があります。おそらく、RGBAを期待しているが、ARGBを要求している、またはその逆です。または、バイト順序が間違っている可能性があります(iPhoneのエンディアンが何であるかはわかりません)。

または、事前に乗算されたアルファ値を誤解しましたか?

それはそれのように聞こえません。

知らない人のために:事前乗算とは、色成分がアルファによって事前乗算されることを意味します。アルファ成分は、カラー成分が事前に乗算されているかどうかに関係なく同じです。色成分をアルファで割ることにより、これを逆にする(事前乗算を解除する)ことができます。

3
Peter Hosey