OpenCV实现别踩白块

OpenCV实现别踩白块

最近用OpenCV做外挂实现别踩白块小游戏,用的知识不是很难,所以写文章记录当时遇到过的问题,OpenCV版本3.3.0。
程序运行的视频网址:http://v.youku.com/v_show/id_XMzI5MTc4MDM5Ng==.html?spm=a2h3j.8428770.3416059.1

获取当前屏幕图像

首先就是要实现截取当前屏幕的图像,网上查了很多,结果发现了这篇有价值的文章:http://blog.csdn.net/sinat_36219858/article/details/70877677,
但这篇文章写的比较久远,用的是OpenCV1.0的函数,所以参考这篇文章的代码,将它改成OpenCV2.0以后的。具体的代码如下:

void get_screen(Mat &result)
{
    LPVOID screenCaptureData = NULL;//NULL为获取全屏
    HBITMAP hBitmap;
    HDC hDDC;
    HDC hCDC;

    int nWidth = GetSystemMetrics(SM_CXSCREEN);//得到屏幕的分辨率的x    
    int nHeight = GetSystemMetrics(SM_CYSCREEN);//得到屏幕分辨率的y    
    screenCaptureData = new char[nWidth*nHeight * 4];
    memset(screenCaptureData, 0, nWidth);
    // Get desktop DC, create a compatible dc, create a comaptible bitmap and select into compatible dc.    
    hDDC = GetDC(GetDesktopWindow());//得到屏幕的dc    
    hCDC = CreateCompatibleDC(hDDC);//    
    hBitmap = CreateCompatibleBitmap(hDDC, nWidth, nHeight);//得到位图    
    SelectObject(hCDC, hBitmap);


    BitBlt(hCDC, 0, 0, nWidth, nHeight, hDDC, 0, 0, SRCCOPY);
    GetBitmapBits(hBitmap, nWidth*nHeight * 4, screenCaptureData);//得到位图的数据,并存到screenCaptureData数组中。   

/**************************************************************
Mat img(Size(nWidth, nHeight), CV_8UC4, Scalar(0));//创建一个4通道图像rgba
**************************************************************/

    Mat img(Size(nWidth, nHeight), CV_8UC4, Scalar(0));//创建一个4通道图像rgba
    memcpy((uchar*)img.data, screenCaptureData, nWidth*nHeight * 4);
    Mat img2(Size(nWidth, nHeight), CV_8UC4, Scalar(0));
    cvtColor(img, img2, CV_BGRA2BGR);

    result = img2;
}

中间遇到的问题就是这句话:

memcpy((uchar*)img.data, screenCaptureData, nWidth*nHeight * 4);

这句话一直出错,试了好多的方法,结果是这句话的问题:

Mat img(Size(nWidth, nHeight), CV_8UC4, Scalar(0));

以前一直是CV_8UC3,后来试了一下,改成了CV_8UC4,这样才没报错。应该是得到的图像是4通道的。
剩下的就没有什么问题了。

鼠标以及按键操作

经过百度,得到了这篇有价值的文章:http://blog.sina.com.cn/s/blog_161af74b60102wmyl.html,
经过参考文章里的程序,得到了我们需要用到的函数:

//鼠标操作
void mouse_press(int x, int y)
{
    POINT p;
    p.x = x;
    p.y = y;
    SetCursorPos(p.x, p.y); //设置鼠标位置
    mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);  //按下鼠标
    mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);    //松开鼠标
}

//按键操作
void key_press(int Ascii)
{
    keybd_event(Ascii, 0, 0, 0);    //68是D的ASCII码,按下按键‘a’
    keybd_event(Ascii, 0, KEYEVENTF_KEYUP, 0);  //松开按键
}

OpenCV图像处理

最后一部就是图像分析了,用的知识简单,只是简单实现了功能,以后还需改进。代码如下:

#include 
#include 
#include 

#include 

using namespace cv;
using namespace std;

//获取屏幕图像
void get_screen(Mat &result)
{
    LPVOID screenCaptureData = NULL;//NULL为获取全屏
    HBITMAP hBitmap;
    HDC hDDC;
    HDC hCDC;

    int nWidth = GetSystemMetrics(SM_CXSCREEN);//得到屏幕的分辨率的x    
    int nHeight = GetSystemMetrics(SM_CYSCREEN);//得到屏幕分辨率的y    
    screenCaptureData = new char[nWidth*nHeight * 4];
    memset(screenCaptureData, 0, nWidth);  
    hDDC = GetDC(GetDesktopWindow());//得到屏幕的dc    
    hCDC = CreateCompatibleDC(hDDC);
    hBitmap = CreateCompatibleBitmap(hDDC, nWidth, nHeight);//得到位图    
    SelectObject(hCDC, hBitmap);


    BitBlt(hCDC, 0, 0, nWidth, nHeight, hDDC, 0, 0, SRCCOPY);
    GetBitmapBits(hBitmap, nWidth*nHeight * 4, screenCaptureData);//得到位图的数据,并存到screenCaptureData数组中。   

    Mat img(Size(nWidth, nHeight), CV_8UC4, Scalar(0));//创建一个4通道图像rgba
    memcpy((uchar*)img.data, screenCaptureData, nWidth*nHeight * 4);
    Mat img2(Size(nWidth, nHeight), CV_8UC4, Scalar(0));
    cvtColor(img, img2, CV_BGRA2BGR);

    result = img2;
}

