Tesseract-OCR學習系列(三)簡例

Tesseract API Basic Example using CMake Configuration

參考文檔:https://github.com/tesseract-ocr/tesseract/wiki/APIExample

Tesseract提供的API可以在baseapi.h文件中找到。然而,如果沒有個示例帶我們飛一會兒,也是頗難搞懂到底該怎么調用tesseract的api。

我們知道,如果要調用一個第三方的庫,那我們需要在工程的屬性中增加:

  1. 第三方庫頭文件的位置。
  2. 第三方庫庫文件的位置。
  3. 第三方庫中,需要鏈接的lib文件的文件名。

而且,Debug和Release需要分開來配置。手動配置真的是麻煩極了。而且,即使你配置好了,如果你第三方庫的位置改變了,那對不起,請重新配置;如果你要把工程給別人來使用,而別人的第三方庫所放的位置與你所放的不同,對不起,需要重新配置;如果你想要換一個操作系統進行開發,對不起,請重新配置!那有沒有辦法可以繞過這些麻煩事,使得只用麻煩一次,以后永遠簡單呢?答案是有。工具就是CMake??蓞⒖次业牧硪黄恼拢?a href="http://www.lxweimin.com/p/bbf68f9ddffa" target="_blank">CMake簡要教程。這里,我給大家舉例介紹,如何使用CMake來添加第三方庫。

首先,我們需要將第三方庫Tesseract所提供的東西集中存放。比如,我在F盤的extralib中建立了一個Tesseract文件夾。文件夾中,有bin文件夾include文件夾,lib文件夾,以及tessdata文件夾。其中:

  • bin:存放.dll文件。
  • include:存放.h文件。
  • lib:存放.lib文件。
  • tessdata:存放.traineddata文件。
bin文件夾
include文件夾

其中,tesseract的.h文件比較分散。我是直接在原來的tessract中搜索所有的.h文件,然后再拷貝到這邊來的。

lib文件夾
tessdata文件夾

其中,chi_sim代表簡體中文,eng就不用說了,代表英文。tessdata文件夾中的內容可以在官方網站中下載到。

好了,現在有了這樣一個文件夾,我們下面的目標是讓CMake可以找到這些文件夾。為了達到這個目的,首先需要自己寫名為TesseractConfig.cmake一個文件,放在剛剛建立的tesseract文件夾中。所以,tesseract文件夾最終看起來是這個樣子的:

tesseract文件夾

如果CMake能找到TesseractConfig.cmake這個文件,就可以通過find_package函數來找到Tesseract的各個文件夾的路徑了。但問題是,CMake如何找到TesseractConfig.cmake這個文件呢?在Windows操作系統的環境下,有兩種方法:

  1. 將TesseractConfig.cmake這個文件所在的文件夾路徑添加到系統環境變量的Path中。
  2. 在CMake的GUI界面中手動配置。

在正式介紹之前,先來看一看TesseractConfig.cmake中該怎么寫:

# ===================================================================================
#  The Tesseract CMake configuration file
#
#  Usage from an external project:
#    In your CMakeLists.txt, add these lines:
#
#    FIND_PACKAGE(Tesseract REQUIRED)
#    TARGET_LINK_LIBRARIES(MY_TARGET_NAME ${Tesseract_LIBS})
#
#    This file will define the following variables:
#      - Tesseract_LIBS                     : The list of libraries to link against.
#      - Tesseract_LIB_DIR                  : The directory(es) where lib files are. Calling 
#                                             LINK_DIRECTORIES with this path is NOT needed.
#      - Tesseract_INCLUDE_DIRS             : The Tesseract include directories.
#      - Tesseract_VERSION                  : The version of this Tesseract build. Example: "2.4.0"
#      - Tesseract_VERSION_MAJOR            : Major version part of Tesseract_VERSION. Example: "2"
#      - Tesseract_VERSION_MINOR            : Minor version part of Tesseract_VERSION. Example: "4"
#      - Tesseract_VERSION_PATCH            : Patch version part of Tesseract_VERSION. Example: "0"
#
#    Advanced variables:
#      - Tesseract_CONFIG_PATH
#
# ===================================================================================

