APP下载

Windows位图和Leptonica位图的转换

2019-03-08袁智勇刘文林

科技创新导报 2019年30期

袁智勇 刘文林

摘   要:很多屏幕文字识别应用把截屏后的数据保存为图片文件,然后从硬盘上读取图片文件进行文字识别,这种做法效率很低。本文介绍了Windows位图和Leptonica位图格式的不同特点,提出了一套简便的,在内存中进行转换的方法。这使得直接在内存中识别文字成为可能,省去在硬盘上存取图片文件的步骤,从而大大提高屏幕文字识别应用的效率。

关键词:屏幕文字识别  tesseract  leptonica  GDI+  位图数据转换

中图分类号:TP391.4                               文献标识码:A                        文章编号:1674-098X(2019)10(c)-0113-02

1  引言

有一类屏幕文字识别应用,其流程是这样的:

(1)屏幕截图,将包含文字的截图保存为图片文件。

(2)调用文字识别模块,读取保存好的图片文件,识别其中包含的文字。

(3)将识别出来的文字反馈给用户。

在上述过程中,截图须保存到硬盘上,然后由文字识别模块读取识别。这就涉及硬盘文件的读写,而硬盘文件的读写相对来说比较慢。很多情况下,保存下来的截图并没有其他用途,这样一来不但增加了一些无谓的开销,而且也拖慢了应用的整体响应速度。如果能将保存在内存中的截图数据直接发送到文字识别模块,略去存取文件的过程,无疑是最佳的。这正是本文要解决的问题。

2  开发平台和工具

应用平台为Windows 7以上Windows操作系统。开发工具为集成于Visual Studio中的Visual C++。因为目前的Windows API(编程接口)并不直接支持文字识别,需要为Visual C++安装支持文字识别的扩展包——tesseract。之所以使用tesseract,一是它是免費开源软件,可以免费使用;同时,虽然它是一款免费开源软件,但其文字识别率非常高,不次于同档次商业软件。

Tesseract是在Linux平台上开发的,要移植到Windows平台,需要的步骤相当繁琐,而且容易出错。幸运的是微软专门开发了一款称为vcpkg的软件,专门用于从Linux平台移植软件。通过vcpkg安装好tesseract扩展包后,还需下载对应语言的训练数据(training data),这是文字识别的支撑数据库。

3  使用tesseract进行文字识别

通过调用tesseract API,可以以编程的方式实现文字识别。Tesseract API的核心功能封装于TessBaseAPI类中,通过其成员函数GetUTF8Text完成文字识别,使用起来非常简单方便。在调用GetUTF8Text前需要进行一些初始化操作,其中一步就是读取硬盘上的包含文字的图像文件,构造一个Pix对象。然后以这个Pix对象为参数调用SetImage成员函数,为TessBaseAPI指定要识别的图像。

4  Leptonica位图

Tesseract没有为一些基本的图像处理,比如图像文件的存取开发相应的库函数,而是借用了Leptonica。Leptonica也是一款著名的Linux下的免费开源软件,专门用于基本的图像处理,包括图像文件的存取、格式转换、图像变换等。在Leptonica中,图像被封装成Pix类。除了读取硬盘上的图像文件来构造Pix对象,也可以从内存中的图像数据来构造Pix对象,比如通过下面的库函数:

Pix * px = pixCreate(w, h, bpp);

W是图像水平像素数量,h是图像垂直像素数量,bpp是每个像素的bit长度。

不过这个库函数构建的Pix对象只是个框架,对象的一些字段是空的,还需进一步初始化。其中最重要的是Pix::data字段,这个字段指向的缓存用于存储位图数据。如果我们将屏幕截图保存下来的位图数据拷贝到Pix对象中,并完成其他一些必要的初始化操作,就可以在内存中构造一个完整的Pix对象,用于tesseract文字识别,无需进行图像文件的硬盘存取操作。

5  位图数据转换

在Windows平台下可以将截图后获得的图像数据封装于GDI+的Bitmap对象中:

HDC hdcScreen = GetDC(NULL);

HDC hdcMemDC = CreateCompatibleDC(hdcScreen);

HBITMAP hbmScreen = CreateCompatibleBitmap(hdcScreen, cx, cy);

SelectObject(hdcMemDC, hbmScreen);

BitBlt(hdcMemDC, 0, 0, cx, cy, hdcScreen, x0, y0, SRCCOPY);

Bitmap* bmp = new Bitmap(hbmScreen, NULL);

然后我们可以调用Bitmap类的成员函数Bitmap:: LockBits来获取位图数据。但要注意的是,从Bitmap对象获取的位图数据不能直接拷贝到Pix::data字段中去。这是因为GDI+的位图数据格式和Leptonica的位图数据格式是不同的。

首先,Linux平台习惯用BGR的格式存储颜色,而Windows平臺用RGB的个数存储颜色。现在原始图像的一般是每像素24bit长,但为了和32bit的CPU字长对齐,实际上一般将24bit的像素扩张到32bit来存储。多出的1字节空间可以不用,也可以存储Alpha通道数据。因此Windows平台颜色数据的格式为ARGB,而Linux平台数据的格式为ABGR。另外,Linux平台内存中数据是以big-endian方式存储的,最低位的R数据存储在32bit数据的最高1个字节,最高位的A数据存储在32bit数据的最低1个字节。因此,最终像素颜色的存储格式为RGBA,这也是Leptonica在内存中存储颜色数据的方式。我们可以将Windows平台的颜色数据ARGB向左移位8bit,变成RGB0。移位后A数据丢失,但这无关紧要,因为A数据一般不用(设置为0)。这样就可以将Windows颜色数据转换成Leptonica颜色数据RGB0。

其次,Windows位图数据可以是bottom-up(上下颠倒)的,意思是图像的最后一行像素存储在位图数据的第一行,第一行数据反而存储在位图数据的最后一行。而leptonica的位图数据都是top-down(从上往下)的,意思是图像的第一行像素存储在位图数据的第一行,最后一行数据存储在位图数据的最后一行。如果Windows位图数据是bottom-up的(这种情况非常少),必须相应地调整像素行的存储顺序。下面是相应的代码:

BitmapData bd;

bmp->LockBits(new Rect(0, 0, w, h), ImageLockModeRead, PixelFormat32bppRGB, &bd);

UINT* p = (UINT*)bd.Scan0;

if (bd.Stride < 0) p -= w * (h - 1);//Stride小于0位图是bottom-up的,否则是top-down的

for (int i = 0; i < w * h; i++) p[i] = p[i] << 8; //颜色数据移位

参考文献

[1] 周鸣扬,赵景亮.精通GDI+编程[M].北京:清华大学出版社,2004.

[2] Ivor Horton.Visual C++ 2010入门经典[M].5版.北京:清华大学出版社, 2010.

[3] Jeffrey Richter.Windows核心编程[M].北京:机械工业出版社,2008.