/src/tesseract/src/classify/shapeclassifier.cpp
Line | Count | Source |
1 | | // Copyright 2011 Google Inc. All Rights Reserved. |
2 | | // Author: rays@google.com (Ray Smith) |
3 | | /////////////////////////////////////////////////////////////////////// |
4 | | // File: shapeclassifier.cpp |
5 | | // Description: Base interface class for classifiers that return a |
6 | | // shape index. |
7 | | // Author: Ray Smith |
8 | | // |
9 | | // (C) Copyright 2011, Google Inc. |
10 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
11 | | // you may not use this file except in compliance with the License. |
12 | | // You may obtain a copy of the License at |
13 | | // http://www.apache.org/licenses/LICENSE-2.0 |
14 | | // Unless required by applicable law or agreed to in writing, software |
15 | | // distributed under the License is distributed on an "AS IS" BASIS, |
16 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
17 | | // See the License for the specific language governing permissions and |
18 | | // limitations under the License. |
19 | | // |
20 | | /////////////////////////////////////////////////////////////////////// |
21 | | |
22 | | #ifdef HAVE_CONFIG_H |
23 | | # include "config_auto.h" |
24 | | #endif |
25 | | |
26 | | #include "shapeclassifier.h" |
27 | | |
28 | | #include "scrollview.h" |
29 | | #include "shapetable.h" |
30 | | #ifndef GRAPHICS_DISABLED |
31 | | #include "svmnode.h" |
32 | | #endif |
33 | | #include "tesserrstream.h" // for tesserr |
34 | | #include "trainingsample.h" |
35 | | |
36 | | namespace tesseract { |
37 | | |
38 | | // Classifies the given [training] sample, writing to results. |
39 | | // See shapeclassifier.h for a full description. |
40 | | // Default implementation calls the ShapeRating version. |
41 | | int ShapeClassifier::UnicharClassifySample(const TrainingSample &sample, Image page_pix, int debug, |
42 | | UNICHAR_ID keep_this, |
43 | 0 | std::vector<UnicharRating> *results) { |
44 | 0 | results->clear(); |
45 | 0 | std::vector<ShapeRating> shape_results; |
46 | 0 | int num_shape_results = ClassifySample(sample, page_pix, debug, keep_this, &shape_results); |
47 | 0 | const ShapeTable *shapes = GetShapeTable(); |
48 | 0 | std::vector<int> unichar_map(shapes->unicharset().size(), -1); |
49 | 0 | for (int r = 0; r < num_shape_results; ++r) { |
50 | 0 | shapes->AddShapeToResults(shape_results[r], &unichar_map, results); |
51 | 0 | } |
52 | 0 | return results->size(); |
53 | 0 | } |
54 | | |
55 | | // Classifies the given [training] sample, writing to results. |
56 | | // See shapeclassifier.h for a full description. |
57 | | // Default implementation aborts. |
58 | | int ShapeClassifier::ClassifySample(const TrainingSample &/*sample*/, |
59 | | Image /*page_pix*/, int /*debug*/, |
60 | | int /*keep_this*/, |
61 | 0 | std::vector<ShapeRating> */*results*/) { |
62 | 0 | ASSERT_HOST("Must implement ClassifySample!" == nullptr); |
63 | 0 | return 0; |
64 | 0 | } |
65 | | |
66 | | // Returns the shape that contains unichar_id that has the best result. |
67 | | // If result is not nullptr, it is set with the shape_id and rating. |
68 | | // Does not need to be overridden if ClassifySample respects the keep_this |
69 | | // rule. |
70 | | int ShapeClassifier::BestShapeForUnichar(const TrainingSample &sample, Image page_pix, |
71 | 0 | UNICHAR_ID unichar_id, ShapeRating *result) { |
72 | 0 | std::vector<ShapeRating> results; |
73 | 0 | const ShapeTable *shapes = GetShapeTable(); |
74 | 0 | int num_results = ClassifySample(sample, page_pix, 0, unichar_id, &results); |
75 | 0 | for (int r = 0; r < num_results; ++r) { |
76 | 0 | if (shapes->GetShape(results[r].shape_id).ContainsUnichar(unichar_id)) { |
77 | 0 | if (result != nullptr) { |
78 | 0 | *result = results[r]; |
79 | 0 | } |
80 | 0 | return results[r].shape_id; |
81 | 0 | } |
82 | 0 | } |
83 | 0 | return -1; |
84 | 0 | } |
85 | | |
86 | | // Provides access to the UNICHARSET that this classifier works with. |
87 | | // Only needs to be overridden if GetShapeTable() can return nullptr. |
88 | 0 | const UNICHARSET &ShapeClassifier::GetUnicharset() const { |
89 | 0 | return GetShapeTable()->unicharset(); |
90 | 0 | } |
91 | | |
92 | | #ifndef GRAPHICS_DISABLED |
93 | | |
94 | | // Visual debugger classifies the given sample, displays the results and |
95 | | // solicits user input to display other classifications. Returns when |
96 | | // the user has finished with debugging the sample. |
97 | | // Probably doesn't need to be overridden if the subclass provides |
98 | | // DisplayClassifyAs. |
99 | | void ShapeClassifier::DebugDisplay(const TrainingSample &sample, Image page_pix, |
100 | | UNICHAR_ID unichar_id) { |
101 | | static ScrollView *terminator = nullptr; |
102 | | if (terminator == nullptr) { |
103 | | terminator = new ScrollView("XIT", 0, 0, 50, 50, 50, 50, true); |
104 | | } |
105 | | ScrollView *debug_win = CreateFeatureSpaceWindow("ClassifierDebug", 0, 0); |
106 | | // Provide a right-click menu to choose the class. |
107 | | auto *popup_menu = new SVMenuNode(); |
108 | | popup_menu->AddChild("Choose class to debug", 0, "x", "Class to debug"); |
109 | | popup_menu->BuildMenu(debug_win, false); |
110 | | // Display the features in green. |
111 | | const INT_FEATURE_STRUCT *features = sample.features(); |
112 | | uint32_t num_features = sample.num_features(); |
113 | | for (uint32_t f = 0; f < num_features; ++f) { |
114 | | RenderIntFeature(debug_win, &features[f], ScrollView::GREEN); |
115 | | } |
116 | | debug_win->Update(); |
117 | | std::vector<UnicharRating> results; |
118 | | // Debug classification until the user quits. |
119 | | const UNICHARSET &unicharset = GetUnicharset(); |
120 | | SVEventType ev_type; |
121 | | do { |
122 | | std::vector<ScrollView *> windows; |
123 | | if (unichar_id >= 0) { |
124 | | tprintf("Debugging class %d = %s\n", unichar_id, unicharset.id_to_unichar(unichar_id)); |
125 | | UnicharClassifySample(sample, page_pix, 1, unichar_id, &results); |
126 | | DisplayClassifyAs(sample, page_pix, unichar_id, 1); |
127 | | } else { |
128 | | tprintf("Invalid unichar_id: %d\n", unichar_id); |
129 | | UnicharClassifySample(sample, page_pix, 1, -1, &results); |
130 | | } |
131 | | if (unichar_id >= 0) { |
132 | | tprintf("Debugged class %d = %s\n", unichar_id, unicharset.id_to_unichar(unichar_id)); |
133 | | } |
134 | | tprintf("Right-click in ClassifierDebug window to choose debug class,"); |
135 | | tprintf(" Left-click or close window to quit...\n"); |
136 | | UNICHAR_ID old_unichar_id; |
137 | | do { |
138 | | old_unichar_id = unichar_id; |
139 | | auto ev = debug_win->AwaitEvent(SVET_ANY); |
140 | | ev_type = ev->type; |
141 | | if (ev_type == SVET_POPUP) { |
142 | | if (unicharset.contains_unichar(ev->parameter.c_str())) { |
143 | | unichar_id = unicharset.unichar_to_id(ev->parameter.c_str()); |
144 | | } else { |
145 | | tesserr << "Char class '" << ev->parameter << "' not found in unicharset"; |
146 | | } |
147 | | } |
148 | | } while (unichar_id == old_unichar_id && ev_type != SVET_CLICK && ev_type != SVET_DESTROY); |
149 | | for (auto window : windows) { |
150 | | delete window; |
151 | | } |
152 | | } while (ev_type != SVET_CLICK && ev_type != SVET_DESTROY); |
153 | | delete debug_win; |
154 | | } |
155 | | |
156 | | #endif // !GRAPHICS_DISABLED |
157 | | |
158 | | // Displays classification as the given shape_id. Creates as many windows |
159 | | // as it feels fit, using index as a guide for placement. Adds any created |
160 | | // windows to the windows output and returns a new index that may be used |
161 | | // by any subsequent classifiers. Caller waits for the user to view and |
162 | | // then destroys the windows by clearing the vector. |
163 | | int ShapeClassifier::DisplayClassifyAs(const TrainingSample &/*sample*/, |
164 | | Image /*page_pix*/, |
165 | 0 | UNICHAR_ID /*unichar_id*/, int index) { |
166 | | // Does nothing in the default implementation. |
167 | 0 | return index; |
168 | 0 | } |
169 | | |
170 | | // Prints debug information on the results. |
171 | | void ShapeClassifier::UnicharPrintResults(const char *context, |
172 | 0 | const std::vector<UnicharRating> &results) const { |
173 | 0 | tprintf("%s\n", context); |
174 | 0 | for (const auto &result : results) { |
175 | 0 | tprintf("%g: c_id=%d=%s", result.rating, result.unichar_id, |
176 | 0 | GetUnicharset().id_to_unichar(result.unichar_id)); |
177 | 0 | if (!result.fonts.empty()) { |
178 | 0 | tprintf(" Font Vector:"); |
179 | 0 | for (auto &&font : result.fonts) { |
180 | 0 | tprintf(" %d", font.fontinfo_id); |
181 | 0 | } |
182 | 0 | } |
183 | 0 | tprintf("\n"); |
184 | 0 | } |
185 | 0 | } |
186 | | void ShapeClassifier::PrintResults(const char *context, |
187 | 0 | const std::vector<ShapeRating> &results) const { |
188 | 0 | tprintf("%s\n", context); |
189 | 0 | for (const auto &result : results) { |
190 | 0 | tprintf("%g:", result.rating); |
191 | 0 | if (result.joined) { |
192 | 0 | tprintf("[J]"); |
193 | 0 | } |
194 | 0 | if (result.broken) { |
195 | 0 | tprintf("[B]"); |
196 | 0 | } |
197 | 0 | tprintf(" %s\n", GetShapeTable()->DebugStr(result.shape_id).c_str()); |
198 | 0 | } |
199 | 0 | } |
200 | | |
201 | | // Removes any result that has all its unichars covered by a better choice, |
202 | | // regardless of font. |
203 | 0 | void ShapeClassifier::FilterDuplicateUnichars(std::vector<ShapeRating> *results) const { |
204 | 0 | std::vector<ShapeRating> filtered_results; |
205 | | // Copy results to filtered results and knock out duplicate unichars. |
206 | 0 | const ShapeTable *shapes = GetShapeTable(); |
207 | 0 | for (unsigned r = 0; r < results->size(); ++r) { |
208 | 0 | if (r > 0) { |
209 | 0 | const Shape &shape_r = shapes->GetShape((*results)[r].shape_id); |
210 | 0 | int c; |
211 | 0 | for (c = 0; c < shape_r.size(); ++c) { |
212 | 0 | int unichar_id = shape_r[c].unichar_id; |
213 | 0 | unsigned s; |
214 | 0 | for (s = 0; s < r; ++s) { |
215 | 0 | const Shape &shape_s = shapes->GetShape((*results)[s].shape_id); |
216 | 0 | if (shape_s.ContainsUnichar(unichar_id)) { |
217 | 0 | break; // We found unichar_id. |
218 | 0 | } |
219 | 0 | } |
220 | 0 | if (s == r) { |
221 | 0 | break; // We didn't find unichar_id. |
222 | 0 | } |
223 | 0 | } |
224 | 0 | if (c == shape_r.size()) { |
225 | 0 | continue; // We found all the unichar ids in previous answers. |
226 | 0 | } |
227 | 0 | } |
228 | 0 | filtered_results.push_back((*results)[r]); |
229 | 0 | } |
230 | 0 | *results = std::move(filtered_results); |
231 | 0 | } |
232 | | |
233 | | } // namespace tesseract. |