Tesseract是一個開源的OCR(光學字符識別)引擎,用於識別並輸出圖片中的文字。雖然和商業軟件比起來識別精度不算很高,但是如果你要尋找免費開源的OCR引擎,可能Tesseract就是唯一的選擇了。Tesseract用起來還算是比較方便。它提供了一個簡單的命令行工具,沒有很多選項,輸入圖片輸出就是文字。因為是開源的,你也可以直接編譯使用它基於C++的庫。
最近一段時間我對Python產生了很大的興趣。它是如此的簡潔高效,只要可以用Python完成的工作就懶得使用其他編程語言。所以到了應用Tesseract的時候我首先想到了去Google一下有沒有Python binding。確實有人使用swig做了個tesseract的封裝,不過不幸的是實際應用存在不少問題。首先是安裝不便,尤其在mac上的安裝令人崩潰。即使完成安裝,不知為何又segment fault。其次,很多方法只做了簡單的封裝,又缺乏文檔,想做深入一點的應用例如輸出文字在圖中的位置,感覺無從着手。不如從Tesseract的源代碼入手,自己編寫python的擴展,對tesseract的某些感興趣的方法做個封裝,也順便熟悉下Python和C/C++集成的方法。可以在擴展里為所欲為,真是令人心情愉快。
首先,新建一個cpp源文件,然后為這個新模塊想個名字,比如 tessex。然后,需要定義這個新模塊,以及模塊里需要暴露出來的方法。這樣在Python里就可以用import tessex來載入模塊。
static PyMethodDef tessexMethods[] = { {"recognize", (PyCFunction)tessex_recognize, METH_VARARGS|METH_KEYWORDS, "recognize text in an image."}, {NULL, NULL, 0, NULL} }; PyMODINIT_FUNC inittessex(void) { (void) Py_InitModule("tessex", tessexMethods); }
這里,我們暴露一個方法recognize,用來掃描給定圖片然后返回識別的文字以及位置。大家知道Python方法可以傳兩種參數,一種是無名的,一種是有名的,分別對應METH_VARARGS以及METH_KEYWORDS。作為一個有點完美主義傾向的人,我把兩個選項都勾上了。然后我們看下recognize方法的定義。正如之前講的,需要聲明參數args以及kw。
static PyObject* tessex_recognize(PyObject* self, PyObject* args, PyObject* kw){
接下來是展開從Python調用傳進來的參數。要使用有名參數,需要把所有參數名都先列出來。
static const char* kwlist[]={"data", "w", "h", "channels", "step", NULL};
然后調用PyArg_ParseTupleAndKeywords展開有名參數。一個格式字符串用於聲明參數的類型。data是圖像的像素buffer,適用S類型。w、h、channels、step分別是圖像的寬度、高度、信道數、步長,都是整型變量,適用i類型。
PyStringObject *data; int w=0; int h=0; int channels=0; int step=0; if (!PyArg_ParseTupleAndKeywords(args, kw, "Siiii", (char**)(kwlist), &data, &w, &h, &channels, &step)) { PyErr_SetString(PyExc_Exception, "Tessex: Failed to parse arguments."); return NULL; }
我們要開始使用Tesseract的API了。Tesseract支持多種語言,不過語言包要分別下載安裝。這里我們使用英語。
tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI(); if (api->Init(NULL, "eng")) { PyErr_SetString(PyExc_Exception, "Tessex: Failed to initialize tesseract API."); return NULL; }
把傳入的圖像數據傳遞給Tesseract,然后進行識別。
api->SetImage((const unsigned char*)PyString_AS_STRING(data), w, h, channels, step); api->Recognize(0);
識別結果的處理稍微復雜點。一般的應用如果只想得到所有文字,只要調用GetUTF8Text()就完了。但是我想知道每一行的文字,它們的具體位置以及可信度,就需要對識別結果進行詳細的分析。幸運的是Tesseract提供了iterator接口,可以返回不同粒度的數據。這里我選擇了按行輸出,即RIL_TEXTLINE。
PyObject* l = PyList_New(0); tesseract::ResultIterator* it = api->GetIterator(); it->Begin(); while(1) { char* utf8_text; int left, top, right, bottom; int confidence = 0; utf8_text = it->GetUTF8Text(tesseract::RIL_TEXTLINE); if (utf8_text == NULL) break; confidence = it->Confidence(tesseract::RIL_TEXTLINE); it->BoundingBox(tesseract::RIL_TEXTLINE, &left, &top, &right, &bottom); PyObject* t = Py_BuildValue("(siiiii)", utf8_text, left, top, right, bottom, confidence); PyList_Append(l, t); delete []utf8_text; it->Next(tesseract::RIL_TEXTLINE); } api->End(); PyObject* o = Py_BuildValue("O", l); return o;
返回的是一個list,其每個元素都是一個tuple,代表識別出來的文字行,包括文字、位置和可信度。
最后不要忘了include需要的頭文件,並在鏈接選項里加入需要的庫。
#include "Python.h" #include <tesseract/baseapi.h> #include <tesseract/resultiterator.h>
具體如何編譯tesseract可以參考https://code.google.com/p/tesseract-ocr/wiki/Compiling/
在示例代碼里我們並沒有用到任何圖形庫。但是因為要使用Tesseract就必須鏈接leptonica庫,所以需要加入鏈接選項-llept。
如果你想在擴展里使用leptonica的功能可以include <leptonica/allheaders.h>。或者你想使用openCV,可以include <opencv2/opencv.hpp>並鏈接-lopencv_XXXXX。
這樣代碼部分算是完成了。不過接下來還有一步,我們需要打包完成一個Python擴展使之容易編譯和安裝。可以使用distutils模塊。
from distutils.core import setup, Extension tessenigma = Extension ( 'tessex', sources=['tessex.cpp'], include_dirs = ['/usr/local/include'], libraries=[ 'tesseract'], library_dirs=['/usr/local/lib'] ) setup (name='tessex', version='1.0', description='This is a tesseract extensiion.', ext_modules = [tessex])
把這些定義寫入一個setup.py文件里。這樣我們就可以用通常的方式編譯和安裝模塊了。編譯用setup.py build。一個動態鏈接庫會生成。例如在Linux下面就是tessex.so。安裝模塊使用setup.py install。前面生成的庫文件會被復制到Python的site-packages下面。當然你也可以手動復制到$PYTHONPATH路徑下面,一樣能被Python找到。
安裝好擴展后,在Python里是這樣調用的,假定我們使用一個openCV圖像:
import tessex
import cv
cv_img = cv.LoadImage(path, cv.CV_LOAD_IMAGE_COLOR)
lines = tessex.recognize( data=cv_img.tostring(), w=cv_img.width, h=cv_img.height, channels=cv_img.nChannels, step=cv_img.width * cv_img.nChannels * cv_img.depth / 8) for line in lines: line_text, left, top, right, bottom, confidence = line
通過Tesseract擴展,可以在Python中比較方便地識別圖像中的文字以及位置,對基於圖像識別的自動化測試是很有幫助的。