tesseract
3.03
|
00001 00002 // File: pain_points.cpp 00003 // Description: Functions that utilize the knowledge about the properties 00004 // of the paths explored by the segmentation search in order 00005 // to "pain points" - the locations in the ratings matrix 00006 // which should be classified next. 00007 // Author: Rika Antonova 00008 // Created: Mon Jun 20 11:26:43 PST 2012 00009 // 00010 // (C) Copyright 2012, Google Inc. 00011 // Licensed under the Apache License, Version 2.0 (the "License"); 00012 // you may not use this file except in compliance with the License. 00013 // You may obtain a copy of the License at 00014 // http://www.apache.org/licenses/LICENSE-2.0 00015 // Unless required by applicable law or agreed to in writing, software 00016 // distributed under the License is distributed on an "AS IS" BASIS, 00017 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00018 // See the License for the specific language governing permissions and 00019 // limitations under the License. 00020 // 00022 00023 #include "lm_pain_points.h" 00024 00025 #include "associate.h" 00026 #include "dict.h" 00027 #include "genericheap.h" 00028 #include "lm_state.h" 00029 #include "matrix.h" 00030 #include "pageres.h" 00031 00032 namespace tesseract { 00033 00034 const float LMPainPoints::kDefaultPainPointPriorityAdjustment = 2.0f; 00035 const float LMPainPoints::kLooseMaxCharWhRatio = 2.5f; 00036 00037 LMPainPointsType LMPainPoints::Deque(MATRIX_COORD *pp, float *priority) { 00038 for (int h = 0; h < LM_PPTYPE_NUM; ++h) { 00039 if (pain_points_heaps_[h].empty()) continue; 00040 *priority = pain_points_heaps_[h].PeekTop().key; 00041 *pp = pain_points_heaps_[h].PeekTop().data; 00042 pain_points_heaps_[h].Pop(NULL); 00043 return static_cast<LMPainPointsType>(h); 00044 } 00045 return LM_PPTYPE_NUM; 00046 } 00047 00048 void LMPainPoints::GenerateInitial(WERD_RES *word_res) { 00049 MATRIX *ratings = word_res->ratings; 00050 AssociateStats associate_stats; 00051 for (int col = 0; col < ratings->dimension(); ++col) { 00052 int row_end = MIN(ratings->dimension(), col + ratings->bandwidth() + 1); 00053 for (int row = col + 1; row < row_end; ++row) { 00054 MATRIX_COORD coord(col, row); 00055 if (coord.Valid(*ratings) && 00056 ratings->get(col, row) != NOT_CLASSIFIED) continue; 00057 // Add an initial pain point if needed. 00058 if (ratings->Classified(col, row - 1, dict_->WildcardID()) || 00059 (col + 1 < ratings->dimension() && 00060 ratings->Classified(col + 1, row, dict_->WildcardID()))) { 00061 GeneratePainPoint(col, row, LM_PPTYPE_SHAPE, 0.0, 00062 true, max_char_wh_ratio_, word_res); 00063 } 00064 } 00065 } 00066 } 00067 00068 void LMPainPoints::GenerateFromPath(float rating_cert_scale, 00069 ViterbiStateEntry *vse, 00070 WERD_RES *word_res) { 00071 ViterbiStateEntry *curr_vse = vse; 00072 BLOB_CHOICE *curr_b = vse->curr_b; 00073 // The following pain point generation and priority calculation approaches 00074 // prioritize exploring paths with low average rating of the known part of 00075 // the path, while not relying on the ratings of the pieces to be combined. 00076 // 00077 // A pain point to combine the neighbors is generated for each pair of 00078 // neighboring blobs on the path (the path is represented by vse argument 00079 // given to GenerateFromPath()). The priority of each pain point is set to 00080 // the average rating (per outline length) of the path, not including the 00081 // ratings of the blobs to be combined. 00082 // The ratings of the blobs to be combined are not used to calculate the 00083 // priority, since it is not possible to determine from their magnitude 00084 // whether it will be beneficial to combine the blobs. The reason is that 00085 // chopped junk blobs (/ | - ') can have very good (low) ratings, however 00086 // combining them will be beneficial. Blobs with high ratings might be 00087 // over-joined pieces of characters, but also could be blobs from an unseen 00088 // font or chopped pieces of complex characters. 00089 while (curr_vse->parent_vse != NULL) { 00090 ViterbiStateEntry* parent_vse = curr_vse->parent_vse; 00091 const MATRIX_COORD& curr_cell = curr_b->matrix_cell(); 00092 const MATRIX_COORD& parent_cell = parent_vse->curr_b->matrix_cell(); 00093 MATRIX_COORD pain_coord(parent_cell.col, curr_cell.row); 00094 if (!pain_coord.Valid(*word_res->ratings) || 00095 !word_res->ratings->Classified(parent_cell.col, curr_cell.row, 00096 dict_->WildcardID())) { 00097 // rat_subtr contains ratings sum of the two adjacent blobs to be merged. 00098 // rat_subtr will be subtracted from the ratings sum of the path, since 00099 // the blobs will be joined into a new blob, whose rating is yet unknown. 00100 float rat_subtr = curr_b->rating() + parent_vse->curr_b->rating(); 00101 // ol_subtr contains the outline length of the blobs that will be joined. 00102 float ol_subtr = 00103 AssociateUtils::ComputeOutlineLength(rating_cert_scale, *curr_b) + 00104 AssociateUtils::ComputeOutlineLength(rating_cert_scale, 00105 *(parent_vse->curr_b)); 00106 // ol_dif is the outline of the path without the two blobs to be joined. 00107 float ol_dif = vse->outline_length - ol_subtr; 00108 // priority is set to the average rating of the path per unit of outline, 00109 // not counting the ratings of the pieces to be joined. 00110 float priority = ol_dif > 0 ? (vse->ratings_sum-rat_subtr)/ol_dif : 0.0; 00111 GeneratePainPoint(pain_coord.col, pain_coord.row, LM_PPTYPE_PATH, 00112 priority, true, max_char_wh_ratio_, word_res); 00113 } else if (debug_level_ > 3) { 00114 tprintf("NO pain point (Classified) for col=%d row=%d type=%s\n", 00115 pain_coord.col, pain_coord.row, 00116 LMPainPointsTypeName[LM_PPTYPE_PATH]); 00117 BLOB_CHOICE_IT b_it(word_res->ratings->get(pain_coord.col, 00118 pain_coord.row)); 00119 for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { 00120 BLOB_CHOICE* choice = b_it.data(); 00121 choice->print_full(); 00122 } 00123 } 00124 00125 curr_vse = parent_vse; 00126 curr_b = curr_vse->curr_b; 00127 } 00128 } 00129 00130 void LMPainPoints::GenerateFromAmbigs(const DANGERR &fixpt, 00131 ViterbiStateEntry *vse, 00132 WERD_RES *word_res) { 00133 // Begins and ends in DANGERR vector now record the blob indices as used 00134 // by the ratings matrix. 00135 for (int d = 0; d < fixpt.size(); ++d) { 00136 const DANGERR_INFO &danger = fixpt[d]; 00137 // Only use dangerous ambiguities. 00138 if (danger.dangerous) { 00139 GeneratePainPoint(danger.begin, danger.end - 1, 00140 LM_PPTYPE_AMBIG, vse->cost, true, 00141 kLooseMaxCharWhRatio, word_res); 00142 } 00143 } 00144 } 00145 00146 bool LMPainPoints::GeneratePainPoint( 00147 int col, int row, LMPainPointsType pp_type, float special_priority, 00148 bool ok_to_extend, float max_char_wh_ratio, 00149 WERD_RES *word_res) { 00150 MATRIX_COORD coord(col, row); 00151 if (coord.Valid(*word_res->ratings) && 00152 word_res->ratings->Classified(col, row, dict_->WildcardID())) { 00153 return false; 00154 } 00155 if (debug_level_ > 3) { 00156 tprintf("Generating pain point for col=%d row=%d type=%s\n", 00157 col, row, LMPainPointsTypeName[pp_type]); 00158 } 00159 // Compute associate stats. 00160 AssociateStats associate_stats; 00161 AssociateUtils::ComputeStats(col, row, NULL, 0, fixed_pitch_, 00162 max_char_wh_ratio, word_res, debug_level_, 00163 &associate_stats); 00164 // For fixed-pitch fonts/languages: if the current combined blob overlaps 00165 // the next blob on the right and it is ok to extend the blob, try extending 00166 // the blob until there is no overlap with the next blob on the right or 00167 // until the width-to-height ratio becomes too large. 00168 if (ok_to_extend) { 00169 while (associate_stats.bad_fixed_pitch_right_gap && 00170 row + 1 < word_res->ratings->dimension() && 00171 !associate_stats.bad_fixed_pitch_wh_ratio) { 00172 AssociateUtils::ComputeStats(col, ++row, NULL, 0, fixed_pitch_, 00173 max_char_wh_ratio, word_res, debug_level_, 00174 &associate_stats); 00175 } 00176 } 00177 if (associate_stats.bad_shape) { 00178 if (debug_level_ > 3) { 00179 tprintf("Discarded pain point with a bad shape\n"); 00180 } 00181 return false; 00182 } 00183 00184 // Insert the new pain point into pain_points_heap_. 00185 if (pain_points_heaps_[pp_type].size() < max_heap_size_) { 00186 // Compute pain point priority. 00187 float priority; 00188 if (pp_type == LM_PPTYPE_PATH) { 00189 priority = special_priority; 00190 } else { 00191 priority = associate_stats.gap_sum; 00192 } 00193 MatrixCoordPair pain_point(priority, MATRIX_COORD(col, row)); 00194 pain_points_heaps_[pp_type].Push(&pain_point); 00195 if (debug_level_) { 00196 tprintf("Added pain point with priority %g\n", priority); 00197 } 00198 return true; 00199 } else { 00200 if (debug_level_) tprintf("Pain points heap is full\n"); 00201 return false; 00202 } 00203 } 00204 00205 // Adjusts the pain point coordinates to cope with expansion of the ratings 00206 // matrix due to a split of the blob with the given index. 00207 void LMPainPoints::RemapForSplit(int index) { 00208 for (int i = 0; i < LM_PPTYPE_NUM; ++i) { 00209 GenericVector<MatrixCoordPair>* heap = pain_points_heaps_[i].heap(); 00210 for (int j = 0; j < heap->size(); ++j) 00211 (*heap)[j].data.MapForSplit(index); 00212 } 00213 } 00214 00215 } // namespace tesseract 00216