Tesseract-OCR學(xué)習(xí)系列(四)API

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的算法原理静浴。這不是一件容易的事堰氓。這個系列可能要暫停一段時間了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末苹享,一起剝皮案震驚了整個濱河市双絮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖掷邦,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件白胀,死亡現(xiàn)場離奇詭異,居然都是意外死亡抚岗,警方通過查閱死者的電腦和手機(jī)或杠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宣蔚,“玉大人向抢,你說我怎么就攤上這事∨呶” “怎么了挟鸠?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長亩冬。 經(jīng)常有香客問我艘希,道長,這世上最難降的妖魔是什么硅急? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任覆享,我火速辦了婚禮,結(jié)果婚禮上营袜,老公的妹妹穿的比我還像新娘撒顿。我一直安慰自己,他們只是感情好荚板,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布凤壁。 她就那樣靜靜地躺著,像睡著了一般跪另。 火紅的嫁衣襯著肌膚如雪拧抖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天免绿,我揣著相機(jī)與錄音徙鱼,去河邊找鬼。 笑死针姿,一個胖子當(dāng)著我的面吹牛袱吆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播距淫,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼绞绒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了榕暇?” 一聲冷哼從身側(cè)響起蓬衡,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤喻杈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后狰晚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體筒饰,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年壁晒,在試婚紗的時候發(fā)現(xiàn)自己被綠了瓷们。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡秒咐,死狀恐怖谬晕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情携取,我是刑警寧澤攒钳,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站雷滋,受9級特大地震影響不撑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜晤斩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一焕檬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尸昧,春花似錦揩页、人聲如沸旷偿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽萍程。三九已至幢妄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茫负,已是汗流浹背蕉鸳。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留忍法,地道東北人潮尝。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像饿序,于是被迫代替她去往敵國和親勉失。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫原探、插件乱凿、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,103評論 4 62
  • 轉(zhuǎn)載自:https://github.com/Tim9Liu9/TimLiu-iOS 目錄 UI下拉刷新模糊效果A...
    袁俊亮技術(shù)博客閱讀 11,926評論 9 105
  • 設(shè)想中的堅(jiān)持寫作顽素,并沒有發(fā)生。轉(zhuǎn)眼過去了那么久徒蟆,似乎別無所得胁出。調(diào)動不起情緒,生硬地拉扯幾句文字段审。我也不知道自己在寫...
    遠(yuǎn)古禮儀閱讀 189評論 0 0
  • 在天晴了的時候 在天晴了的時候 墻角的風(fēng)箏張望著 酵了一冬的情緒 趁風(fēng)而逃 在蔚藍(lán)映襯里四下招搖 在天晴了的時候 ...
    大俠小花卷閱讀 378評論 0 2
  • #轉(zhuǎn)型與蛻變30天自由寫作第13篇# 看到今天的命題——生命中的最后一個月全蝶,心情立刻沉重下來了。這讓我想起了看過的...
    旅途中的文森特閱讀 797評論 1 5