mirror of
https://github.com/tesseract-ocr/tesseract.git
synced 2024-11-25 11:39:06 +08:00
524a61452d
Squashed commit from https://github.com/tesseract-ocr/tesseract/tree/more-doxygen closes #14 Commits:6317305
doxygen9f42f69
doxygen0fc4d52
doxygen37b4b55
fix typobded8f1
some more doxy020eb00
slight tweak524666d
doxygenify2a36a3e
doxygenify229d218
doxygenify7fd28ae
doxygenifya8c64bc
doxygenifyf5d21b6
fix5d8ede8
doxygenifya58a4e0
language_model.cppfa85709
lm_pain_points.cpp lm_state.cpp6418da3
merge06190ba
Merge branch 'old_doxygen_merge' into more-doxygen84acf08
Merge branch 'master' into more-doxygen50fe1ff
pagewalk.cpp cube_reco_context.cpp2982583
change to relative192a24a
applybox.cpp, take one8eeb053
delete docs for obsolete params52e4c77
modernise classify/ocrfeatures.cpp2a1cba6
modernise cutil/emalloc.cpp773e006
silence doxygen warningaeb1731
silence doxygen warningf18387f
silence doxygen; new params are unused?15ad6bd
doxygenify cutil/efio.cppc8b5dad
doxygenify cutil/danerror.cpp784450f
the globals and exceptions parts are obsolete; remove8bca324
doxygen classify/normfeat.cpp9bcbe16
doxygen classify/normmatch.cppaa9a971
doxygen ccmain/cube_control.cppc083ff2
doxygen ccmain/cube_reco_context.cppf842850
params changed5c94f12
doxygen ccmain/cubeclassifier.cpp15ba750
case sensitivef5c71d4
case sensitivef85655b
doxygen classify/intproto.cpp4bbc7aa
partial doxygen classify/mfx.cppdbb6041
partial doxygen classify/intproto.cpp2aa72db
finish doxygen classify/intproto.cpp0b8de99
doxygen training/mftraining.cpp0b5b35c
partial doxygen ccstruct/coutln.cppb81c766
partial doxygen ccstruct/coutln.cpp40fc415
finished? doxygen ccstruct/coutln.cpp6e4165c
doxygen classify/clusttool.cpp0267dec
doxygen classify/cutoffs.cpp7f0c70c
doxygen classify/fpoint.cpp512f3bd
ignore ~ files5668a52
doxygen classify/intmatcher.cpp84788d4
doxygen classify/kdtree.cpp29f36ca
doxygen classify/mfoutline.cpp40b94b1
silence doxygen warnings6c511b9
doxygen classify/mfx.cppf9b4080
doxygen classify/outfeat.cppaa1df05
doxygen classify/picofeat.cppcc5f466
doxygen training/cntraining.cppcce044f
doxygen training/commontraining.cpp167e216
missing param9498383
renamed params37eeac2
renamed paramd87b5dd
casec8ee174
renamed paramsb858db8
typo4c2a838
h2 context?81a2c0c
fix some param names; add some missing params, no docsbcf8a4c
add some missing params, no docsaf77f86
add some missing params, no docs; fix some param names01df24e
fix some params6161056
fix some params68508b6
fix some params285aeb6
doxygen complains here no matter what529bcfa
rm some missing params, typoscd21226
rm some missing params, add some new ones48a4bc2
fix paramsc844628
missing param312ce37
missing param; rename oneec2fdec
missing param05e15e0
missing paramsd515858
change "<" to < to make doxygen happyb476a28
wrong place
1940 lines
66 KiB
C++
1940 lines
66 KiB
C++
/******************************************************************************
|
|
** Filename: intproto.c
|
|
** Purpose: Definition of data structures for integer protos.
|
|
** Author: Dan Johnson
|
|
** History: Thu Feb 7 14:38:16 1991, DSJ, Created.
|
|
**
|
|
** (c) Copyright Hewlett-Packard Company, 1988.
|
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
|
** you may not use this file except in compliance with the License.
|
|
** You may obtain a copy of the License at
|
|
** http://www.apache.org/licenses/LICENSE-2.0
|
|
** Unless required by applicable law or agreed to in writing, software
|
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
** See the License for the specific language governing permissions and
|
|
** limitations under the License.
|
|
******************************************************************************/
|
|
/*-----------------------------------------------------------------------------
|
|
Include Files and Type Defines
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#ifdef __UNIX__
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "classify.h"
|
|
#include "const.h"
|
|
#include "emalloc.h"
|
|
#include "fontinfo.h"
|
|
#include "genericvector.h"
|
|
#include "globals.h"
|
|
#include "helpers.h"
|
|
#include "intproto.h"
|
|
#include "mfoutline.h"
|
|
#include "ndminx.h"
|
|
#include "picofeat.h"
|
|
#include "points.h"
|
|
#include "shapetable.h"
|
|
#include "svmnode.h"
|
|
|
|
// Include automatically generated configuration file if running autoconf.
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config_auto.h"
|
|
#endif
|
|
|
|
using tesseract::FontInfo;
|
|
using tesseract::FontSet;
|
|
using tesseract::FontSpacingInfo;
|
|
|
|
/* match debug display constants*/
|
|
#define PROTO_PRUNER_SCALE (4.0)
|
|
|
|
#define INT_DESCENDER (0.0 * INT_CHAR_NORM_RANGE)
|
|
#define INT_BASELINE (0.25 * INT_CHAR_NORM_RANGE)
|
|
#define INT_XHEIGHT (0.75 * INT_CHAR_NORM_RANGE)
|
|
#define INT_CAPHEIGHT (1.0 * INT_CHAR_NORM_RANGE)
|
|
|
|
#define INT_XCENTER (0.5 * INT_CHAR_NORM_RANGE)
|
|
#define INT_YCENTER (0.5 * INT_CHAR_NORM_RANGE)
|
|
#define INT_XRADIUS (0.2 * INT_CHAR_NORM_RANGE)
|
|
#define INT_YRADIUS (0.2 * INT_CHAR_NORM_RANGE)
|
|
#define INT_MIN_X 0
|
|
#define INT_MIN_Y 0
|
|
#define INT_MAX_X INT_CHAR_NORM_RANGE
|
|
#define INT_MAX_Y INT_CHAR_NORM_RANGE
|
|
|
|
/** define pad used to snap near horiz/vertical protos to horiz/vertical */
|
|
#define HV_TOLERANCE (0.0025) /* approx 0.9 degrees */
|
|
|
|
typedef enum
|
|
{ StartSwitch, EndSwitch, LastSwitch }
|
|
SWITCH_TYPE;
|
|
#define MAX_NUM_SWITCHES 3
|
|
|
|
typedef struct
|
|
{
|
|
SWITCH_TYPE Type;
|
|
inT8 X, Y;
|
|
inT16 YInit;
|
|
inT16 Delta;
|
|
}
|
|
|
|
|
|
FILL_SWITCH;
|
|
|
|
typedef struct
|
|
{
|
|
uinT8 NextSwitch;
|
|
uinT8 AngleStart, AngleEnd;
|
|
inT8 X;
|
|
inT16 YStart, YEnd;
|
|
inT16 StartDelta, EndDelta;
|
|
FILL_SWITCH Switch[MAX_NUM_SWITCHES];
|
|
}
|
|
|
|
|
|
TABLE_FILLER;
|
|
|
|
typedef struct
|
|
{
|
|
inT8 X;
|
|
inT8 YStart, YEnd;
|
|
uinT8 AngleStart, AngleEnd;
|
|
}
|
|
|
|
|
|
FILL_SPEC;
|
|
|
|
|
|
/* constants for conversion from old inttemp format */
|
|
#define OLD_MAX_NUM_CONFIGS 32
|
|
#define OLD_WERDS_PER_CONFIG_VEC ((OLD_MAX_NUM_CONFIGS + BITS_PER_WERD - 1) /\
|
|
BITS_PER_WERD)
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Macros
|
|
-----------------------------------------------------------------------------*/
|
|
/** macro for performing circular increments of bucket indices */
|
|
#define CircularIncrement(i,r) (((i) < (r) - 1)?((i)++):((i) = 0))
|
|
|
|
/** macro for mapping floats to ints without bounds checking */
|
|
#define MapParam(P,O,N) (floor (((P) + (O)) * (N)))
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Private Function Prototypes
|
|
----------------------------------------------------------------------------*/
|
|
FLOAT32 BucketStart(int Bucket, FLOAT32 Offset, int NumBuckets);
|
|
|
|
FLOAT32 BucketEnd(int Bucket, FLOAT32 Offset, int NumBuckets);
|
|
|
|
void DoFill(FILL_SPEC *FillSpec,
|
|
CLASS_PRUNER_STRUCT* Pruner,
|
|
register uinT32 ClassMask,
|
|
register uinT32 ClassCount,
|
|
register uinT32 WordIndex);
|
|
|
|
BOOL8 FillerDone(TABLE_FILLER *Filler);
|
|
|
|
void FillPPCircularBits(uinT32
|
|
ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR],
|
|
int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug);
|
|
|
|
void FillPPLinearBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR],
|
|
int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug);
|
|
|
|
void GetCPPadsForLevel(int Level,
|
|
FLOAT32 *EndPad,
|
|
FLOAT32 *SidePad,
|
|
FLOAT32 *AnglePad);
|
|
|
|
ScrollView::Color GetMatchColorFor(FLOAT32 Evidence);
|
|
|
|
void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill);
|
|
|
|
void InitTableFiller(FLOAT32 EndPad,
|
|
FLOAT32 SidePad,
|
|
FLOAT32 AnglePad,
|
|
PROTO Proto,
|
|
TABLE_FILLER *Filler);
|
|
|
|
#ifndef GRAPHICS_DISABLED
|
|
void RenderIntFeature(ScrollView *window, const INT_FEATURE_STRUCT* Feature,
|
|
ScrollView::Color color);
|
|
|
|
void RenderIntProto(ScrollView *window,
|
|
INT_CLASS Class,
|
|
PROTO_ID ProtoId,
|
|
ScrollView::Color color);
|
|
#endif // GRAPHICS_DISABLED
|
|
|
|
int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id);
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Global Data Definitions and Declarations
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/* global display lists used to display proto and feature match information*/
|
|
ScrollView *IntMatchWindow = NULL;
|
|
ScrollView *FeatureDisplayWindow = NULL;
|
|
ScrollView *ProtoDisplayWindow = NULL;
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Variables
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
/* control knobs */
|
|
INT_VAR(classify_num_cp_levels, 3, "Number of Class Pruner Levels");
|
|
double_VAR(classify_cp_angle_pad_loose, 45.0,
|
|
"Class Pruner Angle Pad Loose");
|
|
double_VAR(classify_cp_angle_pad_medium, 20.0,
|
|
"Class Pruner Angle Pad Medium");
|
|
double_VAR(classify_cp_angle_pad_tight, 10.0,
|
|
"CLass Pruner Angle Pad Tight");
|
|
double_VAR(classify_cp_end_pad_loose, 0.5, "Class Pruner End Pad Loose");
|
|
double_VAR(classify_cp_end_pad_medium, 0.5, "Class Pruner End Pad Medium");
|
|
double_VAR(classify_cp_end_pad_tight, 0.5, "Class Pruner End Pad Tight");
|
|
double_VAR(classify_cp_side_pad_loose, 2.5, "Class Pruner Side Pad Loose");
|
|
double_VAR(classify_cp_side_pad_medium, 1.2, "Class Pruner Side Pad Medium");
|
|
double_VAR(classify_cp_side_pad_tight, 0.6, "Class Pruner Side Pad Tight");
|
|
double_VAR(classify_pp_angle_pad, 45.0, "Proto Pruner Angle Pad");
|
|
double_VAR(classify_pp_end_pad, 0.5, "Proto Prune End Pad");
|
|
double_VAR(classify_pp_side_pad, 2.5, "Proto Pruner Side Pad");
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Public Code
|
|
-----------------------------------------------------------------------------*/
|
|
/// Builds a feature from an FCOORD for position with all the necessary
|
|
/// clipping and rounding.
|
|
INT_FEATURE_STRUCT::INT_FEATURE_STRUCT(const FCOORD& pos, uinT8 theta)
|
|
: X(ClipToRange<inT16>(static_cast<inT16>(pos.x() + 0.5), 0, 255)),
|
|
Y(ClipToRange<inT16>(static_cast<inT16>(pos.y() + 0.5), 0, 255)),
|
|
Theta(theta),
|
|
CP_misses(0) {
|
|
}
|
|
/** Builds a feature from ints with all the necessary clipping and casting. */
|
|
INT_FEATURE_STRUCT::INT_FEATURE_STRUCT(int x, int y, int theta)
|
|
: X(static_cast<uinT8>(ClipToRange(x, 0, MAX_UINT8))),
|
|
Y(static_cast<uinT8>(ClipToRange(y, 0, MAX_UINT8))),
|
|
Theta(static_cast<uinT8>(ClipToRange(theta, 0, MAX_UINT8))),
|
|
CP_misses(0) {
|
|
}
|
|
|
|
/**
|
|
* This routine adds a new class structure to a set of
|
|
* templates. Classes have to be added to Templates in
|
|
* the order of increasing ClassIds.
|
|
*
|
|
* @param Templates templates to add new class to
|
|
* @param ClassId class id to associate new class with
|
|
* @param Class class data structure to add to templates
|
|
*
|
|
* Globals: none
|
|
*
|
|
* @note Exceptions: none
|
|
* @note History: Mon Feb 11 11:52:08 1991, DSJ, Created.
|
|
*/
|
|
void AddIntClass(INT_TEMPLATES Templates, CLASS_ID ClassId, INT_CLASS Class) {
|
|
int Pruner;
|
|
|
|
assert (LegalClassId (ClassId));
|
|
if (ClassId != Templates->NumClasses) {
|
|
fprintf(stderr, "Please make sure that classes are added to templates");
|
|
fprintf(stderr, " in increasing order of ClassIds\n");
|
|
exit(1);
|
|
}
|
|
ClassForClassId (Templates, ClassId) = Class;
|
|
Templates->NumClasses++;
|
|
|
|
if (Templates->NumClasses > MaxNumClassesIn (Templates)) {
|
|
Pruner = Templates->NumClassPruners++;
|
|
Templates->ClassPruners[Pruner] = new CLASS_PRUNER_STRUCT;
|
|
memset(Templates->ClassPruners[Pruner], 0, sizeof(CLASS_PRUNER_STRUCT));
|
|
}
|
|
} /* AddIntClass */
|
|
|
|
|
|
/**
|
|
* This routine returns the index of the next free config
|
|
* in Class.
|
|
*
|
|
* @param Class class to add new configuration to
|
|
*
|
|
* Globals: none
|
|
*
|
|
* @return Index of next free config.
|
|
* @note Exceptions: none
|
|
* @note History: Mon Feb 11 14:44:40 1991, DSJ, Created.
|
|
*/
|
|
int AddIntConfig(INT_CLASS Class) {
|
|
int Index;
|
|
|
|
assert(Class->NumConfigs < MAX_NUM_CONFIGS);
|
|
|
|
Index = Class->NumConfigs++;
|
|
Class->ConfigLengths[Index] = 0;
|
|
return Index;
|
|
} /* AddIntConfig */
|
|
|
|
|
|
/**
|
|
* This routine allocates the next free proto in Class and
|
|
* returns its index.
|
|
*
|
|
* @param Class class to add new proto to
|
|
*
|
|
* Globals: none
|
|
*
|
|
* @return Proto index of new proto.
|
|
* @note Exceptions: none
|
|
* @note History: Mon Feb 11 13:26:41 1991, DSJ, Created.
|
|
*/
|
|
int AddIntProto(INT_CLASS Class) {
|
|
int Index;
|
|
int ProtoSetId;
|
|
PROTO_SET ProtoSet;
|
|
INT_PROTO Proto;
|
|
register uinT32 *Word;
|
|
|
|
if (Class->NumProtos >= MAX_NUM_PROTOS)
|
|
return (NO_PROTO);
|
|
|
|
Index = Class->NumProtos++;
|
|
|
|
if (Class->NumProtos > MaxNumIntProtosIn(Class)) {
|
|
ProtoSetId = Class->NumProtoSets++;
|
|
|
|
ProtoSet = (PROTO_SET) Emalloc(sizeof(PROTO_SET_STRUCT));
|
|
Class->ProtoSets[ProtoSetId] = ProtoSet;
|
|
memset(ProtoSet, 0, sizeof(*ProtoSet));
|
|
|
|
/* reallocate space for the proto lengths and install in class */
|
|
Class->ProtoLengths =
|
|
(uinT8 *)Erealloc(Class->ProtoLengths,
|
|
MaxNumIntProtosIn(Class) * sizeof(uinT8));
|
|
memset(&Class->ProtoLengths[Index], 0,
|
|
sizeof(*Class->ProtoLengths) * (MaxNumIntProtosIn(Class) - Index));
|
|
}
|
|
|
|
/* initialize proto so its length is zero and it isn't in any configs */
|
|
Class->ProtoLengths[Index] = 0;
|
|
Proto = ProtoForProtoId (Class, Index);
|
|
for (Word = Proto->Configs;
|
|
Word < Proto->Configs + WERDS_PER_CONFIG_VEC; *Word++ = 0);
|
|
|
|
return (Index);
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* This routine adds Proto to the class pruning tables
|
|
* for the specified class in Templates.
|
|
*
|
|
* Globals:
|
|
* - classify_num_cp_levels number of levels used in the class pruner
|
|
* @param Proto floating-pt proto to add to class pruner
|
|
* @param ClassId class id corresponding to Proto
|
|
* @param Templates set of templates containing class pruner
|
|
* @return none
|
|
* @note Exceptions: none
|
|
* @note History: Wed Feb 13 08:49:54 1991, DSJ, Created.
|
|
*/
|
|
void AddProtoToClassPruner (PROTO Proto, CLASS_ID ClassId,
|
|
INT_TEMPLATES Templates)
|
|
#define MAX_LEVEL 2
|
|
{
|
|
CLASS_PRUNER_STRUCT* Pruner;
|
|
uinT32 ClassMask;
|
|
uinT32 ClassCount;
|
|
uinT32 WordIndex;
|
|
int Level;
|
|
FLOAT32 EndPad, SidePad, AnglePad;
|
|
TABLE_FILLER TableFiller;
|
|
FILL_SPEC FillSpec;
|
|
|
|
Pruner = CPrunerFor (Templates, ClassId);
|
|
WordIndex = CPrunerWordIndexFor (ClassId);
|
|
ClassMask = CPrunerMaskFor (MAX_LEVEL, ClassId);
|
|
|
|
for (Level = classify_num_cp_levels - 1; Level >= 0; Level--) {
|
|
GetCPPadsForLevel(Level, &EndPad, &SidePad, &AnglePad);
|
|
ClassCount = CPrunerMaskFor (Level, ClassId);
|
|
InitTableFiller(EndPad, SidePad, AnglePad, Proto, &TableFiller);
|
|
|
|
while (!FillerDone (&TableFiller)) {
|
|
GetNextFill(&TableFiller, &FillSpec);
|
|
DoFill(&FillSpec, Pruner, ClassMask, ClassCount, WordIndex);
|
|
}
|
|
}
|
|
} /* AddProtoToClassPruner */
|
|
|
|
|
|
/**
|
|
* This routine updates the proto pruner lookup tables
|
|
* for Class to include a new proto identified by ProtoId
|
|
* and described by Proto.
|
|
* @param Proto floating-pt proto to be added to proto pruner
|
|
* @param ProtoId id of proto
|
|
* @param Class integer class that contains desired proto pruner
|
|
* @param debug debug flag
|
|
* @note Globals: none
|
|
* @return none
|
|
* @note Exceptions: none
|
|
* @note History: Fri Feb 8 13:07:19 1991, DSJ, Created.
|
|
*/
|
|
void AddProtoToProtoPruner(PROTO Proto, int ProtoId,
|
|
INT_CLASS Class, bool debug) {
|
|
FLOAT32 Angle, X, Y, Length;
|
|
FLOAT32 Pad;
|
|
int Index;
|
|
PROTO_SET ProtoSet;
|
|
|
|
if (ProtoId >= Class->NumProtos)
|
|
cprintf("AddProtoToProtoPruner:assert failed: %d < %d",
|
|
ProtoId, Class->NumProtos);
|
|
assert(ProtoId < Class->NumProtos);
|
|
|
|
Index = IndexForProto (ProtoId);
|
|
ProtoSet = Class->ProtoSets[SetForProto (ProtoId)];
|
|
|
|
Angle = Proto->Angle;
|
|
#ifndef _WIN32
|
|
assert(!isnan(Angle));
|
|
#endif
|
|
|
|
FillPPCircularBits (ProtoSet->ProtoPruner[PRUNER_ANGLE], Index,
|
|
Angle + ANGLE_SHIFT, classify_pp_angle_pad / 360.0,
|
|
debug);
|
|
|
|
Angle *= 2.0 * PI;
|
|
Length = Proto->Length;
|
|
|
|
X = Proto->X + X_SHIFT;
|
|
Pad = MAX (fabs (cos (Angle)) * (Length / 2.0 +
|
|
classify_pp_end_pad *
|
|
GetPicoFeatureLength ()),
|
|
fabs (sin (Angle)) * (classify_pp_side_pad *
|
|
GetPicoFeatureLength ()));
|
|
|
|
FillPPLinearBits(ProtoSet->ProtoPruner[PRUNER_X], Index, X, Pad, debug);
|
|
|
|
Y = Proto->Y + Y_SHIFT;
|
|
Pad = MAX (fabs (sin (Angle)) * (Length / 2.0 +
|
|
classify_pp_end_pad *
|
|
GetPicoFeatureLength ()),
|
|
fabs (cos (Angle)) * (classify_pp_side_pad *
|
|
GetPicoFeatureLength ()));
|
|
|
|
FillPPLinearBits(ProtoSet->ProtoPruner[PRUNER_Y], Index, Y, Pad, debug);
|
|
} /* AddProtoToProtoPruner */
|
|
|
|
|
|
/**
|
|
* Returns a quantized bucket for the given param shifted by offset,
|
|
* notionally (param + offset) * num_buckets, but clipped and casted to the
|
|
* appropriate type.
|
|
*/
|
|
uinT8 Bucket8For(FLOAT32 param, FLOAT32 offset, int num_buckets) {
|
|
int bucket = IntCastRounded(MapParam(param, offset, num_buckets));
|
|
return static_cast<uinT8>(ClipToRange(bucket, 0, num_buckets - 1));
|
|
}
|
|
uinT16 Bucket16For(FLOAT32 param, FLOAT32 offset, int num_buckets) {
|
|
int bucket = IntCastRounded(MapParam(param, offset, num_buckets));
|
|
return static_cast<uinT16>(ClipToRange(bucket, 0, num_buckets - 1));
|
|
}
|
|
|
|
/**
|
|
* Returns a quantized bucket for the given circular param shifted by offset,
|
|
* notionally (param + offset) * num_buckets, but modded and casted to the
|
|
* appropriate type.
|
|
*/
|
|
uinT8 CircBucketFor(FLOAT32 param, FLOAT32 offset, int num_buckets) {
|
|
int bucket = IntCastRounded(MapParam(param, offset, num_buckets));
|
|
return static_cast<uinT8>(Modulo(bucket, num_buckets));
|
|
} /* CircBucketFor */
|
|
|
|
|
|
#ifndef GRAPHICS_DISABLED
|
|
/**
|
|
* This routine clears the global feature and proto
|
|
* display lists.
|
|
*
|
|
* Globals:
|
|
* - FeatureShapes display list for features
|
|
* - ProtoShapes display list for protos
|
|
* @return none
|
|
* @note Exceptions: none
|
|
* @note History: Thu Mar 21 15:40:19 1991, DSJ, Created.
|
|
*/
|
|
void UpdateMatchDisplay() {
|
|
if (IntMatchWindow != NULL)
|
|
IntMatchWindow->Update();
|
|
} /* ClearMatchDisplay */
|
|
#endif
|
|
|
|
/**
|
|
* This operation updates the config vectors of all protos
|
|
* in Class to indicate that the protos with 1's in Config
|
|
* belong to a new configuration identified by ConfigId.
|
|
* It is assumed that the length of the Config bit vector is
|
|
* equal to the number of protos in Class.
|
|
* @param Config config to be added to class
|
|
* @param ConfigId id to be used for new config
|
|
* @param Class class to add new config to
|
|
* @return none
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Mon Feb 11 14:57:31 1991, DSJ, Created.
|
|
*/
|
|
void ConvertConfig(BIT_VECTOR Config, int ConfigId, INT_CLASS Class) {
|
|
int ProtoId;
|
|
INT_PROTO Proto;
|
|
int TotalLength;
|
|
|
|
for (ProtoId = 0, TotalLength = 0;
|
|
ProtoId < Class->NumProtos; ProtoId++) {
|
|
if (test_bit(Config, ProtoId)) {
|
|
Proto = ProtoForProtoId(Class, ProtoId);
|
|
SET_BIT(Proto->Configs, ConfigId);
|
|
TotalLength += Class->ProtoLengths[ProtoId];
|
|
}
|
|
}
|
|
Class->ConfigLengths[ConfigId] = TotalLength;
|
|
} /* ConvertConfig */
|
|
|
|
|
|
namespace tesseract {
|
|
/**
|
|
* This routine converts Proto to integer format and
|
|
* installs it as ProtoId in Class.
|
|
* @param Proto floating-pt proto to be converted to integer format
|
|
* @param ProtoId id of proto
|
|
* @param Class integer class to add converted proto to
|
|
* @return none
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Fri Feb 8 11:22:43 1991, DSJ, Created.
|
|
*/
|
|
void Classify::ConvertProto(PROTO Proto, int ProtoId, INT_CLASS Class) {
|
|
INT_PROTO P;
|
|
FLOAT32 Param;
|
|
|
|
assert(ProtoId < Class->NumProtos);
|
|
|
|
P = ProtoForProtoId(Class, ProtoId);
|
|
|
|
Param = Proto->A * 128;
|
|
P->A = TruncateParam(Param, -128, 127, NULL);
|
|
|
|
Param = -Proto->B * 256;
|
|
P->B = TruncateParam(Param, 0, 255, NULL);
|
|
|
|
Param = Proto->C * 128;
|
|
P->C = TruncateParam(Param, -128, 127, NULL);
|
|
|
|
Param = Proto->Angle * 256;
|
|
if (Param < 0 || Param >= 256)
|
|
P->Angle = 0;
|
|
else
|
|
P->Angle = (uinT8) Param;
|
|
|
|
/* round proto length to nearest integer number of pico-features */
|
|
Param = (Proto->Length / GetPicoFeatureLength()) + 0.5;
|
|
Class->ProtoLengths[ProtoId] = TruncateParam(Param, 1, 255, NULL);
|
|
if (classify_learning_debug_level >= 2)
|
|
cprintf("Converted ffeat to (A=%d,B=%d,C=%d,L=%d)",
|
|
P->A, P->B, P->C, Class->ProtoLengths[ProtoId]);
|
|
} /* ConvertProto */
|
|
|
|
|
|
/**
|
|
* This routine converts from the old floating point format
|
|
* to the new integer format.
|
|
* @param FloatProtos prototypes in old floating pt format
|
|
* @param target_unicharset the UNICHARSET to use
|
|
* @return New set of training templates in integer format.
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Thu Feb 7 14:40:42 1991, DSJ, Created.
|
|
*/
|
|
INT_TEMPLATES Classify::CreateIntTemplates(CLASSES FloatProtos,
|
|
const UNICHARSET&
|
|
target_unicharset) {
|
|
INT_TEMPLATES IntTemplates;
|
|
CLASS_TYPE FClass;
|
|
INT_CLASS IClass;
|
|
int ClassId;
|
|
int ProtoId;
|
|
int ConfigId;
|
|
|
|
IntTemplates = NewIntTemplates();
|
|
|
|
for (ClassId = 0; ClassId < target_unicharset.size(); ClassId++) {
|
|
FClass = &(FloatProtos[ClassId]);
|
|
if (FClass->NumProtos == 0 && FClass->NumConfigs == 0 &&
|
|
strcmp(target_unicharset.id_to_unichar(ClassId), " ") != 0) {
|
|
cprintf("Warning: no protos/configs for %s in CreateIntTemplates()\n",
|
|
target_unicharset.id_to_unichar(ClassId));
|
|
}
|
|
assert(UnusedClassIdIn(IntTemplates, ClassId));
|
|
IClass = NewIntClass(FClass->NumProtos, FClass->NumConfigs);
|
|
FontSet fs;
|
|
fs.size = FClass->font_set.size();
|
|
fs.configs = new int[fs.size];
|
|
for (int i = 0; i < fs.size; ++i) {
|
|
fs.configs[i] = FClass->font_set.get(i);
|
|
}
|
|
if (this->fontset_table_.contains(fs)) {
|
|
IClass->font_set_id = this->fontset_table_.get_id(fs);
|
|
delete[] fs.configs;
|
|
} else {
|
|
IClass->font_set_id = this->fontset_table_.push_back(fs);
|
|
}
|
|
AddIntClass(IntTemplates, ClassId, IClass);
|
|
|
|
for (ProtoId = 0; ProtoId < FClass->NumProtos; ProtoId++) {
|
|
AddIntProto(IClass);
|
|
ConvertProto(ProtoIn(FClass, ProtoId), ProtoId, IClass);
|
|
AddProtoToProtoPruner(ProtoIn(FClass, ProtoId), ProtoId, IClass,
|
|
classify_learning_debug_level >= 2);
|
|
AddProtoToClassPruner(ProtoIn(FClass, ProtoId), ClassId, IntTemplates);
|
|
}
|
|
|
|
for (ConfigId = 0; ConfigId < FClass->NumConfigs; ConfigId++) {
|
|
AddIntConfig(IClass);
|
|
ConvertConfig(FClass->Configurations[ConfigId], ConfigId, IClass);
|
|
}
|
|
}
|
|
return (IntTemplates);
|
|
} /* CreateIntTemplates */
|
|
} // namespace tesseract
|
|
|
|
|
|
#ifndef GRAPHICS_DISABLED
|
|
/**
|
|
* This routine renders the specified feature into a
|
|
* global display list.
|
|
*
|
|
* Globals:
|
|
* - FeatureShapes global display list for features
|
|
* @param Feature pico-feature to be displayed
|
|
* @param Evidence best evidence for this feature (0-1)
|
|
* @return none
|
|
* @note Exceptions: none
|
|
* @note History: Thu Mar 21 14:45:04 1991, DSJ, Created.
|
|
*/
|
|
void DisplayIntFeature(const INT_FEATURE_STRUCT* Feature, FLOAT32 Evidence) {
|
|
ScrollView::Color color = GetMatchColorFor(Evidence);
|
|
RenderIntFeature(IntMatchWindow, Feature, color);
|
|
if (FeatureDisplayWindow) {
|
|
RenderIntFeature(FeatureDisplayWindow, Feature, color);
|
|
}
|
|
} /* DisplayIntFeature */
|
|
|
|
|
|
/**
|
|
* This routine renders the specified proto into a
|
|
* global display list.
|
|
*
|
|
* Globals:
|
|
* - ProtoShapes global display list for protos
|
|
* @param Class class to take proto from
|
|
* @param ProtoId id of proto in Class to be displayed
|
|
* @param Evidence total evidence for proto (0-1)
|
|
* @return none
|
|
* @note Exceptions: none
|
|
* @note History: Thu Mar 21 14:45:04 1991, DSJ, Created.
|
|
*/
|
|
void DisplayIntProto(INT_CLASS Class, PROTO_ID ProtoId, FLOAT32 Evidence) {
|
|
ScrollView::Color color = GetMatchColorFor(Evidence);
|
|
RenderIntProto(IntMatchWindow, Class, ProtoId, color);
|
|
if (ProtoDisplayWindow) {
|
|
RenderIntProto(ProtoDisplayWindow, Class, ProtoId, color);
|
|
}
|
|
} /* DisplayIntProto */
|
|
#endif
|
|
|
|
/**
|
|
* This routine creates a new integer class data structure
|
|
* and returns it. Sufficient space is allocated
|
|
* to handle the specified number of protos and configs.
|
|
* @param MaxNumProtos number of protos to allocate space for
|
|
* @param MaxNumConfigs number of configs to allocate space for
|
|
* @return New class created.
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Fri Feb 8 10:51:23 1991, DSJ, Created.
|
|
*/
|
|
INT_CLASS NewIntClass(int MaxNumProtos, int MaxNumConfigs) {
|
|
INT_CLASS Class;
|
|
PROTO_SET ProtoSet;
|
|
int i;
|
|
|
|
assert(MaxNumConfigs <= MAX_NUM_CONFIGS);
|
|
|
|
Class = (INT_CLASS) Emalloc(sizeof(INT_CLASS_STRUCT));
|
|
Class->NumProtoSets = ((MaxNumProtos + PROTOS_PER_PROTO_SET - 1) /
|
|
PROTOS_PER_PROTO_SET);
|
|
|
|
assert(Class->NumProtoSets <= MAX_NUM_PROTO_SETS);
|
|
|
|
Class->NumProtos = 0;
|
|
Class->NumConfigs = 0;
|
|
|
|
for (i = 0; i < Class->NumProtoSets; i++) {
|
|
/* allocate space for a proto set, install in class, and initialize */
|
|
ProtoSet = (PROTO_SET) Emalloc(sizeof(PROTO_SET_STRUCT));
|
|
memset(ProtoSet, 0, sizeof(*ProtoSet));
|
|
Class->ProtoSets[i] = ProtoSet;
|
|
|
|
/* allocate space for the proto lengths and install in class */
|
|
}
|
|
if (MaxNumIntProtosIn (Class) > 0) {
|
|
Class->ProtoLengths =
|
|
(uinT8 *)Emalloc(MaxNumIntProtosIn (Class) * sizeof (uinT8));
|
|
memset(Class->ProtoLengths, 0,
|
|
MaxNumIntProtosIn(Class) * sizeof(*Class->ProtoLengths));
|
|
} else {
|
|
Class->ProtoLengths = NULL;
|
|
}
|
|
memset(Class->ConfigLengths, 0, sizeof(Class->ConfigLengths));
|
|
|
|
return (Class);
|
|
|
|
} /* NewIntClass */
|
|
|
|
|
|
void free_int_class(INT_CLASS int_class) {
|
|
int i;
|
|
|
|
for (i = 0; i < int_class->NumProtoSets; i++) {
|
|
Efree (int_class->ProtoSets[i]);
|
|
}
|
|
if (int_class->ProtoLengths != NULL) {
|
|
Efree (int_class->ProtoLengths);
|
|
}
|
|
Efree(int_class);
|
|
}
|
|
|
|
|
|
/**
|
|
* This routine allocates a new set of integer templates
|
|
* initialized to hold 0 classes.
|
|
* @return The integer templates created.
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Fri Feb 8 08:38:51 1991, DSJ, Created.
|
|
*/
|
|
INT_TEMPLATES NewIntTemplates() {
|
|
INT_TEMPLATES T;
|
|
int i;
|
|
|
|
T = (INT_TEMPLATES) Emalloc (sizeof (INT_TEMPLATES_STRUCT));
|
|
T->NumClasses = 0;
|
|
T->NumClassPruners = 0;
|
|
|
|
for (i = 0; i < MAX_NUM_CLASSES; i++)
|
|
ClassForClassId (T, i) = NULL;
|
|
|
|
return (T);
|
|
} /* NewIntTemplates */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
void free_int_templates(INT_TEMPLATES templates) {
|
|
int i;
|
|
|
|
for (i = 0; i < templates->NumClasses; i++)
|
|
free_int_class(templates->Class[i]);
|
|
for (i = 0; i < templates->NumClassPruners; i++)
|
|
delete templates->ClassPruners[i];
|
|
Efree(templates);
|
|
}
|
|
|
|
|
|
namespace tesseract {
|
|
/**
|
|
* This routine reads a set of integer templates from
|
|
* File. File must already be open and must be in the
|
|
* correct binary format.
|
|
* @param File open file to read templates from
|
|
* @return Pointer to integer templates read from File.
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Wed Feb 27 11:48:46 1991, DSJ, Created.
|
|
*/
|
|
INT_TEMPLATES Classify::ReadIntTemplates(FILE *File) {
|
|
int i, j, w, x, y, z;
|
|
BOOL8 swap;
|
|
int nread;
|
|
int unicharset_size;
|
|
int version_id = 0;
|
|
INT_TEMPLATES Templates;
|
|
CLASS_PRUNER_STRUCT* Pruner;
|
|
INT_CLASS Class;
|
|
uinT8 *Lengths;
|
|
PROTO_SET ProtoSet;
|
|
|
|
/* variables for conversion from older inttemp formats */
|
|
int b, bit_number, last_cp_bit_number, new_b, new_i, new_w;
|
|
CLASS_ID class_id, max_class_id;
|
|
inT16 *IndexFor = new inT16[MAX_NUM_CLASSES];
|
|
CLASS_ID *ClassIdFor = new CLASS_ID[MAX_NUM_CLASSES];
|
|
CLASS_PRUNER_STRUCT **TempClassPruner =
|
|
new CLASS_PRUNER_STRUCT*[MAX_NUM_CLASS_PRUNERS];
|
|
uinT32 SetBitsForMask = // word with NUM_BITS_PER_CLASS
|
|
(1 << NUM_BITS_PER_CLASS) - 1; // set starting at bit 0
|
|
uinT32 Mask, NewMask, ClassBits;
|
|
int MaxNumConfigs = MAX_NUM_CONFIGS;
|
|
int WerdsPerConfigVec = WERDS_PER_CONFIG_VEC;
|
|
|
|
/* first read the high level template struct */
|
|
Templates = NewIntTemplates();
|
|
// Read Templates in parts for 64 bit compatibility.
|
|
if (fread(&unicharset_size, sizeof(int), 1, File) != 1)
|
|
cprintf("Bad read of inttemp!\n");
|
|
if (fread(&Templates->NumClasses,
|
|
sizeof(Templates->NumClasses), 1, File) != 1 ||
|
|
fread(&Templates->NumClassPruners,
|
|
sizeof(Templates->NumClassPruners), 1, File) != 1)
|
|
cprintf("Bad read of inttemp!\n");
|
|
// Swap status is determined automatically.
|
|
swap = Templates->NumClassPruners < 0 ||
|
|
Templates->NumClassPruners > MAX_NUM_CLASS_PRUNERS;
|
|
if (swap) {
|
|
Reverse32(&Templates->NumClassPruners);
|
|
Reverse32(&Templates->NumClasses);
|
|
Reverse32(&unicharset_size);
|
|
}
|
|
if (Templates->NumClasses < 0) {
|
|
// This file has a version id!
|
|
version_id = -Templates->NumClasses;
|
|
if (fread(&Templates->NumClasses, sizeof(Templates->NumClasses),
|
|
1, File) != 1)
|
|
cprintf("Bad read of inttemp!\n");
|
|
if (swap)
|
|
Reverse32(&Templates->NumClasses);
|
|
}
|
|
|
|
if (version_id < 3) {
|
|
MaxNumConfigs = OLD_MAX_NUM_CONFIGS;
|
|
WerdsPerConfigVec = OLD_WERDS_PER_CONFIG_VEC;
|
|
}
|
|
|
|
if (version_id < 2) {
|
|
for (i = 0; i < unicharset_size; ++i) {
|
|
if (fread(&IndexFor[i], sizeof(inT16), 1, File) != 1)
|
|
cprintf("Bad read of inttemp!\n");
|
|
}
|
|
for (i = 0; i < Templates->NumClasses; ++i) {
|
|
if (fread(&ClassIdFor[i], sizeof(CLASS_ID), 1, File) != 1)
|
|
cprintf("Bad read of inttemp!\n");
|
|
}
|
|
if (swap) {
|
|
for (i = 0; i < Templates->NumClasses; i++)
|
|
Reverse16(&IndexFor[i]);
|
|
for (i = 0; i < Templates->NumClasses; i++)
|
|
Reverse32(&ClassIdFor[i]);
|
|
}
|
|
}
|
|
|
|
/* then read in the class pruners */
|
|
for (i = 0; i < Templates->NumClassPruners; i++) {
|
|
Pruner = new CLASS_PRUNER_STRUCT;
|
|
if ((nread =
|
|
fread(Pruner, 1, sizeof(CLASS_PRUNER_STRUCT),
|
|
File)) != sizeof(CLASS_PRUNER_STRUCT))
|
|
cprintf("Bad read of inttemp!\n");
|
|
if (swap) {
|
|
for (x = 0; x < NUM_CP_BUCKETS; x++) {
|
|
for (y = 0; y < NUM_CP_BUCKETS; y++) {
|
|
for (z = 0; z < NUM_CP_BUCKETS; z++) {
|
|
for (w = 0; w < WERDS_PER_CP_VECTOR; w++) {
|
|
Reverse32(&Pruner->p[x][y][z][w]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (version_id < 2) {
|
|
TempClassPruner[i] = Pruner;
|
|
} else {
|
|
Templates->ClassPruners[i] = Pruner;
|
|
}
|
|
}
|
|
|
|
/* fix class pruners if they came from an old version of inttemp */
|
|
if (version_id < 2) {
|
|
// Allocate enough class pruners to cover all the class ids.
|
|
max_class_id = 0;
|
|
for (i = 0; i < Templates->NumClasses; i++)
|
|
if (ClassIdFor[i] > max_class_id)
|
|
max_class_id = ClassIdFor[i];
|
|
for (i = 0; i <= CPrunerIdFor(max_class_id); i++) {
|
|
Templates->ClassPruners[i] = new CLASS_PRUNER_STRUCT;
|
|
memset(Templates->ClassPruners[i], 0, sizeof(CLASS_PRUNER_STRUCT));
|
|
}
|
|
// Convert class pruners from the old format (indexed by class index)
|
|
// to the new format (indexed by class id).
|
|
last_cp_bit_number = NUM_BITS_PER_CLASS * Templates->NumClasses - 1;
|
|
for (i = 0; i < Templates->NumClassPruners; i++) {
|
|
for (x = 0; x < NUM_CP_BUCKETS; x++)
|
|
for (y = 0; y < NUM_CP_BUCKETS; y++)
|
|
for (z = 0; z < NUM_CP_BUCKETS; z++)
|
|
for (w = 0; w < WERDS_PER_CP_VECTOR; w++) {
|
|
if (TempClassPruner[i]->p[x][y][z][w] == 0)
|
|
continue;
|
|
for (b = 0; b < BITS_PER_WERD; b += NUM_BITS_PER_CLASS) {
|
|
bit_number = i * BITS_PER_CP_VECTOR + w * BITS_PER_WERD + b;
|
|
if (bit_number > last_cp_bit_number)
|
|
break; // the rest of the bits in this word are not used
|
|
class_id = ClassIdFor[bit_number / NUM_BITS_PER_CLASS];
|
|
// Single out NUM_BITS_PER_CLASS bits relating to class_id.
|
|
Mask = SetBitsForMask << b;
|
|
ClassBits = TempClassPruner[i]->p[x][y][z][w] & Mask;
|
|
// Move these bits to the new position in which they should
|
|
// appear (indexed corresponding to the class_id).
|
|
new_i = CPrunerIdFor(class_id);
|
|
new_w = CPrunerWordIndexFor(class_id);
|
|
new_b = CPrunerBitIndexFor(class_id) * NUM_BITS_PER_CLASS;
|
|
if (new_b > b) {
|
|
ClassBits <<= (new_b - b);
|
|
} else {
|
|
ClassBits >>= (b - new_b);
|
|
}
|
|
// Copy bits relating to class_id to the correct position
|
|
// in Templates->ClassPruner.
|
|
NewMask = SetBitsForMask << new_b;
|
|
Templates->ClassPruners[new_i]->p[x][y][z][new_w] &= ~NewMask;
|
|
Templates->ClassPruners[new_i]->p[x][y][z][new_w] |= ClassBits;
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < Templates->NumClassPruners; i++) {
|
|
delete TempClassPruner[i];
|
|
}
|
|
}
|
|
|
|
/* then read in each class */
|
|
for (i = 0; i < Templates->NumClasses; i++) {
|
|
/* first read in the high level struct for the class */
|
|
Class = (INT_CLASS) Emalloc (sizeof (INT_CLASS_STRUCT));
|
|
if (fread(&Class->NumProtos, sizeof(Class->NumProtos), 1, File) != 1 ||
|
|
fread(&Class->NumProtoSets, sizeof(Class->NumProtoSets), 1, File) != 1 ||
|
|
fread(&Class->NumConfigs, sizeof(Class->NumConfigs), 1, File) != 1)
|
|
cprintf ("Bad read of inttemp!\n");
|
|
if (version_id == 0) {
|
|
// Only version 0 writes 5 pointless pointers to the file.
|
|
for (j = 0; j < 5; ++j) {
|
|
int junk;
|
|
if (fread(&junk, sizeof(junk), 1, File) != 1)
|
|
cprintf ("Bad read of inttemp!\n");
|
|
}
|
|
}
|
|
if (version_id < 4) {
|
|
for (j = 0; j < MaxNumConfigs; ++j) {
|
|
if (fread(&Class->ConfigLengths[j], sizeof(uinT16), 1, File) != 1)
|
|
cprintf ("Bad read of inttemp!\n");
|
|
}
|
|
if (swap) {
|
|
Reverse16(&Class->NumProtos);
|
|
for (j = 0; j < MaxNumConfigs; j++)
|
|
Reverse16(&Class->ConfigLengths[j]);
|
|
}
|
|
} else {
|
|
ASSERT_HOST(Class->NumConfigs < MaxNumConfigs);
|
|
for (j = 0; j < Class->NumConfigs; ++j) {
|
|
if (fread(&Class->ConfigLengths[j], sizeof(uinT16), 1, File) != 1)
|
|
cprintf ("Bad read of inttemp!\n");
|
|
}
|
|
if (swap) {
|
|
Reverse16(&Class->NumProtos);
|
|
for (j = 0; j < MaxNumConfigs; j++)
|
|
Reverse16(&Class->ConfigLengths[j]);
|
|
}
|
|
}
|
|
if (version_id < 2) {
|
|
ClassForClassId (Templates, ClassIdFor[i]) = Class;
|
|
} else {
|
|
ClassForClassId (Templates, i) = Class;
|
|
}
|
|
|
|
/* then read in the proto lengths */
|
|
Lengths = NULL;
|
|
if (MaxNumIntProtosIn (Class) > 0) {
|
|
Lengths = (uinT8 *)Emalloc(sizeof(uinT8) * MaxNumIntProtosIn(Class));
|
|
if ((nread =
|
|
fread((char *)Lengths, sizeof(uinT8),
|
|
MaxNumIntProtosIn(Class), File)) != MaxNumIntProtosIn (Class))
|
|
cprintf ("Bad read of inttemp!\n");
|
|
}
|
|
Class->ProtoLengths = Lengths;
|
|
|
|
/* then read in the proto sets */
|
|
for (j = 0; j < Class->NumProtoSets; j++) {
|
|
ProtoSet = (PROTO_SET)Emalloc(sizeof(PROTO_SET_STRUCT));
|
|
if (version_id < 3) {
|
|
if ((nread =
|
|
fread((char *) &ProtoSet->ProtoPruner, 1,
|
|
sizeof(PROTO_PRUNER), File)) != sizeof(PROTO_PRUNER))
|
|
cprintf("Bad read of inttemp!\n");
|
|
for (x = 0; x < PROTOS_PER_PROTO_SET; x++) {
|
|
if ((nread = fread((char *) &ProtoSet->Protos[x].A, 1,
|
|
sizeof(inT8), File)) != sizeof(inT8) ||
|
|
(nread = fread((char *) &ProtoSet->Protos[x].B, 1,
|
|
sizeof(uinT8), File)) != sizeof(uinT8) ||
|
|
(nread = fread((char *) &ProtoSet->Protos[x].C, 1,
|
|
sizeof(inT8), File)) != sizeof(inT8) ||
|
|
(nread = fread((char *) &ProtoSet->Protos[x].Angle, 1,
|
|
sizeof(uinT8), File)) != sizeof(uinT8))
|
|
cprintf("Bad read of inttemp!\n");
|
|
for (y = 0; y < WerdsPerConfigVec; y++)
|
|
if ((nread = fread((char *) &ProtoSet->Protos[x].Configs[y], 1,
|
|
sizeof(uinT32), File)) != sizeof(uinT32))
|
|
cprintf("Bad read of inttemp!\n");
|
|
}
|
|
} else {
|
|
if ((nread =
|
|
fread((char *) ProtoSet, 1, sizeof(PROTO_SET_STRUCT),
|
|
File)) != sizeof(PROTO_SET_STRUCT))
|
|
cprintf("Bad read of inttemp!\n");
|
|
}
|
|
if (swap) {
|
|
for (x = 0; x < NUM_PP_PARAMS; x++)
|
|
for (y = 0; y < NUM_PP_BUCKETS; y++)
|
|
for (z = 0; z < WERDS_PER_PP_VECTOR; z++)
|
|
Reverse32(&ProtoSet->ProtoPruner[x][y][z]);
|
|
for (x = 0; x < PROTOS_PER_PROTO_SET; x++)
|
|
for (y = 0; y < WerdsPerConfigVec; y++)
|
|
Reverse32(&ProtoSet->Protos[x].Configs[y]);
|
|
}
|
|
Class->ProtoSets[j] = ProtoSet;
|
|
}
|
|
if (version_id < 4)
|
|
Class->font_set_id = -1;
|
|
else {
|
|
fread(&Class->font_set_id, sizeof(int), 1, File);
|
|
if (swap)
|
|
Reverse32(&Class->font_set_id);
|
|
}
|
|
}
|
|
|
|
if (version_id < 2) {
|
|
/* add an empty NULL class with class id 0 */
|
|
assert(UnusedClassIdIn (Templates, 0));
|
|
ClassForClassId (Templates, 0) = NewIntClass (1, 1);
|
|
ClassForClassId (Templates, 0)->font_set_id = -1;
|
|
Templates->NumClasses++;
|
|
/* make sure the classes are contiguous */
|
|
for (i = 0; i < MAX_NUM_CLASSES; i++) {
|
|
if (i < Templates->NumClasses) {
|
|
if (ClassForClassId (Templates, i) == NULL) {
|
|
fprintf(stderr, "Non-contiguous class ids in inttemp\n");
|
|
exit(1);
|
|
}
|
|
} else {
|
|
if (ClassForClassId (Templates, i) != NULL) {
|
|
fprintf(stderr, "Class id %d exceeds NumClassesIn (Templates) %d\n",
|
|
i, Templates->NumClasses);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (version_id >= 4) {
|
|
this->fontinfo_table_.read(File, NewPermanentTessCallback(read_info), swap);
|
|
if (version_id >= 5) {
|
|
this->fontinfo_table_.read(File,
|
|
NewPermanentTessCallback(read_spacing_info),
|
|
swap);
|
|
}
|
|
this->fontset_table_.read(File, NewPermanentTessCallback(read_set), swap);
|
|
}
|
|
|
|
// Clean up.
|
|
delete[] IndexFor;
|
|
delete[] ClassIdFor;
|
|
delete[] TempClassPruner;
|
|
|
|
return (Templates);
|
|
} /* ReadIntTemplates */
|
|
|
|
|
|
#ifndef GRAPHICS_DISABLED
|
|
/**
|
|
* This routine sends the shapes in the global display
|
|
* lists to the match debugger window.
|
|
*
|
|
* Globals:
|
|
* - FeatureShapes display list containing feature matches
|
|
* - ProtoShapes display list containing proto matches
|
|
* @return none
|
|
* @note Exceptions: none
|
|
* @note History: Thu Mar 21 15:47:33 1991, DSJ, Created.
|
|
*/
|
|
void Classify::ShowMatchDisplay() {
|
|
InitIntMatchWindowIfReqd();
|
|
if (ProtoDisplayWindow) {
|
|
ProtoDisplayWindow->Clear();
|
|
}
|
|
if (FeatureDisplayWindow) {
|
|
FeatureDisplayWindow->Clear();
|
|
}
|
|
ClearFeatureSpaceWindow(
|
|
static_cast<NORM_METHOD>(static_cast<int>(classify_norm_method)),
|
|
IntMatchWindow);
|
|
IntMatchWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y,
|
|
INT_MAX_X, INT_MAX_Y);
|
|
if (ProtoDisplayWindow) {
|
|
ProtoDisplayWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y,
|
|
INT_MAX_X, INT_MAX_Y);
|
|
}
|
|
if (FeatureDisplayWindow) {
|
|
FeatureDisplayWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y,
|
|
INT_MAX_X, INT_MAX_Y);
|
|
}
|
|
} /* ShowMatchDisplay */
|
|
|
|
/// Clears the given window and draws the featurespace guides for the
|
|
/// appropriate normalization method.
|
|
void ClearFeatureSpaceWindow(NORM_METHOD norm_method, ScrollView* window) {
|
|
window->Clear();
|
|
|
|
window->Pen(ScrollView::GREY);
|
|
// Draw the feature space limit rectangle.
|
|
window->Rectangle(0, 0, INT_MAX_X, INT_MAX_Y);
|
|
if (norm_method == baseline) {
|
|
window->SetCursor(0, INT_DESCENDER);
|
|
window->DrawTo(INT_MAX_X, INT_DESCENDER);
|
|
window->SetCursor(0, INT_BASELINE);
|
|
window->DrawTo(INT_MAX_X, INT_BASELINE);
|
|
window->SetCursor(0, INT_XHEIGHT);
|
|
window->DrawTo(INT_MAX_X, INT_XHEIGHT);
|
|
window->SetCursor(0, INT_CAPHEIGHT);
|
|
window->DrawTo(INT_MAX_X, INT_CAPHEIGHT);
|
|
} else {
|
|
window->Rectangle(INT_XCENTER - INT_XRADIUS, INT_YCENTER - INT_YRADIUS,
|
|
INT_XCENTER + INT_XRADIUS, INT_YCENTER + INT_YRADIUS);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* This routine writes Templates to File. The format
|
|
* is an efficient binary format. File must already be open
|
|
* for writing.
|
|
* @param File open file to write templates to
|
|
* @param Templates templates to save into File
|
|
* @param target_unicharset the UNICHARSET to use
|
|
* @return none
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Wed Feb 27 11:48:46 1991, DSJ, Created.
|
|
*/
|
|
void Classify::WriteIntTemplates(FILE *File, INT_TEMPLATES Templates,
|
|
const UNICHARSET& target_unicharset) {
|
|
int i, j;
|
|
INT_CLASS Class;
|
|
int unicharset_size = target_unicharset.size();
|
|
int version_id = -5; // When negated by the reader -1 becomes +1 etc.
|
|
|
|
if (Templates->NumClasses != unicharset_size) {
|
|
cprintf("Warning: executing WriteIntTemplates() with %d classes in"
|
|
" Templates, while target_unicharset size is %d\n",
|
|
Templates->NumClasses, unicharset_size);
|
|
}
|
|
|
|
/* first write the high level template struct */
|
|
fwrite(&unicharset_size, sizeof(unicharset_size), 1, File);
|
|
fwrite(&version_id, sizeof(version_id), 1, File);
|
|
fwrite(&Templates->NumClassPruners, sizeof(Templates->NumClassPruners),
|
|
1, File);
|
|
fwrite(&Templates->NumClasses, sizeof(Templates->NumClasses), 1, File);
|
|
|
|
/* then write out the class pruners */
|
|
for (i = 0; i < Templates->NumClassPruners; i++)
|
|
fwrite(Templates->ClassPruners[i],
|
|
sizeof(CLASS_PRUNER_STRUCT), 1, File);
|
|
|
|
/* then write out each class */
|
|
for (i = 0; i < Templates->NumClasses; i++) {
|
|
Class = Templates->Class[i];
|
|
|
|
/* first write out the high level struct for the class */
|
|
fwrite(&Class->NumProtos, sizeof(Class->NumProtos), 1, File);
|
|
fwrite(&Class->NumProtoSets, sizeof(Class->NumProtoSets), 1, File);
|
|
ASSERT_HOST(Class->NumConfigs == this->fontset_table_.get(Class->font_set_id).size);
|
|
fwrite(&Class->NumConfigs, sizeof(Class->NumConfigs), 1, File);
|
|
for (j = 0; j < Class->NumConfigs; ++j) {
|
|
fwrite(&Class->ConfigLengths[j], sizeof(uinT16), 1, File);
|
|
}
|
|
|
|
/* then write out the proto lengths */
|
|
if (MaxNumIntProtosIn (Class) > 0) {
|
|
fwrite ((char *) (Class->ProtoLengths), sizeof (uinT8),
|
|
MaxNumIntProtosIn (Class), File);
|
|
}
|
|
|
|
/* then write out the proto sets */
|
|
for (j = 0; j < Class->NumProtoSets; j++)
|
|
fwrite ((char *) Class->ProtoSets[j],
|
|
sizeof (PROTO_SET_STRUCT), 1, File);
|
|
|
|
/* then write the fonts info */
|
|
fwrite(&Class->font_set_id, sizeof(int), 1, File);
|
|
}
|
|
|
|
/* Write the fonts info tables */
|
|
this->fontinfo_table_.write(File, NewPermanentTessCallback(write_info));
|
|
this->fontinfo_table_.write(File,
|
|
NewPermanentTessCallback(write_spacing_info));
|
|
this->fontset_table_.write(File, NewPermanentTessCallback(write_set));
|
|
} /* WriteIntTemplates */
|
|
} // namespace tesseract
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
Private Code
|
|
-----------------------------------------------------------------------------*/
|
|
/**
|
|
* This routine returns the parameter value which
|
|
* corresponds to the beginning of the specified bucket.
|
|
* The bucket number should have been generated using the
|
|
* BucketFor() function with parameters Offset and NumBuckets.
|
|
* @param Bucket bucket whose start is to be computed
|
|
* @param Offset offset used to map params to buckets
|
|
* @param NumBuckets total number of buckets
|
|
* @return Param value corresponding to start position of Bucket.
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Thu Feb 14 13:24:33 1991, DSJ, Created.
|
|
*/
|
|
FLOAT32 BucketStart(int Bucket, FLOAT32 Offset, int NumBuckets) {
|
|
return (((FLOAT32) Bucket / NumBuckets) - Offset);
|
|
|
|
} /* BucketStart */
|
|
|
|
|
|
/**
|
|
* This routine returns the parameter value which
|
|
* corresponds to the end of the specified bucket.
|
|
* The bucket number should have been generated using the
|
|
* BucketFor() function with parameters Offset and NumBuckets.
|
|
* @param Bucket bucket whose end is to be computed
|
|
* @param Offset offset used to map params to buckets
|
|
* @param NumBuckets total number of buckets
|
|
* @return Param value corresponding to end position of Bucket.
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Thu Feb 14 13:24:33 1991, DSJ, Created.
|
|
*/
|
|
FLOAT32 BucketEnd(int Bucket, FLOAT32 Offset, int NumBuckets) {
|
|
return (((FLOAT32) (Bucket + 1) / NumBuckets) - Offset);
|
|
} /* BucketEnd */
|
|
|
|
|
|
/**
|
|
* This routine fills in the section of a class pruner
|
|
* corresponding to a single x value for a single proto of
|
|
* a class.
|
|
* @param FillSpec specifies which bits to fill in pruner
|
|
* @param Pruner class pruner to be filled
|
|
* @param ClassMask indicates which bits to change in each word
|
|
* @param ClassCount indicates what to change bits to
|
|
* @param WordIndex indicates which word to change
|
|
* @return none
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Tue Feb 19 11:11:29 1991, DSJ, Created.
|
|
*/
|
|
void DoFill(FILL_SPEC *FillSpec,
|
|
CLASS_PRUNER_STRUCT* Pruner,
|
|
register uinT32 ClassMask,
|
|
register uinT32 ClassCount,
|
|
register uinT32 WordIndex) {
|
|
register int X, Y, Angle;
|
|
register uinT32 OldWord;
|
|
|
|
X = FillSpec->X;
|
|
if (X < 0)
|
|
X = 0;
|
|
if (X >= NUM_CP_BUCKETS)
|
|
X = NUM_CP_BUCKETS - 1;
|
|
|
|
if (FillSpec->YStart < 0)
|
|
FillSpec->YStart = 0;
|
|
if (FillSpec->YEnd >= NUM_CP_BUCKETS)
|
|
FillSpec->YEnd = NUM_CP_BUCKETS - 1;
|
|
|
|
for (Y = FillSpec->YStart; Y <= FillSpec->YEnd; Y++)
|
|
for (Angle = FillSpec->AngleStart;
|
|
TRUE; CircularIncrement (Angle, NUM_CP_BUCKETS)) {
|
|
OldWord = Pruner->p[X][Y][Angle][WordIndex];
|
|
if (ClassCount > (OldWord & ClassMask)) {
|
|
OldWord &= ~ClassMask;
|
|
OldWord |= ClassCount;
|
|
Pruner->p[X][Y][Angle][WordIndex] = OldWord;
|
|
}
|
|
if (Angle == FillSpec->AngleEnd)
|
|
break;
|
|
}
|
|
} /* DoFill */
|
|
|
|
|
|
/**
|
|
* Return TRUE if the specified table filler is done, i.e.
|
|
* if it has no more lines to fill.
|
|
* @param Filler table filler to check if done
|
|
* @return TRUE if no more lines to fill, FALSE otherwise.
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Tue Feb 19 10:08:05 1991, DSJ, Created.
|
|
*/
|
|
BOOL8 FillerDone(TABLE_FILLER *Filler) {
|
|
FILL_SWITCH *Next;
|
|
|
|
Next = &(Filler->Switch[Filler->NextSwitch]);
|
|
|
|
if (Filler->X > Next->X && Next->Type == LastSwitch)
|
|
return (TRUE);
|
|
else
|
|
return (FALSE);
|
|
|
|
} /* FillerDone */
|
|
|
|
|
|
/**
|
|
* This routine sets Bit in each bit vector whose
|
|
* bucket lies within the range Center +- Spread. The fill
|
|
* is done for a circular dimension, i.e. bucket 0 is adjacent
|
|
* to the last bucket. It is assumed that Center and Spread
|
|
* are expressed in a circular coordinate system whose range
|
|
* is 0 to 1.
|
|
* @param ParamTable table of bit vectors, one per param bucket
|
|
* @param Bit bit position in vectors to be filled
|
|
* @param Center center of filled area
|
|
* @param Spread spread of filled area
|
|
* @param debug debug flag
|
|
* @return none
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Tue Oct 16 09:26:54 1990, DSJ, Created.
|
|
*/
|
|
void FillPPCircularBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR],
|
|
int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug) {
|
|
int i, FirstBucket, LastBucket;
|
|
|
|
if (Spread > 0.5)
|
|
Spread = 0.5;
|
|
|
|
FirstBucket = (int) floor ((Center - Spread) * NUM_PP_BUCKETS);
|
|
if (FirstBucket < 0)
|
|
FirstBucket += NUM_PP_BUCKETS;
|
|
|
|
LastBucket = (int) floor ((Center + Spread) * NUM_PP_BUCKETS);
|
|
if (LastBucket >= NUM_PP_BUCKETS)
|
|
LastBucket -= NUM_PP_BUCKETS;
|
|
if (debug) tprintf("Circular fill from %d to %d", FirstBucket, LastBucket);
|
|
for (i = FirstBucket; TRUE; CircularIncrement (i, NUM_PP_BUCKETS)) {
|
|
SET_BIT (ParamTable[i], Bit);
|
|
|
|
/* exit loop after we have set the bit for the last bucket */
|
|
if (i == LastBucket)
|
|
break;
|
|
}
|
|
|
|
} /* FillPPCircularBits */
|
|
|
|
|
|
/**
|
|
* This routine sets Bit in each bit vector whose
|
|
* bucket lies within the range Center +- Spread. The fill
|
|
* is done for a linear dimension, i.e. there is no wrap-around
|
|
* for this dimension. It is assumed that Center and Spread
|
|
* are expressed in a linear coordinate system whose range
|
|
* is approximately 0 to 1. Values outside this range will
|
|
* be clipped.
|
|
* @param ParamTable table of bit vectors, one per param bucket
|
|
* @param Bit bit number being filled
|
|
* @param Center center of filled area
|
|
* @param Spread spread of filled area
|
|
* @param debug debug flag
|
|
* @return none
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Tue Oct 16 09:26:54 1990, DSJ, Created.
|
|
*/
|
|
void FillPPLinearBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR],
|
|
int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug) {
|
|
int i, FirstBucket, LastBucket;
|
|
|
|
FirstBucket = (int) floor ((Center - Spread) * NUM_PP_BUCKETS);
|
|
if (FirstBucket < 0)
|
|
FirstBucket = 0;
|
|
|
|
LastBucket = (int) floor ((Center + Spread) * NUM_PP_BUCKETS);
|
|
if (LastBucket >= NUM_PP_BUCKETS)
|
|
LastBucket = NUM_PP_BUCKETS - 1;
|
|
|
|
if (debug) tprintf("Linear fill from %d to %d", FirstBucket, LastBucket);
|
|
for (i = FirstBucket; i <= LastBucket; i++)
|
|
SET_BIT (ParamTable[i], Bit);
|
|
|
|
} /* FillPPLinearBits */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
#ifndef GRAPHICS_DISABLED
|
|
namespace tesseract {
|
|
/**
|
|
* This routine prompts the user with Prompt and waits
|
|
* for the user to enter something in the debug window.
|
|
* @param Prompt prompt to print while waiting for input from window
|
|
* @param adaptive_on
|
|
* @param pretrained_on
|
|
* @param shape_id
|
|
* @return Character entered in the debug window.
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Thu Mar 21 16:55:13 1991, DSJ, Created.
|
|
*/
|
|
CLASS_ID Classify::GetClassToDebug(const char *Prompt, bool* adaptive_on,
|
|
bool* pretrained_on, int* shape_id) {
|
|
tprintf("%s\n", Prompt);
|
|
SVEvent* ev;
|
|
SVEventType ev_type;
|
|
int unichar_id = INVALID_UNICHAR_ID;
|
|
// Wait until a click or popup event.
|
|
do {
|
|
ev = IntMatchWindow->AwaitEvent(SVET_ANY);
|
|
ev_type = ev->type;
|
|
if (ev_type == SVET_POPUP) {
|
|
if (ev->command_id == IDA_SHAPE_INDEX) {
|
|
if (shape_table_ != NULL) {
|
|
*shape_id = atoi(ev->parameter);
|
|
*adaptive_on = false;
|
|
*pretrained_on = true;
|
|
if (*shape_id >= 0 && *shape_id < shape_table_->NumShapes()) {
|
|
int font_id;
|
|
shape_table_->GetFirstUnicharAndFont(*shape_id, &unichar_id,
|
|
&font_id);
|
|
tprintf("Shape %d, first unichar=%d, font=%d\n",
|
|
*shape_id, unichar_id, font_id);
|
|
return unichar_id;
|
|
}
|
|
tprintf("Shape index '%s' not found in shape table\n", ev->parameter);
|
|
} else {
|
|
tprintf("No shape table loaded!\n");
|
|
}
|
|
} else {
|
|
if (unicharset.contains_unichar(ev->parameter)) {
|
|
unichar_id = unicharset.unichar_to_id(ev->parameter);
|
|
if (ev->command_id == IDA_ADAPTIVE) {
|
|
*adaptive_on = true;
|
|
*pretrained_on = false;
|
|
*shape_id = -1;
|
|
} else if (ev->command_id == IDA_STATIC) {
|
|
*adaptive_on = false;
|
|
*pretrained_on = true;
|
|
} else {
|
|
*adaptive_on = true;
|
|
*pretrained_on = true;
|
|
}
|
|
if (ev->command_id == IDA_ADAPTIVE || shape_table_ == NULL) {
|
|
*shape_id = -1;
|
|
return unichar_id;
|
|
}
|
|
for (int s = 0; s < shape_table_->NumShapes(); ++s) {
|
|
if (shape_table_->GetShape(s).ContainsUnichar(unichar_id)) {
|
|
tprintf("%s\n", shape_table_->DebugStr(s).string());
|
|
}
|
|
}
|
|
} else {
|
|
tprintf("Char class '%s' not found in unicharset",
|
|
ev->parameter);
|
|
}
|
|
}
|
|
}
|
|
delete ev;
|
|
} while (ev_type != SVET_CLICK);
|
|
return 0;
|
|
} /* GetClassToDebug */
|
|
|
|
} // namespace tesseract
|
|
#endif
|
|
|
|
/**
|
|
* This routine copies the appropriate global pad variables
|
|
* into EndPad, SidePad, and AnglePad. This is a kludge used
|
|
* to get around the fact that global control variables cannot
|
|
* be arrays. If the specified level is illegal, the tightest
|
|
* possible pads are returned.
|
|
* @param Level "tightness" level to return pads for
|
|
* @param EndPad place to put end pad for Level
|
|
* @param SidePad place to put side pad for Level
|
|
* @param AnglePad place to put angle pad for Level
|
|
* @return none (results are returned in EndPad, SidePad, and AnglePad.
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Thu Feb 14 08:26:49 1991, DSJ, Created.
|
|
*/
|
|
void GetCPPadsForLevel(int Level,
|
|
FLOAT32 *EndPad,
|
|
FLOAT32 *SidePad,
|
|
FLOAT32 *AnglePad) {
|
|
switch (Level) {
|
|
case 0:
|
|
*EndPad = classify_cp_end_pad_loose * GetPicoFeatureLength ();
|
|
*SidePad = classify_cp_side_pad_loose * GetPicoFeatureLength ();
|
|
*AnglePad = classify_cp_angle_pad_loose / 360.0;
|
|
break;
|
|
|
|
case 1:
|
|
*EndPad = classify_cp_end_pad_medium * GetPicoFeatureLength ();
|
|
*SidePad = classify_cp_side_pad_medium * GetPicoFeatureLength ();
|
|
*AnglePad = classify_cp_angle_pad_medium / 360.0;
|
|
break;
|
|
|
|
case 2:
|
|
*EndPad = classify_cp_end_pad_tight * GetPicoFeatureLength ();
|
|
*SidePad = classify_cp_side_pad_tight * GetPicoFeatureLength ();
|
|
*AnglePad = classify_cp_angle_pad_tight / 360.0;
|
|
break;
|
|
|
|
default:
|
|
*EndPad = classify_cp_end_pad_tight * GetPicoFeatureLength ();
|
|
*SidePad = classify_cp_side_pad_tight * GetPicoFeatureLength ();
|
|
*AnglePad = classify_cp_angle_pad_tight / 360.0;
|
|
break;
|
|
}
|
|
if (*AnglePad > 0.5)
|
|
*AnglePad = 0.5;
|
|
|
|
} /* GetCPPadsForLevel */
|
|
|
|
|
|
/**
|
|
* @param Evidence evidence value to return color for
|
|
* @return Color which corresponds to specified Evidence value.
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Thu Mar 21 15:24:52 1991, DSJ, Created.
|
|
*/
|
|
ScrollView::Color GetMatchColorFor(FLOAT32 Evidence) {
|
|
assert (Evidence >= 0.0);
|
|
assert (Evidence <= 1.0);
|
|
|
|
if (Evidence >= 0.90)
|
|
return ScrollView::WHITE;
|
|
else if (Evidence >= 0.75)
|
|
return ScrollView::GREEN;
|
|
else if (Evidence >= 0.50)
|
|
return ScrollView::RED;
|
|
else
|
|
return ScrollView::BLUE;
|
|
} /* GetMatchColorFor */
|
|
|
|
|
|
/**
|
|
* This routine returns (in Fill) the specification of
|
|
* the next line to be filled from Filler. FillerDone() should
|
|
* always be called before GetNextFill() to ensure that we
|
|
* do not run past the end of the fill table.
|
|
* @param Filler filler to get next fill spec from
|
|
* @param Fill place to put spec for next fill
|
|
* @return none (results are returned in Fill)
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Tue Feb 19 10:17:42 1991, DSJ, Created.
|
|
*/
|
|
void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill) {
|
|
FILL_SWITCH *Next;
|
|
|
|
/* compute the fill assuming no switches will be encountered */
|
|
Fill->AngleStart = Filler->AngleStart;
|
|
Fill->AngleEnd = Filler->AngleEnd;
|
|
Fill->X = Filler->X;
|
|
Fill->YStart = Filler->YStart >> 8;
|
|
Fill->YEnd = Filler->YEnd >> 8;
|
|
|
|
/* update the fill info and the filler for ALL switches at this X value */
|
|
Next = &(Filler->Switch[Filler->NextSwitch]);
|
|
while (Filler->X >= Next->X) {
|
|
Fill->X = Filler->X = Next->X;
|
|
if (Next->Type == StartSwitch) {
|
|
Fill->YStart = Next->Y;
|
|
Filler->StartDelta = Next->Delta;
|
|
Filler->YStart = Next->YInit;
|
|
}
|
|
else if (Next->Type == EndSwitch) {
|
|
Fill->YEnd = Next->Y;
|
|
Filler->EndDelta = Next->Delta;
|
|
Filler->YEnd = Next->YInit;
|
|
}
|
|
else { /* Type must be LastSwitch */
|
|
break;
|
|
}
|
|
Filler->NextSwitch++;
|
|
Next = &(Filler->Switch[Filler->NextSwitch]);
|
|
}
|
|
|
|
/* prepare the filler for the next call to this routine */
|
|
Filler->X++;
|
|
Filler->YStart += Filler->StartDelta;
|
|
Filler->YEnd += Filler->EndDelta;
|
|
|
|
} /* GetNextFill */
|
|
|
|
|
|
/**
|
|
* This routine computes a data structure (Filler)
|
|
* which can be used to fill in a rectangle surrounding
|
|
* the specified Proto.
|
|
*
|
|
* @param EndPad, SidePad, AnglePad padding to add to proto
|
|
* @param Proto proto to create a filler for
|
|
* @param Filler place to put table filler
|
|
*
|
|
* @return none (results are returned in Filler)
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Thu Feb 14 09:27:05 1991, DSJ, Created.
|
|
*/
|
|
void InitTableFiller (FLOAT32 EndPad, FLOAT32 SidePad,
|
|
FLOAT32 AnglePad, PROTO Proto, TABLE_FILLER * Filler)
|
|
#define XS X_SHIFT
|
|
#define YS Y_SHIFT
|
|
#define AS ANGLE_SHIFT
|
|
#define NB NUM_CP_BUCKETS
|
|
{
|
|
FLOAT32 Angle;
|
|
FLOAT32 X, Y, HalfLength;
|
|
FLOAT32 Cos, Sin;
|
|
FLOAT32 XAdjust, YAdjust;
|
|
FPOINT Start, Switch1, Switch2, End;
|
|
int S1 = 0;
|
|
int S2 = 1;
|
|
|
|
Angle = Proto->Angle;
|
|
X = Proto->X;
|
|
Y = Proto->Y;
|
|
HalfLength = Proto->Length / 2.0;
|
|
|
|
Filler->AngleStart = CircBucketFor(Angle - AnglePad, AS, NB);
|
|
Filler->AngleEnd = CircBucketFor(Angle + AnglePad, AS, NB);
|
|
Filler->NextSwitch = 0;
|
|
|
|
if (fabs (Angle - 0.0) < HV_TOLERANCE || fabs (Angle - 0.5) < HV_TOLERANCE) {
|
|
/* horizontal proto - handle as special case */
|
|
Filler->X = Bucket8For(X - HalfLength - EndPad, XS, NB);
|
|
Filler->YStart = Bucket16For(Y - SidePad, YS, NB * 256);
|
|
Filler->YEnd = Bucket16For(Y + SidePad, YS, NB * 256);
|
|
Filler->StartDelta = 0;
|
|
Filler->EndDelta = 0;
|
|
Filler->Switch[0].Type = LastSwitch;
|
|
Filler->Switch[0].X = Bucket8For(X + HalfLength + EndPad, XS, NB);
|
|
} else if (fabs(Angle - 0.25) < HV_TOLERANCE ||
|
|
fabs(Angle - 0.75) < HV_TOLERANCE) {
|
|
/* vertical proto - handle as special case */
|
|
Filler->X = Bucket8For(X - SidePad, XS, NB);
|
|
Filler->YStart = Bucket16For(Y - HalfLength - EndPad, YS, NB * 256);
|
|
Filler->YEnd = Bucket16For(Y + HalfLength + EndPad, YS, NB * 256);
|
|
Filler->StartDelta = 0;
|
|
Filler->EndDelta = 0;
|
|
Filler->Switch[0].Type = LastSwitch;
|
|
Filler->Switch[0].X = Bucket8For(X + SidePad, XS, NB);
|
|
} else {
|
|
/* diagonal proto */
|
|
|
|
if ((Angle > 0.0 && Angle < 0.25) || (Angle > 0.5 && Angle < 0.75)) {
|
|
/* rising diagonal proto */
|
|
Angle *= 2.0 * PI;
|
|
Cos = fabs(cos(Angle));
|
|
Sin = fabs(sin(Angle));
|
|
|
|
/* compute the positions of the corners of the acceptance region */
|
|
Start.x = X - (HalfLength + EndPad) * Cos - SidePad * Sin;
|
|
Start.y = Y - (HalfLength + EndPad) * Sin + SidePad * Cos;
|
|
End.x = 2.0 * X - Start.x;
|
|
End.y = 2.0 * Y - Start.y;
|
|
Switch1.x = X - (HalfLength + EndPad) * Cos + SidePad * Sin;
|
|
Switch1.y = Y - (HalfLength + EndPad) * Sin - SidePad * Cos;
|
|
Switch2.x = 2.0 * X - Switch1.x;
|
|
Switch2.y = 2.0 * Y - Switch1.y;
|
|
|
|
if (Switch1.x > Switch2.x) {
|
|
S1 = 1;
|
|
S2 = 0;
|
|
}
|
|
|
|
/* translate into bucket positions and deltas */
|
|
Filler->X = Bucket8For(Start.x, XS, NB);
|
|
Filler->StartDelta = -(inT16) ((Cos / Sin) * 256);
|
|
Filler->EndDelta = (inT16) ((Sin / Cos) * 256);
|
|
|
|
XAdjust = BucketEnd(Filler->X, XS, NB) - Start.x;
|
|
YAdjust = XAdjust * Cos / Sin;
|
|
Filler->YStart = Bucket16For(Start.y - YAdjust, YS, NB * 256);
|
|
YAdjust = XAdjust * Sin / Cos;
|
|
Filler->YEnd = Bucket16For(Start.y + YAdjust, YS, NB * 256);
|
|
|
|
Filler->Switch[S1].Type = StartSwitch;
|
|
Filler->Switch[S1].X = Bucket8For(Switch1.x, XS, NB);
|
|
Filler->Switch[S1].Y = Bucket8For(Switch1.y, YS, NB);
|
|
XAdjust = Switch1.x - BucketStart(Filler->Switch[S1].X, XS, NB);
|
|
YAdjust = XAdjust * Sin / Cos;
|
|
Filler->Switch[S1].YInit = Bucket16For(Switch1.y - YAdjust, YS, NB * 256);
|
|
Filler->Switch[S1].Delta = Filler->EndDelta;
|
|
|
|
Filler->Switch[S2].Type = EndSwitch;
|
|
Filler->Switch[S2].X = Bucket8For(Switch2.x, XS, NB);
|
|
Filler->Switch[S2].Y = Bucket8For(Switch2.y, YS, NB);
|
|
XAdjust = Switch2.x - BucketStart(Filler->Switch[S2].X, XS, NB);
|
|
YAdjust = XAdjust * Cos / Sin;
|
|
Filler->Switch[S2].YInit = Bucket16For(Switch2.y + YAdjust, YS, NB * 256);
|
|
Filler->Switch[S2].Delta = Filler->StartDelta;
|
|
|
|
Filler->Switch[2].Type = LastSwitch;
|
|
Filler->Switch[2].X = Bucket8For(End.x, XS, NB);
|
|
} else {
|
|
/* falling diagonal proto */
|
|
Angle *= 2.0 * PI;
|
|
Cos = fabs(cos(Angle));
|
|
Sin = fabs(sin(Angle));
|
|
|
|
/* compute the positions of the corners of the acceptance region */
|
|
Start.x = X - (HalfLength + EndPad) * Cos - SidePad * Sin;
|
|
Start.y = Y + (HalfLength + EndPad) * Sin - SidePad * Cos;
|
|
End.x = 2.0 * X - Start.x;
|
|
End.y = 2.0 * Y - Start.y;
|
|
Switch1.x = X - (HalfLength + EndPad) * Cos + SidePad * Sin;
|
|
Switch1.y = Y + (HalfLength + EndPad) * Sin + SidePad * Cos;
|
|
Switch2.x = 2.0 * X - Switch1.x;
|
|
Switch2.y = 2.0 * Y - Switch1.y;
|
|
|
|
if (Switch1.x > Switch2.x) {
|
|
S1 = 1;
|
|
S2 = 0;
|
|
}
|
|
|
|
/* translate into bucket positions and deltas */
|
|
Filler->X = Bucket8For(Start.x, XS, NB);
|
|
Filler->StartDelta = -(inT16) ((Sin / Cos) * 256);
|
|
Filler->EndDelta = (inT16) ((Cos / Sin) * 256);
|
|
|
|
XAdjust = BucketEnd(Filler->X, XS, NB) - Start.x;
|
|
YAdjust = XAdjust * Sin / Cos;
|
|
Filler->YStart = Bucket16For(Start.y - YAdjust, YS, NB * 256);
|
|
YAdjust = XAdjust * Cos / Sin;
|
|
Filler->YEnd = Bucket16For(Start.y + YAdjust, YS, NB * 256);
|
|
|
|
Filler->Switch[S1].Type = EndSwitch;
|
|
Filler->Switch[S1].X = Bucket8For(Switch1.x, XS, NB);
|
|
Filler->Switch[S1].Y = Bucket8For(Switch1.y, YS, NB);
|
|
XAdjust = Switch1.x - BucketStart(Filler->Switch[S1].X, XS, NB);
|
|
YAdjust = XAdjust * Sin / Cos;
|
|
Filler->Switch[S1].YInit = Bucket16For(Switch1.y + YAdjust, YS, NB * 256);
|
|
Filler->Switch[S1].Delta = Filler->StartDelta;
|
|
|
|
Filler->Switch[S2].Type = StartSwitch;
|
|
Filler->Switch[S2].X = Bucket8For(Switch2.x, XS, NB);
|
|
Filler->Switch[S2].Y = Bucket8For(Switch2.y, YS, NB);
|
|
XAdjust = Switch2.x - BucketStart(Filler->Switch[S2].X, XS, NB);
|
|
YAdjust = XAdjust * Cos / Sin;
|
|
Filler->Switch[S2].YInit = Bucket16For(Switch2.y - YAdjust, YS, NB * 256);
|
|
Filler->Switch[S2].Delta = Filler->EndDelta;
|
|
|
|
Filler->Switch[2].Type = LastSwitch;
|
|
Filler->Switch[2].X = Bucket8For(End.x, XS, NB);
|
|
}
|
|
}
|
|
} /* InitTableFiller */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
#ifndef GRAPHICS_DISABLED
|
|
/**
|
|
* This routine renders the specified feature into ShapeList.
|
|
* @param window to add feature rendering to
|
|
* @param Feature feature to be rendered
|
|
* @param color color to use for feature rendering
|
|
* @return New shape list with rendering of Feature added.
|
|
* @note Globals: none
|
|
* @note Exceptions: none
|
|
* @note History: Thu Mar 21 14:57:41 1991, DSJ, Created.
|
|
*/
|
|
void RenderIntFeature(ScrollView *window, const INT_FEATURE_STRUCT* Feature,
|
|
ScrollView::Color color) {
|
|
FLOAT32 X, Y, Dx, Dy, Length;
|
|
|
|
window->Pen(color);
|
|
assert(Feature != NULL);
|
|
assert(color != 0);
|
|
|
|
X = Feature->X;
|
|
Y = Feature->Y;
|
|
Length = GetPicoFeatureLength() * 0.7 * INT_CHAR_NORM_RANGE;
|
|
// The -PI has no significant effect here, but the value of Theta is computed
|
|
// using BinaryAnglePlusPi in intfx.cpp.
|
|
Dx = (Length / 2.0) * cos((Feature->Theta / 256.0) * 2.0 * PI - PI);
|
|
Dy = (Length / 2.0) * sin((Feature->Theta / 256.0) * 2.0 * PI - PI);
|
|
|
|
window->SetCursor(X, Y);
|
|
window->DrawTo(X + Dx, Y + Dy);
|
|
} /* RenderIntFeature */
|
|
|
|
|
|
/**
|
|
* This routine extracts the parameters of the specified
|
|
* proto from the class description and adds a rendering of
|
|
* the proto onto the ShapeList.
|
|
*
|
|
* @param window ScrollView instance
|
|
* @param Class class that proto is contained in
|
|
* @param ProtoId id of proto to be rendered
|
|
* @param color color to render proto in
|
|
*
|
|
* Globals: none
|
|
*
|
|
* @return New shape list with a rendering of one proto added.
|
|
* @note Exceptions: none
|
|
* @note History: Thu Mar 21 10:21:09 1991, DSJ, Created.
|
|
*/
|
|
void RenderIntProto(ScrollView *window,
|
|
INT_CLASS Class,
|
|
PROTO_ID ProtoId,
|
|
ScrollView::Color color) {
|
|
PROTO_SET ProtoSet;
|
|
INT_PROTO Proto;
|
|
int ProtoSetIndex;
|
|
int ProtoWordIndex;
|
|
FLOAT32 Length;
|
|
int Xmin, Xmax, Ymin, Ymax;
|
|
FLOAT32 X, Y, Dx, Dy;
|
|
uinT32 ProtoMask;
|
|
int Bucket;
|
|
|
|
assert(ProtoId >= 0);
|
|
assert(Class != NULL);
|
|
assert(ProtoId < Class->NumProtos);
|
|
assert(color != 0);
|
|
window->Pen(color);
|
|
|
|
ProtoSet = Class->ProtoSets[SetForProto(ProtoId)];
|
|
ProtoSetIndex = IndexForProto(ProtoId);
|
|
Proto = &(ProtoSet->Protos[ProtoSetIndex]);
|
|
Length = (Class->ProtoLengths[ProtoId] *
|
|
GetPicoFeatureLength() * INT_CHAR_NORM_RANGE);
|
|
ProtoMask = PPrunerMaskFor(ProtoId);
|
|
ProtoWordIndex = PPrunerWordIndexFor(ProtoId);
|
|
|
|
// find the x and y extent of the proto from the proto pruning table
|
|
Xmin = Ymin = NUM_PP_BUCKETS;
|
|
Xmax = Ymax = 0;
|
|
for (Bucket = 0; Bucket < NUM_PP_BUCKETS; Bucket++) {
|
|
if (ProtoMask & ProtoSet->ProtoPruner[PRUNER_X][Bucket][ProtoWordIndex]) {
|
|
UpdateRange(Bucket, &Xmin, &Xmax);
|
|
}
|
|
|
|
if (ProtoMask & ProtoSet->ProtoPruner[PRUNER_Y][Bucket][ProtoWordIndex]) {
|
|
UpdateRange(Bucket, &Ymin, &Ymax);
|
|
}
|
|
}
|
|
X = (Xmin + Xmax + 1) / 2.0 * PROTO_PRUNER_SCALE;
|
|
Y = (Ymin + Ymax + 1) / 2.0 * PROTO_PRUNER_SCALE;
|
|
// The -PI has no significant effect here, but the value of Theta is computed
|
|
// using BinaryAnglePlusPi in intfx.cpp.
|
|
Dx = (Length / 2.0) * cos((Proto->Angle / 256.0) * 2.0 * PI - PI);
|
|
Dy = (Length / 2.0) * sin((Proto->Angle / 256.0) * 2.0 * PI - PI);
|
|
|
|
window->SetCursor(X - Dx, Y - Dy);
|
|
window->DrawTo(X + Dx, Y + Dy);
|
|
} /* RenderIntProto */
|
|
#endif
|
|
|
|
/**
|
|
* This routine truncates Param to lie within the range
|
|
* of Min-Max inclusive. If a truncation is performed, and
|
|
* Id is not null, an warning message is printed.
|
|
*
|
|
* @param Param parameter value to be truncated
|
|
* @param Min, Max parameter limits (inclusive)
|
|
* @param Id string id of parameter for error messages
|
|
*
|
|
* Globals: none
|
|
*
|
|
* @return Truncated parameter.
|
|
* @note Exceptions: none
|
|
* @note History: Fri Feb 8 11:54:28 1991, DSJ, Created.
|
|
*/
|
|
int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id) {
|
|
if (Param < Min) {
|
|
if (Id)
|
|
cprintf("Warning: Param %s truncated from %f to %d!\n",
|
|
Id, Param, Min);
|
|
Param = Min;
|
|
} else if (Param > Max) {
|
|
if (Id)
|
|
cprintf("Warning: Param %s truncated from %f to %d!\n",
|
|
Id, Param, Max);
|
|
Param = Max;
|
|
}
|
|
return static_cast<int>(floor(Param));
|
|
} /* TruncateParam */
|
|
|
|
|
|
#ifndef GRAPHICS_DISABLED
|
|
/**
|
|
* Initializes the int matcher window if it is not already
|
|
* initialized.
|
|
*/
|
|
void InitIntMatchWindowIfReqd() {
|
|
if (IntMatchWindow == NULL) {
|
|
IntMatchWindow = CreateFeatureSpaceWindow("IntMatchWindow", 50, 200);
|
|
SVMenuNode* popup_menu = new SVMenuNode();
|
|
|
|
popup_menu->AddChild("Debug Adapted classes", IDA_ADAPTIVE,
|
|
"x", "Class to debug");
|
|
popup_menu->AddChild("Debug Static classes", IDA_STATIC,
|
|
"x", "Class to debug");
|
|
popup_menu->AddChild("Debug Both", IDA_BOTH,
|
|
"x", "Class to debug");
|
|
popup_menu->AddChild("Debug Shape Index", IDA_SHAPE_INDEX,
|
|
"0", "Index to debug");
|
|
popup_menu->BuildMenu(IntMatchWindow, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initializes the proto display window if it is not already
|
|
* initialized.
|
|
*/
|
|
void InitProtoDisplayWindowIfReqd() {
|
|
if (ProtoDisplayWindow == NULL) {
|
|
ProtoDisplayWindow = CreateFeatureSpaceWindow("ProtoDisplayWindow",
|
|
550, 200);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initializes the feature display window if it is not already
|
|
* initialized.
|
|
*/
|
|
void InitFeatureDisplayWindowIfReqd() {
|
|
if (FeatureDisplayWindow == NULL) {
|
|
FeatureDisplayWindow = CreateFeatureSpaceWindow("FeatureDisplayWindow",
|
|
50, 700);
|
|
}
|
|
}
|
|
|
|
/// Creates a window of the appropriate size for displaying elements
|
|
/// in feature space.
|
|
ScrollView* CreateFeatureSpaceWindow(const char* name, int xpos, int ypos) {
|
|
return new ScrollView(name, xpos, ypos, 520, 520, 260, 260, true);
|
|
}
|
|
#endif // GRAPHICS_DISABLED
|