找茬外挂制作

找茬游戏大家肯定都很熟悉吧,两张类似的图片,找里面的不同。在下眼神不大好,经常瞪图片半天也找不到区别。于是乎决定做个辅助工具来解放一下自己的双眼。

一、使用工具

  1. Qt:主要是用来做界面的
  2. OpenCV: 用于图像处理
  3. C++: 基本实现语言

Qt中OpenCV的配置在【QT】OpenCV配置中讲过了,不会配置的可以看看。

二、实现方案

我要做一个通用的找茬辅助工具,即可以在所有PC找茬游戏中使用。这意味着我们不能通过获取游戏窗口句柄来定位游戏界面。那怎么办呢?灵光一闪,我想到了截图。把有游戏画面的区域截下来,再根据图片的内容作分析,切割出两张画面,进行对比。

实现界面和使用过程中的效果如下图:

  

可以看到,有四个按钮。

锁定按钮:是用来截取游戏画面位置和找出不同点的。当点击了锁定后,用鼠标像截图一样截取游戏的图片,就像第二张图那样,覆盖住两个待对比的画面。之后软件自动分析图片位置,实现切割查找。结果如第三张图显示的那样。

找茬按钮:大家会觉得,锁定按钮都可以找出不同了为什么还有一个找茬按钮。主要是因为,锁定中截取游戏画面太过耗时了,经常要瞄准半天,要是每张图都这么截取窗口会让人有点烦。好在一般游戏过程中,我们基本不会改变游戏窗口的位置。所以,如果我们之前锁定过了,就不需要再再次截取位置了,只要根据之前的位置来截取就好了。所以找茬按钮就是根据之前的图片截取位置来获取游戏画面,然后分析给出结果的。当然,如果说窗口位置变化了,那么就只好再次用锁定按钮了。

帮助、退出:就是使用说明和退出功能。

2.1 图片截取

图片截取我自己之前也没做过,于是从网上找了一个截图的源码。但具体来源不记得了...截图的大体思路是:首先,截取全屏,用一个Label全屏显示这张截图,再用事件过滤器在这张大截图上截取小块区域。

全屏获取:Qt中有一个QScreen类,里面的grabWindow函数可以实现截取全屏的功能。

void MainWindow::grapWindowScreen()
{if (!fullScreenLabel){fullScreenLabel = new QLabel();}//获取全屏截图fullScreenPixmap,并将其放入fullScreenLabelQScreen *screen = QGuiApplication::primaryScreen();fullScreenPixmap = screen->grabWindow(QApplication::desktop()->winId());fullScreenLabel->setPixmap(fullScreenPixmap);//label全屏显示fullScreenLabel->showFullScreen();
}

获取区域截图:采用事件过滤器。其中利用了QRubberBand类(橡皮筋窗口)来实现区域选取中的掩膜版的功能。事件过滤器我自己也是第一次用,感觉挺强大的。

