26

の非透明ピクセルのみでのタッチをUIImageView効率的に検出するにはどうすればよいでしょうか?

で表示された下のような画像を考えてみましょうUIImageView。目標は、画像の非透明 (この場合は黒) 領域でタッチが発生した場合にのみジェスチャ認識機能が応答するようにすることです。

ここに画像の説明を入力

アイデア

  • hitTest:withEvent:orをオーバーライドpointInside:withEvent:します。ただし、これらのメソッドはタッチ イベント中に何度も呼び出されるため、このアプローチは非常に非効率的です。
  • 指は 1 ピクセルよりも大きいため、1 つのピクセルが透明かどうかを確認すると、予期しない結果が生じる可能性があります。ヒット ポイントの周囲のピクセルの円形領域をチェックするか、エッジに向かう透明なパスを見つけようとする方がうまくいく場合があります。

ボーナス

  • 画像の外側と内側の透明なピクセルを区別できると便利です。この例では、ゼロ内の透明なピクセルも有効と見なされます。
  • 画像に変換がある場合はどうなりますか?
  • 画像処理をハードウェア アクセラレーションできますか?
4

3 に答える 3

19

これが私の簡単な実装です:( UIImage のピクセルアルファ値の取得に基づく)

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    //Using code from https://stackoverflow.com/questions/1042830/retrieving-a-pixel-alpha-value-for-a-uiimage

    unsigned char pixel[1] = {0};
    CGContextRef context = CGBitmapContextCreate(pixel,
                                                 1, 1, 8, 1, NULL,
                                                 kCGImageAlphaOnly);
    UIGraphicsPushContext(context);
    [image drawAtPoint:CGPointMake(-point.x, -point.y)];
    UIGraphicsPopContext();
    CGContextRelease(context);
    CGFloat alpha = pixel[0]/255.0f;
    BOOL transparent = alpha < 0.01f;

    return !transparent;
}

これは、イメージが と同じ座標空間にあることを前提としていpointます。スケーリングが続く場合はpoint、ピクセル データをチェックする前に を変換する必要がある場合があります。

私にはかなり速く動作するように見えます。私は約測定していた。このメソッド呼び出しに 0.1 ~ 0.4 ミリ秒。それは内部空間を行わず、おそらく最適ではありません.

于 2012-11-08T17:42:55.857 に答える
6

github では、 Ole Begemann によるプロジェクトを見つけることができます。これUIButtonは、ボタンの画像が透明でない場所でのタッチのみを検出するように拡張されています。

UIButtonは のサブクラスであるためUIView、 への適応UIImageViewは簡単です。

お役に立てれば。

于 2012-11-08T15:26:25.760 に答える
3

さて、あなたがそれを本当に速くする必要があるならば、あなたはマスクを事前に計算する必要があります。

抽出する方法は次のとおりです。

UIImage *image = [UIImage imageNamed:@"some_image.png"];
NSData *data = (NSData *) CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
unsigned char *pixels = (unsigned char *)[data bytes];
BOOL *mask = (BOOL *)malloc(data.length);
for (int i = 0; i < data.length; i += 4) {
  mask[i >> 2] = pixels[i + 3] == 0xFF; // alpha, I hope
}
// TODO: save mask somewhere

または、1x1ビットマップコンテキストソリューションを使用してマスクを事前計算することもできます。マスクがあるということは、1回のインデックス付きメモリアクセスのコストで任意のポイントをチェックできることを意味します。

1ピクセルよりも広い領域をチェックする場合、タッチポイントを中心とする円上のピクセルをチェックします。円上の約16ポイントで十分です。

内側のピクセルも検出する:別の事前計算ステップ-マスクの凸包を見つける必要があります。これは、「グラハムスキャン」アルゴリズムhttp://softsurfer.com/Archive/algorithm_0109/algorithm_0109.htmを使用して行うことができます。 次に、マスクのその領域を塗りつぶすか、ポリゴンを保存して、代わりにポリゴンポイントテストを使用します。

最後に、画像に変換がある場合は、ポイント座標を画面空間から画像空間に変換する必要があります。そうすれば、事前に計算されたマスクを確認できます。

于 2012-11-08T19:16:44.633 に答える