2011-10-11 47 views
13

我想在圖像(乾草堆)內找到圖像()。在C中識別圖像內的圖像#

爲了讓事情變得簡單,我拍了兩張桌面截圖。一個全尺寸(乾草堆)和一個小()。然後,我通過乾草堆圖像循環,嘗試找到針圖像。

  1. 捕獲針和乾草堆截圖
  2. 遍歷大海撈針,尋找出草堆[I] ==針的第一像素
  3. [2.若爲真:]通過第二到最後一個像素循環針並將其與乾草堆比較[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; 
     } 
    } 
} 

的代碼的其餘部分似乎確定,與這句話,它不會是非常有效的對於某些輸入。例如,您的桌面背景可能會有很大的純色,這可能會導致很多checkmatch調用。

如果性能是你的興趣,你可能要檢查不同的方式來加快搜索(有點像修改Rabin-Karp想到的,但我肯定有它確保無效候選人跳過一些現有的算法立即)。

+0

這個技巧。先生,你是一位真正的天才。非常感謝您的幫助,以及您如何改進編碼的努力。 – fanti

3

相反的以兩個時間間隔製作桌面的兩個屏幕截圖,我會截取一次屏幕截圖並從這些相同的位圖源中剪下「針」和「乾草堆」。否則,在拍攝屏幕截圖的兩個時刻之間,您有可能會更改桌面內容。

編輯:當你的問題仍然發生之後,我會嘗試將圖像保存到一個文件,並使用您的調試器再次使用該文件,給你一個可重現的情況。

+0

似乎有點傻,他說要從他試圖找到的圖像中剪出物體。雞蛋? – Druegor

+1

@Druegor:我懷疑截圖的例子只是OP的測試用例,他希望找到一個匹配項。 –

+0

你好,布朗博士。這是一個有效的建議。我也想到了這一點,並可以向你保證沒有任何東西會改變屏幕的這個區域。不過我會盡力的。 – fanti

2

我不認爲你的公式爲haystack_indexneedle_index是正確的。看起來您在複製位圖數據時考慮了Scan0偏移量,但在計算字節位置時需要使用位圖的Stride

此外,Format32bppArgb格式使用每個像素4個字節。它看起來像你假設每個像素1個字節。

這是我用來幫助這些方程的網站:http://www.bobpowell.net/lockingbits.htm

Format32BppArgb:鑑於X和Y座標,在 像素的第一個元素的地址是SCAN0 +(Y *步幅)+(X * 4)。這指向藍色字節。三個字節後面的 包含綠色,紅色和字母字節。

+1

我也被這個事實弄糊塗了,變量被命名爲'bytes',並且被4除。但'bytes'實際上是一個'int'數組的長度。所以該算法比較了完整的RGBA像素,而不是單個組件。 – Groo