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