set(Tesseract_VERSION_MAJOR 3)
set(Tesseract_VERSION_MINOR 4)
set(Tesseract_VERSION_PATCH 1)
set(Tesseract_VERSION ${Tesseract_VERSION_MAJOR}.${Tesseract_VERSION_MINOR}.${Tesseract_VERSION_PATCH})

get_filename_component(Tesseract_CONFIG_PATH "${CMAKE_CURRENT_LIST_FILE}" PATH CACHE)
set(Tesseract_LIB_DIR "${Tesseract_CONFIG_PATH}/lib")
set(Tesseract_INCLUDE_DIRS "${Tesseract_CONFIG_PATH}/include")

set(Tesseract_LIBS_DBG "liblept171d.lib" "libtesseract304d.lib")
set(Tesseract_LIBS_OPT "liblept171.lib" "libtesseract304.lib")
foreach(__tesslib ${Tesseract_LIBS_DBG})
  list(APPEND Tesseract_LIBS debug "${Tesseract_LIB_DIR}/${__tesslib}")
endforeach()
foreach(__tesslib ${Tesseract_LIBS_OPT})
  list(APPEND Tesseract_LIBS optimized "${Tesseract_LIB_DIR}/${__tesslib}")
endforeach()

set(Tesseract_FOUND TRUE CACHE BOOL "" FORCE)

好了,準備工作到此為之,接下來我們可以開始正式地構建示例程序Basic-example了。首先新建文件夾samples。然后在samples文件夾中新建文件夾Basic-example,新建文件CMakeLists.txt。

samples文件夾

這里的CMakeLists.txt可以很簡單(當然也可以很復雜,但作為示例,理當簡單一點)。

cmake_minimum_required(VERSION 3.0)
project(tesseract-api-examples)
add_subdirectory(Basic-example)

第一句話表示,cmake的版本號最小為3.0(低于cmake 3.0則無法構建)。第二句話表示構建一個解決方案,名字叫做tesseract-api-examples。第三句表示添加子目錄Basic-example。添加子目錄的意思,其實是開始執行子目錄中的CMakeLists.txt。所以,如果想通過add_subdirectory添加子目錄,那就必須保證這個子目錄中有CMakeLists.txt這個文件。

現在,我們進入Basic-example文件夾中,新建兩個文件:Basic-example.cpp以及CMakeLists.txt

Basic-example.cpp中,我們將官網上提供的代碼粘上來:

#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>

int main()
{
    char *outText;
    
    tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
    // Initialize tesseract-ocr with English, without specifying tessdata path
    if (api->Init(NULL, "eng")){
        fprintf(stderr, "Could not initialize tesseract.\n");
        exit(1);
    }
    
    // Open input image with leptonica library
    Pix *image = pixRead("D:\\open_source\\tesseract-3.04.01\\tesseract\\testing\\phototest.tif");
    api->SetImage(image);
    // Get OCR result
    outText = api->GetUTF8Text();
    printf("OCR output:\n%s", outText);
    
    // Destroy used object and release memory
    api->End();
    delete [] outText;
    pixDestroy(&image);
    
    return 0;
}


而在CMakeLists.txt中,可以用6句話來完成:

set(the_target "Basic-example")
find_package(Tesseract REQUIRED)
aux_source_directory(. SRC_LIST)
include_directories(${Tesseract_INCLUDE_DIRS})
add_executable(${the_target} ${SRC_LIST})
target_link_libraries(${the_target} ${Tesseract_LIBS})

其中,

  • 第一行設定the_target名為"Basic-example"。
  • 第二行尋找Tesseract第三方庫。
  • 第三行尋找當前文件夾下的所有.c文件和.cpp文件,并把文件名放在SRC_LIST中。
  • 第四行添加第三方庫目錄Tesseract_INCLUDE_DIRS。
  • 第五行設定項目Basic-example的生成目標是一個可執行文件。
  • 第六行添加依賴的第三方庫。

好了,一切準備就緒,就差構建了!打開CMake-GUI軟件。

設定cmake的源路徑和目標路徑。如果對這兩個路徑不是很清楚的,還是請移步CMake簡要教程。

點擊config

