Tesseract  3.02
tesseract-ocr/cube/cube_line_object.cpp
Go to the documentation of this file.
00001 /**********************************************************************
00002  * File:        cube_line_object.cpp
00003  * Description: Implementation of the Cube Line Object Class
00004  * Author:    Ahmad Abdulkader
00005  * Created:   2007
00006  *
00007  * (C) Copyright 2008, Google Inc.
00008  ** Licensed under the Apache License, Version 2.0 (the "License");
00009  ** you may not use this file except in compliance with the License.
00010  ** You may obtain a copy of the License at
00011  ** http://www.apache.org/licenses/LICENSE-2.0
00012  ** Unless required by applicable law or agreed to in writing, software
00013  ** distributed under the License is distributed on an "AS IS" BASIS,
00014  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015  ** See the License for the specific language governing permissions and
00016  ** limitations under the License.
00017  *
00018  **********************************************************************/
00019 
00020 #include <algorithm>
00021 #include "cube_line_object.h"
00022 
00023 namespace tesseract {
00024 CubeLineObject::CubeLineObject(CubeRecoContext *cntxt, Pix *pix) {
00025   line_pix_ = pix;
00026   own_pix_ = false;
00027   processed_ = false;
00028   cntxt_ = cntxt;
00029   phrase_cnt_ = 0;
00030   phrases_ = NULL;
00031 }
00032 
00033 CubeLineObject::~CubeLineObject() {
00034   if (line_pix_ != NULL && own_pix_ == true) {
00035     pixDestroy(&line_pix_);
00036     line_pix_ = NULL;
00037   }
00038 
00039   if (phrases_ != NULL) {
00040     for (int phrase_idx = 0; phrase_idx < phrase_cnt_; phrase_idx++) {
00041       if (phrases_[phrase_idx] != NULL) {
00042         delete phrases_[phrase_idx];
00043       }
00044     }
00045 
00046     delete []phrases_;
00047     phrases_ = NULL;
00048   }
00049 }
00050 
00051 // Recognize the specified pix as one line returning the recognized
00052 bool CubeLineObject::Process() {
00053   // do nothing if pix had already been processed
00054   if (processed_) {
00055     return true;
00056   }
00057 
00058   // validate data
00059   if (line_pix_ == NULL || cntxt_ == NULL) {
00060     return false;
00061   }
00062 
00063   // create a CharSamp
00064   CharSamp *char_samp = CubeUtils::CharSampleFromPix(line_pix_, 0, 0,
00065                                                      line_pix_->w,
00066                                                      line_pix_->h);
00067   if (char_samp == NULL) {
00068     return false;
00069   }
00070 
00071   // compute connected components.
00072   int con_comp_cnt = 0;
00073   ConComp **con_comps = char_samp->FindConComps(&con_comp_cnt,
00074       cntxt_->Params()->MinConCompSize());
00075   // no longer need char_samp, delete it
00076   delete char_samp;
00077   // no connected components, bail out
00078   if (con_comp_cnt <= 0 || con_comps == NULL) {
00079     return false;
00080   }
00081 
00082   // sort connected components based on reading order
00083   bool rtl = (cntxt_->ReadingOrder() == tesseract::CubeRecoContext::R2L);
00084   qsort(con_comps, con_comp_cnt, sizeof(*con_comps), rtl ?
00085       ConComp::Right2LeftComparer : ConComp::Left2RightComparer);
00086 
00087   // compute work breaking threshold as a ratio of line height
00088   bool ret_val = false;
00089   int word_break_threshold = ComputeWordBreakThreshold(con_comp_cnt, con_comps,
00090                                                        rtl);
00091   if (word_break_threshold > 0) {
00092     // over-allocate phrases object buffer
00093     phrases_ = new CubeObject *[con_comp_cnt];
00094     if (phrases_ != NULL) {
00095       // create a phrase if the horizontal distance between two consecutive
00096       // concomps is higher than threshold
00097       int start_con_idx = 0;
00098       int current_phrase_limit = rtl ? con_comps[0]->Left() :
00099                                        con_comps[0]->Right();
00100 
00101       for (int con_idx = 1; con_idx <= con_comp_cnt; con_idx++) {
00102         bool create_new_phrase = true;
00103         // if not at the end, compute the distance between two consecutive
00104         // concomps
00105         if (con_idx < con_comp_cnt) {
00106           int dist = 0;
00107           if (cntxt_->ReadingOrder() == tesseract::CubeRecoContext::R2L) {
00108             dist = current_phrase_limit - con_comps[con_idx]->Right();
00109           } else {
00110             dist = con_comps[con_idx]->Left() - current_phrase_limit;
00111           }
00112           create_new_phrase = (dist > word_break_threshold);
00113         }
00114 
00115         // create a new phrase
00116         if (create_new_phrase) {
00117           // create a phrase corresponding to a range on components
00118           bool left_most;
00119           bool right_most;
00120           CharSamp *phrase_char_samp =
00121               CharSamp::FromConComps(con_comps, start_con_idx,
00122                                      con_idx - start_con_idx, NULL,
00123                                      &left_most, &right_most,
00124                                      line_pix_->h);
00125           if (phrase_char_samp == NULL) {
00126             break;
00127           }
00128           phrases_[phrase_cnt_] = new CubeObject(cntxt_, phrase_char_samp);
00129           if (phrases_[phrase_cnt_] == NULL) {
00130             delete phrase_char_samp;
00131             break;
00132           }
00133           // set the ownership of the charsamp to the cube object
00134           phrases_[phrase_cnt_]->SetCharSampOwnership(true);
00135           phrase_cnt_++;
00136           // advance the starting index to the current index
00137           start_con_idx = con_idx;
00138           // set the limit of the newly starting phrase (if any)
00139           if (con_idx < con_comp_cnt) {
00140             current_phrase_limit = rtl ? con_comps[con_idx]->Left() :
00141                                          con_comps[con_idx]->Right();
00142           }
00143         } else {
00144           // update the limit of the current phrase
00145           if (cntxt_->ReadingOrder() == tesseract::CubeRecoContext::R2L) {
00146             current_phrase_limit = MIN(current_phrase_limit,
00147                                        con_comps[con_idx]->Left());
00148           } else {
00149             current_phrase_limit = MAX(current_phrase_limit,
00150                                        con_comps[con_idx]->Right());
00151           }
00152         }
00153       }
00154       ret_val = true;
00155     }
00156   }
00157 
00158   // clean-up connected comps
00159   for (int con_idx = 0; con_idx < con_comp_cnt; con_idx++) {
00160     delete con_comps[con_idx];
00161   }
00162   delete []con_comps;
00163 
00164   // success
00165   processed_ = true;
00166   return ret_val;
00167 }
00168 
00169 // Compute the least word breaking threshold that is required to produce a
00170 // valid set of phrases. Phrases are validated using the Aspect ratio
00171 // constraints specified in the language specific Params object
00172 int CubeLineObject::ComputeWordBreakThreshold(int con_comp_cnt,
00173                                               ConComp **con_comps, bool rtl) {
00174   // initial estimate of word breaking threshold
00175   int word_break_threshold =
00176       static_cast<int>(line_pix_->h * cntxt_->Params()->MaxSpaceHeightRatio());
00177   bool valid = false;
00178 
00179   // compute the resulting words and validate each's aspect ratio
00180   do {
00181     // group connected components into words based on breaking threshold
00182     int start_con_idx = 0;
00183     int current_phrase_limit = (rtl ? con_comps[0]->Left() :
00184                                       con_comps[0]->Right());
00185     int min_x = con_comps[0]->Left();
00186     int max_x = con_comps[0]->Right();
00187     int min_y = con_comps[0]->Top();
00188     int max_y = con_comps[0]->Bottom();
00189     valid = true;
00190     for (int con_idx = 1; con_idx <= con_comp_cnt; con_idx++) {
00191       bool create_new_phrase = true;
00192       // if not at the end, compute the distance between two consecutive
00193       // concomps
00194       if (con_idx < con_comp_cnt) {
00195         int dist = 0;
00196         if (rtl) {
00197           dist = current_phrase_limit - con_comps[con_idx]->Right();
00198         } else {
00199           dist = con_comps[con_idx]->Left() - current_phrase_limit;
00200         }
00201         create_new_phrase = (dist > word_break_threshold);
00202       }
00203 
00204       // create a new phrase
00205       if (create_new_phrase) {
00206         // check aspect ratio. Break if invalid
00207         if ((max_x - min_x + 1) >
00208             (cntxt_->Params()->MaxWordAspectRatio() * (max_y - min_y + 1))) {
00209           valid = false;
00210           break;
00211         }
00212         // advance the starting index to the current index
00213         start_con_idx = con_idx;
00214         // set the limit of the newly starting phrase (if any)
00215         if (con_idx < con_comp_cnt) {
00216           current_phrase_limit = rtl ? con_comps[con_idx]->Left() :
00217                                        con_comps[con_idx]->Right();
00218           // re-init bounding box
00219           min_x = con_comps[con_idx]->Left();
00220           max_x = con_comps[con_idx]->Right();
00221           min_y = con_comps[con_idx]->Top();
00222           max_y = con_comps[con_idx]->Bottom();
00223         }
00224       } else {
00225         // update the limit of the current phrase
00226         if (rtl) {
00227           current_phrase_limit = MIN(current_phrase_limit,
00228                                      con_comps[con_idx]->Left());
00229         } else {
00230           current_phrase_limit = MAX(current_phrase_limit,
00231                                      con_comps[con_idx]->Right());
00232         }
00233         // update bounding box
00234         UpdateRange(con_comps[con_idx]->Left(),
00235                     con_comps[con_idx]->Right(), &min_x, &max_x);
00236         UpdateRange(con_comps[con_idx]->Top(),
00237                     con_comps[con_idx]->Bottom(), &min_y, &max_y);
00238       }
00239     }
00240 
00241     // return the breaking threshold if all broken word dimensions are valid
00242     if (valid) {
00243       return word_break_threshold;
00244     }
00245 
00246     // decrease the threshold and try again
00247     word_break_threshold--;
00248   } while (!valid && word_break_threshold > 0);
00249 
00250   // failed to find a threshold that acheives the target aspect ratio.
00251   // Just use the default threshold
00252   return  static_cast<int>(line_pix_->h *
00253                            cntxt_->Params()->MaxSpaceHeightRatio());
00254 }
00255 }