基於VS+Opencv2.4.10的微信跳一跳輔助工具

說明:最近微信的跳一跳小程序可謂火了一把,不是因爲它本身多好玩,而是有大部分的程序員們加入其中,利用各種領域方法,實現了微信跳一跳的外掛,分數輕鬆上千或上萬。之前也看了基於Python開源的代碼,GitHub上現在的star已經快超過1W了,簡直不敢想。趁着今天禮拜天,在實驗室中也簡單的實現了一下微信跳一跳的輔助工具,精度還不夠高,我跑了一下才到90,純屬娛樂好玩的,後期再繼續改進,主要是依賴C++來實現了一下。
環境: Win10+VS2012+Opencv2.4.10+ADB工具
環境的搭建請查閱相關資料!

主要思路:
通過adb圖像獲取部分大家可以查閱相關資料,代碼也很簡單:

adb shell screencap -p /sdcard/autojump.png
adb pull /sdcard/autojump.png

利用上面兩行代碼即可將手機當前的屏幕進行截圖並且上傳到工程文件路徑下。

首先就是在上傳的autojump.png圖片上進行模板匹配,匹配出小人,並計算小人的座標;如上圖所示,框出來的用紅色箭頭所指即爲小人的座標!
然後就是通過Canny()函數進行圖像的邊緣檢測,這裏使用的閾值爲5,10基本可以檢測出所有邊緣信息;
然後根據一般下一個要跳的地點始終在小人的左半屏或又半屏部分這一先驗知識,來進行查找範圍的確定,進行行掃描,掃描到的第一個值爲255的即返回當前座標值;然後通過計算與小人座標的距離即可得到下一步要跳躍的距離,(注:本代碼中在下一個座標的縱座標進行+50處理,由於本文中只利用了一個關鍵點進行測試的,這樣做是顯然不合理的,接下來可以再利用第二個關鍵點進行下一個跳躍目標中心點的計算),由於本人手機是1080*1920的所以再得到距離過後乘以一個跳躍係數1.35,(這裏不同分辨率的手機係數是不一樣的),這樣就得到了跳躍按壓時間,從而通過system()命令進行調用ADB工具進行與手機通訊實現模擬人的點擊。本文僅僅是簡單的實現了一下看看效果,如果想跑高分還得進行代碼的優化與更改!其次因爲每次按壓的地點肯定是不一樣的,而本文也採用簡單的同一位置按壓,這樣做很容易被騰訊反作弊給查出來的,所以這裏可以添加一個隨機數從而可以簡單的避免位置重複!

代碼如下:

/*
    時間:2018-1-7
    地點:SHNU
    功能:wechat簡單跳一跳C++代碼的實現,有待改進,僅供學習之用!歡迎大家提出新算法
*/

#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>

using namespace cv;
using namespace std;

//全局變量定義區
Mat srcImage;
Mat dstImage;
Mat Character;
//get_screenshot();獲取手機上的圖像
void get_screenshot();
//Canny_Dec();邊緣檢測
void Canny_Dec(Mat& srcImage);
//獲取Character座標
Point get_Character_Loc(Mat& srcImage,Mat& Tem_img);
//獲取下一個要跳的點
Point get_next_img_Loc(Mat& srcImage,Point& Character_Loc);
//計算距離
int get_distance(Point& first_point,Point& next_point);
//跳躍
void jump(int&g_distance);
int main(int argc,char** argv)
{
    system("color 3F");
    while (true)
    {
    get_screenshot();
    srcImage = imread("autojump.png");
    dstImage = srcImage.clone();
    Character = imread("./Template/character.png");
    //imshow("Character",Character);
    //cvtColor(srcImage,srcImage,CV_BGR2GRAY);
    Point next_p = get_Character_Loc(srcImage,Character);
    cout<<"next_p:"<<1111<<endl;

    Point get_next = get_next_img_Loc(srcImage,next_p);

    int g_distance = get_distance(next_p,get_next);

    jump(g_distance);
    //cout<<"get_next_img_Loc:"<<get_next<<endl;
    circle(dstImage,get_next,8,Scalar(0,221,2));
    //imshow("test",dstImage);
    imwrite("Canny.png",dstImage);
    _sleep(1500);
    }
    return 0;
}

void get_screenshot()
{
    system("adb shell screencap -p /sdcard/autojump.png");
    system("adb pull /sdcard/autojump.png");
}

Point get_Character_Loc(Mat& srcImage,Mat& Tem_img)
{
    matchTemplate(srcImage,Tem_img,dstImage,CV_TM_SQDIFF);
    double minVal,maxVal;
    Point minLoc,maxLoc,matchLoc;
    minMaxLoc(dstImage,&minVal,&maxVal,&minLoc,&maxLoc,Mat());
    matchLoc = minLoc; //matchLoc是最佳匹配的區域左上角點

    rectangle(srcImage,Rect(matchLoc,Size(Character.cols,Character.rows)),Scalar(255,255,0),1,8,0);
    //Canny(srcImage,srcImage,1,10);
    putText(srcImage,"Wang",Point(matchLoc.x+Character.cols*0.5,matchLoc.y+Character.rows),1,2,Scalar(0,0,255));//畫出Character小人的座標
    return Point(matchLoc.x+Character.cols*0.5,matchLoc.y+Character.rows);
}

