基于opencv3.1.0+VS2013微信跳一跳辅助工具

准备材料:

1. adb工具(版本新点)比如 platform-tools-latest-windows.zip

 2.opencv基于C++ 的开发环境

3.一部装有最新微信的安卓智能机

实现原理:根据分析跳一跳界面截图,可以获知几个重要信息

1.背景单调,但是渐变色 会自动变色

2.棋子形状固定,不随时间变化

3.棋子跳转的距离与按压的时间成线性关系

4.每步跳转的倾斜角是30°。

5.下一步的物块必定比其它物块高

6.将每步截图以中心建立座标系,发现一个重要特点

    (1)棋子只会在左下与右下区域

    (2)棋子如果在左下区域 下一步必定在右上区域;反之亦然

    (3)每步中心点连线必过整个截图的中心

根据如此多的特点 我们便可以着手进行辅助开发,首先要熟悉一下安卓的调试工具ADB,其具体指令方式不做过多介绍,给出网址有全面的介绍  https://github.com/mzlogin/awesome-adb 程序中只用了两个指令

 截图:adb exec-out screencap -p >sc1.png 这个指令是直接截图保存到电脑端

触摸按压:adb shell input swipe 500 500 500 500 500 这个其实是滑动指令 这5个500 分别是 按压的起始位置x y 和松开的结束位置 x y  最后一个参数是按压的时间单位 ms 

具体手机与PC的连接不做过多介绍,明显是使用开发者选项,打开USB调试模式


接下来便是程序的重点

    1.获取一张屏幕截图,并读取。根据ADB指令读取到一帧图像,读入程序。根据这个游戏的工作方式我们很清楚的了解到辅助工具只要计算出来两点的距离 然后转换成跳跃的按压时间就行了。因此我们就其实只需要获取两个座标点: 棋子 与下一步

2.如何获取棋子的座标点: 我根据毛星云书本附带例程中的模板匹配 很轻松的就稳定的获取到了棋子座标

Mat g_resultImage;
Mat Mode = imread("mode.png");//读取棋子模板 用作模板匹配
Point findmode(Mat &SrcImg)
{
	//Mat SrcImg = imread("G:\\src4.png");
	int g_nMatchMethod = 0;
	matchTemplate(SrcImg, Mode, g_resultImage, g_nMatchMethod);
	normalize(g_resultImage, g_resultImage, 0, 1, NORM_MINMAX, -1, Mat());

	//【4】通过函数 minMaxLoc 定位最匹配的位置
	double minValue; double maxValue; Point minLocation; Point maxLocation;
	Point matchLocation;
	minMaxLoc(g_resultImage, &minValue, &maxValue, &minLocation, &maxLocation, Mat());
	//cout << minValue << maxValue << endl; 
	//【5】对于方法 SQDIFF 和 SQDIFF_NORMED, 越小的数值有着更高的匹配结果. 而其余的方法, 数值越大匹配效果越好
	if (g_nMatchMethod == CV_TM_SQDIFF || g_nMatchMethod == CV_TM_SQDIFF_NORMED)
	{
		matchLocation = minLocation;
	}
	else
	{
		matchLocation = maxLocation;
	}

	//【6】绘制出矩形,并显示最终结果
	rectangle(SrcImg, matchLocation, Point(matchLocation.x + Mode.cols, matchLocation.y + Mode.rows), Scalar(0, 0, 255), 2, 8, 0);
	matchLocation.x = matchLocation.x + Mode.cols / 2;
	matchLocation.y = matchLocation.y + Mode.rows;
	return matchLocation;
	//rectangle(g_resultImage, matchLocation, Point(matchLocation.x + Mode.cols, matchLocation.y + Mode.rows), Scalar(0, 0, 255), 2, 8, 0);
}

获取到棋子座标比较容易,但是下一步的座标中心点 一直没有很好的注意获取, 这里就不说我的程序的发展历程,直接展示V3.0版实现的方式,根据重要信息5 我们可以考虑逐行遍历的方式来获得物块的最高点 这个点肯定接近物块中心,其实还可以发现这个点的横座标与目标点x座标几乎是一样的。

但是怎么遍历呢 ,这里有两种方案,(1)根据分析背景可知,背景是渐变色,每行的背景色数值是一样的 ,所以我们可以嵌套for进行判断 如果下一个像素点与这个点不一样 ,那么它就是物块上的点  ,找到物块最高点座标 寻找结束

    (  2)方案2使用漫水填充将背景填充0(即去除), 然后同样遍历像素点找到有数据的座标 ,此座标即为物块最高点