//鼠标操作
void mouse_press(int x, int y)
{
    ////获取鼠标事实位置
    //while (1)
    //{
    //  POINT point;
    //  GetCursorPos(&point);
    //  cout << "x=" << point.x << " y=" << point.y << endl;
    //}

    POINT p;
    p.x = x;
    p.y = y;
    SetCursorPos(p.x, p.y); //设置鼠标位置
    mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);  //按下鼠标
    mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);    //松开鼠标
}

//按键操作
void key_press(int Ascii)
{
    keybd_event(Ascii, 0, 0, 0);    //68是D的ASCII码,按下按键‘a’
    keybd_event(Ascii, 0, KEYEVENTF_KEYUP, 0);  //松开按键
}

//二值化函数
void mythreshold(Mat &img, uchar T)
{
    int n1 = img.rows;
    int nc = img.cols * img.channels();
    if (img.isContinuous())//判断图像是否连续
    {
        nc = nc * n1;
        n1 = 1;
    }
    for (int i = 0; i < n1; i++)
    {
        uchar *p = img.ptr(i);
        for (int j = 0; j < nc; j++)
        {
            if (p[j] < T)
                p[j] = 0;
            else p[j] = 255;
        }
    }
}

int main()
{
    system("pause");

    mouse_press(400, 500);

    while (1)
    {
        Mat img;
        //img = imread("test_img.jpg"); //1536x864
        get_screen(img);    //得到当前屏幕图像
        //cout << "img.width = " << img.cols << "img.height = " << img.rows << endl;
        Mat img_ROI = img(Range(320, 590), Range(500, 1110));   //通过预估,确定游戏界面的大体位置

        //Mat img_show; //用来显示
        //img_ROI.copyTo(img_show);

        cvtColor(img_ROI, img_ROI, COLOR_BGR2GRAY);
        mythreshold(img_ROI, 100);
        //先膨胀后腐蚀  闭运算 排除小型黑洞
        //先腐蚀后膨胀  开运算 在纤细除分离物体
        Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
        dilate(img_ROI, img_ROI, element, Point(-1, -1), 1);
        //erode(temp, temp, element, Point(-1, -1), 1);//腐蚀3次

        Point point_press(500, 320 + 130);  //鼠标点击位置坐标
        vector < int > value;

        //预估,确定遍历的纵坐标
        //只要存在边界,就记录它的坐标
        uchar *p = img_ROI.ptr(130);
        for (int i = 0; i < img_ROI.cols - 1; i++)
        {
            if (p[i] == 255)
            {
                if (p[i + 1] == 0)
                {
                    value.push_back(i);
                }
            }

            if (p[i] == 0)
            {
                if (p[i + 1] == 255)
                {
                    value.push_back(i);
                }
            }
        }
        //分析坐标:黑块在中间的两个位置
        if (value.size() == 4)
        {
            cout << "already find!" << endl;
            point_press.x = value[1] + 40;

            float num = float(value[1] - value[0]) / 100.0;     //每个格子宽度大概是100
            cout << "num = " << num << endl;

            if (num > 0.7 && num < 1.3)
                key_press(70);  //按下F
            else if (num > 1.7 && num < 2.3)
                key_press(74);  //按下J
            else
                break;
        }
        //分析坐标:黑块在旁边的两个位置
        else if (value.size() == 2)
        {
            cout << "already find!" << endl;
            point_press.x = value[1] + 40;

            float num = float(value[0]) / 100.0;
            cout << "num = " << num << endl;

            if (num > 1.7 && num < 2.3)
                key_press(68);  //按下D
            else if (num > 0.7 && num < 1.3)
                key_press(75);  //按下K
            else
                break;
        }
        else
        {
            cout << "not found!" << endl;
            break;
        }

        Sleep(60);  //延时控制速度
    }
    return 0;
}

别踩白块用的是4399小游戏上的:http://www.4399.com/flash/135255_3.htm

注意:运行的时候浏览器必须全屏。

如果有什么需要改进的地方,下方评论指出或与我联系:
QQ:1570553025

你可能感兴趣的:(OpenCV)