1. 程式人生 > >OCR----Tesseract引擎核心類TessBaseAPI的操作

OCR----Tesseract引擎核心類TessBaseAPI的操作

前言


寫了兩篇博文介紹了:

這一篇將更加深入OCR的世界!

不得不把一些和本專欄(後面會整理出一個系列)相關的參考資料列出來,幫助大家建立知識體系。

1 Tesseract的環境安裝 –> 谷震平的傳送門
2 Tesseract的使用方法:主要是命令列的使用 –> 傳送門
3 Tesseract的Python呼叫 –> 傳送門
4 Tesseract的核心API函式的中文註解 –> 傳送門
5 Tesseract的英文API –>

傳送門
6 Tesseract原理的必讀專業論文(大牛Ray Smith所寫) –> 傳送門

最最最重要的一個網址,所有資料都是從這裡衍生的!那就是Tesseract-ocr在Github上的Wiki,這裡是Tesseract最權威的參考資料,沒有之一。
權威資料:谷震平的傳送門!


Tesseract API


我是在使用Python來做OCR的開發,所以和直接用C/C++的童鞋有點區別。

提個問題:
在Python呼叫該TessBaseAPI類(Tesseract的核心類)時,使用的是C++ 的API,還是C的API,為什麼不是Python自己的API???

答案:Tesseract是C++和C混編而成,核心的程式碼都是C++寫的。當然,核心類TessBaseAPI也是C++。想用Python語言再實現一遍Tesseract的核心類是不現實的,只能讓Python自己去呼叫C++提供的介面。但這是萬萬不能的,因為Python無法呼叫C++。BUT!Python可以通過ctypes模組(Python2.4以後的標準庫之一)呼叫C語言。於是,Tesseract的維護者們通過C語言將C++函式的功能封裝成介面,再通過Python(ctypes模組)去對接C介面。這樣,Python語言可以使用Tesseract引擎了。

所以,Python呼叫TessBaseAPI類時,使用的是C的API,不是Python的,更不是C++的。

概括下,原理就是:C++實現Tesseract的功能,C作為一個橋樑(橋接),連線Python。這樣是不影響執行效率的,因為在做運算時,都是C++,非常快。

Python呼叫Tesseract引擎,內容谷震平的blog


TessBaseAPI類


Python呼叫TessBaseAPICreate()的方法【第一個呼叫的方法】,就是為了使用TessBaseAPI這個類。不知所以的童鞋,請去API檢索TessBaseAPICreate(),它return new TessBaseAPI。

TessBaseAPI這是Tesseract引擎的核心類,搞定它是第一步。該類的內部function請自己查閱API。特別是關於Init() ,setImage()的。

Init()官方原始碼:

  276 int TessBaseAPI::Init(const char* datapath, const char* language,
  277                       OcrEngineMode oem, char **configs, int configs_size,
  278                       const GenericVector<STRING> *vars_vec,
  279                       const GenericVector<STRING> *vars_values,
  280                       bool set_only_non_debug_params) {
  281   PERF_COUNT_START("TessBaseAPI::Init")
  282   // 預設識別語言是English:"eng"
  283   if (language == NULL) language = "eng";
  284   // 如果資料路徑,OCR引擎模型或者識別語言被改變,就再次啟動引擎
  285   // Note that the language_ field stores the last requested language that was
  286   // initialized successfully, while tesseract_->lang stores the language
  287   // actually used. They differ only if the requested language was NULL, in
  288   // which case tesseract_->lang is set to the Tesseract default ("eng").
  289   if (tesseract_ != NULL &&
  290       (datapath_ == NULL || language_ == NULL ||
  291        *datapath_ != datapath || last_oem_requested_ != oem ||
  292        (*language_ != language && tesseract_->lang != language))) {
  293     delete tesseract_;
  294     tesseract_ = NULL;
  295   }
  296   // PERF_COUNT_SUB("delete tesseract_")
  297 #ifdef USE_OPENCL
  298   OpenclDevice od;
  299   od.InitEnv();
  300 #endif
  301   PERF_COUNT_SUB("OD::InitEnv()")
  302   bool reset_classifier = true;
  303   if (tesseract_ == NULL) {
  304     reset_classifier = false;
  305     tesseract_ = new Tesseract;
  306     if (tesseract_->init_tesseract(
  307         datapath, output_file_ != NULL ? output_file_->string() : NULL,
  308         language, oem, configs, configs_size, vars_vec, vars_values,
  309         set_only_non_debug_params) != 0) {
  310       return -1;
  311     }
  312   }
  313   PERF_COUNT_SUB("update tesseract_")
  314   // 需要最新且有效的初始化,才能更新資料路徑和識別語言
  315   if (datapath_ == NULL)
  316     datapath_ = new STRING(datapath);
  317   else
  318     *datapath_ = datapath;
  319   if ((strcmp(datapath_->string(), "") == 0) &&
  320       (strcmp(tesseract_->datadir.string(), "") != 0))
  321      *datapath_ = tesseract_->datadir;
  322 
  323   if (language_ == NULL)
  324     language_ = new STRING(language);
  325   else
  326     *language_ = language;
  327   last_oem_requested_ = oem;
  328   // PERF_COUNT_SUB("update last_oem_requested_")
  329   // 對於同樣的識別語言和資料路徑,只需要重置自適應分類器(the adaptive classifier)
  330   if (reset_classifier) {
  331     tesseract_->ResetAdaptiveClassifier();
  332     PERF_COUNT_SUB("tesseract_->ResetAdaptiveClassifier()")
  333   }
  334   PERF_COUNT_END
  335   return 0;
  336 }

