Coverage Report

Created: 2026-10-28 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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.