//事件过滤器,在全屏的截图上截取想要的部分
bool MainWindow::eventFilter(QObject *o, QEvent *e)
{if (o != fullScreenLabel){return MainWindow::eventFilter(o, e);}QMouseEvent *mouseEvent = static_cast<QMouseEvent*> (e);//true 鼠标左键按下且按键还未弹起if ((mouseEvent->button() == Qt::LeftButton)&& (mouseEvent->type() == QEvent::MouseButtonPress)){//鼠标左键标志位按下leftMousePress = true;//获取鼠标点origin = mouseEvent->pos();if (!rubberBand){rubberBand = new QRubberBand(QRubberBand::Rectangle, fullScreenLabel);}rubberBand->setGeometry(QRect(origin,QSize()));rubberBand->show();return true;}//true 鼠标左键按下并拖动if ((mouseEvent->type() == QEvent::MouseMove)&& (leftMousePress)){if (rubberBand){rubberBand->setGeometry(QRect(origin, mouseEvent->pos()).normalized());}return true;}//鼠标左键松开if ((mouseEvent->button() == Qt::LeftButton)&& (mouseEvent->type() == QEvent::MouseButtonRelease)){//鼠标标志位弹起leftMousePress = false;if (rubberBand){//获取橡皮筋框的终止坐标termination = mouseEvent->pos();rect = QRect(origin, termination);rubberBand->hide(); //把橡皮筋窗口隐藏//根据橡皮筋框截取全屏上的信息,并将其放入shotScreenLabelshotScreenPixmap = fullScreenPixmap.grabWidget(fullScreenLabel,rect.x(),rect.y(),rect.width(),rect.height());shotScreenPixmap.save("tmp.jpg"); //不会QPixmap转QImage或Mat只好出此下策
fullScreenLabel->hide();findDifference(); //找不同的处理过程if(!bNeedLocation) //不需要重新锁定表示结果有效,这时显示结果
            {//将shotScreenLabel的用户区大小固定为所截图片大小shotScreenLabel->setFixedSize(showDifferenceImage.width(), showDifferenceImage.height());shotScreenLabel->setPixmap(QPixmap::fromImage(showDifferenceImage));shotScreenLabel->show();}}return true;}return false;
}

2.2 图片定位

我们截取的图片基本上是下面的样子:

 

区别就是图片是横向排列还是纵向排列的。两张图片在水平或者垂直维度上像素点是对应的。我们先假设图片是两张横向排列的(就像第一张图一样),如果遇到了纵向排列的,我们把图旋转90即可。

图片定位分下面几个步骤:

1. 找到两张图片对应列的横向距离

2. 找到两张图片的左右界限

3. 找到两张图片的上下界限

2.2.1找到两张图片对应列的横向距离

就是如图所示的距离

方法是在图片的左半部分找N条线,我取的是在图片从左边开始的1/10到4/10的范围内以均匀的间隔取8条线。然后从4/10到图片最右边依次扫描每一列像素,找到像素点差异最小的那一列作为相应的匹配位置。两个列坐标相减就是所求的距离。取8条线是为了增加容错的能力。在取位置时,我们用出现次数最多的那个距离。

注意:再判断两个像素是否相同时要考虑噪声。我这里认为三个通道的像素差异加起来超过10的才算是不一样的点。

查找匹配线的代码:

//查找垂直的匹配线
void findFitLineVertical(Mat src, int y, int& fit_y)
{fit_y = y; //初始化为y
    Mat src2;src.copyTo(src2);int different_num = src.size().height;for(int c = src.size().width * 0.4 + 1; c < src.size().width; c++) //遍历垂直方向的线条
    {int different_num_tmp = 0;for(int r = 0; r < src.size().height; r++) //遍历线条上的每个点
        {int d = 0;for(int k = 0; k < 3; k++){d += abs(src.at<Vec3b>(r, c)[k] - src.at<Vec3b>(r, y)[k]);}if(d > 10)  //很关键,要允许有少量的噪声 否则误差很大different_num_tmp++;}if(different_num_tmp < different_num){different_num = different_num_tmp;fit_y = c;//line( src2, Point(c, 0), Point(c, src.size().height - 1), Scalar(0,255,0), 3, 8 );//imshow("pic2", src2);//cvWaitKey(15);
        }}return;
}

获取相应距离的代码:

const int N = 8; //需要匹配的线条数int s = src.size().width *0.1; //测试线条的开始列int e = src.size().width * 0.4; //测试线条的结束列int gap = (e - s + 1) / (N - 1); //测试线条取样间隔int dis = 0;  //两幅图的横向间隔int disnum = 0;for(int i = 0; i < N; i++){int dis_tmp = 0;int y = s + i * gap;//line( src2, Point(y, 0), Point(y, src.size().height - 1), Scalar(255,0,0), 3, 8 );//imshow("pic1", src2);int fit_y;findFitLineVertical(src, y, fit_y);//下面这段通过典型的找数组中出现超过一半的数字的算法来找两幅图的横向间隔dis_tmp = fit_y - y;if(disnum == 0){dis = dis_tmp;disnum++;}else{if(dis_tmp == dis)disnum++;elsedisnum--;}//printf("%d, %d, %d\n", y, fit_y, fit_y - y);//line( src2, Point(fit_y, 0), Point(fit_y, src.size().height - 1), Scalar(0,255,0), 3, 8 );//imshow("pic1", src2);//cvWaitKey(0);}

2.2.2找到两张图片的左右界限

有了对应点距离后,找左右界限就容易多了。左边界就从图片的最左边开始扫描,看看理论的对应位置上不用的点是否超过了阈值,超过了就表示这不是图片的起始位置,继续扫描下一列,直到找到左边界。右边界同理,就是从右往左找。

2.2.3找到两张图片的上下界限

有了左右界限后,找上下界限也容易了。找上边界时把图片1左右边界范围内的像素和相应行图片2左右边界范围内的像素做比较,相同点超过阈值就表示是对应的边界。下边界同理。

注意:这种找出来的边界可能比实际的图片大一些,不过不影响结果。

2.2.4当图片方向不对时旋转图片

我们第一次找左右边界时,如果没有找到匹配线,这时我们就旋转图片再次找左右边界。如果还没有可能就是图截取的不好了。这时候就需要重新锁定了。

// Load an imageMat srcRGB = imread("tmp.jpg");Mat srcRotate;//imshow("src", srcRGB);bool bRotate= false; //图片是否经过旋转//int left1, left2, right1, right2, up, down;bool b1 = findLeftRightBoard(srcRGB, left1, right1, left2, right2);if(!b1) //第一次没有找到匹配线 有可能是因为两张图是上下存放的 我们把图片旋转一下 再试试
        {srcRotate = rotateImage(srcRGB);srcRGB.release();srcRotate.copyTo(srcRGB);bRotate = true;}b1 = findLeftRightBoard(srcRGB, left1, right1, left2, right2);bNeedLocation |= (!b1);if(bNeedLocation) //如果两个方向都失败了 需要重新锁定
        {QMessageBox::warning(this, tr("error"), tr("Please locate again!\n图片不理想,请重新锁定!"));return;}findUpDownBoard(srcRGB, left1, right1, left2, right2, up,  down);

2.2.5图片类型转换

在上述处理过程中需要各种图片类型的转换。抓屏获取的是QPixmap类型,图片处理用的是Mat类型,图片显示用的是QImage类型。

QPixmap转Mat : 在网上找了很久都没找到。只好先把图片保存在当前目录,再用Mat类型读出。

Mat转QImage: 我直接在网上找的代码

QImage const copy_mat_to_qimage(cv::Mat const &mat, QImage::Format format){QImage image(mat.cols, mat.rows, format);for (int i = 0; i != mat.rows; ++i){memcpy(image.scanLine(i), mat.ptr(i), image.bytesPerLine() );}return image;}QImage const mat_to_qimage_cpy(cv::Mat &mat)
{if(mat.type() == CV_8UC3){cv::cvtColor(mat, mat, CV_BGR2RGB);return copy_mat_to_qimage(mat, QImage::Format_RGB888);}if(mat.type() == CV_8U){return copy_mat_to_qimage(mat, QImage::Format_Indexed8);}return QImage();
}

2.3查找不同

当我们得到两张待对比的图片后,我们通过逐点对比获取不同点。把两张图片像素点差异超过30的取出来,做二值化。然后腐蚀膨胀去噪声。最后通过连通区域提取来把不同的区域取出并画出来。

这部分直接看代码非常直接:

        Mat mSub = abs((img1 - img2)) > 30;Mat gray;cvtColor(mSub, gray, CV_RGB2GRAY);gray = gray > 0;erode(gray, gray, Mat());dilate(gray, gray, Mat());erode(gray, gray, Mat());dilate(gray, gray, Mat());
//连通区域提取相关vector<vector<Point> > contours;vector<Vec4i> hierarchy;//连通区域提取
        findContours(gray, contours, hierarchy, CV_RETR_CCOMP , CV_CHAIN_APPROX_NONE );for(int i = 0; i < contours.size(); i++){if(contours[i].size() >= 4) // 点数大于等于 的连通区域才考虑
            {Rect r = boundingRect(contours[i]);rectangle(img1, r, Scalar(255,0,255), 2); //画出连通区域
            }}//结果转换为QImageif(!bRotate)showDifferenceImage = mat_to_qimage_cpy(img1);else  //如果旋转过,需要再转270恢复原来的方向
        {img1 = rotateImage(img1);img1 = rotateImage(img1);img1 = rotateImage(img1);showDifferenceImage = mat_to_qimage_cpy(img1);}

2.4界面美化

在做的时候我争取做的漂亮一点。Qt的界面加图片很容易,所以我就找或者制作了一些图片。效果在上面显示过了。

背景图片是在网上找的。

按钮的字是在随便找的字体网站上做的http://www.diyiziti.com/katong。做了几个版本。本来打算做按钮点击时候效果的,后来懒得做了,只用了其中一版。

  

  

  

我自己还设计了个中国风的Logo:

“茬”就表示找茬游戏,Help表示辅助工具,整体的意思就是找茬辅助工具。“茬”字跟上面一样都是网站声成的。毛笔圈是我在百度上找的图片,然后用美图秀秀抠图把毛笔圈扣成透明的,加在“茬”字外面。HELP在美图秀秀上的文字添加上去。

程序左上角的图标可以通过在cpp文件中加入下面语句实现:

setWindowIcon(QIcon(":/pic/chaH.jpg"));

程序的exe图标需要先自己制作一个ico图标文件,可以用iconEditor软件制作。然后把图标放在工程目录下面,再在pro文件里加入

RC_ICONS = chaH2.ico

其中chaH2.ico是自己的图标名称。

三、实现过程中的问题

3.1截图过程中的问题

QT自带的截图似乎噪声很多,也许是我保存成图片再读取的过程中像素发生了变化。之前实验的时候用的是QQ截图,然后做处理,噪声很少,效果也不错。改成QT的截图后效果变差了很多,为了容错不得不增大阈值。结果导致有些图片区别被当做噪声去掉了。

3.2阈值选取问题

多大的阈值合适?这个可能需要进一步分析。就拿下面的图来说,里面有5处不同,那一缕头发,一个手纹的真心跟噪声似的。阈值调小了,这里面的差异倒是出来了,可是换成别的图就是一堆噪点。这里顺带说一句,QQ的大家来找茬,不同时一块一块的,即图片基本都是整块不同,比较好找。而网上其他杂七杂八的,很可能使用photoshop在某个边角抹了抹,很难识别。

3.3图片截取的失败方案:Hough变换

Hough变换是用来检测图片中直线的。我开始想,直接用霍夫变换提取直线,然后找相同大小的区域就可以了。可是实现后我发现霍夫变换的效果很差,那些很明显的直线都不能完整得到。

3.4定位窗口

开始想做针对QQ大家来找茬的外挂。我用按键精灵确定了游戏窗口的名字是“大家来找茬”但是我在QT上通过这个名字却无法获取有效的窗口。为什么呢?

实验时的代码如下,有人帮我看看么?

#include <windows.h>
#include "mainwindow.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();HWND p = FindWindowA(NULL, "大家来找茬");qDebug("p = %d\n",(int)p);// 获取屏幕鼠标坐标
    POINT pt;GetCursorPos(&pt);qDebug("%d %d\n",pt.x,pt.y);HWND h = WindowFromPoint(pt);qDebug("%d\n",h);//wchar_t text[200];//GetWindowText(h,text,200);//qDebug("%s\n",text);return a.exec();
}

