tesseract  3.05.02
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  // create a phrase if the horizontal distance between two consecutive
95  // concomps is higher than threshold
96  int start_con_idx = 0;
97  int current_phrase_limit = rtl ? con_comps[0]->Left() :
98  con_comps[0]->Right();
99 
100  for (int con_idx = 1; con_idx <= con_comp_cnt; con_idx++) {
101  bool create_new_phrase = true;
102  // if not at the end, compute the distance between two consecutive
103  // concomps
104  if (con_idx < con_comp_cnt) {
105  int dist = 0;
107  dist = current_phrase_limit - con_comps[con_idx]->Right();
108  } else {
109  dist = con_comps[con_idx]->Left() - current_phrase_limit;
110  }
111  create_new_phrase = (dist > word_break_threshold);
112  }
113 
114  // create a new phrase
115  if (create_new_phrase) {
116  // create a phrase corresponding to a range on components
117  bool left_most;
118  bool right_most;
119  CharSamp *phrase_char_samp =
120  CharSamp::FromConComps(con_comps, start_con_idx,
121  con_idx - start_con_idx, NULL,
122  &left_most, &right_most,
123  line_pix_->h);
124  if (phrase_char_samp == NULL) {
125  break;
126  }
127  phrases_[phrase_cnt_] = new CubeObject(cntxt_, phrase_char_samp);
128  // set the ownership of the charsamp to the cube object
129  phrases_[phrase_cnt_]->SetCharSampOwnership(true);
130  phrase_cnt_++;
131  // advance the starting index to the current index
132  start_con_idx = con_idx;
133  // set the limit of the newly starting phrase (if any)
134  if (con_idx < con_comp_cnt) {
135  current_phrase_limit = rtl ? con_comps[con_idx]->Left() :
136  con_comps[con_idx]->Right();
137  }
138  } else {
139  // update the limit of the current phrase
141  current_phrase_limit = MIN(current_phrase_limit,
142  con_comps[con_idx]->Left());
143  } else {
144  current_phrase_limit = MAX(current_phrase_limit,
145  con_comps[con_idx]->Right());
146  }
147  }
148  }
149  ret_val = true;
150  }
151 
152  // clean-up connected comps
153  for (int con_idx = 0; con_idx < con_comp_cnt; con_idx++) {
154  delete con_comps[con_idx];
155  }
156  delete []con_comps;
157 
158  // success
159  processed_ = true;
160  return ret_val;
161 }
162 
163 // Compute the least word breaking threshold that is required to produce a
164 // valid set of phrases. Phrases are validated using the Aspect ratio
165 // constraints specified in the language specific Params object
166 int CubeLineObject::ComputeWordBreakThreshold(int con_comp_cnt,
167  ConComp **con_comps, bool rtl) {
168  // initial estimate of word breaking threshold
169  int word_break_threshold =
170  static_cast<int>(line_pix_->h * cntxt_->Params()->MaxSpaceHeightRatio());
171  bool valid = false;
172 
173  // compute the resulting words and validate each's aspect ratio
174  do {
175  // group connected components into words based on breaking threshold
176  int start_con_idx = 0;
177  int current_phrase_limit = (rtl ? con_comps[0]->Left() :
178  con_comps[0]->Right());
179  int min_x = con_comps[0]->Left();
180  int max_x = con_comps[0]->Right();
181  int min_y = con_comps[0]->Top();
182  int max_y = con_comps[0]->Bottom();
183  valid = true;
184  for (int con_idx = 1; con_idx <= con_comp_cnt; con_idx++) {
185  bool create_new_phrase = true;
186  // if not at the end, compute the distance between two consecutive
187  // concomps
188  if (con_idx < con_comp_cnt) {
189  int dist = 0;
190  if (rtl) {
191  dist = current_phrase_limit - con_comps[con_idx]->Right();
192  } else {
193  dist = con_comps[con_idx]->Left() - current_phrase_limit;
194  }
195  create_new_phrase = (dist > word_break_threshold);
196  }
197 
198  // create a new phrase
199  if (create_new_phrase) {
200  // check aspect ratio. Break if invalid
201  if ((max_x - min_x + 1) >
202  (cntxt_->Params()->MaxWordAspectRatio() * (max_y - min_y + 1))) {
203  valid = false;
204  break;
205  }
206  // advance the starting index to the current index
207  start_con_idx = con_idx;
208  // set the limit of the newly starting phrase (if any)
209  if (con_idx < con_comp_cnt) {
210  current_phrase_limit = rtl ? con_comps[con_idx]->Left() :
211  con_comps[con_idx]->Right();
212  // re-init bounding box
213  min_x = con_comps[con_idx]->Left();
214  max_x = con_comps[con_idx]->Right();
215  min_y = con_comps[con_idx]->Top();
216  max_y = con_comps[con_idx]->Bottom();
217  }
218  } else {
219  // update the limit of the current phrase
220  if (rtl) {
221  current_phrase_limit = MIN(current_phrase_limit,
222  con_comps[con_idx]->Left());
223  } else {
224  current_phrase_limit = MAX(current_phrase_limit,
225  con_comps[con_idx]->Right());
226  }
227  // update bounding box
228  UpdateRange(con_comps[con_idx]->Left(),
229  con_comps[con_idx]->Right(), &min_x, &max_x);
230  UpdateRange(con_comps[con_idx]->Top(),
231  con_comps[con_idx]->Bottom(), &min_y, &max_y);
232  }
233  }
234 
235  // return the breaking threshold if all broken word dimensions are valid
236  if (valid) {
237  return word_break_threshold;
238  }
239 
240  // decrease the threshold and try again
241  word_break_threshold--;
242  } while (!valid && word_break_threshold > 0);
243 
244  // failed to find a threshold that achieves the target aspect ratio.
245  // Just use the default threshold
246  return static_cast<int>(line_pix_->h *
247  cntxt_->Params()->MaxSpaceHeightRatio());
248 }
249 }
double MaxSpaceHeightRatio() const
Definition: tuning_params.h:61
static int Right2LeftComparer(const void *comp1, const void *comp2)
Definition: con_comp.h:82
void UpdateRange(const T1 &x, T2 *lower_bound, T2 *upper_bound)
Definition: helpers.h:125
ReadOrder ReadingOrder() const
#define MIN(x, y)
Definition: ndminx.h:28
TuningParams * Params() const
double MaxWordAspectRatio() const
Definition: tuning_params.h:59
void SetCharSampOwnership(bool own_char_samp)
Definition: cube_object.h:135
#define MAX(x, y)
Definition: ndminx.h:24
CubeLineObject(CubeRecoContext *cntxt, Pix *pix)
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:439
int MinConCompSize() const
Definition: tuning_params.h:58
static int Left2RightComparer(const void *comp1, const void *comp2)
Definition: con_comp.h:73
static CharSamp * CharSampleFromPix(Pix *pix, int left, int top, int wid, int hgt)
Definition: cube_utils.cpp:101