出現一個選框,選擇你所使用的C++編譯器。我使用的是VS2012。點擊Finish。

在一段時間的等待之后,出現如下的界面:

注意Tesseract_DIR那一行。我這邊自動找到了。那是因為這個我已經把這個路徑放置到環境變量的Path中了。你可以選擇將你的路徑放置到環境變量中,也可以在這里手動選擇這個目錄。如果是通過手動選擇的方式,那么這個目錄會保存在Cache中,下次配置也不需要再次選擇了。

再次點擊Configure。

紅色條帶消失,消息欄顯示Configuring done。此時,點擊Generate。

生成成功!接下來,就可以打開build文件夾下面的tesseract-api-examples.sln這一工程文件了。

Basic-example設為啟動項。生成,成功!

運行!啊哦!

唉,不好意思,太激動了,腦殘了一把!我們現在還需要將Tesseract的bin文件夾放到環境變量的Path中,這樣,程序才能找到dll文件。

現在可以開始調試程序了。

phototest.tif

OK。運行程序。

成功執行~

我們再回過頭來看一看這個示例程序。看看它做了一些什么事。

#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>

int main()
{
    char *outText;
    
    tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
    // Initialize tesseract-ocr with English, without specifying tessdata path
    if (api->Init(NULL, "eng")){
        fprintf(stderr, "Could not initialize tesseract.\n");
        exit(1);
    }
    
    // Open input image with leptonica library
    Pix *image = pixRead("D:\\open_source\\tesseract-3.04.01\\tesseract\\testing\\phototest.tif");
    api->SetImage(image);
    // Get OCR result
    outText = api->GetUTF8Text();
    printf("OCR output:\n%s", outText);
    
    // Destroy used object and release memory
    api->End();
    delete [] outText;
    pixDestroy(&image);
    
    return 0;
}

首先包含了兩個頭文件:

#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>

這其實說明了,這個示例程序用到了兩個庫。一個是tesseract,一個是leptonica。tesseract用來做OCR。leptonica可以處理基本的圖像處理的需求。

接下來,在main函數中,定義了一個對象:

tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();

其中,tesseract是命名空間。TessBaseAPI是一個類名。這個類的注釋是這么寫的:

/**
* Base class for all tesseract APIs.
* Specific classes can add ability to work on different inputs or produce
* different outputs.
* This class is mostly an interface layer on top of the Tesseract instance
* class to hide the data types so that users of this class don't have to
* include any other Tesseract headers.
*/

也就是說:

所有的tesseract的API都在這個類中。

所以,如果我們把這個類搞明白了,也就知道Tesseract的API的所有調用方法了。好事啊~這個類一會兒再回過來看。先把代碼讀完。

    // Initialize tesseract-ocr with English, without specifying tessdata path
    if (api->Init(NULL, "eng")){
        fprintf(stderr, "Could not initialize tesseract.\n");
        exit(1);
    }

來看一看Init的注釋~

/**
   * Instances are now mostly thread-safe and totally independent,
   * but some global parameters remain. Basically it is safe to use multiple
   * TessBaseAPIs in different threads in parallel, UNLESS:
   * you use SetVariable on some of the Params in classify and textord.
   * If you do, then the effect will be to change it for all your instances.
   *
   * Start tesseract. Returns zero on success and -1 on failure.
   * NOTE that the only members that may be called before Init are those
   * listed above here in the class definition.
   *
   * The datapath must be the name of the parent directory of tessdata and
   * must end in / . Any name after the last / will be stripped.
   * The language is (usually) an ISO 639-3 string or NULL will default to eng.
   * It is entirely safe (and eventually will be efficient too) to call
   * Init multiple times on the same instance to change language, or just
   * to reset the classifier.
   * The language may be a string of the form [~]<lang>[+[~]<lang>]* indicating
   * that multiple languages are to be loaded. Eg hin+eng will load Hindi and
   * English. Languages may specify internally that they want to be loaded
   * with one or more other languages, so the ~ sign is available to override
   * that. Eg if hin were set to load eng by default, then hin+~eng would force
   * loading only hin. The number of loaded languages is limited only by
   * memory, with the caveat that loading additional languages will impact
   * both speed and accuracy, as there is more work to do to decide on the
   * applicable language, and there is more chance of hallucinating incorrect
   * words.
   * WARNING: On changing languages, all Tesseract parameters are reset
   * back to their default values. (Which may vary between languages.)
   * If you have a rare need to set a Variable that controls
   * initialization for a second call to Init you should explicitly
   * call End() and then use SetVariable before Init. This is only a very
   * rare use case, since there are very few uses that require any parameters
   * to be set before Init.
   *
   * If set_only_non_debug_params is true, only params that do not contain
   * "debug" in the name will be set.
   */