四、应用程序即源码下载

http://yun.baidu.com/share/link?shareid=782947870&uk=2757788903

转载于:https://www.cnblogs.com/dplearning/p/4612104.html

【QT】找茬外挂制作相关推荐

  1. C#写的QQ找茬外挂

    这个暑假转眼就要过完了.本来打算在学校干两件事:学车和竞赛,结果竞赛没能杀进全国总决赛,想想也是意料中的事.学车就更郁闷了,从上学期开学报名到科目一考试,足足等了近五个月.终于摸到真车了,结果每天冒着 ...

  2. 基于OpenCV与MFC的大家来找茬外挂[升级版]

    上次编写的大家来找茬外挂虽然是实现了查找不同区域并把不同的区域显示在屏幕上,但是得手动点击,显得很是麻烦,所以最近把程序又改了一改,实现了自动点击功能. 程序的编写流程: 1.做一幅游戏窗口背景图片S ...

  3. 【转】菜鸟也来打造全自动QQ大家来找茬外挂

    菜鸟也来打造全自动QQ大家来找茬外挂 转载请注明:www.UNPACK.cn by y3y3y3 定位关键代码 zSound\\ClickRight.wav .text:0042DB40 loc_42 ...

  4. C#编写QQ找茬外挂

    QQ找茬外挂,用C#代码编写. 使用方法 这个工具的主要运行流程很简单: 游戏截图->比较图片->标记图片不同点. 实现代码 截图的处理类ScreenCapture: /// /// 提供 ...

  5. QQ游戏美女找茬外挂

    前段时间看到别人玩QQ游戏,美女来找茬,突然之间想到自己可以做个小外挂,自动比较两幅图,把不同之处标出来.软件自动化测试和开发简单的游戏外挂很相似.都是控制UI,然后模拟键盘和鼠标操作 思路: 1. ...

  6. 才子佳人与QQ游戏美女找茬外挂实现

    "我未成名君未嫁,可能俱是不如人?",唐朝才子罗隐一生怀才不遇,屡考未中,话说当初以寒士身份赴举,路过锺陵,即今天的江西进贤,结识了当地的一名乐营女子云英,郎才女貌,把酒言欢,一醉 ...

  7. 自己写的QQ美女找茬外挂

    这里首先说一下原理:利用截屏的 技术,讲当前QQ美女找茬的图片截屏下来,然后分析两幅图片相同位置的像素点,当两个点的差大于某个阀值时,将该点的颜色设置为红色. 下面根据 源码具体讲解:     1.首 ...

  8. 用VC++, OpenCV写大家来找茬外挂

    本文作者:longlongago     博客地址:http://blog.csdn.net/longlongago2000 1.问题:最近看到很多人在玩 大家来找茬,于是自己也下载玩了一下,但是由于 ...

  9. 我们来找茬外挂思路之一

    之前完QQ美女找茬,实在是玩的不给力,两只眼睛都快看不见了...所以就想做个外挂,限于能力,做不了特别犀利的,不过想到一个思路,就是做图片快速切换对比,效果还不错,而且外挂功能只要能截屏切换图片,很是 ...

  10. 也谈QQ美女找茬外挂的编写

    不久前发表在首页的QQ美女找茬(外挂)学习笔记一文让我深受启发,我看了该文章后,也自己动手实现了一个外挂,效果挺不错的.请看截图: 从图中可以看出,我没有让结果显示在原游戏窗口中,而是显示在了外挂窗口 ...

