tesseract
3.03
|
00001 // Copyright 2011 Google Inc. All Rights Reserved. 00002 // Author: rays@google.com (Ray Smith) 00004 // File: shapeclassifier.h 00005 // Description: Base interface class for classifiers that return a 00006 // shape index. 00007 // Author: Ray Smith 00008 // Created: Thu Dec 15 15:24:27 PST 2011 00009 // 00010 // (C) Copyright 2011, 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 #ifdef HAVE_CONFIG_H 00024 #include "config_auto.h" 00025 #endif 00026 00027 #include "shapeclassifier.h" 00028 #include "genericvector.h" 00029 #include "scrollview.h" 00030 #include "shapetable.h" 00031 #include "svmnode.h" 00032 #include "trainingsample.h" 00033 #include "tprintf.h" 00034 00035 namespace tesseract { 00036 00037 // Classifies the given [training] sample, writing to results. 00038 // See shapeclassifier.h for a full description. 00039 // Default implementation calls the ShapeRating version. 00040 int ShapeClassifier::UnicharClassifySample( 00041 const TrainingSample& sample, Pix* page_pix, int debug, 00042 UNICHAR_ID keep_this, GenericVector<UnicharRating>* results) { 00043 results->truncate(0); 00044 GenericVector<ShapeRating> shape_results; 00045 int num_shape_results = ClassifySample(sample, page_pix, debug, keep_this, 00046 &shape_results); 00047 const ShapeTable* shapes = GetShapeTable(); 00048 GenericVector<int> unichar_map; 00049 unichar_map.init_to_size(shapes->unicharset().size(), -1); 00050 for (int r = 0; r < num_shape_results; ++r) { 00051 shapes->AddShapeToResults(shape_results[r], &unichar_map, results); 00052 } 00053 return results->size(); 00054 } 00055 00056 // Classifies the given [training] sample, writing to results. 00057 // See shapeclassifier.h for a full description. 00058 // Default implementation aborts. 00059 int ShapeClassifier::ClassifySample(const TrainingSample& sample, Pix* page_pix, 00060 int debug, int keep_this, 00061 GenericVector<ShapeRating>* results) { 00062 ASSERT_HOST("Must implement ClassifySample!" == NULL); 00063 return 0; 00064 } 00065 00066 // Returns the shape that contains unichar_id that has the best result. 00067 // If result is not NULL, it is set with the shape_id and rating. 00068 // Does not need to be overridden if ClassifySample respects the keep_this 00069 // rule. 00070 int ShapeClassifier::BestShapeForUnichar(const TrainingSample& sample, 00071 Pix* page_pix, UNICHAR_ID unichar_id, 00072 ShapeRating* result) { 00073 GenericVector<ShapeRating> results; 00074 const ShapeTable* shapes = GetShapeTable(); 00075 int num_results = ClassifySample(sample, page_pix, 0, unichar_id, &results); 00076 for (int r = 0; r < num_results; ++r) { 00077 if (shapes->GetShape(results[r].shape_id).ContainsUnichar(unichar_id)) { 00078 if (result != NULL) 00079 *result = results[r]; 00080 return results[r].shape_id; 00081 } 00082 } 00083 return -1; 00084 } 00085 00086 // Provides access to the UNICHARSET that this classifier works with. 00087 // Only needs to be overridden if GetShapeTable() can return NULL. 00088 const UNICHARSET& ShapeClassifier::GetUnicharset() const { 00089 return GetShapeTable()->unicharset(); 00090 } 00091 00092 // Visual debugger classifies the given sample, displays the results and 00093 // solicits user input to display other classifications. Returns when 00094 // the user has finished with debugging the sample. 00095 // Probably doesn't need to be overridden if the subclass provides 00096 // DisplayClassifyAs. 00097 void ShapeClassifier::DebugDisplay(const TrainingSample& sample, 00098 Pix* page_pix, 00099 UNICHAR_ID unichar_id) { 00100 #ifndef GRAPHICS_DISABLED 00101 static ScrollView* terminator = NULL; 00102 if (terminator == NULL) { 00103 terminator = new ScrollView("XIT", 0, 0, 50, 50, 50, 50, true); 00104 } 00105 ScrollView* debug_win = CreateFeatureSpaceWindow("ClassifierDebug", 0, 0); 00106 // Provide a right-click menu to choose the class. 00107 SVMenuNode* popup_menu = new SVMenuNode(); 00108 popup_menu->AddChild("Choose class to debug", 0, "x", "Class to debug"); 00109 popup_menu->BuildMenu(debug_win, false); 00110 // Display the features in green. 00111 const INT_FEATURE_STRUCT* features = sample.features(); 00112 int num_features = sample.num_features(); 00113 for (int f = 0; f < num_features; ++f) { 00114 RenderIntFeature(debug_win, &features[f], ScrollView::GREEN); 00115 } 00116 debug_win->Update(); 00117 GenericVector<UnicharRating> results; 00118 // Debug classification until the user quits. 00119 const UNICHARSET& unicharset = GetUnicharset(); 00120 SVEvent* ev; 00121 SVEventType ev_type; 00122 do { 00123 PointerVector<ScrollView> windows; 00124 if (unichar_id >= 0) { 00125 tprintf("Debugging class %d = %s\n", 00126 unichar_id, unicharset.id_to_unichar(unichar_id)); 00127 UnicharClassifySample(sample, page_pix, 1, unichar_id, &results); 00128 DisplayClassifyAs(sample, page_pix, unichar_id, 1, &windows); 00129 } else { 00130 tprintf("Invalid unichar_id: %d\n", unichar_id); 00131 UnicharClassifySample(sample, page_pix, 1, -1, &results); 00132 } 00133 if (unichar_id >= 0) { 00134 tprintf("Debugged class %d = %s\n", 00135 unichar_id, unicharset.id_to_unichar(unichar_id)); 00136 } 00137 tprintf("Right-click in ClassifierDebug window to choose debug class,"); 00138 tprintf(" Left-click or close window to quit...\n"); 00139 UNICHAR_ID old_unichar_id; 00140 do { 00141 old_unichar_id = unichar_id; 00142 ev = debug_win->AwaitEvent(SVET_ANY); 00143 ev_type = ev->type; 00144 if (ev_type == SVET_POPUP) { 00145 if (unicharset.contains_unichar(ev->parameter)) { 00146 unichar_id = unicharset.unichar_to_id(ev->parameter); 00147 } else { 00148 tprintf("Char class '%s' not found in unicharset", ev->parameter); 00149 } 00150 } 00151 delete ev; 00152 } while (unichar_id == old_unichar_id && 00153 ev_type != SVET_CLICK && ev_type != SVET_DESTROY); 00154 } while (ev_type != SVET_CLICK && ev_type != SVET_DESTROY); 00155 delete debug_win; 00156 #endif // GRAPHICS_DISABLED 00157 } 00158 00159 // Displays classification as the given shape_id. Creates as many windows 00160 // as it feels fit, using index as a guide for placement. Adds any created 00161 // windows to the windows output and returns a new index that may be used 00162 // by any subsequent classifiers. Caller waits for the user to view and 00163 // then destroys the windows by clearing the vector. 00164 int ShapeClassifier::DisplayClassifyAs( 00165 const TrainingSample& sample, Pix* page_pix, 00166 UNICHAR_ID unichar_id, int index, 00167 PointerVector<ScrollView>* windows) { 00168 // Does nothing in the default implementation. 00169 return index; 00170 } 00171 00172 // Prints debug information on the results. 00173 void ShapeClassifier::UnicharPrintResults( 00174 const char* context, const GenericVector<UnicharRating>& results) const { 00175 tprintf("%s\n", context); 00176 for (int i = 0; i < results.size(); ++i) { 00177 tprintf("%g: c_id=%d=%s", results[i].rating, results[i].unichar_id, 00178 GetUnicharset().id_to_unichar(results[i].unichar_id)); 00179 if (results[i].fonts.size() != 0) { 00180 tprintf(" Font Vector:"); 00181 for (int f = 0; f < results[i].fonts.size(); ++f) { 00182 tprintf(" %d", results[i].fonts[f]); 00183 } 00184 } 00185 tprintf("\n"); 00186 } 00187 } 00188 void ShapeClassifier::PrintResults( 00189 const char* context, const GenericVector<ShapeRating>& results) const { 00190 tprintf("%s\n", context); 00191 for (int i = 0; i < results.size(); ++i) { 00192 tprintf("%g:", results[i].rating); 00193 if (results[i].joined) 00194 tprintf("[J]"); 00195 if (results[i].broken) 00196 tprintf("[B]"); 00197 tprintf(" %s\n", GetShapeTable()->DebugStr(results[i].shape_id).string()); 00198 } 00199 } 00200 00201 // Removes any result that has all its unichars covered by a better choice, 00202 // regardless of font. 00203 void ShapeClassifier::FilterDuplicateUnichars( 00204 GenericVector<ShapeRating>* results) const { 00205 GenericVector<ShapeRating> filtered_results; 00206 // Copy results to filtered results and knock out duplicate unichars. 00207 const ShapeTable* shapes = GetShapeTable(); 00208 for (int r = 0; r < results->size(); ++r) { 00209 if (r > 0) { 00210 const Shape& shape_r = shapes->GetShape((*results)[r].shape_id); 00211 int c; 00212 for (c = 0; c < shape_r.size(); ++c) { 00213 int unichar_id = shape_r[c].unichar_id; 00214 int s; 00215 for (s = 0; s < r; ++s) { 00216 const Shape& shape_s = shapes->GetShape((*results)[s].shape_id); 00217 if (shape_s.ContainsUnichar(unichar_id)) 00218 break; // We found unichar_id. 00219 } 00220 if (s == r) 00221 break; // We didn't find unichar_id. 00222 } 00223 if (c == shape_r.size()) 00224 continue; // We found all the unichar ids in previous answers. 00225 } 00226 filtered_results.push_back((*results)[r]); 00227 } 00228 *results = filtered_results; 00229 } 00230 00231 } // namespace tesseract. 00232 00233 00234 00235 00236