• 如果您想对本站表示支持,请随手点击一下广告即可~
  • 本站致力于提供原创、优秀的技术文章~
  • 有任何疑问或建议 均可以在站点右侧栏处 通过各种方式联系站长哦~
  • 图像识别 – C++读取BMP位图入门

    C/C++ EXP 291阅读 0评论

    前言

    要识别图像中的字符,首先要会处理图像,把图像的信息读出来。这就必须先了解图像的结构,存储方式。这里推荐清华大学出版的一本《数字图像处理编程入门》,第一章的“Windows位图和调色板”有详细的介绍。

    对于彩色图,可以用RGB模型来表示,这是BMP位图最常用的编码方法。基本上所有颜色都可以用这三种颜色的组合来形成。但实际上也有一些差别,小于24位图都利用到了调色板(也叫颜色表),也就是一张R、G、B表,主要是为了节省存储空间。

    实际上,使用RGB模型表示的图像,也叫 三通道图(每个通道对应R、G、B)。三通道图每个像素值都通过R、G、B混合计算生成,R、G、B的取值范围为0~255。

    与之对应的则是 单通道图,也称灰度图,其每个像素点只能有有一个值表示颜色,它的像素值范围为0~255,0是黑色,255是白色,中间值是一些不同等级的灰色。

    除此之外,还有 四通道图 RGBA,最后的A表示透明度。


    BMP位图的数据结构

    BMP的数据结构由4部分组成:

    数据段名称 对应的Windows结构体定义 大小(Bytes)
    位图文件头 BITMAPFILEHEADER 14
    位图信息头 BITMAPINFOHEADER 40
    调色板 由颜色索引数决定
    位图数据 由图像尺寸决定

    BMP图像的文件结构


    P1. 位图文件头:BITMAPFILEHEADER

    这部分是一个结构体,其定义如下:

    这个结构的长度是固定的,为14个字节(WORD为无符号16位整数,DWORD为无符号32位整数),各个域的说明如下:

    变量名 地址偏移 大小(Bytes) 作用
    bfType 0000H 2 指定文件类型,可取值为:
    BM : Windows 3.1x, 95, NT, …
    BA : OS/2 Bitmap Array
    CI : OS/2 Color Icon
    CP : OS/2 Color Pointer
    IC : OS/2 Icon
    PI : OS/2 Pointer
    实际上windows下固定值为0x424D,即字符串“BM”
    bfSize 0002H 4 说明该位图文件的大小(单位字节,且包括这14个字节)
    bfReserved1 0006H 2 保留字,必须设置为0
    bfReserved2 0008H 2 保留字,必须设置为0
    bfOffBits 000AH 4 说明从文件头开始到实际图像数据之间的字节偏移量
    即上图中前三个部分的长度之和)。
    这个参数是非常有用的,因为位图信息头和调色板的
    长度会根据不同情况而变化,而我们可以利用这个
    偏移值迅速从文件中读到位图数据

    P2. 位图信息头:BITMAPINFOHEADER

    这部分也是一个结构体,其定义如下:

    这个结构的长度是固定的,为40个字节(LONG为32位整数),各个域的说明如下:

    变量名 地址偏移 大小(Bytes) 作用
    biSize 000EH 4 指定这个结构的长度,为40字节
    biWidth 0012H 4 指定图象的宽度,单位是像素
    biHeight 0016H 4 指定图象的高度,单位是像素。
    注:这个值除了用来描述图像的高度之外,它还用于指明
    这个图像是倒向还是正向的图像。
    若是正数则是倒向图像,若是负数则是正向图像。
    多数BMP图像都是正数,亦即是倒向图像。
    biPlanes 001AH 2 为目标设备说明颜色的平面数。
    其值总是为1。
    biBitCount 001CH 2 指定表示颜色时要用到的位数,常用的值为:
    1:黑白二色图
    4:16色图
    8:256色
    24:真彩色图
    注:新的BMP格式支持32位色
    biCompression 001EH 4 说明图像数据的压缩类型,取值范围为:
    0:BI_RGB(不压缩,最常用)
    1:BI_RLE8(8比特游程编码,只用于8位位图)
    2:BI_RLE4(4比特游程编码,只用于4位位图)
    3:BI_BITFIELDS(比特位,用于16/32位位图)
    4:BI_JPEG(JPEG位图,仅用于打印机)
    5:BI_PNG(PNG位图,仅用于打印机)
    注:我们后面所讨论的只有不压缩的情况。
    biSizeImage 0022H 4 指定实际的位图数据占用的字节数。
    若biCompression=BI_RGB,则可设置为0。
    其计算公式为:
    biSizeImage = biWidth’ × biHeight
    注:biWidth’ 必须是4的整倍数,因此公式里不是 biWidth。
    如:若 biWidth=240,则biWidth’=240
      若 biWidth=241,则biWidth’=244
    biXPelsPerMeter 0026H 2 指定目标设备的水平分辨率。
    单位是每米的像素个数,是有符号整数。
    biYPelsPerMeter 002AH 2 指定目标设备的垂直分辨率。
    单位是每米的像素个数,是有符号整数。
    biClrUsed 002EH 2 指定本图象实际用到彩色表中的颜色索引数。
    若为0,则说明使用所有调色板项(数量为\small{2^{biBitCount}})。
    biClrImportant 0032H 2 指定本图象中重要的颜色数。
    若为0,则认为所有的颜色都是重要的。

    P3. 调色板/颜色表:Palette

    当然,这里是对那些需要调色板的位图文件而言的。

    前面已经说过,有些位图(如真彩色图)是不需要调色板的,即在 位图信息头BITMAPINFOHEADER 的后面直接就是位图数据。

    调色板实际上是一个数组,共有biClrUsed个元素(如果该值为零,则有\small{2^{biBitCount}}个元素)。

    数组中每个元素的类型是一个RGBQUAD结构,占4个字节,其定义如下:


    P4. 实际的位图数据

    对于用到调色板的位图,图象数据就是该象素颜在调色板中的索引值。

    而对于不用调色板的位图,又分为2色、16色、256色位图和真彩色位图:

    • 2色位图:用1位表示该象素的颜色(一般0表示黑,1表示白),所以一个字节可以表示8个象素
    • 16色位图:用4位表示一个象素的颜色,所以一个字节可以表示2个象素
    • 256色位图:一个字节刚好可以表示1个象素
    • 真彩色图:图象数据就是实际的R、G、B值,即三个字节才能表示1个象素,这使得图像颜色显得更亮丽呢,但在存储上更费空间。

    要注意两点:
    ① 位图数据的每一行的字节数必须是4的整倍数;若不是,则需要补齐(这在前面介绍 biSizeImage 时已经提到了)。
    ② 一般来说,BMP文件的数据从下到上,从左到右的(即倒向图像,者在前面介绍 biHeight 时已经提到了)。
      也就是说,从文件中最先读到的是图象最下面一行的左边第一个象素,然后是左边第二个象素……接下来是倒数第二行左边第一个象素,左边第二个象素……依次类推 ,最后得到的是最上面一行的最右一个象素。


    读取BMP位图并简单处理

    当了解了这些后,就可以编程读取BMP位图了。其像素信息,可以存储在一个一维数组里面,以后处理图片就是直接对这个数组进行处理。

    下面是示例源码:


    关于图像识别

    在图像识别领域中(如验证码识别),常规的图像处理流程是:灰度化/二值化、去噪(包括嘈点和干扰线)、字符分隔、字符归一化、识别训练。


    其中灰度化/二值化其实都是为去噪做准备的,毕竟相对于彩色图像,黑白图像在进行噪点识别时会更简单、运算量也少。

    去噪的方法有很多,常见的有:

    • 8邻域降噪(均值滤波/中值滤波):对于去除小的噪点很有效,计算量也不大
    • 连通域降噪(泛水填充法):适合去除大的噪点,常作为二次降噪手段,与8邻域降噪配合使用

    但如果实际的验证码的嘈点或干扰线的颜色,明显与主图像不同,那么可以先去噪再进行灰度化/二值化。

    经过前面去除噪点/干扰线,验证码图像现在只剩下两个部分:白色的背景色,黑色的字符前景色。而为了字符的识别,此时就需要对图像中上的字符进行切割:把它们一个一个“抠”下来,得到单个的字符,再进行OCR识别。

    但是OCR通常只能用于比较正规的字符识别。而验证码图片的一般字符都是经过特殊处理的,如扭曲、倾斜、旋转等,这种字符是无法用OCR识别的。此时就需要对字符进行归一化处理了。

    所谓的归一化,其实就是通过一些特殊处理,把字符尽可能还原成扭曲、倾斜、旋转之前的形状,然后再把缩放到一个固定大小(如32×32)。此时再把这个32×32的字符进行网格划分成4×4共16块(当然若有必要可以继续划分这些子块)。我们可以计算每一块的特征值,最后就得到这个字符的4×4矩阵的网格特征值,这就是归一化。

    最后就是字符识别了:可以预先准备标准的字符图像库,通过归一化提前计算图像库中每个字符的网格特征值作为参照特征值。这时只需要把从验证码图像中提取的字符的网格特征值,与这些参照特征值进行匹配,相似度最高的,就认为识别成功了。

    当然,由于真实图像中的字符的变形程度都不同,所以单凭一次的特征值匹配是不可信的,由此就需要大量的验证码图像训练进行逼近(通常需要至少数万次的匹配训练才可能能得到一个相对可靠的训练库)。

    而这就是现在比较热门的深度学习领域在做的事情了,有兴趣进一步入门的同学,可以了解一下MINIST机器学习,会对图像识别有更深一层的了解(推荐使用python做)


    转载请注明:EXP 技术分享博客 » 图像识别 – C++读取BMP位图入门

    喜欢 (1) 分享 (0)
    发表我的评论
    取消评论

    表情

    Hi,您需要填写昵称和邮箱!

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址