tesseract 3.04.01

wordrec/pieces.cpp

Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines