對"QQGame-你們來找茬"的輔助工具的改進

  【前言】最近在博客園首頁上看到有「你們來找茬」這個遊戲(此遊戲爲找出兩個相近圖片的不一樣點)外掛的相關帖子,因此這裏我也翻看了我以前(2009年5月)的寫的一個簡單的輔助程序(採用 VC6 開發的)。我在當時的寫法是圖快速和簡單,代碼效率上不是最高的,因此我如今使用更加合理的方法將其改進(採用 VC2005 開發)。由於發表時間臨近春節,即將回籍貫老家,因此行文和排版等不免倉促,有待在未來繼續改進。html

 

  以前我在 2009 年 5 月寫的文章爲:《快速「美女找茬」(輔助工具)》多線程

 

  解決方案的主要步驟是相同的,找到遊戲窗口,而後將遊戲窗口的內容截圖,而後把不一樣點用一個透明圖層的方式,對齊覆蓋到遊戲窗口的相應位置。這裏並無繼續去幫助用戶作點擊,由於這一步須要把圖像上的差別點集合劃分紅五個區域,這須要進一步處理(留待未來考慮)。ide

 

  相比以前的作法,本次改進內容包括:函數

  (1)增長配置文件記錄遊戲窗口的佈局信息(這些信息爲截圖後在 Photoshop 中測量得出),這樣在遊戲佈局由於升級等緣由發生改變時,程序不須要進行從新編譯,普通用戶只須要自行修改配置文件便可。工具

  (2)檢測差別像素點時,直接對圖像像素數據進行分析,這樣的效率會更高。(因爲當前的 PC 已經普及多核 CPU,因此這一步也採用了多線程技術。)佈局

  (3)增長全局熱鍵的配置,由於發出一個命令時,鍵盤操做的速度比鼠標這種定點設備操做 GUI 的速度更快。post

 

  在遊戲時,效果以下圖所示:url

 

  

 

  左側是一個「鼠標穿透」的半透明圖層窗口(稱之爲「提示窗口」),因此用戶能夠直接在左側的紅色方塊內部點擊便可。用戶能夠在程序位於通知欄的圖標右鍵菜單中,選擇設置,而後設置全局性熱鍵,提示窗口的不透明度等信息。設置對話框以下圖:spa

 

  

 

  接下來主要分析以上改進(主要是 2 和 3 )的實現。操作系統

  改進(1)比較容易,只須要增長一個配置文件便可,其主要內容以下:

; 連連看遊戲窗口信息
[GameWnd]
Class=#32770
Text=你們來找茬

;圖片 1 的位置(客戶區座標)
[Image1]
Left=10
Top=186
Right=390
Bottom=471