Point get_next_img_Loc(Mat& srcImage,Point& Character_Loc)
{
    cout<<"get_next_img_Loc"<<endl;
    cvtColor(srcImage,srcImage,CV_BGR2GRAY);
    Canny(srcImage,srcImage,5,10);
    imwrite("get_next_img_Loc.png",srcImage);
    //imshow("get",srcImage);
    cout<<"Character_Loc.x:"<<Character_Loc.x<<endl;
    if(Character_Loc.x < 540)
    {
        for(int j = int(srcImage.rows*0.2);j<int(srcImage.rows*0.8);j++)
        {
            uchar* data = srcImage.ptr<uchar>(j);
            for(int i = 1079;i > 540 ;i--)
            {

                if(data[i] == 255)
                {
                    return Point(i,j);
                    //cout<<"Point:"<<Point(i,j)<<endl;
                }
            }
        }
    }
    else
    {
        for(int j = int(srcImage.rows*0.2);j<int(srcImage.rows*0.8);j++)
        {
            uchar* data = srcImage.ptr<uchar>(j);
            for(int i = 0;i<540;i++)
            {
                if(data[i] == 255)
                    return Point(i,j);
            }
        }
    }
}

int get_distance(Point& first_point,Point& next_point)
{
    int A = first_point.x - next_point.x;
    int B = first_point.y - (next_point.y+50);
    return int(pow(pow(A,2)+pow(B,2),0.5));
}

void jump(int&g_distance)
{
    char AA[50];
    int distance_ = g_distance * 1.35;
    sprintf(AA,"adb shell input swipe 320 410 320 410 %d",distance_);
    cout<<AA<<endl;
    system(AA);
}

邊緣檢測圖片:
這裏寫圖片描述

下一個關鍵點定位:
這裏寫圖片描述
上圖中畫出的小圈圈,不太清晰,將就着看下!小菜水平有限,僅僅是基於好玩就弄了下!

結果:用開源的隨便跑跑幾百,自己的怎麼跑,90 【累哭】
這裏寫圖片描述

華麗的分割線————————————————

又來更新一下啦!
晚上不想看論文就想到了之前的跳一跳,經過一邊顯示命令窗口輸出和一邊顯示Canny()邊緣化處理終於找到了上次跑的分數低的原因啦!
主要原因如下所示:
這裏寫圖片描述
如上所示,由於之前選取的Canny()中的閾值爲1和10,這導致一旦要跳到的下一個目標物體的顏色和背景色很接近時就很容易使得邊緣的梯度小於10,因此就不會被認爲是邊緣,從而導致上面的這種情況出現。索性今天就將閾值設爲3和8,並在Canny()函數前面加上了一個高斯濾波器。如下:

GaussianBlur(srcImage,srcImage,Size(3,3),0);

還有就是上面的_sleep(1500)函數,這裏如果閒時間比較久的話也是可以改爲1000的,速度上有所提升。
同時加上按壓位置的隨機數,使得每次按壓點都是在(320,410)—(350,460)之間。代碼如下:

    int rand_x = int(320+rand()%50); //加上隨機數使得每次按壓都是在點(320,410)-(370,460)之間
    int rand_y = int(410+rand()%50);
    sprintf(AA,"adb shell input swipe %d %d %d %d %d",rand_x,rand_y,rand_x,rand_y,distance_);

如下圖所示:
這裏寫圖片描述
由上圖可知,每次按壓的位置都是在變的。
完整版代碼如下:

/*
    時間:2018-1-7
    地點:SHNU
    功能:wechat簡單跳一跳C++代碼的實現,有待改進,僅供學習之用!歡迎大家提出新算法
*/

#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include<stdlib.h>//rand()隨機數頭文件

using namespace cv;
using namespace std;

//全局變量定義區
Mat srcImage;
Mat dstImage;
Mat Character;

