2011-10-11 8 views
13

画像(haystack)内に画像()があります。C内の画像内の画像を認識しています。

私は、デスクトップのスクリーンショットを2枚撮ります。フルサイズのもの(haystack)と小さなもの()。私は、干し草の画像をループし、針の画像を見つけようとします。

  1. 捕捉針と干し草の山のスクリーンショット
  2. ループ干し草の山を介して、干し草の山を探して[I] ==針
  3. の最初の画素[2が真である場合:]の最後の画素に2をループ針をhaystack [i]と比較してください。

予想される結果:針の画像は正しい位置にあります。

私は既にいくつかの座標/幅/高さ(A)で動作しています。

しかし、ビットが「オフ」のように見えることがあるので、は一致しません。(B)。

私は間違っていますか?どんな提案も大歓迎です。ありがとう。


var needle_height = 25; 
var needle_width = 25; 
var haystack_height = 400; 
var haystack_width = 500; 

A.例の入力 - マッチ

var needle = screenshot(5, 3, needle_width, needle_height); 
var haystack = screenshot(0, 0, haystack_width, haystack_height); 
var result = findmatch(haystack, needle); 

B.例の入力 - NO一致

var needle = screenshot(5, 5, needle_width, needle_height); 
var haystack = screenshot(0, 0, haystack_width, haystack_height); 
var result = findmatch(haystack, needle); 

1.キャプチャ針と干し草の山の画像

private int[] screenshot(int x, int y, int width, int height) 
{ 
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb); 
Graphics.FromImage(bmp).CopyFromScreen(x, y, 0, 0, bmp.Size); 

var bmd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), 
    ImageLockMode.ReadOnly, bmp.PixelFormat); 
var ptr = bmd.Scan0; 

var bytes = bmd.Stride * bmp.Height/4; 
var result = new int[bytes]; 

Marshal.Copy(ptr, result, 0, bytes); 
bmp.UnlockBits(bmd); 

return result; 
} 

2試合

public Point findmatch(int[] haystack, int[] needle) 
{ 
var firstpixel = needle[0]; 

for (int i = 0; i < haystack.Length; i++) 
{ 
    if (haystack[i] == firstpixel) 
    { 
    var y = i/haystack_height; 
    var x = i % haystack_width; 

    var matched = checkmatch(haystack, needle, x, y); 
    if (matched) 
     return (new Point(x,y)); 
    } 
}  
return new Point(); 
} 

3.代わりに

public bool checkmatch(int[] haystack, int[] needle, int startx, int starty) 
{ 
    for (int y = starty; y < starty + needle_height; y++) 
    { 
     for (int x = startx; x < startx + needle_width; x++) 
     { 
      int haystack_index = y * haystack_width + x; 
      int needle_index = (y - starty) * needle_width + x - startx; 
      if (haystack[haystack_index] != needle[needle_index]) 
       return false; 
     } 
    } 
    return true; 
} 

答えて

2

まず、findmatchループに問題があります。

public Point? findmatch(int[] haystack, int[] needle) 
{ 
    var firstpixel = needle[0]; 

    for (int y = 0; y < haystack_height - needle_height; y++) 
     for (int x = 0; x < haystack_width - needle_width; x++) 
     { 
      if (haystack[y * haystack_width + x] == firstpixel) 
      { 
       var matched = checkmatch(haystack, needle, x, y); 
       if (matched) 
        return (new Point(x, y)); 
      } 
     } 

    return null; 
} 

おそらく問題を解決する必要があること:あなたは、それぞれ右と下から針の幅と高さを減算する必要があるので、あなただけの、配列として干し草の画像を使用しないでください。また、の複数の一致がである可能性があることに注意してください。たとえば、「針」がウィンドウの完全に白い矩形の部分である場合、画面全体に多くの一致が存在する可能性が高くなります。これは可能性がある場合は、最初のものが発見された後に結果を探し続けるために、あなたのfindmatch方法を変更します。