做到这步其实已经可以勉强使这个工具工作了,但是每次的目标点不是很准,拿不到高分,想拿到连续跳到中点的高分 就要继续利用发现到的重要信息 4 和 6.3  

    目标点与棋子当前座标是成30°的 那么我在刚刚已经获取到了目标点的x座标,利用初中的一元一次方程知识 我不就可以获知到经过棋子座标点获得这个函数 利用已知座标x 即可求出更加准确的目标点的y 座标 

    


注意事项:棋子的当前座标不一定是中心点 以棋子座标列方程 数据有点不准 ,所以我 直接以截图的中心点列方程,经过实验观察,数据非常稳定。

同时我为了减少程序的运算量,将截图的以中心分成的4个区域只选取其中有目标点的 进行数据分析

Point findNextStep(Mat &SrcImg)
{
	Point NextStep;
	Rect rec;
	//blur(SrcImg, SrcImg,Size(3,3));
	floodFill(SrcImg, Point(1, 1), Scalar(0, 0, 0),&rec, Scalar(5,5,5),
		Scalar(5,5,5));
	imshow("DEBUG", SrcImg);
	for(int i = 0; i < SrcImg.rows;i++)//列遍历
	{
		for(int j = 0; j < SrcImg.cols; j++)//行遍历
		{
			if (SrcImg.at < Vec3b >(i,j) != Vec3b(0,0,0) && j>10)
			{
				NextStep = Point(j,i);
				return NextStep;
			}
		}
	}
	//
	return NextStep;
}

这样两个座标点获取后 再进行距离计算,以像素点为单位

		center1 = findmode(SrcImg);//获取到棋子座标
		if (center1.x > 540)//棋子在右半侧
		{
			TLImg = SrcImg(Range(400, 960), Range(0, 540)).clone();
			center2=findNextStep(TLImg)+Point(0,400);		
		}
		else
		{
			TRImg = SrcImg(Range(400, 960), Range(540, 1080)).clone();
			center2 = findNextStep(TRImg) + Point(540, 400);
		}
		center2.y = CenterPoint.y - 0.5*abs(center2.x - CenterPoint.x);//路径倾斜30°
		center1 = center2 - center1;
		int length = sqrt(pow(center1.x, 2) + pow(center1.y, 2));

获取距离后便调试 距离与按压时间的转换比例 ,以我手机1080x1920分辨率为例 在截图不压缩的情况下 距离与时间的换算比是 1.35.至此程序设计完毕。昨日程序运行的最高得分8915 ,可以说是非常稳定了。

虽然也发现一些小问题需要完善 但是出于时间与精力 ,未作调整

已知问题: 距离太近则可能造成无法找到目标点;跳速太快 会使 跳到中心点产生的 波纹对目标点的造成距离误判。  计算速度略慢


完整代码供参考学习:

// main.cpp : 定义控制台应用程序的入口点。
//openCV3.1.0模板

#include "stdafx.h"
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