上述程式碼可以看到:tesseract_->init_tesseract(datapath, output_file_ != NULL ? output_file_->string() : NULL, language, oem, configs, configs_size, vars_vec, vars_values,set_only_non_debug_params) 這條語句才是核心!

好吧,再去Tesseract類裡找init_tesseract()方法。官方原始碼:

  281 // Initialize for potentially a set of languages defined by the language
  282 // string and recursively any additional languages required by any language
  283 // traineddata file (via tessedit_load_sublangs in its config) that is loaded.
  284 // See init_tesseract_internal for args.
  285 int Tesseract::init_tesseract(
  286     const char *arg0, const char *textbase, const char *language,
  287     OcrEngineMode oem, char **configs, int configs_size,
  288     const GenericVector<STRING> *vars_vec,
  289     const GenericVector<STRING> *vars_values,
  290     bool set_only_non_debug_params) {
  291   GenericVector<STRING> langs_to_load;
  292   GenericVector<STRING> langs_not_to_load;
  293   ParseLanguageString(language, &langs_to_load, &langs_not_to_load);
  294 
  295   sub_langs_.delete_data_pointers();
  296   sub_langs_.clear();
  297   // Find the first loadable lang and load into this.
  298   // Add any languages that this language requires
  299   bool loaded_primary = false;
  300   // Load the rest into sub_langs_.
  301   for (int lang_index = 0; lang_index < langs_to_load.size(); ++lang_index) {
  302     if (!IsStrInList(langs_to_load[lang_index], langs_not_to_load)) {
  303       const char *lang_str = langs_to_load[lang_index].string();
  304       Tesseract *tess_to_init;
  305       if (!loaded_primary) {
  306         tess_to_init = this;
  307       } else {
  308         tess_to_init = new Tesseract;
  309       }
  310 
  311       int result = tess_to_init->init_tesseract_internal(
  312           arg0, textbase, lang_str, oem, configs, configs_size,
  313           vars_vec, vars_values, set_only_non_debug_params);
  314 
  315       if (!loaded_primary) {
  316         if (result < 0) {
  317           tprintf("Failed loading language '%s'\n", lang_str);
  318         } else {
  319           if (tessdata_manager_debug_level)
  320             tprintf("Loaded language '%s' as main language\n", lang_str);
  321           ParseLanguageString(tess_to_init->tessedit_load_sublangs.string(),
  322                               &langs_to_load, &langs_not_to_load);
  323           loaded_primary = true;
  324         }
  325       } else {
  326         if (result < 0) {
  327           tprintf("Failed loading language '%s'\n", lang_str);
  328           delete tess_to_init;
  329         } else {
  330           if (tessdata_manager_debug_level)
  331             tprintf("Loaded language '%s' as secondary language\n", lang_str);
  332           sub_langs_.push_back(tess_to_init);
  333           // Add any languages that this language requires
  334           ParseLanguageString(tess_to_init->tessedit_load_sublangs.string(),
  335                               &langs_to_load, &langs_not_to_load);
  336         }
  337       }
  338     }
  339   }
  340   if (!loaded_primary) {
  341     tprintf("Tesseract couldn't load any languages!\n");
  342     return -1;  // Couldn't load any language!
  343   }
  344   if (!sub_langs_.empty()) {
  345     // In multilingual mode word ratings have to be directly comparable,
  346     // so use the same language model weights for all languages:
  347     // use the primary language's params model if
  348     // tessedit_use_primary_params_model is set,
  349     // otherwise use default language model weights.
  350     if (tessedit_use_primary_params_model) {
  351       for (int s = 0; s < sub_langs_.size(); ++s) {
  352         sub_langs_[s]->language_model_->getParamsModel().Copy(
  353             this->language_model_->getParamsModel());
  354       }
  355       tprintf("Using params model of the primary language\n");
  356       if (tessdata_manager_debug_level)  {
  357         this->language_model_->getParamsModel().Print();
  358       }
  359     } else {
  360       this->language_model_->getParamsModel().Clear();
  361       for (int s = 0; s < sub_langs_.size(); ++s) {
  362         sub_langs_[s]->language_model_->getParamsModel().Clear();
  363       }
  364       if (tessdata_manager_debug_level)
  365         tprintf("Using default language params\n");
  366     }
  367   }
  368 
  369   SetupUniversalFontIds();
  370   return 0;
  371 }

上述程式碼可以看到:int result = tess_to_init->init_tesseract_internal(arg0, textbase, lang_str, oem, configs, configs_size,vars_vec, vars_values, set_only_non_debug_params); 這條語句才是核心程式碼!

就這樣。。。你需要一個一個地去看。想看的懂程式碼,就需要自己去積累、去琢磨。寫到這裡,後續更新!


The End


歡迎交流,不得不說,人家花10年搞出來的東西,你想改核心程式碼,還是很困難的。好就好在,人可以堅持不懈,可以團結合作!

內容來自谷震平的blog,尊重原創,轉載註明出處!
謝謝大家,希望批評交流!

新開通微信公眾號,歡迎關注原創文章:
谷震平的微信公眾號 二維碼