public IEnumerable<Point> FindMatches(int[] haystack, int[] needle) 
{ 
    var firstpixel = needle[0]; 
    for (int y = 0; y < haystack_height - needle_height; y++) 
     for (int x = 0; x < haystack_width - needle_width; x++) 
     { 
      if (haystack[y * haystack_width + x] == firstpixel) 
      { 
       if (checkmatch(haystack, needle, x, y)) 
        yield return (new Point(x, y)); 
      } 
     } 
} 

次に、手動で作成したIDisposableを実装するすべてのオブジェクトを、配置する習慣を維持する必要がありますあなた自身。 BitmapGraphicsは、あなたのscreenshot方法がusing文でこれらのオブジェクトをラップするように変更する必要があることを意味し、そのようなオブジェクトです:

private int[] screenshot(int x, int y, int width, int height) 
{ 
    // dispose 'bmp' after use 
    using (var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb)) 
    { 
     // dispose 'g' after use 
     using (var g = Graphics.FromImage(bmp)) 
     { 
      g.CopyFromScreen(x, y, 0, 0, bmp.Size); 

      var bmd = bmp.LockBits(
       new Rectangle(0, 0, bmp.Width, bmp.Height), 
       ImageLockMode.ReadOnly, 
       bmp.PixelFormat); 

      var ptr = bmd.Scan0; 

      // as David pointed out, "bytes" might be 
      // a bit misleading name for a length of 
      // a 32-bit int array (so I've changed it to "len") 

      var len = bmd.Stride * bmp.Height/4; 
      var result = new int[len]; 
      Marshal.Copy(ptr, result, 0, len); 

      bmp.UnlockBits(bmd); 

      return result; 
     } 
    } 
} 

コードの残りの部分は、それは非常に効率的でないことを発言して、[OKらしいです特定の入力に対して。たとえば、デスクトップの背景として大きなソリッドカラーを使用すると、checkmatchコールが多数発生する可能性があります。

パフォーマンスが重要な場合は、検索を高速化するさまざまな方法を確認することができます(変更されたRabin-Karpのようなものですが、無効な候補がスキップされるようにする既存のアルゴリズムがいくつかありますすぐに)。

+0

それはトリックでした。あなた、本当の天才です。私のコーディングを改善するためのあなたの努力と努力に感謝します。 – fanti

3

完全一致を確認し検索してみてくださいのデスクトップの2つのスクリーンショットを時間間隔で作成すると、スクリーンショットを1回撮り、同じビットマップソースから「針」と「干し草」をカットします。そうしないと、スクリーンショットが撮られた2つの瞬間にデスクトップコンテンツが変更される危険があります。

EDIT:それでも問題が発生した場合は、イメージをファイルに保存し、デバッガを使用してそのファイルを再試行して再現性のある状況にします。

+0

彼が見つけようとしているイメージからオブジェクトを切り取るために、少しばかげているように見えます。鶏卵? – Druegor

+1

@Druegor:私は、スクリーンショットの例は、彼が一致が見込まれると予想されるOPのテストケースにすぎないと考えています。 –

+0

こんにちは、Doc Brown。それは有効な提案です。私はそれも考えていて、スクリーンのこの領域を変更するものは何もないことをあなたに保証することができます。それにもかかわらず私は試みます。 – fanti

2

haystack_indexまたはneedle_indexの方程式は正しいとは思いません。ビットマップデータをコピーするときにオフセットを考慮したようですが、バイト位置を計算するときはビットマップのStrideを使用する必要があります。

また、Format32bppArgb形式では、1ピクセルあたり4バイトが使用されます。 1ピクセルあたり1バイトと仮定しているようです。

ここで私は、これらの方程式を支援するために使用されるサイトの:http://www.bobpowell.net/lockingbits.htm

Format32BppArgb:XおよびY座標が与えられると、 画素の最初の要素のアドレスがSCAN0 +(Y軸*ストライド)+(Xであるが* 4)。青いバイトを指します。 に続く3バイトには、緑、赤、およびアルファバイトが含まれています。

+1

変数の名前が 'bytes'であり、4で除算されていることも混乱しました。しかし、' bytes'は実際には 'int'配列の長さです。そのアルゴリズムは個々のコンポーネントではなく、完全なRGBAピクセルを比較します。 – Groo

関連する問題