tesseract  5.0.0-alpha-619-ge9db
intfx.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  ** Filename: intfx.c
3  ** Purpose: Integer character normalization & feature extraction
4  ** Author: Robert Moss, rays@google.com (Ray Smith)
5  **
6  ** (c) Copyright Hewlett-Packard Company, 1988.
7  ** Licensed under the Apache License, Version 2.0 (the "License");
8  ** you may not use this file except in compliance with the License.
9  ** You may obtain a copy of the License at
10  ** http://www.apache.org/licenses/LICENSE-2.0
11  ** Unless required by applicable law or agreed to in writing, software
12  ** distributed under the License is distributed on an "AS IS" BASIS,
13  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  ** See the License for the specific language governing permissions and
15  ** limitations under the License.
16  *****************************************************************************/
21 #define _USE_MATH_DEFINES // for M_PI
22 #include "intfx.h"
23 #include <cmath> // for M_PI
24 #include <mutex> // for std::mutex
25 #include "allheaders.h"
26 #include "classify.h"
27 #include <tesseract/helpers.h>
28 #include "intmatcher.h"
29 #include "linlsq.h"
30 #include "normalis.h"
31 #include "statistc.h"
32 #include "trainingsample.h"
33 
35 
39 // Look up table for cos and sin to turn the intfx feature angle to a vector.
40 // Protected by atan_table_mutex.
41 // The entries are in binary degrees where a full circle is 256 binary degrees.
42 static float cos_table[INT_CHAR_NORM_RANGE];
43 static float sin_table[INT_CHAR_NORM_RANGE];
44 
49 void InitIntegerFX() {
50  // Guards write access to AtanTable so we don't create it more than once.
51  static std::mutex atan_table_mutex;
52  static bool atan_table_init = false;
53  std::lock_guard<std::mutex> guard(atan_table_mutex);
54  if (!atan_table_init) {
55  for (int i = 0; i < INT_CHAR_NORM_RANGE; ++i) {
56  cos_table[i] = cos(i * 2 * M_PI / INT_CHAR_NORM_RANGE + M_PI);
57  sin_table[i] = sin(i * 2 * M_PI / INT_CHAR_NORM_RANGE + M_PI);
58  }
59  atan_table_init = true;
60  }
61 }
62 
63 // Returns a vector representing the direction of a feature with the given
64 // theta direction in an INT_FEATURE_STRUCT.
65 FCOORD FeatureDirection(uint8_t theta) {
66  return FCOORD(cos_table[theta], sin_table[theta]);
67 }
68 
69 namespace tesseract {
70 
71 // Generates a TrainingSample from a TBLOB. Extracts features and sets
72 // the bounding box, so classifiers that operate on the image can work.
73 // TODO(rays) Make BlobToTrainingSample a member of Classify now that
74 // the FlexFx and FeatureDescription code have been removed and LearnBlob
75 // is now a member of Classify.
77  const TBLOB& blob, bool nonlinear_norm, INT_FX_RESULT_STRUCT* fx_info,
78  GenericVector<INT_FEATURE_STRUCT>* bl_features) {
80  Classify::ExtractFeatures(blob, nonlinear_norm, bl_features,
81  &cn_features, fx_info, nullptr);
82  // TODO(rays) Use blob->PreciseBoundingBox() instead.
83  TBOX box = blob.bounding_box();
84  TrainingSample* sample = nullptr;
85  int num_features = fx_info->NumCN;
86  if (num_features > 0) {
87  sample = TrainingSample::CopyFromFeatures(*fx_info, box, &cn_features[0],
88  num_features);
89  }
90  if (sample != nullptr) {
91  // Set the bounding box (in original image coordinates) in the sample.
92  TPOINT topleft, botright;
93  topleft.x = box.left();
94  topleft.y = box.top();
95  botright.x = box.right();
96  botright.y = box.bottom();
97  TPOINT original_topleft, original_botright;
98  blob.denorm().DenormTransform(nullptr, topleft, &original_topleft);
99  blob.denorm().DenormTransform(nullptr, botright, &original_botright);
100  sample->set_bounding_box(TBOX(original_topleft.x, original_botright.y,
101  original_botright.x, original_topleft.y));
102  }
103  return sample;
104 }
105 
106 // Computes the DENORMS for bl(baseline) and cn(character) normalization
107 // during feature extraction. The input denorm describes the current state
108 // of the blob, which is usually a baseline-normalized word.
109 // The Transforms setup are as follows:
110 // Baseline Normalized (bl) Output:
111 // We center the grapheme by aligning the x-coordinate of its centroid with
112 // x=128 and leaving the already-baseline-normalized y as-is.
113 //
114 // Character Normalized (cn) Output:
115 // We align the grapheme's centroid at the origin and scale it
116 // asymmetrically in x and y so that the 2nd moments are a standard value
117 // (51.2) ie the result is vaguely square.
118 // If classify_nonlinear_norm is true:
119 // A non-linear normalization is setup that attempts to evenly distribute
120 // edges across x and y.
121 //
122 // Some of the fields of fx_info are also setup:
123 // Length: Total length of outline.
124 // Rx: Rounded y second moment. (Reversed by convention.)
125 // Ry: rounded x second moment.
126 // Xmean: Rounded x center of mass of the blob.
127 // Ymean: Rounded y center of mass of the blob.
128 void Classify::SetupBLCNDenorms(const TBLOB& blob, bool nonlinear_norm,
129  DENORM* bl_denorm, DENORM* cn_denorm,
130  INT_FX_RESULT_STRUCT* fx_info) {
131  // Compute 1st and 2nd moments of the original outline.
132  FCOORD center, second_moments;
133  int length = blob.ComputeMoments(&center, &second_moments);
134  if (fx_info != nullptr) {
135  fx_info->Length = length;
136  fx_info->Rx = IntCastRounded(second_moments.y());
137  fx_info->Ry = IntCastRounded(second_moments.x());
138 
139  fx_info->Xmean = IntCastRounded(center.x());
140  fx_info->Ymean = IntCastRounded(center.y());
141  }
142  // Setup the denorm for Baseline normalization.
143  bl_denorm->SetupNormalization(nullptr, nullptr, &blob.denorm(), center.x(), 128.0f,
144  1.0f, 1.0f, 128.0f, 128.0f);
145  // Setup the denorm for character normalization.
146  if (nonlinear_norm) {
149  TBOX box;
150  blob.GetPreciseBoundingBox(&box);
151  box.pad(1, 1);
152  blob.GetEdgeCoords(box, &x_coords, &y_coords);
153  cn_denorm->SetupNonLinear(&blob.denorm(), box, UINT8_MAX, UINT8_MAX,
154  0.0f, 0.0f, x_coords, y_coords);
155  } else {
156  cn_denorm->SetupNormalization(nullptr, nullptr, &blob.denorm(),
157  center.x(), center.y(),
158  51.2f / second_moments.x(),
159  51.2f / second_moments.y(),
160  128.0f, 128.0f);
161  }
162 }
163 
164 // Helper normalizes the direction, assuming that it is at the given
165 // unnormed_pos, using the given denorm, starting at the root_denorm.
166 static uint8_t NormalizeDirection(uint8_t dir, const FCOORD& unnormed_pos,
167  const DENORM& denorm,
168  const DENORM* root_denorm) {
169  // Convert direction to a vector.
170  FCOORD unnormed_end;
171  unnormed_end.from_direction(dir);
172  unnormed_end += unnormed_pos;
173  FCOORD normed_pos, normed_end;
174  denorm.NormTransform(root_denorm, unnormed_pos, &normed_pos);
175  denorm.NormTransform(root_denorm, unnormed_end, &normed_end);
176  normed_end -= normed_pos;
177  return normed_end.to_direction();
178 }
179 
180 // Helper returns the mean direction vector from the given stats. Use the
181 // mean direction from dirs if there is information available, otherwise, use
182 // the fit_vector from point_diffs.
183 static FCOORD MeanDirectionVector(const LLSQ& point_diffs, const LLSQ& dirs,
184  const FCOORD& start_pt,
185  const FCOORD& end_pt) {
186  FCOORD fit_vector;
187  if (dirs.count() > 0) {
188  // There were directions, so use them. To avoid wrap-around problems, we
189  // have 2 accumulators in dirs: x for normal directions and y for
190  // directions offset by 128. We will use the one with the least variance.
191  FCOORD mean_pt = dirs.mean_point();
192  double mean_dir = 0.0;
193  if (dirs.x_variance() <= dirs.y_variance()) {
194  mean_dir = mean_pt.x();
195  } else {
196  mean_dir = mean_pt.y() + 128;
197  }
198  fit_vector.from_direction(Modulo(IntCastRounded(mean_dir), 256));
199  } else {
200  // There were no directions, so we rely on the vector_fit to the points.
201  // Since the vector_fit is 180 degrees ambiguous, we align with the
202  // supplied feature_dir by making the scalar product non-negative.
203  FCOORD feature_dir(end_pt - start_pt);
204  fit_vector = point_diffs.vector_fit();
205  if (fit_vector.x() == 0.0f && fit_vector.y() == 0.0f) {
206  // There was only a single point. Use feature_dir directly.
207  fit_vector = feature_dir;
208  } else {
209  // Sometimes the least mean squares fit is wrong, due to the small sample
210  // of points and scaling. Use a 90 degree rotated vector if that matches
211  // feature_dir better.
212  FCOORD fit_vector2 = !fit_vector;
213  // The fit_vector is 180 degrees ambiguous, so resolve the ambiguity by
214  // insisting that the scalar product with the feature_dir should be +ve.
215  if (fit_vector % feature_dir < 0.0)
216  fit_vector = -fit_vector;
217  if (fit_vector2 % feature_dir < 0.0)
218  fit_vector2 = -fit_vector2;
219  // Even though fit_vector2 has a higher mean squared error, it might be
220  // a better fit, so use it if the dot product with feature_dir is bigger.
221  if (fit_vector2 % feature_dir > fit_vector % feature_dir)
222  fit_vector = fit_vector2;
223  }
224  }
225  return fit_vector;
226 }
227 
228 // Helper computes one or more features corresponding to the given points.
229 // Emitted features are on the line defined by:
230 // start_pt + lambda * (end_pt - start_pt) for scalar lambda.
231 // Features are spaced at feature_length intervals.
232 static int ComputeFeatures(const FCOORD& start_pt, const FCOORD& end_pt,
233  double feature_length,
235  FCOORD feature_vector(end_pt - start_pt);
236  if (feature_vector.x() == 0.0f && feature_vector.y() == 0.0f) return 0;
237  // Compute theta for the feature based on its direction.
238  uint8_t theta = feature_vector.to_direction();
239  // Compute the number of features and lambda_step.
240  double target_length = feature_vector.length();
241  int num_features = IntCastRounded(target_length / feature_length);
242  if (num_features == 0) return 0;
243  // Divide the length evenly into num_features pieces.
244  double lambda_step = 1.0 / num_features;
245  double lambda = lambda_step / 2.0;
246  for (int f = 0; f < num_features; ++f, lambda += lambda_step) {
247  FCOORD feature_pt(start_pt);
248  feature_pt += feature_vector * lambda;
249  INT_FEATURE_STRUCT feature(feature_pt, theta);
250  features->push_back(feature);
251  }
252  return num_features;
253 }
254 
255 // Gathers outline points and their directions from start_index into dirs by
256 // stepping along the outline and normalizing the coordinates until the
257 // required feature_length has been collected or end_index is reached.
258 // On input pos must point to the position corresponding to start_index and on
259 // return pos is updated to the current raw position, and pos_normed is set to
260 // the normed version of pos.
261 // Since directions wrap-around, they need special treatment to get the mean.
262 // Provided the cluster of directions doesn't straddle the wrap-around point,
263 // the simple mean works. If they do, then, unless the directions are wildly
264 // varying, the cluster rotated by 180 degrees will not straddle the wrap-
265 // around point, so mean(dir + 180 degrees) - 180 degrees will work. Since
266 // LLSQ conveniently stores the mean of 2 variables, we use it to store
267 // dir and dir+128 (128 is 180 degrees) and then use the resulting mean
268 // with the least variance.
269 static int GatherPoints(const C_OUTLINE* outline, double feature_length,
270  const DENORM& denorm, const DENORM* root_denorm,
271  int start_index, int end_index,
272  ICOORD* pos, FCOORD* pos_normed,
273  LLSQ* points, LLSQ* dirs) {
274  int step_length = outline->pathlength();
275  ICOORD step = outline->step(start_index % step_length);
276  // Prev_normed is the start point of this collection and will be set on the
277  // first iteration, and on later iterations used to determine the length
278  // that has been collected.
279  FCOORD prev_normed;
280  points->clear();
281  dirs->clear();
282  int num_points = 0;
283  int index;
284  for (index = start_index; index <= end_index; ++index, *pos += step) {
285  step = outline->step(index % step_length);
286  int edge_weight = outline->edge_strength_at_index(index % step_length);
287  if (edge_weight == 0) {
288  // This point has conflicting gradient and step direction, so ignore it.
289  continue;
290  }
291  // Get the sub-pixel precise location and normalize.
292  FCOORD f_pos = outline->sub_pixel_pos_at_index(*pos, index % step_length);
293  denorm.NormTransform(root_denorm, f_pos, pos_normed);
294  if (num_points == 0) {
295  // The start of this segment.
296  prev_normed = *pos_normed;
297  } else {
298  FCOORD offset = *pos_normed - prev_normed;
299  float length = offset.length();
300  if (length > feature_length) {
301  // We have gone far enough from the start. We will use this point in
302  // the next set so return what we have so far.
303  return index;
304  }
305  }
306  points->add(pos_normed->x(), pos_normed->y(), edge_weight);
307  int direction = outline->direction_at_index(index % step_length);
308  if (direction >= 0) {
309  direction = NormalizeDirection(direction, f_pos, denorm, root_denorm);
310  // Use both the direction and direction +128 so we are not trying to
311  // take the mean of something straddling the wrap-around point.
312  dirs->add(direction, Modulo(direction + 128, 256));
313  }
314  ++num_points;
315  }
316  return index;
317 }
318 
319 // Extracts Tesseract features and appends them to the features vector.
320 // Startpt to lastpt, inclusive, MUST have the same src_outline member,
321 // which may be nullptr. The vector from lastpt to its next is included in
322 // the feature extraction. Hidden edges should be excluded by the caller.
323 // If force_poly is true, the features will be extracted from the polygonal
324 // approximation even if more accurate data is available.
325 static void ExtractFeaturesFromRun(
326  const EDGEPT* startpt, const EDGEPT* lastpt,
327  const DENORM& denorm, double feature_length, bool force_poly,
329  const EDGEPT* endpt = lastpt->next;
330  const C_OUTLINE* outline = startpt->src_outline;
331  if (outline != nullptr && !force_poly) {
332  // Detailed information is available. We have to normalize only from
333  // the root_denorm to denorm.
334  const DENORM* root_denorm = denorm.RootDenorm();
335  int total_features = 0;
336  // Get the features from the outline.
337  int step_length = outline->pathlength();
338  int start_index = startpt->start_step;
339  // pos is the integer coordinates of the binary image steps.
340  ICOORD pos = outline->position_at_index(start_index);
341  // We use an end_index that allows us to use a positive increment, but that
342  // may be beyond the bounds of the outline steps/ due to wrap-around, to
343  // so we use % step_length everywhere, except for start_index.
344  int end_index = lastpt->start_step + lastpt->step_count;
345  if (end_index <= start_index)
346  end_index += step_length;
347  LLSQ prev_points;
348  LLSQ prev_dirs;
349  FCOORD prev_normed_pos = outline->sub_pixel_pos_at_index(pos, start_index);
350  denorm.NormTransform(root_denorm, prev_normed_pos, &prev_normed_pos);
351  LLSQ points;
352  LLSQ dirs;
353  FCOORD normed_pos(0.0f, 0.0f);
354  int index = GatherPoints(outline, feature_length, denorm, root_denorm,
355  start_index, end_index, &pos, &normed_pos,
356  &points, &dirs);
357  while (index <= end_index) {
358  // At each iteration we nominally have 3 accumulated sets of points and
359  // dirs: prev_points/dirs, points/dirs, next_points/dirs and sum them
360  // into sum_points/dirs, but we don't necessarily get any features out,
361  // so if that is the case, we keep accumulating instead of rotating the
362  // accumulators.
363  LLSQ next_points;
364  LLSQ next_dirs;
365  FCOORD next_normed_pos(0.0f, 0.0f);
366  index = GatherPoints(outline, feature_length, denorm, root_denorm,
367  index, end_index, &pos, &next_normed_pos,
368  &next_points, &next_dirs);
369  LLSQ sum_points(prev_points);
370  // TODO(rays) find out why it is better to use just dirs and next_dirs
371  // in sum_dirs, instead of using prev_dirs as well.
372  LLSQ sum_dirs(dirs);
373  sum_points.add(points);
374  sum_points.add(next_points);
375  sum_dirs.add(next_dirs);
376  bool made_features = false;
377  // If we have some points, we can try making some features.
378  if (sum_points.count() > 0) {
379  // We have gone far enough from the start. Make a feature and restart.
380  FCOORD fit_pt = sum_points.mean_point();
381  FCOORD fit_vector = MeanDirectionVector(sum_points, sum_dirs,
382  prev_normed_pos, normed_pos);
383  // The segment to which we fit features is the line passing through
384  // fit_pt in direction of fit_vector that starts nearest to
385  // prev_normed_pos and ends nearest to normed_pos.
386  FCOORD start_pos = prev_normed_pos.nearest_pt_on_line(fit_pt,
387  fit_vector);
388  FCOORD end_pos = normed_pos.nearest_pt_on_line(fit_pt, fit_vector);
389  // Possible correction to match the adjacent polygon segment.
390  if (total_features == 0 && startpt != endpt) {
391  FCOORD poly_pos(startpt->pos.x, startpt->pos.y);
392  denorm.LocalNormTransform(poly_pos, &start_pos);
393  }
394  if (index > end_index && startpt != endpt) {
395  FCOORD poly_pos(endpt->pos.x, endpt->pos.y);
396  denorm.LocalNormTransform(poly_pos, &end_pos);
397  }
398  int num_features = ComputeFeatures(start_pos, end_pos, feature_length,
399  features);
400  if (num_features > 0) {
401  // We made some features so shuffle the accumulators.
402  prev_points = points;
403  prev_dirs = dirs;
404  prev_normed_pos = normed_pos;
405  points = next_points;
406  dirs = next_dirs;
407  made_features = true;
408  total_features += num_features;
409  }
410  // The end of the next set becomes the end next time around.
411  normed_pos = next_normed_pos;
412  }
413  if (!made_features) {
414  // We didn't make any features, so keep the prev accumulators and
415  // add the next ones into the current.
416  points.add(next_points);
417  dirs.add(next_dirs);
418  }
419  }
420  } else {
421  // There is no outline, so we are forced to use the polygonal approximation.
422  const EDGEPT* pt = startpt;
423  do {
424  FCOORD start_pos(pt->pos.x, pt->pos.y);
425  FCOORD end_pos(pt->next->pos.x, pt->next->pos.y);
426  denorm.LocalNormTransform(start_pos, &start_pos);
427  denorm.LocalNormTransform(end_pos, &end_pos);
428  ComputeFeatures(start_pos, end_pos, feature_length, features);
429  } while ((pt = pt->next) != endpt);
430  }
431 }
432 
433 // Extracts sets of 3-D features of length kStandardFeatureLength (=12.8), as
434 // (x,y) position and angle as measured counterclockwise from the vector
435 // <-1, 0>, from blob using two normalizations defined by bl_denorm and
436 // cn_denorm. See SetpuBLCNDenorms for definitions.
437 // If outline_cn_counts is not nullptr, on return it contains the cumulative
438 // number of cn features generated for each outline in the blob (in order).
439 // Thus after the first outline, there were (*outline_cn_counts)[0] features,
440 // after the second outline, there were (*outline_cn_counts)[1] features etc.
441 void Classify::ExtractFeatures(const TBLOB& blob,
442  bool nonlinear_norm,
445  INT_FX_RESULT_STRUCT* results,
446  GenericVector<int>* outline_cn_counts) {
447  DENORM bl_denorm, cn_denorm;
448  tesseract::Classify::SetupBLCNDenorms(blob, nonlinear_norm,
449  &bl_denorm, &cn_denorm, results);
450  if (outline_cn_counts != nullptr)
451  outline_cn_counts->truncate(0);
452  // Iterate the outlines.
453  for (TESSLINE* ol = blob.outlines; ol != nullptr; ol = ol->next) {
454  // Iterate the polygon.
455  EDGEPT* loop_pt = ol->FindBestStartPt();
456  EDGEPT* pt = loop_pt;
457  if (pt == nullptr) continue;
458  do {
459  if (pt->IsHidden()) continue;
460  // Find a run of equal src_outline.
461  EDGEPT* last_pt = pt;
462  do {
463  last_pt = last_pt->next;
464  } while (last_pt != loop_pt && !last_pt->IsHidden() &&
465  last_pt->src_outline == pt->src_outline);
466  last_pt = last_pt->prev;
467  // Until the adaptive classifier can be weaned off polygon segments,
468  // we have to force extraction from the polygon for the bl_features.
469  ExtractFeaturesFromRun(pt, last_pt, bl_denorm, kStandardFeatureLength,
470  true, bl_features);
471  ExtractFeaturesFromRun(pt, last_pt, cn_denorm, kStandardFeatureLength,
472  false, cn_features);
473  pt = last_pt;
474  } while ((pt = pt->next) != loop_pt);
475  if (outline_cn_counts != nullptr)
476  outline_cn_counts->push_back(cn_features->size());
477  }
478  results->NumBL = bl_features->size();
479  results->NumCN = cn_features->size();
480  results->YBottom = blob.bounding_box().bottom();
481  results->YTop = blob.bounding_box().top();
482  results->Width = blob.bounding_box().width();
483 }
484 
485 } // namespace tesseract
TBOX
Definition: cleanapi_test.cc:19
C_OUTLINE::sub_pixel_pos_at_index
FCOORD sub_pixel_pos_at_index(const ICOORD &pos, int index) const
Definition: coutln.h:162
C_OUTLINE::edge_strength_at_index
int edge_strength_at_index(int index) const
Definition: coutln.h:186
normalis.h
intfx.h
FCOORD::to_direction
uint8_t to_direction() const
Definition: points.cpp:107
LLSQ::add
void add(double x, double y)
Definition: linlsq.cpp:45
tesseract::BlobToTrainingSample
TrainingSample * BlobToTrainingSample(const TBLOB &blob, bool nonlinear_norm, INT_FX_RESULT_STRUCT *fx_info, GenericVector< INT_FEATURE_STRUCT > *bl_features)
Definition: intfx.cpp:75
LLSQ
Definition: linlsq.h:27
TPOINT
Definition: blobs.h:49
FCOORD::from_direction
void from_direction(uint8_t direction)
Definition: points.cpp:111
LLSQ::y_variance
double y_variance() const
Definition: linlsq.h:86
INT_FX_RESULT_STRUCT::NumBL
int16_t NumBL
Definition: intfx.h:38
INT_FX_RESULT_STRUCT::YBottom
uint8_t YBottom
Definition: intfx.h:40
LLSQ::vector_fit
FCOORD vector_fit() const
Definition: linlsq.cpp:243
DENORM::NormTransform
void NormTransform(const DENORM *first_norm, const TPOINT &pt, TPOINT *transformed) const
Definition: normalis.cpp:334
EDGEPT::src_outline
C_OUTLINE * src_outline
Definition: blobs.h:192
LLSQ::mean_point
FCOORD mean_point() const
Definition: linlsq.cpp:158
TBLOB::outlines
TESSLINE * outlines
Definition: blobs.h:398
TBLOB::denorm
const DENORM & denorm() const
Definition: blobs.h:361
INT_FX_RESULT_STRUCT
Definition: intfx.h:34
INT_FX_RESULT_STRUCT::YTop
uint8_t YTop
Definition: intfx.h:41
EDGEPT::step_count
int step_count
Definition: blobs.h:195
EDGEPT::IsHidden
bool IsHidden() const
Definition: blobs.h:174
FCOORD::y
float y() const
Definition: points.h:209
ICOORD
integer coordinate
Definition: points.h:30
kStandardFeatureLength
const double kStandardFeatureLength
Definition: intfx.h:45
TESSLINE
Definition: blobs.h:201
FCOORD::x
float x() const
Definition: points.h:206
TBOX::top
int16_t top() const
Definition: rect.h:57
INT_CHAR_NORM_RANGE
#define INT_CHAR_NORM_RANGE
Definition: intproto.h:129
tesseract::Classify::ExtractFeatures
static void ExtractFeatures(const TBLOB &blob, bool nonlinear_norm, GenericVector< INT_FEATURE_STRUCT > *bl_features, GenericVector< INT_FEATURE_STRUCT > *cn_features, INT_FX_RESULT_STRUCT *results, GenericVector< int > *outline_cn_counts)
Definition: intfx.cpp:440
LLSQ::count
int32_t count() const
Definition: linlsq.h:42
TBLOB::ComputeMoments
int ComputeMoments(FCOORD *center, FCOORD *second_moments) const
Definition: blobs.cpp:520
IntCastRounded
int IntCastRounded(double x)
Definition: helpers.h:173
tesseract::Classify::SetupBLCNDenorms
static void SetupBLCNDenorms(const TBLOB &blob, bool nonlinear_norm, DENORM *bl_denorm, DENORM *cn_denorm, INT_FX_RESULT_STRUCT *fx_info)
Definition: intfx.cpp:127
DENORM::RootDenorm
const DENORM * RootDenorm() const
Definition: normalis.h:257
TESSLINE::next
TESSLINE * next
Definition: blobs.h:279
FCOORD
Definition: points.h:187
InitIntegerFX
void InitIntegerFX()
Definition: intfx.cpp:48
C_OUTLINE
Definition: coutln.h:71
EDGEPT::prev
EDGEPT * prev
Definition: blobs.h:191
statistc.h
EDGEPT::start_step
int start_step
Definition: blobs.h:194
C_OUTLINE::position_at_index
ICOORD position_at_index(int index) const
Definition: coutln.h:152
FCOORD::length
float length() const
find length
Definition: points.h:227
GenericVector::push_back
int push_back(T object)
Definition: genericvector.h:799
DENORM::SetupNormalization
void SetupNormalization(const BLOCK *block, const FCOORD *rotation, const DENORM *predecessor, float x_origin, float y_origin, float x_scale, float y_scale, float final_xshift, float final_yshift)
Definition: normalis.cpp:95
INT_FX_RESULT_STRUCT::Ry
int16_t Ry
Definition: intfx.h:37
INT_FX_RESULT_STRUCT::Ymean
int16_t Ymean
Definition: intfx.h:36
INT_FX_RESULT_STRUCT::Rx
int16_t Rx
Definition: intfx.h:37
TPOINT::x
int16_t x
Definition: blobs.h:91
DENORM::DenormTransform
void DenormTransform(const DENORM *last_denorm, const TPOINT &pt, TPOINT *original) const
Definition: normalis.cpp:389
trainingsample.h
TPOINT::y
int16_t y
Definition: blobs.h:92
C_OUTLINE::direction_at_index
int direction_at_index(int index) const
Definition: coutln.h:177
LLSQ::clear
void clear()
Definition: linlsq.cpp:30
TBOX::width
int16_t width() const
Definition: rect.h:114
TBOX::bottom
int16_t bottom() const
Definition: rect.h:64
linlsq.h
TBLOB::GetPreciseBoundingBox
void GetPreciseBoundingBox(TBOX *precise_box) const
Definition: blobs.cpp:539
helpers.h
INT_FX_RESULT_STRUCT::NumCN
int16_t NumCN
Definition: intfx.h:38
tesseract
Definition: baseapi.h:65
DENORM::SetupNonLinear
void SetupNonLinear(const DENORM *predecessor, const TBOX &box, float target_width, float target_height, float final_xshift, float final_yshift, const GenericVector< GenericVector< int > > &x_coords, const GenericVector< GenericVector< int > > &y_coords)
Definition: normalis.cpp:267
INT_FX_RESULT_STRUCT::Xmean
int16_t Xmean
Definition: intfx.h:36
sample
Definition: cluster.h:31
TBLOB::bounding_box
TBOX bounding_box() const
Definition: blobs.cpp:466
GenericVector
Definition: baseapi.h:40
INT_FX_RESULT_STRUCT::Width
int16_t Width
Definition: intfx.h:39
TBOX::pad
void pad(int xpad, int ypad)
Definition: rect.h:130
INT_FEATURE_STRUCT
Definition: intproto.h:131
TBLOB
Definition: blobs.h:282
tesseract::TrainingSample
Definition: trainingsample.h:53
GenericVector::truncate
void truncate(int size)
Definition: genericvector.h:132
TBOX::left
int16_t left() const
Definition: rect.h:71
FCOORD::nearest_pt_on_line
FCOORD nearest_pt_on_line(const FCOORD &line_point, const FCOORD &dir_vector) const
Definition: points.cpp:132
INT_FX_RESULT_STRUCT::Length
int32_t Length
Definition: intfx.h:35
TBLOB::GetEdgeCoords
void GetEdgeCoords(const TBOX &box, GenericVector< GenericVector< int > > *x_coords, GenericVector< GenericVector< int > > *y_coords) const
Definition: blobs.cpp:555
TBOX::right
int16_t right() const
Definition: rect.h:78
EDGEPT
Definition: blobs.h:97
Modulo
int Modulo(int a, int b)
Definition: helpers.h:156
DENORM::LocalNormTransform
void LocalNormTransform(const TPOINT &pt, TPOINT *transformed) const
Definition: normalis.cpp:305
C_OUTLINE::pathlength
int32_t pathlength() const
Definition: coutln.h:134
FeatureDirection
FCOORD FeatureDirection(uint8_t theta)
Definition: intfx.cpp:64
classify.h
C_OUTLINE::step
ICOORD step(int index) const
Definition: coutln.h:143
GenericVector::size
int size() const
Definition: genericvector.h:71
tesseract::TrainingSample::CopyFromFeatures
static TrainingSample * CopyFromFeatures(const INT_FX_RESULT_STRUCT &fx_info, const TBOX &bounding_box, const INT_FEATURE_STRUCT *features, int num_features)
Definition: trainingsample.cpp:125
LLSQ::x_variance
double x_variance() const
Definition: linlsq.h:80
EDGEPT::pos
TPOINT pos
Definition: blobs.h:184
EDGEPT::next
EDGEPT * next
Definition: blobs.h:190
TBOX
Definition: rect.h:33
DENORM
Definition: normalis.h:49
intmatcher.h