颜色是一个 int32 类型的数据,我们32位的处理器,一次就能处理一个颜色,很方便是吧?
一张 100x100 的图片,一共才 10000 个点,每个点都是一个颜色,也就是说,这张图片的本质,其实是一个颜色的数组。
你可以把它理解为一个 bitmap[10000] 的一维数组,也可以把它理解为一个 bitmap[100][100] 的二维数组。
这俩东西在memory里的本质其实是一样的。
这里我们用到了 bitmap 这个单词,什么是 bitmap? bit 是位的意思,map 是图的意思,俩字连起来:位图。
Windows 系统里,管 bmp 这种格式的文件叫做位图文件,其实就是 bitmap picture 的缩写。
但实际上,在编程领域,这个单词代表的是像素点阵的意思,bmp只是一种存储格式,其核心要点,还是像素点阵。
我又要拿出这张图来了:
对于一些人来说,这种东西像是一座难以逾越的山峰,怎么识别?
学过我们前几期教程的人应该一眼就能看出来玄机了把?缺口处颜色发暗!
如果我们按照找图,或者找字的思路去做,颜色暗不一定就是缺口,因为被子的颜色也挺暗的,所以没办法二值化。
但就算阴影出现在被子上,我们的眼睛也能很好的识别到,因为被子更暗了!
所以其实这种验证码识别起来多简单啊?
他是不是给人看的?是!那就能识别,问题仅仅在于,现有的工具过于拉跨了,我们需要一套全新的工具来做这件事情。
什么工具?bitmap数据呀!
论坛里大漠也好,乐玩一号,鱼鱼也好,他们做的各种识别工具,其实都是基于 bitmap 数据开发的
使用 bitmap 数据来搞定这个东西,一下子难度就降下来了。
但是这里要先泼个冷水,按键精灵并不是一种擅长处理bitmap数据的语言,具体的事情还是得用C语言来做。
大家可以参考这篇教程:
http://bbs.anjian.com/showtopic-699075-1.aspxbitmap 处理主要有这几个概念:
width:也就是图像的宽度,因为bitmap数据通常是用一个一维数组来表示,那么访问每一个像素点的话,就需要宽度数据来确定第二行、第三行的位置
height:图像的高度,遍历像素点的时候用的
bpp:像素位宽,或者代指一个像素点的数据占多少个字节,一般在数据处理时,都会把图像转换为32位数据,占用4个字节
pitch:扫描行数据大小,就是一行像素点的数据大小,如果没有 pitch,每一个像素定位都需要用 width * 4,电脑计算乘法的性能是比较低的,因此一般会提前把这个数值计算出来。
bitmap[]:就是数据的数组了,这个数组在C语言里一般会定义为union,包括一个int32的颜色值,和4个byte的分量,方便拆分计算,这样可以少很多取分量的操作,提升性能。
一个典型的 bitmap 数据处理代码是这样的:
- for y = 0 to width - 1
- for x = 0 to height - 1
- int c = bitmap[pitch * y + x * bpp]
- // 这里的C就是循环到的颜色了,进行运算然后分析结果
- next
- next
复制代码这里再次给出拼图验证码识别的核心逻辑C语言代码来帮助大家理解 bitmap 数据处理:
- // 识别滑块拼图验证码(返回中心点坐标的位置)
- int CrackSliderVCode(char* sFile, int sw, int sh, int iTop)
- {
- // 滑块至少从 1/ 4 的位置开始,至少从拼图图形一半的地方结束
- ClImage* img = ffbot_LoadImage(sFile);
- int iPosS = sw;
- int iPosE = img->width - (sw / 2);
- int iOffsetY1 = iTop + (0.33 * sh);
- int iOffsetY2 = iTop + (0.67 * sh);
- int light = 255;
- int left = 0;
- for ( int i = iPosS; i < iPosE; i++ ) {
- int l1[43];
- int l2[43];
- int lt[43];
- for ( int p = 0; p < 43; p++ ) {
- int x = i - 21 + p;
- ClRgbQuad c1 = img->bitmapRGBA[ ((img->height - iOffsetY1) * img->width) + x ];
- ClRgbQuad c2 = img->bitmapRGBA[ ((img->height - iOffsetY2) * img->width) + x ];
- l1[p] = (max3(c1.rgbBlue, c1.rgbGreen, c1.rgbRed) + min3(c1.rgbBlue, c1.rgbGreen, c1.rgbRed)) / 2;
- l2[p] = (max3(c1.rgbBlue, c1.rgbGreen, c1.rgbRed) + min3(c1.rgbBlue, c1.rgbGreen, c1.rgbRed)) / 2;
- lt[p] = (l1[p] + l2[p]) / 2;
- }
- int lsum = 0;
- for ( int p = 0; p < 43; p++ ) {
- lsum += lt[p];
- }
- lsum = lsum / 43;
- if (lsum < light) {
- light = lsum;
- left = i;
- }
- //printf("%d, %d\n", i, lsum);
- }
- //printf("最终结果:%d, %d\n", left, light);
- ffbot_FreeBMP(img);
- return left;
- }
复制代码逻辑就是将RGB转换为HSV,然后统计V值(亮度),取平均最暗的位置,为拼图缺口位置。
学会 bitmap 处理,对于颜色来说,你已经成就太乙金仙,剩下的,就是在无尽的bitmap世界和需求中求索了。