看著這么長的英文估計還挺累,不如我來翻譯一下:

實例大多數情況下是線程安全的,并且是完全獨立的。但是仍然保留了一些全局參量。基本上在不同的線程中并行地使用多個TessBaseAPIs是安全的,除非:你使用了SetVariable改變了某些參數的值。如果你這么做了,那么你所有的實例的效果都會為之發生改變。

啟動tesseract。如果成功返回0,如果失敗返回-1。注意能在Init方法前面調用的成員函數是那些在類定義中列在Init之前的那些函數。

datapath必須為tessdata的父目錄,并且必須以/終止。最后一個/后面所出現的字符將被全部刪除。language參數通常是一個ISO639-3的字符串,如果是NULL將被默認設置為eng。在單個實例中,多次調用Init方法來改變語言或重置分類器是沒有問題的,(并且會逐漸變地更快速)。

language參數可以寫成[]<lang>[+[]<lang>]*的形式,即表明可以加載多種語言。例如hin+eng會加載北印度語和英語。Languages可以在內部被設置為一種或多種語言,因此符號可以用來覆蓋。例如,如果hin被設置為默認加載eng,則hin+eng會強制只加載hin。可以被加載的語言的數量僅僅由內存限制,但是加載多種語言會同時影響速度和準確率。因為這需要更多的工作來決定它是哪種語言,并且更有可能產生錯誤。

警告:一旦改變語言,所有的Tesseract參數被重置為默認值。(每種語言可能不一樣。)

再接著看代碼:

    // Open input image with leptonica library
    Pix *image = pixRead("D:\\open_source\\tesseract-3.04.01\\tesseract\\testing\\phototest.tif");

pixRead是Leptonica的函數,它讀取一張圖片,并將圖片的結果保存在Pix結構體中。

    api->SetImage(image);

SetImage函數為Tesseract提供去識別的圖片。

    // Get OCR result
    outText = api->GetUTF8Text();

GetUTF8Text函數識別圖片中的文字,并返回char*數組。

    // Destroy used object and release memory
    api->End();
    delete [] outText;
    pixDestroy(&image);

最后一部分是釋放和銷毀。

關于End方法,代碼中的注釋是這么寫的

  /**
   * Close down tesseract and free up all memory. End() is equivalent to
   * destructing and reconstructing your TessBaseAPI.
   * Once End() has been used, none of the other API functions may be used
   * other than Init and anything declared above it in the class definition.
   */
  void End();

最后釋放數組和圖像。合情合理,沒有什么好說的。

如果需要完整的示例文件及CMakeLists.txt,可以點擊此處下載。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容

  • CMake學習 本篇分享一下有關CMake的一些學習心得以及相關使用。 本文目錄如下: [1、CMake介紹] [...
    AlphaGL閱讀 12,268評論 11 79
  • 注:首發地址 1. 前言 當在做 Android NDK 開發時,如果不熟悉用 CMake 來構建,讀不懂 CMa...
    cfanr閱讀 24,475評論 1 53
  • CMake是我非常喜歡且一直使用的工具。它不但能幫助我跨平臺、跨編譯器,而且最酷的是,它幫我節約了太多的存儲空間。...
    行之與亦安閱讀 100,046評論 5 56
  • 前段時間由于做比賽的事,一直都沒時間寫博客,現在終于可以補上一篇了,一直想學習一點NDK開發的知識,但是遲遲沒有動...
    冰鑒IT閱讀 1,780評論 7 18
  • 幾個常用的基礎命令哈### react-native --version ...
    白公子是貓奴閱讀 344評論 0 0