;圖片 2 的位置(客戶區座標)
[Image2]
Left=403
Top=186

 

  以上內容主要是用於存儲遊戲窗口的類名和標題,以及兩幅圖片在遊戲窗口中的客戶區座標,比較直觀。很顯然,圖片2的右下角座標無需給出,由於兩個圖片很顯然是相同大小的。

 

  對於(2),我首先能想到的是,以前使用了 GetPixel / SetPixel 函數,儘管是對內存 DC 操做,但這樣的效率可能仍然是不夠高的。因此這一次我改成直接對像素數據塊操做。同時,這一次我決定使用對兩個圖像的相同位置的像素數據直接用異或操做來處理,同時也把對應的指針和數據單位擴展爲 4 個字節的 DWORD 類型(而不是單獨操做每一個像素的 R G B 通道),這樣將會加快處理速度。這樣作之後,若是咱們直接把異或後的結果顯示出來,效果以下圖(提示窗口已經爲徹底不透明):

 

  

 

  直接顯示異或結果,很顯然圖像相同的部分會變爲全黑色(由於相同的數據的異或結果爲零),不一樣的位置會顯示出一些比較鮮豔的色彩。這樣的顯示效果其實並不太好,不太容易辨別,因此我仍是把結果變爲紅色。這裏有兩個問題,致使我得出最終的解決方案。

  (2.1)爲了讓我對兩個數據進行異或時,他們的地址能以 DWORD 對齊。因此我對遊戲窗口抓圖時,把兩個圖片分別複製到一個上下排列的內存位圖(以下,其尺寸爲單個圖片寬度 * 二倍圖片高度),而後對這個內存位圖的像素數據進行異或。若是不這樣截圖,那麼直接對遊戲窗口原樣圖片進行操做,因爲圖片在遊戲窗口中的位置對咱們來講是「隨機」的,那麼就沒法保證像素數據能正好對齊到 DWORD(這個問題在 24 bpp 位圖時存在,但當採用 32 bpp 內存位圖時,任何像素都必定會對齊到 DWORD,這個憂慮已經不存在了,但對於提升複製屏幕圖像的效率而言,這樣截圖依然是應該的,由於在兩幅圖像之外的部分是咱們不感興趣的,也就不必所有截取)。(注意這裏有一個基本能夠確定的前提條件,系統在分配位圖時,其像素數據的起始地址必定是對齊到 DWORD 的。)

 

 圖片 1
 圖片 2

 

 

  (2.2)最開始我建立的內存位圖爲 24 BPP,這樣一個像素佔據 3 Bytes,而一個 DWORD 佔據 4 Bytes,因此若是是 24 BPP,即便找到一個操做結果不爲零,要定位到像素或者對此像素設置爲特定顏色(例如紅色)也會比較麻煩(須要作換算)。因此這裏我建立內存位圖時把 BPP 改成 32,這樣每一個 DWORD 就是一個像素,這樣就能對圖片上的差別像素很容易的設爲特定顏色,例如紅色就是 0x00FF0000 (這個數字書寫出來的順序是,0x-AA-RR-GG-BB,與 little - endian 的存儲順序相反)。使用 CreateDIBSection 建立一個內存位圖,代碼比較簡單。

 

  (2.3)執行異或時,採用多線程處理。若是是單個線程執行這個計算,那麼對於圖像數據的輸入來講必然是串行的。爲了發揮當前的多核 CPU 的優點,因此這裏把此異或計算採用多線程執行,把圖片從上到下切分爲幾個高度較小的橫條(橫條的寬度等於圖片寬度,個數等於要建立的線程數),每一個線程分配到圖片的其中一個橫條。因爲幾個橫條部分的數據彼此獨立,不會發生數據相關性,因此能夠直接並行處理,只須要等全部線程任務完成後(WaitForMultipleObjects),再把內存位圖刷新到窗口上便可。如下是線程過程代碼(像素數據的起始地址在建立位圖時已經被轉換爲 LPDWORD):

 

  備註:這裏的任務切分,爲從上到下把圖像進行細分,一是遍歷像素的方便,二是同時 CPU 對「數據局部性」的偏好,這樣能夠保持比較理想的 cache 命中率。在這個問題中,還能夠考慮可否利用上 Intel CPU 的 MMX 和 SSE2 特性,進一步提升處理的並行度,以提升處理效率。(2014年5月4日 補充 -- hoodlum1980)

 

DWORD WINAPI ThreadEntry(LPVOID lpParameter)
{
    int count, i, lineSize;
    LPTASK pTask = (LPTASK)lpParameter;

    //DWORD Per Line;
    lineSize = g_AppData.nStrideLayer / sizeof(DWORD);

    //Total Count of DWORDS;
    count = (pTask->nEnd - pTask->nStart) * lineSize;

    //[End, Start); p1 - Image1; p2 - Image2
    LPDWORD p1 = g_AppData.lpBitsLayer
        + (g_Layout.nImageHeight * 2 - 1 - pTask->nEnd) * lineSize;

    LPDWORD p2 = g_AppData.lpBitsLayer
        + (g_Layout.nImageHeight - 1 - pTask->nEnd) * lineSize;

    for(i = 0; i < count; i++)
    {
        *p1 = *p1 ^ *p2;

        //0xAARRGGBB
        if(*p1) *p1 = 0x00FF0000;
        ++p1;
        ++p2;
    }
    return 0;
}

 

  採用以上方法以後,執行速度有明顯提升,在個人臺式機,CPU 爲 I7, 16GB 內存,操做系統爲 WIN7,以前的程序檢測一次的執行速度爲 260 ms 左右,改進後的程序的執行速度爲 15 ~ 16 ms(並且常常爲 0 ms,讓我以爲難以想象,這裏我採用的只是 GetTickCount 相減獲得的粗略估算,沒有考慮 CPU 調度的影響,而且這個函數自己就對精確性的要求不是很高),速度上改進前大約爲改進後的 16 倍。從用戶體驗角度看,改進前求解有少量延遲感,改進後速度太快感覺不到延遲。因爲速度太快,因此我放棄了本來想在檢測過程當中切換通知欄圖標的想法。

 

  最後是兩個程序的可執行文件的下載連接(採用 VS2005 ,  C++ 開發):

  http://files.cnblogs.com/hoodlum1980/FindItEx_Bin.zip

 

以後的工做則是模擬點擊功能。根據判斷結果把差別像素分解成 5 個集合,而後在這些集合所在位置單擊一次。這將會進一步加快遊戲完成速度。這一步屬於相對簡單的圖像分析處理任務,留待未來考慮。

相關文章
相關標籤/搜索