摘要: 微信跳一跳是时下热门的微信小游戏, 基本原理是根据按压屏幕的时间控制棋子跳过的距离, 使其跳到下一个方块上; 现利用 Android adb 工具, PC 端获取实时截图, 使用 OpenCV 库分析图片计算距离, 从而计算按压时间传回手机.
环境: Ubuntu18.04,OpenCV3.4.0
步骤:
1, 获取 Android 截图, 并传到 PC 端
adb shell /system/bin/screencap -p /sdcard/screenshot.png
adb pull /sdcard/screenshot.png .
2, 程序读入图片并获取 ROI
- cvLoadImage("screenshot.png", 1);
- cvSetImageROI(src, cvRect(0, 600, 1080, 900));
2, 对 ROI 进行模板匹配, 找到 chess 坐标
cvMatchTemplate(src, mode, result, CV_TM_CCOEFF);
3, 对 ROI 灰度处理并边缘检测
- cvCvtColor(src,srcGray, CV_BGR2GRAY);
- cvCanny( srcGray, dst, 50, 100, 3);
4, 对边缘检测结果进行遍历像素点, 获取最上与最右 (左) 的像素 点坐标, 从而得出下一方块中心点.
5, 计算两个点坐标距离乘相应系数得到时间.
adb shell input swipe 400 400 400 400 time
效果图:
具体代码如下:
- #include "cv.h"
- #include "highgui.h"
- #include <iostream>
- #include <unistd.h>
- #include <stdio.h>
- using namespace std;
- using namespace cv;
- CvPoint find_chess_point(IplImage* src, IplImage* mode)
- {
- // 模式匹配
- IplImage* result;
- CvPoint MinLocation, MaxLocation;
- double MinValue, MaxValue;
- int iwidth = src->width - mode->width + 1;
- int iheight = src->height - mode->height + 1;
- result = cvCreateImage(cvSize(iwidth, iheight), 32, 1);
- cvMatchTemplate(src, mode, result, CV_TM_CCOEFF);
- cvMinMaxLoc(result, &MinValue, &MaxValue, &MinLocation, &MaxLocation, NULL);
- return cvPoint(MaxLocation.x + (int)mode->width/2, MaxLocation.y + mode->height);
- }
- CvPoint find_next_point(IplImage* src, CvPoint chess_point, int mode = 0)
- {
- IplImage* tmp = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
- cvCvtColor(src, tmp, CV_BGR2GRAY);
- cvEqualizeHist(tmp, tmp);
- IplImage* dst = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
- cvCanny(src, dst, 50, 100, 3);
- //cvNamedWindow("doCany");
- //cvShowImage("doCany", dst);
- CvPoint next_point;
- if(chess_point.x <500)// 分左右, 如果棋子在左侧, 目标方块则在右侧
- {
- /* 遍历轮廓上顶点 */
- unsigned char *pixel = (unsigned char *)dst->imageData;// 指向首地址
- for(int row = 0; row <300 ; ++row)// 行扫描
- {
- for(int col = 500; col < 1000; ++col)// 列扫描
- {
- if(pixel[row*dst->width + col] == 255)// 灰度图 Canny 边缘处理后轮廓为白色 255
- {
- next_point.x = col;
- if(mode == 1)
- {
- /* 这是根据 chess 与 target 的连线与水平夹角总是 30 度, 只知道横坐标和角度即可算出纵坐标 */
- next_point.y = (int) (chess_point.y - (chess_point.x - next_point.x)/1.732); // 根号 3
- return next_point;
- }
- /* 找到上顶点后, 开始遍历右顶点 */
- for(int col_=999; col_> 500; --col_)// 最右列向左遍历
- {
- for(int row_=0; row_ <400; ++row_)// 从上向下遍历
- {
- if(pixel[row_*dst->width + col_] == 255)
- {
- /* 找到右顶点 */
- next_point.y = row_;
- cvLine(dst, chess_point, next_point, Scalar(255, 255, 255));
- cvShowImage("line", dst);
- cvWaitKey(100);
- return next_point;
- }
- }
- }
- }
- }
- }
- }
- else
- {
- /* 遍历轮廓上顶点 */
- unsigned char *pixel = (unsigned char* )dst->imageData;// 指向首地址
- for(int row = 0; row <300 ; ++row)// 行扫描
- {
- for(int col = 0; col < 500; ++col)// 列扫描
- {
- if(pixel[row*dst->width + col] == 255)
- {
- next_point.x = col;
- if(mode == 1)
- {
- /* 这是根据 chess 与 target 的连线与水平夹角总是 30 度, 只知道横坐标和角度即可算出纵坐标, 这个方法效果好 */
- next_point.y = (int) (chess_point.y - (chess_point.x - next_point.x)/1.732); // 根号 3
- return next_point;
- }
- /* 找到上顶点后, 开始遍历右顶点 */
- for(int col_=499; col_> 0; --col_)// 最正中间列向左遍历
- {
- for(int row_=0; row_ <400; ++row_)// 从上向下遍历
- {
- if(pixel[row_*dst->width + col_] == 255)
- {
- /* 找到右顶点 */
- next_point.y = row_;
- cvLine(dst, chess_point, next_point, Scalar(255, 255, 255));
- cvShowImage("line", dst);
- cvWaitKey(100);
- return next_point;
- }
- }
- }
- }
- }
- }
- }
- return next_point;
- }
- IplImage* cut_src_img(IplImage* src)
- {
- //SetImageROI
- cvSetImageROI(src, cvRect(40, 700, 1040, 400));// 取决于手机分辨率, 可以在上一行 resize 后, 其他地方不需要修改
- cvSaveImage("roi.jpg",src);
- return cvLoadImage("roi.jpg", 1);
- }
- float calculate_distance(CvPoint chess_point, CvPoint next_point)
- {
- float distance;
- distance = sqrt(pow(abs(chess_point.x - next_point.x), 2) + pow(abs(chess_point.y - next_point.y), 2));
- return distance;
- }
- void jump_one_jump(float distance, float parameter)
- {
- char jump[50];
- RNG rng;
- sprintf(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)(distance * parameter));
- system(jump);
- }
- int main(int argc, char** argv)
- {
- IplImage* roi_image = NULL;
- IplImage* chess_mode = cvLoadImage("jump.png", 1);
- float distance;
- CvPoint chess_point, next_point;
- cvNamedWindow("line",1);
- for(;;)
- {
- system("rm -rf screenshot.png && rm -rf roi.jpg");
- system("adb shell /system/bin/screencap -p /sdcard/screenshot.png");
- sleep(0.2);
- system("adb pull /sdcard/screenshot.png .>/dev/null");// 把 adb pull 命令的 shell 提示重定向
- sleep(0.2);
- IplImage* src = cvLoadImage("screenshot.png", 1);
- roi_image = cut_src_img(src);// 取截图中间部分
- chess_point = find_chess_point(roi_image, chess_mode);//match 找到 chess 坐标
- next_point = find_next_point(roi_image, chess_point, 1);// 像素点遍历找到 target 坐标, mode=1 效果好
- //cvLine(roi_image, chess_point, next_point, Scalar(0, 255, 255));
- //cvShowImage("line",roi_image);
- //cvWaitKey(100);
- distance = calculate_distance(chess_point, next_point);// 计算距离
- jump_one_jump(distance, 1.4);
- sleep(1);
- }
- return 0;
- }
代码链接: https://files.cnblogs.com/files/luoyijie/jump2.0.tar.gz
来源: https://www.cnblogs.com/luoyijie/p/9164263.html