Other API Examples
參考文檔:https://github.com/tesseract-ocr/tesseract/wiki/APIExample
在上一篇中芋浮, 我們學(xué)習(xí)了參考文檔中的第一個示例衅胀。用CMake構(gòu)建了工程,并且看了一下例子中調(diào)用到的API蝴簇。在這一篇中,我們繼續(xù)看一看其它的例子缨恒。但如何用CMake構(gòu)建工程的方法就不贅述了。這里給出我寫的例程轮听,若有疑問之處骗露,請閱讀Tesseract-OCR學(xué)習(xí)系列(三)簡例以及CMake簡要教程這兩篇文章。
GetComponentImages example
#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>
int main()
{
Pix *image = pixRead("D:\\open_source\\tesseract-3.04.01\\tesseract\\testing\\phototest.tif");
tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
api->Init(NULL, "eng");
api->SetImage(image);
Boxa* boxes = api->GetComponentImages(tesseract::RIL_TEXTLINE, true, NULL, NULL);
printf("Found %d textline image components.\n", boxes->n);
for (int i = 0; i < boxes->n; i++){
BOX* box = boxaGetBox(boxes, i, L_CLONE);
api->SetRectangle(box->x, box->y, box->w, box->h);
char* ocrResult = api->GetUTF8Text();
int conf = api->MeanTextConf();
fprintf(stdout, "Box[%d]: x=%d, y=%d, w=%d, h=%d, confidence: %d, text: %s",
i, box->x, box->y, box->w, box->h, conf, ocrResult);
}
}
我們知道血巍,如果要進(jìn)行字符識別萧锉,首先要搜索到文字圖塊∈龉眩或者說柿隙,找到包含字符的文字圖塊。這個例子幫助我們將每一個文字圖塊找到鲫凶,并對文字圖塊進(jìn)行識別禀崖。下面來看代碼(之前說過的就不說了):
Pix *image = pixRead("D:\\open_source\\tesseract-3.04.01\\tesseract\\testing\\phototest.tif");
tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
api->Init(NULL, "eng");
api->SetImage(image);
已經(jīng)熟悉了,Pass……
Boxa* boxes = api->GetComponentImages(tesseract::RIL_TEXTLINE, true, NULL, NULL);
這是最關(guān)鍵的一行代碼螟炫。GetComponentImages
用于查找圖像內(nèi)的圖像塊波附,并將分割到的圖像塊返回給Boxa這個結(jié)構(gòu)中。
Boxa* GetComponentImages(const PageIteratorLevel level,
const bool text_only,
Pixa** pixa, int** blockids)
那么,分割到什么程度呢掸屡?這是函數(shù)的第一個參數(shù)來控制的封寞。
enum PageIteratorLevel {
RIL_BLOCK, // Block of text/image/separator line.
RIL_PARA, // Paragraph within a block.
RIL_TEXTLINE, // Line within a paragraph.
RIL_WORD, // Word within a textline.
RIL_SYMBOL // Symbol/character within a word.
};
也就是說,我們可以分割到一塊仅财、一段钥星、一行、一個單詞或者一個單字满着。這特別適合用于做文檔的OCR。一份文檔贯莺,有可能包含圖像和大大小小的各種文字风喇。用這個函數(shù),就可以將圖像缕探、文字等單獨(dú)拎出來魂莫,然后再分別進(jìn)行處理。第二個參數(shù)爹耗,text_only如果是true的話耙考,就表示只返回文字區(qū)域坐標(biāo),不返回圖像區(qū)域坐標(biāo)潭兽。pixa用于返回分割出來的圖像倦始。這里設(shè)為NULL,即表示不需要返回圖像山卦。blockids返回的是序列號鞋邑。這里也不需要,所以設(shè)置成NULL账蓉。最后枚碗,返回值是分割到的矩形數(shù)組。
struct Box
{
l_int32 x;
l_int32 y;
l_int32 w;
l_int32 h;
l_uint32 refcount; /* reference count (1 if no clones) */
};
typedef struct Box BOX;
struct Boxa
{
l_int32 n; /* number of box in ptr array */
l_int32 nalloc; /* number of box ptrs allocated */
l_uint32 refcount; /* reference count (1 if no clones) */
struct Box **box; /* box ptr array */
};
typedef struct Boxa BOXA;
接著铸本,進(jìn)入循環(huán)肮雨。
for (int i = 0; i < boxes->n; i++){
BOX* box = boxaGetBox(boxes, i, L_CLONE);
api->SetRectangle(box->x, box->y, box->w, box->h);
char* ocrResult = api->GetUTF8Text();
int conf = api->MeanTextConf();
fprintf(stdout, "Box[%d]: x=%d, y=%d, w=%d, h=%d, confidence: %d, text: %s",
i, box->x, box->y, box->w, box->h, conf, ocrResult);
}
只有兩個函數(shù)沒有見過,一個是boxaGetBox
箱玷,一個是MeanTextConf
怨规。
-
boxaGetBox
:用于提取矩形數(shù)組中的某個矩形。其它參數(shù)一眼就看出來了锡足。第三個參數(shù)可以選擇L_CLONE
或者L_COPY
椅亚。L_CLONE
是軟拷貝,只增加引用數(shù)目舱污。L_COPY
是硬拷貝呀舔,把數(shù)據(jù)都復(fù)制一遍。 -
MeanTextConf
:用于返回OCR的平均信心。信心的值最低為0媚赖,最高為100霜瘪。
最后,看一下運(yùn)行結(jié)果:
Result iterator example
這個例子是說惧磺,對于OCR的結(jié)果颖对,我們可以一個詞一個詞地遍歷了來看。可以看到每一個詞的OCR結(jié)果、置信度以及在原圖中的位置遥诉。
#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>
int main()
{
Pix *image = pixRead("D:\\open_source\\tesseract-3.04.01\\tesseract\\testing\\phototest.tif");
tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
api->Init(NULL, "eng");
api->SetImage(image);
api->Recognize(0);
tesseract::ResultIterator* ri = api->GetIterator();
tesseract::PageIteratorLevel level = tesseract::RIL_WORD;
if (ri != 0)
{
do
{
const char* word = ri->GetUTF8Text(level);
float conf = ri->Confidence(level);
int x1, y1, x2, y2;
ri->BoundingBox(level, &x1, &y1, &x2, &y2);
printf("word: '%s'; \tconf: %.2f; BoundingBox: %d,%d,%d,%d;\n",
word, conf, x1, y1, x2, y2);
delete[] word;
} while (ri->Next(level));
}
}
最為關(guān)鍵的就是下面這兩行
tesseract::ResultIterator* ri = api->GetIterator();
tesseract::PageIteratorLevel level = tesseract::RIL_WORD;
第一句按照閱讀順序來獲取一個OCR結(jié)果的迭代器柠横。第二句設(shè)置迭代的單位。可用的迭代單位有:
enum PageIteratorLevel {
RIL_BLOCK, // Block of text/image/separator line.
RIL_PARA, // Paragraph within a block.
RIL_TEXTLINE, // Line within a paragraph.
RIL_WORD, // Word within a textline.
RIL_SYMBOL // Symbol/character within a word.
};
運(yùn)行下來的部分結(jié)果如下:
Orientation and script detection (OSD) example
這個例子講了如何進(jìn)行頁面的方向檢測和文字的方向檢測。不知道大家是否與我有同樣的疑問,就是頁面的方向如果檢測出來了徙歼,那文字的方向還用檢測嗎?文字不就是正著的了嗎鳖枕?可是人家說的文字方向檢測根本不是說的這個魄梯,而是說閱讀的方向性。比如宾符,我們知道英文的一行肯定是橫著排的酿秸,閱讀方向是從左到右的。讀完上面一行再讀下面一行魏烫。然而對于古體中文來說允扇,文字是豎著寫的,閱讀方向是從上到下的则奥,行與行之間呢考润,是從右往左讀的。這里文字的方向檢測檢測的是這個读处。
先看代碼:
#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>
int main()
{
const char* inputfile = "D:\\open_source\\tesseract-3.04.01\\tesseract\\testing\\phototest.tif";
tesseract::Orientation orientation;
tesseract::WritingDirection direction;
tesseract::TextlineOrder order;
float deskew_angle;
PIX *image = pixRead(inputfile);
tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
api->Init(NULL, "eng");
api->SetPageSegMode(tesseract::PSM_AUTO_OSD);
api->SetImage(image);
api->Recognize(0);
tesseract::PageIterator* it = api->AnalyseLayout();
it->Orientation(&orientation, &direction, &order, &deskew_angle);
printf("Orientation: %d;\nWritingDirection: %d\nTextlineOrder: %d\n" \
"Deskew angle: %.4f\n",
orientation, direction, order, deskew_angle);
}
只看之前沒有看到過的糊治。
api->SetPageSegMode(tesseract::PSM_AUTO_OSD);
這句是重點(diǎn)。它設(shè)置了頁面分割模式罚舱。頁面分割有如下的模式可供選擇:
enum PageSegMode {
PSM_OSD_ONLY, ///< Orientation and script detection only.
PSM_AUTO_OSD, ///< Automatic page segmentation with orientation and
///< script detection. (OSD)
PSM_AUTO_ONLY, ///< Automatic page segmentation, but no OSD, or OCR.
PSM_AUTO, ///< Fully automatic page segmentation, but no OSD.
PSM_SINGLE_COLUMN, ///< Assume a single column of text of variable sizes.
PSM_SINGLE_BLOCK_VERT_TEXT, ///< Assume a single uniform block of vertically
///< aligned text.
PSM_SINGLE_BLOCK, ///< Assume a single uniform block of text. (Default.)
PSM_SINGLE_LINE, ///< Treat the image as a single text line.
PSM_SINGLE_WORD, ///< Treat the image as a single word.
PSM_CIRCLE_WORD, ///< Treat the image as a single word in a circle.
PSM_SINGLE_CHAR, ///< Treat the image as a single character.
PSM_SPARSE_TEXT, ///< Find as much text as possible in no particular order.
PSM_SPARSE_TEXT_OSD, ///< Sparse text with orientation and script det.
PSM_RAW_LINE, ///< Treat the image as a single text line, bypassing
///< hacks that are Tesseract-specific.
PSM_COUNT ///< Number of enum entries.
};
多提一句井辜,如需使用OSD功能,則需要下載osd.traineddata
api->Recognize(0);
這一句當(dāng)然是用來根據(jù)之前的設(shè)定來進(jìn)行識別的管闷。
tesseract::PageIterator* it = api->AnalyseLayout();
這一句根據(jù)之前SetPageSegMode的設(shè)定來運(yùn)行頁面的布局分析粥脚。這句話其實(shí)也可以在Recognize前面進(jìn)行。
it->Orientation(&orientation, &direction, &order, &deskew_angle);
這個函數(shù)用來獲取頁面和文字的方向包个。其簽名如下:
void Orientation(tesseract::Orientation *orientation,
tesseract::WritingDirection *writing_direction,
tesseract::TextlineOrder *textline_order,
float *deskew_angle) const;
其中刷允,
- tesseract::Orientation指的是頁面的方向。
- tesseract::WritingDirection指的是書寫方向。(比如剛剛說的英文是從左到右树灶,中文是從上到下)
- tesseract::TextlineOrder指的是一行一行的方向纤怒。(比如剛剛說的英文是從上往下閱讀,中文是從右往左閱讀)
- ** deskew_angle**是指傾斜角度天通。因?yàn)榕懦鰜淼膱D片也不可能完全是正著的泊窘。這里可以計(jì)算出偏轉(zhuǎn)的角度。
運(yùn)行的結(jié)果如下:
Example of iterator over the classifier choices for a single symbol
這個例子可以幫助我們學(xué)習(xí)如何找到一個識別對象的其它候選結(jié)果像寒。
#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>
int main()
{
Pix *image = pixRead("D:\\open_source\\tesseract-3.04.01\\tesseract\\testing\\phototest.tif");
tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
api->Init(NULL, "eng");
api->SetImage(image);
api->SetVariable("save_blob_choices", "T");
api->SetRectangle(37, 228, 548, 31);
api->Recognize(NULL);
tesseract::ResultIterator* ri = api->GetIterator();
tesseract::PageIteratorLevel level = tesseract::RIL_SYMBOL;
if (ri != 0)
{
do
{
const char* symbol = ri->GetUTF8Text(level);
float conf = ri->Confidence(level);
if (symbol != 0)
{
printf("symbol %s, conf: %f", symbol, conf);
bool indent = false;
tesseract::ChoiceIterator ci(*ri);
do
{
if (indent) printf("\t\t ");
printf("\t- ");
const char* choice = ci.GetUTF8Text();
printf("%s conf: %f\n", choice, ci.Confidence());
indent = true;
} while (ci.Next());
}
printf("------------------------------------------\n");
delete[] symbol;
} while (ri->Next(level));
}
}
在編譯的時候出現(xiàn)了寫問題烘豹。問題在于:
tesseract::ChoiceIterator ci(*ri);
這個類在dll中沒有。沒有的原因是诺祸,這個類根本就沒有被導(dǎo)出來携悯!如果需要導(dǎo)出這個類,那么就需要在tesseract的源代碼中修改一下序臂,然后再重新編譯。
修改方法為实束,在ltrresultiterator.h
頭文件中奥秆,將:
class ChoiceIterator {
修改為:
class TESS_API ChoiceIterator {
然后要記得重新編譯哦!并且將生成的動態(tài)庫覆蓋原來的動態(tài)庫咸灿。目前我們還不太熟悉的API如下:
api->SetVariable("save_blob_choices", "T");
這個函數(shù)的作用是設(shè)置內(nèi)部的參數(shù)构订。(不過話說我怎么知道內(nèi)部有哪些參數(shù),這些參數(shù)又有什么意義氨苁浮5狂)設(shè)置"save_blob_choices"的目的是將候選項(xiàng)全部保存下來。
tesseract::ChoiceIterator ci(*ri);
這是一個迭代器审胸,通過這個迭代器亥宿,可以將每一個候選的結(jié)果都打印出來。
部分結(jié)果如下:
好了砂沛,就寫到這兒吧烫扼。可以看出碍庵,Tesseract的應(yīng)用是非常靈活的映企。下面一段時間,我希望自己可以慢慢了解Tesseract-OCR的算法原理静浴。這不是一件容易的事堰氓。這個系列可能要暫停一段時間了。