顺晟科技
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捷信:
11
2022-12
17
2022-03
18
2021-11
19
2021-06
16
2021-06
16
2021-06