Tesseract 3.02

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups 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 {
24 CubeLineObject::CubeLineObject(CubeRecoContext *cntxt, Pix *pix) {
25 line_pix_ = pix;
26 own_pix_ = false;
27 processed_ = false;
28 cntxt_ = cntxt;
29 phrase_cnt_ = 0;
30 phrases_ = NULL;
31 }
32
33 CubeLineObject::~CubeLineObject() {
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 ?
85 ConComp::Right2LeftComparer : ConComp::Left2RightComparer);
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;
107 if (cntxt_->ReadingOrder() == tesseract::CubeRecoContext::R2L) {
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
145 if (cntxt_->ReadingOrder() == tesseract::CubeRecoContext::R2L) {
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 }