Tesseract
3.02
|
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 }