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++,非常快。
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,尊重原創,轉載註明出處!
謝謝大家,希望批評交流!
新開通微信公眾號,歡迎關注原創文章: