OpenGLでバイナリイメージのコーナーを検出する方法は?


13

次のような160x120のバイナリイメージがあります。

元の画像

これらの白い塊の角を検出したいと思います。それらは以前は数学的形態によって閉じられていたため、内側に角はありません。この特定のケースでは、次のような16のコーナーが必要です。

コーナー検出の例

私の最初の試みは、goodFeaturesToTrackFASTなどのOpenCV関数を使用することでしたが、それらは特に低速です(さらに、FASTは非常に不安定です)。私のアイデアは、ソースイメージがGPUから取得されるため、GPUでそのような計算を行うことです。私はそのようなシェーダーの書き方に関するアイデアをウェブで探しました(OpenGL ES 2.0を使用しています)が、具体的なものは見つかりませんでした。このようなアルゴリズムをどのように開始できるか考えていますか?


2
FASTは遅いですか?:)
エンドリス

1
はい、面白いでしょう?実際に、それはより速くSURFやSIFTなどの先例アルゴリズムよりもだが、それは、CPU上で実行されるように、まだ十分に速くはない別の画像から、非常に不安定な、あまり正確だと
ステファンPéchard

すべてのフレームでこれらを正確に検出することはどれほど重要ですか?長方形はどれくらい速く動きますか?ほとんどのフレームでコーナーを検出し、アルゴリズムが欠落しているフレームでコーナーを補間しても大丈夫ですか?
ジャスティス

@justisよく、私が今それを行う方法(OpenCVのcvFindContours()およびcvApproxPoly()関数の使用による)は、時間の経過とともにあまり安定していないので、ローパスフィルターで結果をフィルター処理し、ラグを導入します。補間を使用すると、より安定した結果が得られると思いますか?
ステファンペーシャル

回答:


3

どのサイズの画像を操作していますか?どのフレームレートで?どのハードウェアで?FASTは、私の経験上、かなり高速です。

また、FASTがROI検出器として使用され、ROIでgoodFeaturesToTrackが実行され、画像全体でgFTTのペナルティを実行することなく安定性が向上することが確認されています。

「ハリス」のコーナー検出器は、それは非常に簡単な操作で構成されているとして、潜在的に非常に速いもある(例えばピクセルあたりなしのsqrt()!) -ではないgFTTとして安定したとして、しかし、おそらくもっとそうFAST超えます。

(GPU実装に関しては、グーグル gpu cornerは非常に多くのリンクを提供しているように見えますが、それらがどれほど適切かはわかりません。FPGAで実装する傾向があります。)


私の画像は160x120、おそらく30fpsですが、もちろんiPhoneではもっとやらなければならないことがたくさんあります:-)このようなデバイスでFASTを非常に迅速に実装するアプリを見ましたが、それは単なるデモでしたそれをしています...だから私はGPUベースのソリューションを探しています。
ステファンペーシャル

15

私はたまたま、OpenGL ES 2.0でHarrisコーナー検出を使用してこのようなものを実装していましたが、完全には終わっていませんが、これまでのシェーダーベースの実装を共有すると思いました。これはiOSベースのオープンソースフレームワークの一部として行ったため、特定のステップがどのように機能するかについて興味がある場合は、コードをチェックアウトできます。

