Xiangism

从一个无知角落里开始,蹒跚学步,一个未知到另一个未知,在跌跌撞撞中越走越快,越走越远,最后宇宙也为之开源。对于探索者来说,最后他们的思想总是变得和自己的足迹一样伟大。
  博客园  :: 首页  :: 联系 :: 管理

QQ游戏找茬终结者

Posted on 2012-10-27 13:56  Xiangism  阅读(703)  评论(3编辑  收藏  举报

回想起四年前,自己大二刚开始学C#时,发现Bitmap类中有GetPixel方法的时候一阵狂喜。因为那时我玩过一款QQ游戏——大家来找茬,这个游戏是从画幅图中找出不一样的地方。如果可以获取到图片的每个像素值,只要发现其像素值不一样,即可判断图的这个地方不一样了(当然,这得假设腾讯没有对图片进行一些小的处理,比如,一个图的像素值RGB都加1,这时在肉眼是看不出区别的,但可以防止我用这样的方法做出外挂。而实际上,腾讯没有做这样的处理,所以~~~)。再用其它的一些办法(我的方法是自己再创建一个透明窗体,覆盖在一个图片上,不同的像素点用红色标记出来),将这些不同的像素点,显示给我们看,在玩游戏时就可以一下子找到所有的不同。(注意,这里是是标出所有的不同像素点,而还是得我们自己手动去点击

如下所示,将一幅图中所有不同的地方都用红色标记出来,然后我们只需要点击。

然后,因为要实现上面我所说的这些功能,自己学会了用C#调用win32API截屏,并且也发现可以用win32API获取其它窗体的位置、区域之类的信息,可以说这个QQ找茬的外挂开启了自己学习win32API之路。

这个C#版的程序没有多大的难度。

今年上半年时,自己用c++也实现了同样的功能,不过为了和四年前有所不一样,准备完善聚类分析。找茬游戏中只有5处不一样的地方,而用像素找出来的不一样点将非常多,如果程序可以自动将所有这些点正确地聚类成5类,那么程序就可以自动点击了。其实这个功能自己当初就想过,可当时自己能力、视野都有限,没能完成。不过,现在自己在图像处理上小有所获后,再来看这个问题,觉得可以实现了。

首先,自己尝试用了openCV中的cvKMeans2方法,其使用K均值聚类。但实现效果不好,错误率很高。

现在本人想到的方法是,在那些有差别的像素点上找连通区域,最后只取5个最大的连通区域。如果图片中的5个不同区域不彼此靠近,那么就可以完美地解决问题,但实际情况有些是有几个块会连在一起,这样就得用另外的方法重新考虑了。不过,先将简单的情况实现了再说。而找区域连通性的方法自己以前就有实现过,那么剩下的工作就非常少了。于是就有了下面的效果(非常惊艳)——

(我将分成的5个分别用不同的颜色标识出来了) 这时就可以操作鼠标去自动点击了,5个不同的点可以瞬间找出来。以至于在游戏中测试这个程序时,其他的玩家一盘下来就都跑了~~

上面用的区域检测算法有参照《图像编程精髓:从开发自己的Photoshop开始》,实现的思路不是很复杂,在这就直接贴出代码,就不去仔细说明了。

View Code
  1     namespace image
  2     {
  3         using namespace std;
  4         namespace  //名字空间内的私有函数
  5         {
  6             typedef struct 
  7             {
  8                 int Sign;   //标志
  9                 int Area;   //面积
 10             }AreaInfo; //用于统计面积的大小与标记的关系
 11 
 12             inline bool AreaSortFun(const AreaInfo &a1,const AreaInfo &a2)
 13             {
 14                 return a1.Area>a2.Area;
 15             }
 16             inline  void ReplaceSign(TwoDimesionArray<int> &signs,int bottom,int srcSign,int dstSign)
 17             {
 18                 for (int i=0;i<signs.GetWidth();++i)
 19                 {
 20                     for (int j=0;j<=bottom;++j)
 21                     {
 22                         if(signs.GetValue(i,j)==srcSign)
 23                             signs.SetValue(i,j,dstSign);
 24                     }
 25                 }
 26             }
 27  
 28 
 29         }//end namespace
 30 
 31         inline int IsHaveAreaTag(vector<AreaInfo> &areas,int tag) 
 32         {
 33             for (int i=0;i<areas.size();++i)
 34             {
 35                 if(areas[i].Sign ==tag)
 36                     return i;
 37             }
 38             return -1;
 39         }
 40 
 41         inline TwoDimesionArray<int> GetAreaInfo(TwoDimesionArray<bool> bs,  vector<AreaInfo> &areaInfos)
 42         {
 43             //vector<vector<int>> sameTag;
 44             TwoDimesionArray<int> signs(bs.GetWidth(),bs.GetHeight());
 45             signs.SetAllValue(0);
 46             int signNo=1;
 47             for (int x=0;x<bs.GetWidth();++x) //先处理顶行
 48             {
 49                 if(!bs.GetValue(x,0))
 50                 {
 51                     continue;
 52                 }
 53                 while(x<bs.GetWidth() && bs.GetValue(x,0))
 54                 {
 55                     signs.SetValue(x,0,signNo);
 56                     ++x;
 57                 }
 58                 ++signNo;
 59             }
 60             for (int j=1;j<bs.GetHeight();++j)  //处理最左和最右列
 61             {
 62                 if(bs.GetValue(0,j))  //最左列
 63                 {
 64                     if(bs.GetValue(0,j-1))
 65                     {
 66                         signs.SetValue(0,j,signs.GetValue(0,j-1));
 67                     }
 68                     else
 69                     {
 70                         signs.SetValue(0,j,signNo++);
 71                     }
 72                 }
 73                 if(bs.GetValue(bs.GetWidth()-1,j)) //最右列
 74                 {
 75                     if(bs.GetValue(bs.GetWidth()-1,j-1))
 76                     {
 77                         signs.SetValue(bs.GetWidth()-1,j,signs.GetValue(bs.GetWidth()-1,j-1));
 78                     }
 79                     else
 80                     {
 81                         signs.SetValue(bs.GetWidth()-1,j,signNo++);
 82                     }
 83                 }
 84             }
 85             for (int j=1;j<bs.GetHeight();++j)
 86             {
 87                 for (int i=1;i<bs.GetWidth()-1;++i)
 88                 {
 89                     if(!bs.GetValue(i,j))
 90                         continue;
 91                     if(bs.GetValue(i+1,j-1)) //右上
 92                     {
 93                         int sign=signs.GetValue(i+1,j-1);
 94                         signs.SetValue(i,j,sign);
 95                         if(bs.GetValue(i-1,j) && signs.GetValue(i-1,j) != sign)  //右上与左前不同标记
 96                         {
 97                             //AddTag(sameTag,signs.GetValue(i-1,j),tag);
 98                             ReplaceSign(signs,j,signs.GetValue(i-1,j),sign);
 99                         }
100                         else if(bs.GetValue(i-1,j-1) && signs.GetValue(i-1,j-1) != sign) //右上与左上不同标记
101                         {
102                             //AddTag(sameTag,signs.GetValue(i-1,j-1),tag);
103                             ReplaceSign(signs,j,signs.GetValue(i-1,j-1),sign);
104                         }
105                     }
106                     else if(bs.GetValue(i,j-1))  //正上
107                     {
108                         signs.SetValue(i,j,signs.GetValue(i,j-1));
109                     }
110                     else if(bs.GetValue(i-1,j-1)) //左上
111                     {
112                         signs.SetValue(i,j,signs.GetValue(i-1,j-1));
113                     }
114                     else if(bs.GetValue(i-1,j)) //左前
115                     {
116                         signs.SetValue(i,j,signs.GetValue(i-1,j));
117                     }
118                     else
119                     {
120                         signs.SetValue(i,j,signNo++);
121                     }
122                 }
123             }  //end two for
124 
125             for (int i=0;i<signs.GetWidth();++i)
126             {
127                 for (int j=0;j<signs.GetHeight();++j)
128                 {
129                     //find(areas.begin(),areas.end())
130                     //if()
131                     int t=IsHaveAreaTag(areaInfos,signs.GetValue(i,j));
132                     if (t!=-1)
133                     {
134                         ++areaInfos[t].Area;
135                     }
136                     else
137                     {
138                         AreaInfo info;
139                         info.Sign=signs.GetValue(i,j);
140                         info.Area=1;
141                         areaInfos.push_back(info);
142                     }
143                 }
144             }//end for
145             return signs;
146         }//end function
147 
148 
149     }//end namespace image

可运行的程序在这 FindDifference  

源程序在SVN上 :svn://svn.jundie.net/FindDifference

这个程序中自己用的是CxImage图像库,这个库自己应该快用了一年多的时间了,现在越来越觉得这个库太不灵活了,与MFC的结合也不是很好。准备以后彻底转向openCV的学习。

这里总结出一个经验:掌握知识最好的文法就是尝试着用它,改进它,并且创新~~