最近在研究windows的鼠标自动控制程序,前面也写过一篇这方面的博客,今天又在基本控制的基础上加上了一些好玩的东西,那就是在画图软件中自动画图,是不是很炫酷。
基本思路是既然我们已经能控制鼠标的移动和点击了,那只要能打开画图软件,至少能在上面胡乱画些点和线。但是,乱画没什么意义啊,能不能画点有意思的东西呢?答案当然是肯定的了。既然画图那就画点好看的图片,好看的图片可以去百度下载,也可以是你朋友或者亲戚的照片,只要让我们的程序照着这张图片画不就ok了,图片的信息其实就是一些行列像素点,每个像素点由若干表示颜色的字节组成,以24位图为例,每个像素点包含3个字节,分别表示R、G、B,只要我们的程序能读取到这些信息,它就认识了这张图片,就可以照着图片画出相同的样子了。为了简单起见,我们先画黑白色调的图,这样就需要将图片进行二值化,二值化就是把每个像素点用0和1来表示,我采用的二值化算法是(R+G+B)/3的值小于128就取0,反之就取1。这样只要是0的点我们就把鼠标移动到相应的位置,然后点击就画出了一个点,而值为0的点无操作。这样一幅黑白照片就诞生了。
这里有几点注意事项,一是windows默认图片的行是4字节对齐的,对于不满足的行,会自动补充几个字节,而这几个字节是没有意义的,必须要过滤掉才能画出准确的图片,不然多几个点的图断行就会出现偏差,导致整张图片会有倾斜。我一开始就在这里栽了跟头,弄了好久才搞明白。二是画点的速度不能太快,太快了画图软件会反应不过来,他会错误得认为两次点击是一次点击,这样就会画出一条线,而不是我要的两个点,我的实验结果是两次画点间隔至少要2ms。
下面是读取图片并二值化得到一个维数组和图片宽和高的函数
int getMapArray(char *mapname, unsigned char *maparray, int* mapwidth, int* mapheight){
int bytes = 3;//24位图
BITMAPFILEHEADER fh;
BITMAPINFOHEADER ih;
int Row,Col;
int i,j,k;
int added;
int index=0;
FILE *f,*p;
unsigned char *imagearray=(unsigned char*)calloc(6000*6000,sizeof(unsigned char));
unsigned char *imagearray_tmp=(unsigned char*)calloc(6000*6000,sizeof(unsigned char));
if((f=fopen(mapname,"rb"))==NULL){
printf("open %s error,please check\n",mapname);
return 0;
}
fread(&fh,sizeof(BITMAPFILEHEADER),1,f);
if(fh.bfType!='MB')
{
printf("This is not a BMP picture, type is %s\n",fh.bfType);
return 0;
}
fh.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
fread(&ih,sizeof(BITMAPINFOHEADER),1,f);
mapheight[0]=Row=ih.biHeight;
mapwidth[0]=Col=ih.biWidth;
added = 4-(Col*bytes)%4;
printf("added = %d\n",added);
if(Row>2000||Col>2000){
printf("BMP picture is too large, please cheese a small one, row=%d col=%d\n",Row,Col);
return -1;
}
//每个像素点用3个字节表示(RGB),windows 会将图片的每一行4字节补齐,所以会多出一些字节,必须过滤掉这些字节
fread(imagearray_tmp,sizeof(unsigned char),Row*(Col+1)*bytes,f);
//过滤补齐的点
for(i=0;i<Row*(Col*bytes+added);i++){
for(j=0;j<added;j++){
if(i%(Col*3+added)==Col*3+j) i++;
}
imagearray[index++] = imagearray_tmp[i];
}
//二值化
int sum;
index = 0;
for(i=0;i<Row*(Col*bytes+added);i+=bytes){
sum = 0;
for(j=0;j<bytes;j++){
sum+=imagearray[i+j];
}
if(sum/3<100)
maparray[index++]=0;
else
maparray[index++]=1;
}
fclose(f);
return 0;
}
主函数的功能就是调用上面的函数,得到图片信息,再调用move和click函数对鼠标进行操作画出图片,move和click函数在我的上一篇博客中已经贴出了代码,main函数代码如下:
int main(int argc,char *argv[]){
int mapwidth[1];
int mapheight[1];
int i=0;
int x=0;
int y=0;
int speed;//0:fast;1:namal;2:slow
int tmp;
char* mapname = argv[1];
unsigned char* maparray=(unsigned char*)calloc(2000*2000,sizeof(unsigned char));
if(argv[2]==NULL){
speed=2;
}
else{
tmp = atoi(argv[2]);
if(tmp==1)
speed=5;
else if(tmp==2)
speed=10;
else
speed=2;
}
getMapArray(mapname, maparray, mapwidth, mapheight);
system("start /b C:/Windows/system32/mspaint.exe");
int startx = (1920-mapwidth[0])/2;
int starty = (1080+mapheight[0])/2;
move(startx,starty);
for(i=0;i<mapheight[0]*mapwidth[0];i++){
if(maparray[i]==0){
x = startx+i%mapwidth[0];
y = starty-i/mapwidth[0];
move(x,y);
click(0,false);
Sleep(speed);
}
}
return 0;
}