これを行うには、次の手順を使用します。

  • RGB値とベクトル(0.2125、0.7154、0.0721)のドット積を使用して、画像を輝度値に縮小します。
  • XおよびY導関数を計算するには、現在のピクセルの左右および上下のピクセルから赤チャネル値を減算します。次に、赤チャネルで2乗したx導関数、緑チャネルで2乗したY導関数、および青チャネルでXとY導関数の積を保存します。このためのフラグメントシェーダーは次のようになります。

    precision highp float;
    
    varying vec2 textureCoordinate;
    varying vec2 leftTextureCoordinate;
    varying vec2 rightTextureCoordinate;
    
    varying vec2 topTextureCoordinate; 
    varying vec2 bottomTextureCoordinate;
    
    uniform sampler2D inputImageTexture;
    
    void main()
    {
     float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;
     float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;
     float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;
     float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;
    
     float verticalDerivative = abs(-topIntensity + bottomIntensity);
     float horizontalDerivative = abs(-leftIntensity + rightIntensity);
    
     gl_FragColor = vec4(horizontalDerivative * horizontalDerivative, verticalDerivative * verticalDerivative, verticalDerivative * horizontalDerivative, 1.0);
    }
    

    ここで、変動は各方向のオフセットテクスチャ座標にすぎません。頂点シェーダーでこれらを事前計算して、これらのモバイルGPUで有名な低速の依存テクスチャ読み取りを排除します。

  • この微分画像にガウスぼかしを適用します。水平方向と垂直方向に別々のぼかしを使用し、ハードウェアテクスチャフィルタリングを利用して、各パスで5回のテクスチャ読み取りのみで9ヒットのぼかしを行いました。このStack Overflowの回答でこのシェーダーについて説明します

  • ぼやけた入力微分値を使用して、実際のHarrisコーナー検出計算を実行します。この場合、私は実際にアリソンノーブルが博士号で説明した計算を使用しています。論文「画像表面の説明」。これを処理するシェーダーは次のようになります。

    varying highp vec2 textureCoordinate;
    
    uniform sampler2D inputImageTexture;
    
    const mediump float harrisConstant = 0.04;
    
    void main()
    {
     mediump vec3 derivativeElements = texture2D(inputImageTexture, textureCoordinate).rgb;
    
     mediump float derivativeSum = derivativeElements.x + derivativeElements.y;
    
     // This is the Noble variant on the Harris detector, from 
     // Alison Noble, "Descriptions of Image Surfaces", PhD thesis, Department of Engineering Science, Oxford University 1989, p45.     
     mediump float harrisIntensity = (derivativeElements.x * derivativeElements.y - (derivativeElements.z * derivativeElements.z)) / (derivativeSum);
    
     // Original Harris detector
     //     highp float harrisIntensity = derivativeElements.x * derivativeElements.y - (derivativeElements.z * derivativeElements.z) - harrisConstant * derivativeSum * derivativeSum;
    
     gl_FragColor = vec4(vec3(harrisIntensity * 10.0), 1.0);
    }
    
  • ローカル非最大抑制を実行し、しきい値を適用して、通過するピクセルを強調表示します。次のフラグメントシェーダーを使用して、中央ピクセルの近傍で8ピクセルをサンプリングし、そのグループ化で最大かどうかを識別します。

    uniform sampler2D inputImageTexture;
    
    varying highp vec2 textureCoordinate;
    varying highp vec2 leftTextureCoordinate;
    varying highp vec2 rightTextureCoordinate;
    
    varying highp vec2 topTextureCoordinate;
    varying highp vec2 topLeftTextureCoordinate;
    varying highp vec2 topRightTextureCoordinate;
    
    varying highp vec2 bottomTextureCoordinate;
    varying highp vec2 bottomLeftTextureCoordinate;
    varying highp vec2 bottomRightTextureCoordinate;
    
    void main()
    {
        lowp float bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).r;
        lowp float bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;
        lowp float bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;
        lowp vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
        lowp float leftColor = texture2D(inputImageTexture, leftTextureCoordinate).r;
        lowp float rightColor = texture2D(inputImageTexture, rightTextureCoordinate).r;
        lowp float topColor = texture2D(inputImageTexture, topTextureCoordinate).r;
        lowp float topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).r;
        lowp float topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).r;
    
        // Use a tiebreaker for pixels to the left and immediately above this one
        lowp float multiplier = 1.0 - step(centerColor.r, topColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, topLeftColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, leftColor);
        multiplier = multiplier * 1.0 - step(centerColor.r, bottomLeftColor);
    
        lowp float maxValue = max(centerColor.r, bottomColor);
        maxValue = max(maxValue, bottomRightColor);
        maxValue = max(maxValue, rightColor);
        maxValue = max(maxValue, topRightColor);
    
        gl_FragColor = vec4((centerColor.rgb * step(maxValue, centerColor.r) * multiplier), 1.0);
    }
    