static int i = 0;
//get_screenshot();獲取手機上的圖像
void get_screenshot();
//Canny_Dec();邊緣檢測
void Canny_Dec(Mat& srcImage);
//獲取Character座標
Point get_Character_Loc(Mat& srcImage,Mat& Tem_img);
//獲取下一個要跳的點
Point get_next_img_Loc(Mat& srcImage,Point& Character_Loc);
//計算距離
int get_distance(Point& first_point,Point& next_point);
//跳躍
void jump(int&g_distance);
int main(int argc,char** argv)
{
    system("color 3F");
    while (true)
    {
    get_screenshot();
    srcImage = imread("autojump.png");
    dstImage = srcImage.clone();
    Character = imread("./Template/character.png");
    //imshow("Character",Character);
    //cvtColor(srcImage,srcImage,CV_BGR2GRAY);
    Point next_p = get_Character_Loc(srcImage,Character);
    //cout<<"next_p:"<<1111<<endl;

    Point get_next = get_next_img_Loc(srcImage,next_p);

    int g_distance = get_distance(next_p,get_next);

    jump(g_distance);
    //cout<<"get_next_img_Loc:"<<get_next<<endl;
    circle(dstImage,get_next,8,Scalar(0,221,2));
    //imshow("test",dstImage);
    imwrite("Canny.png",dstImage);
    _sleep(1000);
    }
    return 0;
}

void get_screenshot()
{
    system("adb shell screencap -p /sdcard/autojump.png");
    system("adb pull /sdcard/autojump.png");
}

Point get_Character_Loc(Mat& srcImage,Mat& Tem_img)
{
    matchTemplate(srcImage,Tem_img,dstImage,CV_TM_SQDIFF);
    double minVal,maxVal;
    Point minLoc,maxLoc,matchLoc;
    minMaxLoc(dstImage,&minVal,&maxVal,&minLoc,&maxLoc,Mat());
    matchLoc = minLoc; //matchLoc是最佳匹配的區域左上角點
    cout<<"maxVal:"<<maxVal<<endl;
    rectangle(srcImage,Rect(matchLoc,Size(Character.cols,Character.rows)),Scalar(255,255,0),1,8,0);
    //Canny(srcImage,srcImage,1,10);
    putText(srcImage,"Wang",Point(matchLoc.x+Character.cols*0.5,matchLoc.y+Character.rows),1,2,Scalar(0,0,255));//畫出Character小人的座標
    return Point(matchLoc.x+Character.cols*0.5,matchLoc.y+Character.rows);
}

Point get_next_img_Loc(Mat& srcImage,Point& Character_Loc)
{
    cout<<"get_next_img_Loc"<<endl;
    cvtColor(srcImage,srcImage,CV_BGR2GRAY);
    GaussianBlur(srcImage,srcImage,Size(3,3),0);
    Canny(srcImage,srcImage,3,8);
    char AA[30];
    sprintf(AA,"get_next_img_Loc_%d.png",i);
    cout<<AA<<endl;
    imwrite(AA,srcImage);
    i++;
    //imshow("get",srcImage);
    cout<<"Character_Loc.x:"<<Character_Loc.x<<endl;
    if(Character_Loc.x < 540)
    {
        for(int j = int(srcImage.rows*0.2);j<int(srcImage.rows*0.8);j++)
        {
            uchar* data = srcImage.ptr<uchar>(j);
            for(int i = 1079;i > 540 ;i--)
            {               
                if(data[i] == 255)
                {
                    return Point(i,j);
                    //cout<<"Point:"<<Point(i,j)<<endl;
                }
            }
        }
    }
    else
    {
        for(int j = int(srcImage.rows*0.2);j<int(srcImage.rows*0.8);j++)
        {
            uchar* data = srcImage.ptr<uchar>(j);
            for(int i = 0;i<540;i++)
            {
                if(data[i] == 255)
                    return Point(i,j);
            }
        }
    }
    return Character_Loc;
}

int get_distance(Point& first_point,Point& next_point)
{
    int A = first_point.x - next_point.x;
    int B = first_point.y - (next_point.y+50);
    return int(pow(pow(A,2)+pow(B,2),0.5));
}

void jump(int&g_distance)
{
    char AA[50];
    int distance_ = g_distance * 1.35;
    int rand_x = int(320+rand()%50); //加上隨機數使得每次按壓都是在點(320,410)-(370,460)之間
    int rand_y = int(410+rand()%50);
    sprintf(AA,"adb shell input swipe %d %d %d %d %d",rand_x,rand_y,rand_x,rand_y,distance_);
    cout<<AA<<endl;
    system(AA);
}

通過測試效果如下:輕鬆得榜首,到五百多時程序依然可以一直在運行,我覺得時間太長,所以就直接弄掛了。不過並不影響榜首的位置!上圖:
這裏寫圖片描述
總結: 主要本人剛好也是視覺圖像方向的,哈哈,就閒的無聊測試了一把,經過更改和測試。如果不遇到那種與背景色特別相近的,上榜首是沒問題的! 嗯,說了這麼多,這個博客也就到此結束啦,有問題歡迎留言!

附件:
這裏寫圖片描述
說明:上圖黃色方框,是掃描區域(一旦確定小人的座標屬於右半屏就掃描左半屏,之所以選擇黃色區域是爲了避免掃描到分數邊緣和底下的一個分享按鈕,這樣做也加快了代碼的運行速度。小人如果在左半屏,同理,只不過我選擇的掃描方式是從右向左)

ADB工具和工程下載地址: 下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章