|
tesseract 3.04.01
|
00001 /* -*-C-*- 00002 ******************************************************************************** 00003 * 00004 * File: pieces.c (Formerly pieces.c) 00005 * Description: 00006 * Author: Mark Seaman, OCR Technology 00007 * Created: Fri Oct 16 14:37:00 1987 00008 * Modified: Mon May 20 12:12:35 1991 (Mark Seaman) marks@hpgrlt 00009 * Language: C 00010 * Package: N/A 00011 * Status: Reusable Software Component 00012 * 00013 * (c) Copyright 1987, Hewlett-Packard Company. 00014 ** Licensed under the Apache License, Version 2.0 (the "License"); 00015 ** you may not use this file except in compliance with the License. 00016 ** You may obtain a copy of the License at 00017 ** http://www.apache.org/licenses/LICENSE-2.0 00018 ** Unless required by applicable law or agreed to in writing, software 00019 ** distributed under the License is distributed on an "AS IS" BASIS, 00020 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00021 ** See the License for the specific language governing permissions and 00022 ** limitations under the License. 00023 * 00024 *********************************************************************************/ 00025 /*---------------------------------------------------------------------- 00026 I n c l u d e s 00027 ----------------------------------------------------------------------*/ 00028 00029 #include "blobs.h" 00030 #include "freelist.h" 00031 #include "helpers.h" 00032 #include "matrix.h" 00033 #include "ndminx.h" 00034 #include "ratngs.h" 00035 #include "seam.h" 00036 #include "wordrec.h" 00037 00038 // Include automatically generated configuration file if running autoconf. 00039 #ifdef HAVE_CONFIG_H 00040 #include "config_auto.h" 00041 #endif 00042 00043 using tesseract::ScoredFont; 00044 00045 /*---------------------------------------------------------------------- 00046 F u n c t i o n s 00047 ----------------------------------------------------------------------*/ 00048 00049 /********************************************************************** 00050 * classify_piece 00051 * 00052 * Create a larger piece from a collection of smaller ones. Classify 00053 * it and return the results. Take the large piece apart to leave 00054 * the collection of small pieces un modified. 00055 **********************************************************************/ 00056 namespace tesseract { 00057 BLOB_CHOICE_LIST *Wordrec::classify_piece(const GenericVector<SEAM*>& seams, 00058 inT16 start, 00059 inT16 end, 00060 const char* description, 00061 TWERD *word, 00062 BlamerBundle *blamer_bundle) { 00063 if (end > start) SEAM::JoinPieces(seams, word->blobs, start, end); 00064 BLOB_CHOICE_LIST *choices = classify_blob(word->blobs[start], description, 00065 White, blamer_bundle); 00066 // Set the matrix_cell_ entries in all the BLOB_CHOICES. 00067 BLOB_CHOICE_IT bc_it(choices); 00068 for (bc_it.mark_cycle_pt(); !bc_it.cycled_list(); bc_it.forward()) { 00069 bc_it.data()->set_matrix_cell(start, end); 00070 } 00071 00072 if (end > start) SEAM::BreakPieces(seams, word->blobs, start, end); 00073 00074 return (choices); 00075 } 00076 00077 template<class BLOB_CHOICE> 00078 int SortByUnicharID(const void *void1, const void *void2) { 00079 const BLOB_CHOICE *p1 = *reinterpret_cast<const BLOB_CHOICE * const *>(void1); 00080 const BLOB_CHOICE *p2 = *reinterpret_cast<const BLOB_CHOICE * const *>(void2); 00081 00082 return p1->unichar_id() - p2->unichar_id(); 00083 } 00084 00085 template<class BLOB_CHOICE> 00086 int SortByRating(const void *void1, const void *void2) { 00087 const BLOB_CHOICE *p1 = *reinterpret_cast<const BLOB_CHOICE * const *>(void1); 00088 const BLOB_CHOICE *p2 = *reinterpret_cast<const BLOB_CHOICE * const *>(void2); 00089 00090 if (p1->rating() < p2->rating()) 00091 return 1; 00092 return -1; 00093 } 00094 00095 00096 /********************************************************************** 00097 * fill_filtered_fragment_list 00098 * 00099 * Filter the fragment list so that the filtered_choices only contain 00100 * fragments that are in the correct position. choices is the list 00101 * that we are going to filter. fragment_pos is the position in the 00102 * fragment that we are looking for and num_frag_parts is the the 00103 * total number of pieces. The result will be appended to 00104 * filtered_choices. 00105 **********************************************************************/ 00106 void Wordrec::fill_filtered_fragment_list(BLOB_CHOICE_LIST *choices, 00107 int fragment_pos, 00108 int num_frag_parts, 00109 BLOB_CHOICE_LIST *filtered_choices) { 00110 BLOB_CHOICE_IT filtered_choices_it(filtered_choices); 00111 BLOB_CHOICE_IT choices_it(choices); 00112 00113 for (choices_it.mark_cycle_pt(); !choices_it.cycled_list(); 00114 choices_it.forward()) { 00115 UNICHAR_ID choice_unichar_id = choices_it.data()->unichar_id(); 00116 const CHAR_FRAGMENT *frag = unicharset.get_fragment(choice_unichar_id); 00117 00118 if (frag != NULL && frag->get_pos() == fragment_pos && 00119 frag->get_total() == num_frag_parts) { 00120 // Recover the unichar_id of the unichar that this fragment is 00121 // a part of 00122 BLOB_CHOICE *b = new BLOB_CHOICE(*choices_it.data()); 00123 int original_unichar = unicharset.unichar_to_id(frag->get_unichar()); 00124 b->set_unichar_id(original_unichar); 00125 filtered_choices_it.add_to_end(b); 00126 } 00127 } 00128 00129 filtered_choices->sort(SortByUnicharID<BLOB_CHOICE>); 00130 } 00131 00132 00133 /********************************************************************** 00134 * merge_and_put_fragment_lists 00135 * 00136 * Merge the fragment lists in choice_lists and append it to the 00137 * ratings matrix. 00138 **********************************************************************/ 00139 void Wordrec::merge_and_put_fragment_lists(inT16 row, inT16 column, 00140 inT16 num_frag_parts, 00141 BLOB_CHOICE_LIST *choice_lists, 00142 MATRIX *ratings) { 00143 BLOB_CHOICE_IT *choice_lists_it = new BLOB_CHOICE_IT[num_frag_parts]; 00144 00145 for (int i = 0; i < num_frag_parts; i++) { 00146 choice_lists_it[i].set_to_list(&choice_lists[i]); 00147 choice_lists_it[i].mark_cycle_pt(); 00148 } 00149 00150 BLOB_CHOICE_LIST *merged_choice = ratings->get(row, column); 00151 if (merged_choice == NULL) 00152 merged_choice = new BLOB_CHOICE_LIST; 00153 00154 bool end_of_list = false; 00155 BLOB_CHOICE_IT merged_choice_it(merged_choice); 00156 while (!end_of_list) { 00157 // Find the maximum unichar_id of the current entry the iterators 00158 // are pointing at 00159 UNICHAR_ID max_unichar_id = choice_lists_it[0].data()->unichar_id(); 00160 for (int i = 0; i < num_frag_parts; i++) { 00161 UNICHAR_ID unichar_id = choice_lists_it[i].data()->unichar_id(); 00162 if (max_unichar_id < unichar_id) { 00163 max_unichar_id = unichar_id; 00164 } 00165 } 00166 00167 // Move the each iterators until it gets to an entry that has a 00168 // value greater than or equal to max_unichar_id 00169 for (int i = 0; i < num_frag_parts; i++) { 00170 UNICHAR_ID unichar_id = choice_lists_it[i].data()->unichar_id(); 00171 while (!choice_lists_it[i].cycled_list() && 00172 unichar_id < max_unichar_id) { 00173 choice_lists_it[i].forward(); 00174 unichar_id = choice_lists_it[i].data()->unichar_id(); 00175 } 00176 if (choice_lists_it[i].cycled_list()) { 00177 end_of_list = true; 00178 break; 00179 } 00180 } 00181 00182 if (end_of_list) 00183 break; 00184 00185 // Checks if the fragments are parts of the same character 00186 UNICHAR_ID first_unichar_id = choice_lists_it[0].data()->unichar_id(); 00187 bool same_unichar = true; 00188 for (int i = 1; i < num_frag_parts; i++) { 00189 UNICHAR_ID unichar_id = choice_lists_it[i].data()->unichar_id(); 00190 if (unichar_id != first_unichar_id) { 00191 same_unichar = false; 00192 break; 00193 } 00194 } 00195 00196 if (same_unichar) { 00197 // Add the merged character to the result 00198 UNICHAR_ID merged_unichar_id = first_unichar_id; 00199 GenericVector<ScoredFont> merged_fonts = 00200 choice_lists_it[0].data()->fonts(); 00201 float merged_min_xheight = choice_lists_it[0].data()->min_xheight(); 00202 float merged_max_xheight = choice_lists_it[0].data()->max_xheight(); 00203 float positive_yshift = 0, negative_yshift = 0; 00204 int merged_script_id = choice_lists_it[0].data()->script_id(); 00205 BlobChoiceClassifier classifier = choice_lists_it[0].data()->classifier(); 00206 00207 float merged_rating = 0, merged_certainty = 0; 00208 for (int i = 0; i < num_frag_parts; i++) { 00209 float rating = choice_lists_it[i].data()->rating(); 00210 float certainty = choice_lists_it[i].data()->certainty(); 00211 00212 if (i == 0 || certainty < merged_certainty) 00213 merged_certainty = certainty; 00214 merged_rating += rating; 00215 00216 choice_lists_it[i].forward(); 00217 if (choice_lists_it[i].cycled_list()) 00218 end_of_list = true; 00219 IntersectRange(choice_lists_it[i].data()->min_xheight(), 00220 choice_lists_it[i].data()->max_xheight(), 00221 &merged_min_xheight, &merged_max_xheight); 00222 float yshift = choice_lists_it[i].data()->yshift(); 00223 if (yshift > positive_yshift) positive_yshift = yshift; 00224 if (yshift < negative_yshift) negative_yshift = yshift; 00225 // Use the min font rating over the parts. 00226 // TODO(rays) font lists are unsorted. Need to be faster? 00227 const GenericVector<ScoredFont>& frag_fonts = 00228 choice_lists_it[i].data()->fonts(); 00229 for (int f = 0; f < frag_fonts.size(); ++f) { 00230 int merged_f = 0; 00231 for (merged_f = 0; merged_f < merged_fonts.size() && 00232 merged_fonts[merged_f].fontinfo_id != frag_fonts[f].fontinfo_id; 00233 ++merged_f) {} 00234 if (merged_f == merged_fonts.size()) { 00235 merged_fonts.push_back(frag_fonts[f]); 00236 } else if (merged_fonts[merged_f].score > frag_fonts[f].score) { 00237 merged_fonts[merged_f].score = frag_fonts[f].score; 00238 } 00239 } 00240 } 00241 00242 float merged_yshift = positive_yshift != 0 00243 ? (negative_yshift != 0 ? 0 : positive_yshift) 00244 : negative_yshift; 00245 BLOB_CHOICE* choice = new BLOB_CHOICE(merged_unichar_id, 00246 merged_rating, 00247 merged_certainty, 00248 merged_script_id, 00249 merged_min_xheight, 00250 merged_max_xheight, 00251 merged_yshift, 00252 classifier); 00253 choice->set_fonts(merged_fonts); 00254 merged_choice_it.add_to_end(choice); 00255 } 00256 } 00257 00258 if (classify_debug_level) 00259 print_ratings_list("Merged Fragments", merged_choice, 00260 unicharset); 00261 00262 if (merged_choice->empty()) 00263 delete merged_choice; 00264 else 00265 ratings->put(row, column, merged_choice); 00266 00267 delete [] choice_lists_it; 00268 } 00269 00270 00271 /********************************************************************** 00272 * get_fragment_lists 00273 * 00274 * Recursively go through the ratings matrix to find lists of fragments 00275 * to be merged in the function merge_and_put_fragment_lists. 00276 * current_frag is the position of the piece we are looking for. 00277 * current_row is the row in the rating matrix we are currently at. 00278 * start is the row we started initially, so that we can know where 00279 * to append the results to the matrix. num_frag_parts is the total 00280 * number of pieces we are looking for and num_blobs is the size of the 00281 * ratings matrix. 00282 **********************************************************************/ 00283 void Wordrec::get_fragment_lists(inT16 current_frag, inT16 current_row, 00284 inT16 start, inT16 num_frag_parts, 00285 inT16 num_blobs, MATRIX *ratings, 00286 BLOB_CHOICE_LIST *choice_lists) { 00287 if (current_frag == num_frag_parts) { 00288 merge_and_put_fragment_lists(start, current_row - 1, num_frag_parts, 00289 choice_lists, ratings); 00290 return; 00291 } 00292 00293 for (inT16 x = current_row; x < num_blobs; x++) { 00294 BLOB_CHOICE_LIST *choices = ratings->get(current_row, x); 00295 if (choices == NULL) 00296 continue; 00297 00298 fill_filtered_fragment_list(choices, current_frag, num_frag_parts, 00299 &choice_lists[current_frag]); 00300 if (!choice_lists[current_frag].empty()) { 00301 get_fragment_lists(current_frag + 1, x + 1, start, num_frag_parts, 00302 num_blobs, ratings, choice_lists); 00303 choice_lists[current_frag].clear(); 00304 } 00305 } 00306 } 00307 00308 00309 /********************************************************************** 00310 * merge_fragments 00311 * 00312 * Try to merge fragments in the ratings matrix and put the result in 00313 * the corresponding row and column 00314 **********************************************************************/ 00315 void Wordrec::merge_fragments(MATRIX *ratings, inT16 num_blobs) { 00316 BLOB_CHOICE_LIST choice_lists[CHAR_FRAGMENT::kMaxChunks]; 00317 for (inT16 start = 0; start < num_blobs; start++) { 00318 for (int frag_parts = 2; frag_parts <= CHAR_FRAGMENT::kMaxChunks; 00319 frag_parts++) { 00320 get_fragment_lists(0, start, start, frag_parts, num_blobs, 00321 ratings, choice_lists); 00322 } 00323 } 00324 00325 // Delete fragments from the rating matrix 00326 for (inT16 x = 0; x < num_blobs; x++) { 00327 for (inT16 y = x; y < num_blobs; y++) { 00328 BLOB_CHOICE_LIST *choices = ratings->get(x, y); 00329 if (choices != NULL) { 00330 BLOB_CHOICE_IT choices_it(choices); 00331 for (choices_it.mark_cycle_pt(); !choices_it.cycled_list(); 00332 choices_it.forward()) { 00333 UNICHAR_ID choice_unichar_id = choices_it.data()->unichar_id(); 00334 const CHAR_FRAGMENT *frag = 00335 unicharset.get_fragment(choice_unichar_id); 00336 if (frag != NULL) 00337 delete choices_it.extract(); 00338 } 00339 } 00340 } 00341 } 00342 } 00343 00344 00345 } // namespace tesseract