このプロセスは、オブジェクトから次のようなコーナーネスマップを生成します。

コーナーネスマップ

非最大抑制およびしきい値設定に基づいて、次のポイントがコーナーとして識別されます。

特定されたコーナー

このフィルターに適切なしきい値を設定すると、この画像の16個の角すべてを識別できますが、実際にはオブジェクトの実際のエッジの内側に角を1ピクセル程度配置する傾向があります。

iPhone 4では、このコーナー検出はカメラからの640x480フレームのビデオで20 FPSで実行でき、iPhone 4Sはそのサイズのビデオを60+ FPSで簡単に処理できます。これは、このようなタスクのCPUバウンド処理よりもかなり速いはずですが、現在、ポイントを読み取るプロセスはCPUバウンドであり、本来より少し遅いです。

これを実際に見たい場合は、フレームワークのコードを取得して、付属のFilterShowcaseの例を実行してください。ハリスコーナー検出の例は、デバイスカメラからのライブビデオで実行されますが、先ほど述べたように、コーナーポイントの読み取りは現在CPUで行われているため、実際にはこれが遅くなっています。このためにも、GPUベースのプロセスに移行しています。


1
非常に素晴らしい!私はあなたのフレームワークをgithubでフォローしています。本当に面白いようです、おめでとうございます!
ステファンペーシャル

実際にコーナー座標をCPUに戻す方法の例はありますか?スマートGPUの方法はありますか、それともリードバックが必要で、返されたビットマップを介してCPUでループしてマークされたピクセルを探しますか?
カシモンド14年

@Quasimondo- コーナー検出用のピクセルに対するCPUバウンドの反復を回避するために、ポイント抽出にヒストグラムピラミッドの使用に取り組んでいます:tevs.eu/files/vmv06.pdf。最近少し気が散ってしまったので、これはまだ完了していませんが、すぐにしたいと思います。
ブラッドラーソン14年

こんにちは、@ BradLarson、これは非常に古いスレッドであることを知っており、ご回答いただきありがとうございます。GPUImageフレームワークでKGPUImageHarrisCornerDetection.mをチェックしました。画像から角の位置を抽出するには、glReadPixelsを使用して画像をバッファーに読み込み、バッファーでループしてcolotByte> 0の配列を配列に保存しました。これをすべてGPUで行う方法はありますが、バッファとループで画像を読み取る必要はありませんか?
サヒルバジャイ

1
@SahilBajaj-私が見た(まだ実装する時間がなかった)1つの手法は、ヒストグラムピラミッドを使用して、このような疎な画像からポイントを高速に抽出することです。それはこれを大幅にスピードアップします。
ブラッドラーソン

3

Shi-TomasiやMoravecのような「堅牢な」コーナー検出器は非常に遅いことで有名です。ここでそれらをチェックしてください-http://en.wikipedia.org/wiki/Corner_detection おそらく、FASTが唯一の十分な軽量コーナー検出器です。非最大抑制を行うことでFASTを改善できます-最高の「コーナー」スコアを持つFAST出力を選択しました(コーナースコアとしてShi-TomasiとMoravecを含む、いくつかの直観的な方法があります)また、いくつかのFAST検出器から選択できます- FAST-5からFAST-12およびFAST_ER(最後の1つはおそらくモバイルには大きすぎます)もう1つの方法は、FASTを生成することです。作成者サイトからFASTコードジェネレーターを取得し、可能性のある画像のセットでトレーニングします。 http://www.edwardrosten.com/work/fast.html


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.