18910140161

OpenCV实现模仿微信跳一跳小程序

顺晟科技

2021-06-16 10:41:17

147

最近微信上的跳转很流行,因为对计算机视觉感兴趣,所以就想写个程序实现自动跳转。因为前段时间要考试,所以没有写程序。考完之后,这几天又做了一遍,终于差不多在1.22的晚上改进了算法。写这个博客记录一下。

完整的源代码在我的github:https://github.com/myzcl/WeChat-Jump

后续更新会在Github上。

github上有个项目很成熟,可以去看看

https://github.com/wangshub/wechat_jump_game

微信现在加大了反作弊力度

亚行工具的使用

亚行配置

adb工具主要用于与Android手机的通讯,获取截图和新闻屏。亚行工具包可以通过在线搜索下载。如果您不想下载,这里有一个指向网络磁盘的链接:

链接:https://pan.baidu.com/s/1mjRVRZy密码:cz7w

亚行工具环境变量的配置。将adb工具报表的路径添加到环境变量中,比如我的路径是d: \程序文件\ ADB \平台-工具-最新-windows \平台-工具,如图:

将该目录添加到环境变量中,如图所示:

详细使用了adb命令,网上有一篇很不错的文章。分享一下:http://blog.csdn.net/u010375364/article/details/52344120

遇到的问题:

这可能发生在运行指令时,因为端口被移动助理占用。详情见http://blog.csdn.net/liguilicsdn/article/details/50902194

解决方案是打开任务管理器,结束相关流程,如图:

c调用Python

因为最近在学习python,所以用Python调用adb命令,然后用c调用Python,Python的点击抓取法就不赘述了,主要说一下调用Python遇到的问题。

遇到的问题

1.配置问题

配置步骤与在VS下配置OpenCV相同,如图:

2.x64配置

因为我用的是x64版本的Python,所以在VS中必须选择x64版本,否则会报错:

3.将参数传递给Python

c调用Python通用方法,可以自行百度,需要注意的是:

一定要设置Python文件的目录,比如我的就是:

pyrin _ SimpleString(' sys . path . append(' e :/vs 2013 test/3 . py-test/py-test ')');

否则,将找不到Python文件。

通过参数可参考以下代码:

//按屏幕

void press(int x1,int y1,int x2,int y2,int dist)

{

py _ Initialize();//初始化Python

//检查初始化是否成功

if(!Py_IsInitialized())

“python初始化失败!”endl

pyrin _ SimpleString(' import OS ');

丙酮酸_简单字符串('导入系统');

pyrin _ SimpleString(' sys . path . append(' e :/vs 2013 test/3 . py-test/py-test ')');//设置Python路径

PyObject * pModule=NULL

PyObject * pFunc=NULL

PyObject *pArg=空;

p对象*压力=空;

pm module=PyImport _ IMPORT module(' py _ called ');//查找Python文件

if(!pModule)

' cout ' pModule erro!'endl

//pFunc=pyoObject _ GetAttrString(pm module,' add _ func ');//查找add_func函数

//ParG=Py _ BuildVaLue(' I,I '),10,25);//传入两个形状参数

//pResult=PyEval _ call object(pFunc,ParG);//执行函数

pFunc=pyoObject _ GetAttrString(pm module,' press _ value ');

pArg=Py _ BuildVaLue(' I,I,I,I,I '),x1,y1,x2,y2,dist);

PyEval_CallObject(pFunc,ParG);

py _ Finalize();//关闭Python

}

OpenCV算法

检测的思路如下:

1.0:由于棋子形状大小不变,采用简单暴力的模板匹配。对于下一个目标点,最初使用Canny算法寻找目标的轮廓,然后通过估计确定目标点。但是经过几次实验,效果不是很好。当背景和目标颜色相近时,边缘不会被检测到,导致目标点不准确。最后,利用图像特征进行识别。用这种方法,自动跳只能跳一百多分。

2.0:通过分析可知,目标的周围都是背景,并且目标一直处于背景最上部,所以可以每一行每一行进行检测,设定阈值,当检测到到从背景到目标,记录这个位置,在此基础上,再检测到由目标到背景,再记录这个位置,最后,两个位置进行求均值。经过实验,这个方法的准确性非常高。

具体代码:

main.cpp

#包含数学。h

#包含输入输出流

#包含Python.h

#include opencv2/opencv.hpp

使用命名空间标准;

使用命名空间cv;

//二值化

无效的消息(Mat img,uchar T)

{

int n1=img.rows

int NC=img。cols * img。channels();

if(img。Iscontinuity())//判断图像是否连续

{

nc=nc * n1

n1=1;

}

for(int I=0;i n1I)

{

uchar * p=img。ptruchar(I);

for(int j=0;j ncj)

{

if (p[j] T)

p[j]=0;

else p[j]=255;

}

}

}

//获取截图

void get_screen()

