利用Opencv实现微信跳一跳脚本源码放送(C++实现嵌套python)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37406130/article/details/79007335

最近微信的跳一跳很火,无意间在QQ空间看见有人用单片机控制舵机测量距离实现微信跳一跳,于是我雅兴大发,自己也用Opencv实现一个视觉控制的微信跳一跳,百度一下,果然很多,但大多数都是python实现的,于是我自己写了一个C++实现的微信跳一跳脚本。

我是利用360手机助手中 有一个360演示,图标如下
这里写图片描述

控制手机在电脑上进行运行。那只是这样的
这里写图片描述

一、对区域截图

一开始需要截取改界面的图片加以运算,这里使用windows API对C++开放的接口,获取界面所在的位置,由于python截取屏幕简单方便程序远远超过C++,所以在截图这一块,我用C++调用了python,代码如下:

void ScreenShot(void)
{
    RECT rect;  
    HWND hwnd = ::FindWindow(NULL, L"实时演示");    //获取360演示的句柄
    GetWindowRect(hwnd, &rect);     //获取图像的位置
    char sScreenShot[64];
    sprintf(sScreenShot, "grab.exe %d %d %d %d", rect.left, rect.top+135, rect.right, rect.bottom-50);//组成字符串
    system(sScreenShot);    //调用python截取图片
}

python截图图片如下:

from PIL import ImageGrab  
import argparse

parser = argparse.ArgumentParser()   #初始化准备传参
parser.add_argument('integers', metavar='N', type=int, nargs='+',
                    help='location for top left and bottom right')
args = parser.parse_args()
print(args.integers)        #打印出参数

def grab():
    ImageGrab.grab(bbox=(args.integers[0],args.integers[1],args.integers[2],args.integers[3])).convert("L").save(".\\helloworld.jpg")#截图 保存为helloworld.jpg

grab()  #进行截图

二、获取黑子的位置

接下来就是获取黑子所在的位置,这个可以使用Opencv提供的模板匹配matchTemplate,事先,将黑子截图保存好,之后和图像进行对比,找到最相近的区域。
C++代码如下:

Point GetNowPoint(Mat& srcImage, Mat& Tem_img)
{
    cv::Mat image_matched;
    matchTemplate(srcImage, Tem_img, image_matched, CV_TM_SQDIFF);// 匹配黑棋子
    double minVal, maxVal;
    Point minLoc, maxLoc, matchLoc;
    minMaxLoc(image_matched, &minVal, &maxVal, &minLoc, &maxLoc, Mat());
    matchLoc = minLoc; //matchLoc是最佳匹配的区域左上角点

    return Point(matchLoc.x + Tem_img.cols*0.5, matchLoc.y + Tem_img.rows);
}

三、寻找目标位置

接下来就是检测需要跳到的位置检测,我们需要对图像做一些处理,对图像进行高斯滤波减少噪声,之后我们对他进行Canny边缘检测,实现的效果图如下
这里写图片描述

我们要找到两个点,第一个是图像最高点(即Y方向最低点),第二个是图像最靠右边点(即X方向大点)
这里写图片描述

C++代码实现如下:

Point GetNextPoint(Mat& srcImage)
{
    cv::Point point1;
    cv::Point point2;

    cv::GaussianBlur(srcImage, srcImage, cv::Size(5, 5), 0);  //高斯滤波,降低噪声
//  cv::threshold(srcImage, srcImage, 0, 255, 8);
    Canny(srcImage, srcImage, 20, 30);      //进行边缘检测
    vector<vector<Point>> contours; 
    vector<Vec4i> hierarchy;
    findContours(srcImage, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point()); //找到关键的角点
    //遍历每一个轮廓,把多余的轮廓去掉
    std::vector<std::vector<cv::Point> >::const_iterator it = contours.begin();
    while (it != contours.end()) {
        if (it->size()<150)
            it = contours.erase(it);
        else
            ++it;
    }
    int nYMin = srcImage.rows;
    int nXMin = srcImage.cols;
    int nYMax = 0;
    int nXMax = 0;
    int nIdY = 0;
    for (int i = 0; i < contours.size(); i++)
    {
        //contours[i]代表的是第i个轮廓,contours[i].size()代表的是第i个轮廓上所有的像素点数  
        for (int j = 0; j < contours[i].size(); j++)
        {
            if (contours[i][j].y < nYMin)
            {
                nYMin = contours[i][j].y;   //找到最低的y值
                point1 = contours[i][j];    //记录  y值最低点坐标
                nIdY = i;                   //记录哪个区域内的
            }
        }
    }
    int minY = srcImage.cols;

    for (int j = 0; j < contours[nIdY].size(); j++) //在哪个区域内继续变量 找到x最大值
    {
        if (contours[nIdY][j].x >nXMax)
        {
            nXMax = contours[nIdY][j].x;
        }
    }
    for (int j = 0; j < contours[nIdY].size(); j++)
    {//找到x中最大值上的最小值
        if (contours[nIdY][j].x == nXMax && contours[nIdY][j].y < minY)
        {
            point2 = contours[nIdY][j];         
            minY = contours[nIdY][j].y;     //记录X点的最大值
        }
    }
    return cv::Point(point1.x, point2.y);       //返回中点坐标
}

四、计算距离

通过原点坐标和目标点坐标算出距离,这个就很简单了:

int GetDistance(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)
{
    cout << "distance:" << g_distance << endl;
    int time = g_distance*4+330;

    RECT rect;
    HWND hwnd = ::FindWindow(NULL, L"实时演示");
    GetWindowRect(hwnd, &rect);

    ::SetCursorPos(rect.left+100, rect.bottom-500);
    INPUT  Input = { 0 };       //鼠标按下
    Input.type = INPUT_MOUSE;
    Input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
    SendInput(1, &Input, sizeof(INPUT));

    Sleep(time);//鼠标下的时间   

    Input = { 0 };      //鼠标松开
    Input.type = INPUT_MOUSE;
    Input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
    SendInput(1, &Input, sizeof(INPUT));

}

整个的工程已经描述完毕了,包括我的想法和设计,如果有更好的,欢迎交流。整个工程的下载在这里。

没有更多推荐了,返回首页