最新文章

  1. 非递归一次性加载分类数据到TreeViw
  2. ggplot2笔记3:工具箱——误差线、加权数、展示数据分布
  3. python绘制散点图-Python:matplotlib绘制散点图
  4. java如何进行字符串拼接?
  5. CSS实现垂直居中的方法
  6. Android Intent 用法总结
  7. android layer阴影,Android Layer-List实现自定义Shape阴影
  8. UnrealEngine4 学习总结备忘
  9. 水泵综合性能测试系统软件,水泵综合性能测试系统
  10. react 点击使父元素消失_在 React 组件中使用 Refs 指南
  11. st7789 旋转_st7789v spi通信
  12. mysql通过视图插入数据_数据库视图 sql
  13. Drupal迁移 | 如何创建一个Drupal 7 到 Drupal 9 的迁移路线图
  14. 深度解析javaScript常见数据类型检查校验
  15. 通过对虚拟磁盘进行碎片整理来提高VMware VM性能
  16. 2021年美赛解题思路汇总Final!!!
  17. 不是所有的努力都会有结果,但是你若不努力,谁能替你坚强??
  18. android下拉框代码,Android下拉列表spinner的实例代码
  19. 子类调用父类 关键字 super
  20. 深入理解Android之AOP

热门文章

  1. 一文读懂NFT(非同质化通证)
  2. oracle卸载ogg,Oracle GoldenGate(ogg)安装经验大汇总,采坑总结,绝对干货!
  3. 实验一:VLAN实验
  4. idm integration module(idm) Chrome插件 安装
  5. c51单片机蜂鸣器汇编语言,51单片机控制蜂鸣器播放5首歌曲汇编程序
  6. Book04--修改软件的艺术:构建易维护代码的9条最佳实践
  7. jzoj5336 【NOIP2017提高A组模拟8.24】提米树 (dfs序dp,奇异姿势dp)
  8. 浩方对战平台原理初步分析
  9. Unity操作文件对话框
  10. 开源项目9GAG源码解析与Material改造(三)