{

py _ Initialize();//初始化计算机编程语言

//检查初始化是否成功

if(!Py_IsInitialized())

“蟒蛇初始化失败!"恩德尔

pyrn _ SimpleString(' import OS ');

pyrn _ SimpleString(' OS。系统(' ADB shell screen cap-p//sdcard//src。png ')');

int py_test=丙酮酸_ SimpleString(' OS。系统(' ADB pull//sdcard//src。png ')');

if (py_test!=-1)

cout '获取截图成功恩德尔

else cout '获取截图失败恩德尔

py _ Finalize();//关闭计算机编程语言

}

//按压屏幕

void press(int x1,int y1,int x2,int y2,int dist)

{

py _ Initialize();//初始化计算机编程语言

//检查初始化是否成功

if(!Py_IsInitialized())

“蟒蛇初始化失败!"恩德尔

pyrn _ SimpleString(' import OS ');

丙酮酸_简单字符串('导入系统');

pyrn _ SimpleString(' sys。路径。追加(' e :/vs 2013 test/3。py-test/py-test '));//设定计算机编程语言路径

对象*模块=空

p对象* pFunc=空

PyObject *pArg=空;

p对象*压力=空;

pm module=PyImport _ IMPORT module(' py _ called ');//找到计算机编程语言文件

if(!pModule)

cout ' pModule erro!恩德尔

//pFunc=pyoObject _ GetAttrString(pm模块,‘add _ func’);//找到add_func函数

//ParG=Py _ BuildVaLue(' I,I '),10,25);//传入参数两个整形的参数

//pResult=PyEval _ call对象(pFunc,ParG);//执行函数

pFunc=pyoObject _ GetAttrString(pm模块,‘press _ value’);

pArg=Py _ BuildVaLue(' I,I,I,I,I '),x1,y1,x2,y2,dist);

PyEval_CallObject(pFunc,ParG);

py _ Finalize();//关闭计算机编程语言

}

//获取棋子位置

无效loca_start(Mat img_src,Mat img_model,Point point)

{

/************************************模板匹配****************************************/

//创建输出结果的矩阵

int result _ cols=img _ src。cols-img _ model。cols 1;

int result _ rows=img _ src。行-img _ model。行1;

垫子结果(大小(result_cols,result _ rows),CV_8UC1,Scalar(0));

//进行匹配和标准化

int match _ method=3;//选择匹配的方式

/*

CV_TM_SQDIFF=0,

CV_TM_SQDIFF_NORMED=1,

CV_TM_CCORR=2,

CV_TM_CCORR_NORMED=3,

CV_TM_CCOEFF=4,

CV_TM_CCOEFF_NORMED=5

*/

matchTemplate(img_src,img_model,result,match _ method);

归一化(结果,结果,0,1,NORM _ MINMAX,-1,Mat());

//通过函数minMaxLoc定位最匹配的位置

double minValdouble maxVal点minLoc点maxLoc

minMaxLoc(result,minVal,maxVal,minLoc,MaxLoc,Mat());

//对于方法SQDIFF和SQDIFF_NORMED,越小的数值代表更高的匹配结果。而对于其他方法,数值越大匹配越好

点matchLoc

if(match _ method==CV _ TM _ SQDIFF | | match _ method==CV _ TM _ SQDIFF _ NORMED)

{

matchLoc=minLoc

//cout '匹配相似度为:' minVal * 100 endl

}

其他

{

matchLoc=maxLoc

//cout '匹配相似度为:' maxVal * 100 endl

}

//画出棋子位置

//矩形(img_src,matchoc,Point(matchoc。x img _ model。科尔,马乔克。y img _ model。行),标量(0,255,0),2);

//画出中心位置

Point Point _ cir=Point(matchoc。x(img _ model。cols 1),matchoc。y img _ model。第-8行);

//circle(img_src,point_cir,5,Scalar(0,0,255),-1);

point=point _ cir

//imshow('result ',result);

/************************************模板匹配****************************************/

}

//获取目标位置

无效loca_next(Mat img_gray,Point point_start,Point point_next)

{

Rect area_0(0,0,(img_gray.cols 3) * 4,(img_gray。第2行);

if(point _ start。x=(img _ gray。cols 1))//棋子在左边

{

//area _ 0。x=(img _ gray。cols 3)* 3;

area _ 0。x=img _ gray。cols 1;

area _ 0。y=(img _ gray。cols 1)50;

}

其他

{

area _ 0.x=10

area _ 0。y=(img _ gray。cols 1)50;

area _ 0。width=img _ gray。cols 1;

}

////画出下一个目标大体位置

//矩形(img_src,area_0,Scalar(0,255,0),2);

Mat img_scan=img_gray(area_0).clone();

点p_node1=点(0,0);

点p_node2=点(0,0);

点p_node=点(0,0);

弯曲件标志=false

for(int I=5;i img _ scan.rowsI)

{

弯曲件标志=false

vec3b * p=img _ scan。ptrvec3b(I);

for(int j=0;j img _ scan。cols-1;j)

{

int val _ l=p[j 1][0]p[j 1][1]p[j 1][2]-p[j][0]-p[j][1]-p[j][2];

int val _ r=p[j][0]p[j][1]p[j][2]-p[j 1][0]-p[j 1][1]-p[j 1][2];

if (val_l 10 flag1==false)

{

//cout ' 1: ' val _ l endl;

p _ node 1。x=j;

p _ node 1。y=I;

flag1=真

}

if (val_r 10 flag1==true)

{

//cout ' 2: ' val _ r endl;

p _ node 2。x=j;

p _ node 2。y=I;

标志=真

打破;

}

}

if (flag==true)

打破;

}

if (p_node1.x!=0 p_node2.y!=0)

{

//cout '已经恩德尔

p _ node。x=(p _ node 1。x p _ node 2。x)1;

p _ node。y=(p _ node 1。y p _ node 2。y)1;

圆(img_scan,p_node1,10,Scalar(255,0,0),-1);

圆(img_scan,p_node2,10,Scalar(255,0,0),-1);

圆(img_scan,p_node,10,Scalar(0),2);

p _ node。x=area _ 0。x;

p _ node。y=area _ 0。y;

point _ next=p _ node

}

imshow('img_scan ',img _ scan);

}

//计算比例

无效距离(起点、下一点、浮动距离_值)

{

float xx=ABS((float)next。x-(浮动)开始。x);

浮动距离=xx/cos(3.1415926/6);

//float YY=ABS((float)start。y-(float)下一个。y);

//浮动距离=xx/sin(3.1415926/6);

//int distance=sqrt(power(next。开始。x,2)电源(启动。下一个。y,2));

dist_val=(浮点)距离* 2.8419 8.4488;//dist_val=(浮点)距离* 2.8619 1.4488;

cout '距离:"距离"按压时间:' dist _ val endl

}

Point point_mouse=Point(0,0);

void on_mouse(int event,int x,int y,int flags,void *ustc)//event鼠标事件代号,x,y鼠标坐标,旗帜拖拽和键盘操作的代号

{

if(EVENT==CV _ EVENT _ LBBUTTON down)//左键按下,读取初始坐标

{

point _ mouse.x=x

point _ mouse.y=y

//cout '鼠标值已更新点鼠标结束

}

}

int main()

{

//通过鼠标确定下一位置(半自动)

namedWindow(' img _ src ');

setMouseCallback('img_src ',on_mouse,0);//调用回调函数

int count=0;

while (1)

{

//产生一定范围内的随机数,模拟手指进行按压,否则得分会被清除

srand((int)time(NULL));

int mynd _ x=(rand()%(800-700 1))700-1;

int mynd _ y=(rand()%(1500-1400 1))1400-1;

//得到手机截图

get _ screen();

mat img _ src=im read(' e : \ \ vs 2013 test \ \ 3。py-test \ \ py-test \ \ src。png ');

调整大小(img_src,img_src,Size(img_src.cols 1,img_src。第1行);//540, 960

//载入棋子模板

mat img _ model=im read(' e : \ \ vs 2013 test \ \ 3。py-test \ \ py-test \ \ img _ model。jpg ');

调整大小(img_model,img_model,Size(img_model.cols 1,img_model。第1行);

//模板匹配得到棋子位置

起点_起点

loca_start(img_src,img_model,point _ start);

圆(img_src,point_start,5,Scalar(0,0,255),-1);

Mat img _灰色

img _ src。复制到(img _ gray);

//cvtColor(img_src,img_gray,COLOR _ BGR 2 GRAY);

//边缘检测获取目标位置

Point point _ next

loca_next(img_gray,point_start,point _ next);

圆(img_src,point_next,5,Scalar(0,255,0),-1);//画下一个目标点

/*用鼠标获取位置*/

//Point Point _ next;

//point _ next=point _ mouse;

//circle(img_src,point_next,5,Scalar(0,255,0),-1);//画下一个目标点

imshow('img_src ',img _ src);

WaitKey(10);

//如果起点和目标点都存在,发送指令

if (point_start.x!=0 point_start.y!=0 /* point_next.x!=0*/)

{

cout ' start : ' point _ start ' next : ' point _ next endl;

float dist _ val=0;

dist(point_start,point_next,dist _ val);

//发送指令按屏幕

press(my nd _ x,my nd _ y,my nd _ x,my nd _ y,(int)dist _ val);

计数;

Cout '跳数:' count endl

cout endl

////鼠标位置被清除

//point _ mouse . x=0;

//point _ mouse . y=0;

char key=WaitKey(1000);//一定的延迟

}

//WaitKey(0);

//if (key==27)

//break;

}

//系统('暂停');

返回0;

}

总结与反思

1.没有进行任何处理来检测游戏的结束。今天早上试过,用了模板匹配,效果不好,就放弃了。

2.目标的准确位置没有被准确检测到。目前的检测方法只检测X方向上几乎的位置,对于Y方向没有好的方法。

如果有什么比较好的算法,欢迎大家一起讨论。

https://github.com/myzcl捷信:

相关文章
我们已经准备好了,你呢?
2024我们与您携手共赢,为您的企业形象保驾护航