char PC[] = "adb exec-out screencap -p >sc1.png";
int UpDisplay = 1;
String WINDOW_NAME = "NOKIA6_jump";
RNG rng;
float bili = 1.35;
char Jump[50] = "adb shell input swipe 500 500 500 500 500";
int GetLength(Mat &Srcimg);
Mat SrcImg1;
vector<Vec3f> circles;
Point center1, center2,CenterPoint(540,960);
Mat SrcImg,SrcImgC,desImg,TLImg,TRImg,MaskImg;
Point findmode(Mat &SrcImg);//寻找棋子
Point findNextStep(Mat &SrcImg);//寻找下一步
int _tmain(int argc, _TCHAR* argv[])
{
	bool state = 1, jumpstat = 1;
	int num=0;

	while (state)
	{

		system(PC);//获取一张截图
		//system("cls");//CMD清屏
		SrcImg = imread("sc1.png");//读取截取的图像
		//SrcImg = imread("C:\\Users\\cheng\\Desktop\\QQ图片20180217232802.jpg");
		if (SrcImg.empty())//检测是否读到
		{
			cout << "Can't read image  " << endl;
			waitKey();
			return -1;
		}
		if (SrcImg.size().width != 1080){ resize(SrcImg, SrcImg, Size(1080, 1920)); }//统一尺寸 方便计算距离

		center1 = findmode(SrcImg);//获取到棋子座标
		if (center1.x > 540)//棋子在右半侧
		{
			TLImg = SrcImg(Range(400, 960), Range(0, 540)).clone();
			center2=findNextStep(TLImg)+Point(0,400);		
		}
		else
		{
			TRImg = SrcImg(Range(400, 960), Range(540, 1080)).clone();
			center2 = findNextStep(TRImg) + Point(540, 400);
		}
		center2.y = CenterPoint.y - 0.5*abs(center2.x - CenterPoint.x);//路径倾斜30°
		circle(SrcImg, center2, 10, Scalar(255, 255, 0), -1);
		resize(SrcImg, SrcImg, SrcImg.size() / 4);//重设尺寸为了显示
		//circle(SrcImg, Point(SrcImg.cols, SrcImg.rows) / 2, 5, Scalar(0, 0, 0), 2);
		line(SrcImg, Point(0, SrcImg.rows / 2), Point(SrcImg.cols, SrcImg.rows / 2), Scalar(0));
		line(SrcImg, Point(SrcImg.cols / 2, 0), Point(SrcImg.cols / 2, SrcImg.rows), Scalar(0));

		center1 = center2 - center1;
		int length = sqrt(pow(center1.x, 2) + pow(center1.y, 2));
		if (length < 150 || center2.x==300)length = 222;//距离太近会造成错误检测
		printf("第%03d次跳跃。",num++);
		std::cout << "GetLength" << length << endl;//输出距离长度(调试需要)
		sprintf_s(Jump, "adb shell input swipe %d %d %d %d %d", rng.uniform(400, 700), rng.uniform(1500, 1600), rng.uniform(400, 700), rng.uniform(1500, 1700), (int)(length * bili));
		cout <<"目标点"<< center2 << endl;
		system(Jump);//发送按压指令

		imshow("SrcImg", SrcImg);//显示图像
		switch (waitKey(rng.uniform(1000, 1500)))//随机演示等待
		{
		case 27:
			state = !state;

			break;
		case ' ':
			cout << "暂停" << endl;
			if (27 == waitKey(0))state = !state;
			cout << "开始运行" << endl;
			break;
		default:
			break;
		}
	}
	cout << "程序结束" << endl;
	//system("adb shell kill-server");//结束adb进程 
	return 0;
}

Mat g_resultImage;
Mat Mode = imread("mode.png");//读取棋子模板 用作模板匹配
Point findmode(Mat &SrcImg)
{
	//Mat SrcImg = imread("G:\\src4.png");
	int g_nMatchMethod = 0;
	matchTemplate(SrcImg, Mode, g_resultImage, g_nMatchMethod);
	normalize(g_resultImage, g_resultImage, 0, 1, NORM_MINMAX, -1, Mat());

	//【4】通过函数 minMaxLoc 定位最匹配的位置
	double minValue; double maxValue; Point minLocation; Point maxLocation;
	Point matchLocation;
	minMaxLoc(g_resultImage, &minValue, &maxValue, &minLocation, &maxLocation, Mat());
	//cout << minValue << maxValue << endl; 
	//【5】对于方法 SQDIFF 和 SQDIFF_NORMED, 越小的数值有着更高的匹配结果. 而其余的方法, 数值越大匹配效果越好
	if (g_nMatchMethod == CV_TM_SQDIFF || g_nMatchMethod == CV_TM_SQDIFF_NORMED)
	{
		matchLocation = minLocation;
	}
	else
	{
		matchLocation = maxLocation;
	}

	//【6】绘制出矩形,并显示最终结果
	rectangle(SrcImg, matchLocation, Point(matchLocation.x + Mode.cols, matchLocation.y + Mode.rows), Scalar(0, 0, 255), 2, 8, 0);
	matchLocation.x = matchLocation.x + Mode.cols / 2;
	matchLocation.y = matchLocation.y + Mode.rows;
	return matchLocation;
	//rectangle(g_resultImage, matchLocation, Point(matchLocation.x + Mode.cols, matchLocation.y + Mode.rows), Scalar(0, 0, 255), 2, 8, 0);
}
Point findNextStep(Mat &SrcImg)
{
	Point NextStep;
	Rect rec;
	//blur(SrcImg, SrcImg,Size(3,3));
	floodFill(SrcImg, Point(1, 1), Scalar(0, 0, 0),&rec, Scalar(5,5,5),
		Scalar(5,5,5));
	imshow("DEBUG", SrcImg);
	for(int i = 0; i < SrcImg.rows;i++)//列遍历
	{
		for(int j = 0; j < SrcImg.cols; j++)//行遍历
		{
			if (SrcImg.at < Vec3b >(i,j) != Vec3b(0,0,0) && j>10)
			{
				NextStep = Point(j,i);
				return NextStep;
			}
		}
	}
	//
	return NextStep;
}

第一次写博客,希望大家支持,完整示例程序见链接文件。在1080x1920手机上能够完美运行,一般跑500分以上完全没问题。

附完整示例程序:http://download.csdn.net/download/qq_34901073/10254240

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