All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cube_line_object.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: cube_line_object.cpp
3  * Description: Implementation of the Cube Line Object Class
4  * Author: Ahmad Abdulkader
5  * Created: 2007
6  *
7  * (C) Copyright 2008, Google Inc.
8  ** Licensed under the Apache License, Version 2.0 (the "License");
9  ** you may not use this file except in compliance with the License.
10  ** You may obtain a copy of the License at
11  ** http://www.apache.org/licenses/LICENSE-2.0
12  ** Unless required by applicable law or agreed to in writing, software
13  ** distributed under the License is distributed on an "AS IS" BASIS,
14  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  ** See the License for the specific language governing permissions and
16  ** limitations under the License.
17  *
18  **********************************************************************/
19 
20 #include <algorithm>
21 #include "cube_line_object.h"
22 
23 namespace tesseract {
25  line_pix_ = pix;
26  own_pix_ = false;
27  processed_ = false;
28  cntxt_ = cntxt;
29  phrase_cnt_ = 0;
30  phrases_ = NULL;
31 }
32 
34  if (line_pix_ != NULL && own_pix_ == true) {
35  pixDestroy(&line_pix_);
36  line_pix_ = NULL;
37  }
38 
39  if (phrases_ != NULL) {
40  for (int phrase_idx = 0; phrase_idx < phrase_cnt_; phrase_idx++) {
41  if (phrases_[phrase_idx] != NULL) {
42  delete phrases_[phrase_idx];
43  }
44  }
45 
46  delete []phrases_;
47  phrases_ = NULL;
48  }
49 }
50 
51 // Recognize the specified pix as one line returning the recognized
52 bool CubeLineObject::Process() {
53  // do nothing if pix had already been processed
54  if (processed_) {
55  return true;
56  }
57 
58  // validate data
59  if (line_pix_ == NULL || cntxt_ == NULL) {
60  return false;
61  }
62 
63  // create a CharSamp
64  CharSamp *char_samp = CubeUtils::CharSampleFromPix(line_pix_, 0, 0,
65  line_pix_->w,
66  line_pix_->h);
67  if (char_samp == NULL) {
68  return false;
69  }
70 
71  // compute connected components.
72  int con_comp_cnt = 0;
73  ConComp **con_comps = char_samp->FindConComps(&con_comp_cnt,
74  cntxt_->Params()->MinConCompSize());
75  // no longer need char_samp, delete it
76  delete char_samp;
77  // no connected components, bail out
78  if (con_comp_cnt <= 0 || con_comps == NULL) {
79  return false;
80  }
81 
82  // sort connected components based on reading order
83  bool rtl = (cntxt_->ReadingOrder() == tesseract::CubeRecoContext::R2L);
84  qsort(con_comps, con_comp_cnt, sizeof(*con_comps), rtl ?
86 
87  // compute work breaking threshold as a ratio of line height
88  bool ret_val = false;
89  int word_break_threshold = ComputeWordBreakThreshold(con_comp_cnt, con_comps,
90  rtl);
91  if (word_break_threshold > 0) {
92  // over-allocate phrases object buffer
93  phrases_ = new CubeObject *[con_comp_cnt];
94  if (phrases_ != NULL) {
95  // create a phrase if the horizontal distance between two consecutive
96  // concomps is higher than threshold
97  int start_con_idx = 0;
98  int current_phrase_limit = rtl ? con_comps[0]->Left() :
99  con_comps[0]->Right();
100 
101  for (int con_idx = 1; con_idx <= con_comp_cnt; con_idx++) {
102  bool create_new_phrase = true;
103  // if not at the end, compute the distance between two consecutive
104  // concomps
105  if (con_idx < con_comp_cnt) {
106  int dist = 0;
108  dist = current_phrase_limit - con_comps[con_idx]->Right();
109  } else {
110  dist = con_comps[con_idx]->Left() - current_phrase_limit;
111  }
112  create_new_phrase = (dist > word_break_threshold);
113  }
114 
115  // create a new phrase
116  if (create_new_phrase) {
117  // create a phrase corresponding to a range on components
118  bool left_most;
119  bool right_most;
120  CharSamp *phrase_char_samp =
121  CharSamp::FromConComps(con_comps, start_con_idx,
122  con_idx - start_con_idx, NULL,
123  &left_most, &right_most,
124  line_pix_->h);
125  if (phrase_char_samp == NULL) {
126  break;
127  }
128  phrases_[phrase_cnt_] = new CubeObject(cntxt_, phrase_char_samp);
129  if (phrases_[phrase_cnt_] == NULL) {
130  delete phrase_char_samp;
131  break;
132  }
133  // set the ownership of the charsamp to the cube object
134  phrases_[phrase_cnt_]->SetCharSampOwnership(true);
135  phrase_cnt_++;
136  // advance the starting index to the current index
137  start_con_idx = con_idx;
138  // set the limit of the newly starting phrase (if any)
139  if (con_idx < con_comp_cnt) {
140  current_phrase_limit = rtl ? con_comps[con_idx]->Left() :
141  con_comps[con_idx]->Right();
142  }
143  } else {
144  // update the limit of the current phrase
146  current_phrase_limit = MIN(current_phrase_limit,
147  con_comps[con_idx]->Left());
148  } else {
149  current_phrase_limit = MAX(current_phrase_limit,
150  con_comps[con_idx]->Right());
151  }
152  }
153  }
154  ret_val = true;
155  }
156  }
157 
158  // clean-up connected comps
159  for (int con_idx = 0; con_idx < con_comp_cnt; con_idx++) {
160  delete con_comps[con_idx];
161  }
162  delete []con_comps;
163 
164  // success
165  processed_ = true;
166  return ret_val;
167 }
168 
169 // Compute the least word breaking threshold that is required to produce a
170 // valid set of phrases. Phrases are validated using the Aspect ratio
171 // constraints specified in the language specific Params object
172 int CubeLineObject::ComputeWordBreakThreshold(int con_comp_cnt,
173  ConComp **con_comps, bool rtl) {
174  // initial estimate of word breaking threshold
175  int word_break_threshold =
176  static_cast<int>(line_pix_->h * cntxt_->Params()->MaxSpaceHeightRatio());
177  bool valid = false;
178 
179  // compute the resulting words and validate each's aspect ratio
180  do {
181  // group connected components into words based on breaking threshold
182  int start_con_idx = 0;
183  int current_phrase_limit = (rtl ? con_comps[0]->Left() :
184  con_comps[0]->Right());
185  int min_x = con_comps[0]->Left();
186  int max_x = con_comps[0]->Right();
187  int min_y = con_comps[0]->Top();
188  int max_y = con_comps[0]->Bottom();
189  valid = true;
190  for (int con_idx = 1; con_idx <= con_comp_cnt; con_idx++) {
191  bool create_new_phrase = true;
192  // if not at the end, compute the distance between two consecutive
193  // concomps
194  if (con_idx < con_comp_cnt) {
195  int dist = 0;
196  if (rtl) {
197  dist = current_phrase_limit - con_comps[con_idx]->Right();
198  } else {
199  dist = con_comps[con_idx]->Left() - current_phrase_limit;
200  }
201  create_new_phrase = (dist > word_break_threshold);
202  }
203 
204  // create a new phrase
205  if (create_new_phrase) {
206  // check aspect ratio. Break if invalid
207  if ((max_x - min_x + 1) >
208  (cntxt_->Params()->MaxWordAspectRatio() * (max_y - min_y + 1))) {
209  valid = false;
210  break;
211  }
212  // advance the starting index to the current index
213  start_con_idx = con_idx;
214  // set the limit of the newly starting phrase (if any)
215  if (con_idx < con_comp_cnt) {
216  current_phrase_limit = rtl ? con_comps[con_idx]->Left() :
217  con_comps[con_idx]->Right();
218  // re-init bounding box
219  min_x = con_comps[con_idx]->Left();
220  max_x = con_comps[con_idx]->Right();
221  min_y = con_comps[con_idx]->Top();
222  max_y = con_comps[con_idx]->Bottom();
223  }
224  } else {
225  // update the limit of the current phrase
226  if (rtl) {
227  current_phrase_limit = MIN(current_phrase_limit,
228  con_comps[con_idx]->Left());
229  } else {
230  current_phrase_limit = MAX(current_phrase_limit,
231  con_comps[con_idx]->Right());
232  }
233  // update bounding box
234  UpdateRange(con_comps[con_idx]->Left(),
235  con_comps[con_idx]->Right(), &min_x, &max_x);
236  UpdateRange(con_comps[con_idx]->Top(),
237  con_comps[con_idx]->Bottom(), &min_y, &max_y);
238  }
239  }
240 
241  // return the breaking threshold if all broken word dimensions are valid
242  if (valid) {
243  return word_break_threshold;
244  }
245 
246  // decrease the threshold and try again
247  word_break_threshold--;
248  } while (!valid && word_break_threshold > 0);
249 
250  // failed to find a threshold that acheives the target aspect ratio.
251  // Just use the default threshold
252  return static_cast<int>(line_pix_->h *
253  cntxt_->Params()->MaxSpaceHeightRatio());
254 }
255 }
double MaxSpaceHeightRatio() const
Definition: tuning_params.h:61
#define MAX(x, y)
Definition: ndminx.h:24
#define MIN(x, y)
Definition: ndminx.h:28
ReadOrder ReadingOrder() const
static int Left2RightComparer(const void *comp1, const void *comp2)
Definition: con_comp.h:73
static CharSamp * FromConComps(ConComp **concomp_array, int strt_concomp, int seg_flags_size, int *seg_flags, bool *left_most, bool *right_most, int word_hgt)
Definition: char_samp.cpp:457
void UpdateRange(const T1 &x, T2 *lower_bound, T2 *upper_bound)
Definition: helpers.h:125
static int Right2LeftComparer(const void *comp1, const void *comp2)
Definition: con_comp.h:82
CubeLineObject(CubeRecoContext *cntxt, Pix *pix)
TuningParams * Params() const
void SetCharSampOwnership(bool own_char_samp)
Definition: cube_object.h:135
int MinConCompSize() const
Definition: tuning_params.h:58
double MaxWordAspectRatio() const
Definition: tuning_params.h:59
#define NULL
Definition: host.h:144
static CharSamp * CharSampleFromPix(Pix *pix, int left, int top, int wid, int hgt)
Definition: cube_utils.cpp:104