GSaha567/seq_level_training_data
052
1text,length,is_long_context,metric_val,label_metric2"/******************************************************************************3 ** Filename: intproto.c4 ** Purpose: Definition of data structures for integer protos.5 ** Author: Dan Johnson6 **7 ** (c) Copyright Hewlett-Packard Company, 1988.8 ** Licensed under the Apache License, Version 2.0 (the ""License"");9 ** you may not use this file except in compliance with the License.10 ** You may obtain a copy of the License at11 ** http://www.apache.org/licenses/LICENSE-2.012 ** Unless required by applicable law or agreed to in writing, software13 ** distributed under the License is distributed on an ""AS IS"" BASIS,14 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.15 ** See the License for the specific language governing permissions and16 ** limitations under the License.17 ******************************************************************************/18/*-----------------------------------------------------------------------------19 Include Files and Type Defines20-----------------------------------------------------------------------------*/21 22#define _USE_MATH_DEFINES // for M_PI23 24// Include automatically generated configuration file if running autoconf.25#ifdef HAVE_CONFIG_H26# include ""config_auto.h""27#endif28 29#include ""intproto.h""30 31#include ""classify.h""32#include ""fontinfo.h""33#include ""mfoutline.h""34#include ""picofeat.h""35#include ""points.h""36#include ""shapetable.h""37#ifndef GRAPHICS_DISABLED38#include ""svmnode.h""39#endif40 41#include ""helpers.h""42 43#include <algorithm>44#include <cassert>45#include <cmath> // for M_PI, std::floor46#include <cstdio>47 48namespace tesseract {49 50/* match debug display constants*/51#define PROTO_PRUNER_SCALE (4.0)52 53#define INT_DESCENDER (0.0 * INT_CHAR_NORM_RANGE)54#define INT_BASELINE (0.25 * INT_CHAR_NORM_RANGE)55#define INT_XHEIGHT (0.75 * INT_CHAR_NORM_RANGE)56#define INT_CAPHEIGHT (1.0 * INT_CHAR_NORM_RANGE)57 58#define INT_XCENTER (0.5 * INT_CHAR_NORM_RANGE)59#define INT_YCENTER (0.5 * INT_CHAR_NORM_RANGE)60#define INT_XRADIUS (0.2 * INT_CHAR_NORM_RANGE)61#define INT_YRADIUS (0.2 * INT_CHAR_NORM_RANGE)62#define INT_MIN_X 063#define INT_MIN_Y 064#define INT_MAX_X INT_CHAR_NORM_RANGE65#define INT_MAX_Y INT_CHAR_NORM_RANGE66 67/** define pad used to snap near horiz/vertical protos to horiz/vertical */68#define HV_TOLERANCE (0.0025) /* approx 0.9 degrees */69 70typedef enum { StartSwitch, EndSwitch, LastSwitch } SWITCH_TYPE;71#define MAX_NUM_SWITCHES 372 73struct FILL_SWITCH {74 SWITCH_TYPE Type;75 int8_t X, Y;76 int16_t YInit;77 int16_t Delta;78};79 80struct TABLE_FILLER {81 uint8_t NextSwitch;82 uint8_t AngleStart, AngleEnd;83 int8_t X;84 int16_t YStart, YEnd;85 int16_t StartDelta, EndDelta;86 FILL_SWITCH Switch[MAX_NUM_SWITCHES];87};88 89struct FILL_SPEC {90 int8_t X;91 int8_t YStart, YEnd;92 uint8_t AngleStart, AngleEnd;93};94 95/* constants for conversion from old inttemp format */96#define OLD_MAX_NUM_CONFIGS 3297#define OLD_WERDS_PER_CONFIG_VEC ((OLD_MAX_NUM_CONFIGS + BITS_PER_WERD - 1) / BITS_PER_WERD)98 99/*-----------------------------------------------------------------------------100 Macros101-----------------------------------------------------------------------------*/102/** macro for performing circular increments of bucket indices */103#define CircularIncrement(i, r) (((i) < (r)-1) ? ((i)++) : ((i) = 0))104 105/** macro for mapping floats to ints without bounds checking */106#define MapParam(P, O, N) (std::floor(((P) + (O)) * (N)))107 108/*---------------------------------------------------------------------------109 Private Function Prototypes110----------------------------------------------------------------------------*/111float BucketStart(int Bucket, float Offset, int NumBuckets);112 113float BucketEnd(int Bucket, float Offset, int NumBuckets);114 115void DoFill(FILL_SPEC *FillSpec, CLASS_PRUNER_STRUCT *Pruner, uint32_t ClassMask,116 uint32_t ClassCount, uint32_t WordIndex);117 118bool FillerDone(TABLE_FILLER *Filler);119 120void FillPPCircularBits(uint32_t ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], int Bit,121 float Center, float Spread, bool debug);122 123void FillPPLinearBits(uint32_t ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], int Bit,124 float Center, float Spread, bool debug);125 126void GetCPPadsForLevel(int Level, float *EndPad, float *SidePad, float *AnglePad);127 128ScrollView::Color GetMatchColorFor(float Evidence);129 130void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill);131 132void InitTableFiller(float EndPad, float SidePad, float AnglePad, PROTO_STRUCT *Proto,133 TABLE_FILLER *Filler);134 135#ifndef GRAPHICS_DISABLED136void RenderIntFeature(ScrollView *window, const INT_FEATURE_STRUCT *Feature,137 ScrollView::Color color);138 139void RenderIntProto(ScrollView *window, INT_CLASS_STRUCT *Class, PROTO_ID ProtoId, ScrollView::Color color);140#endif // !GRAPHICS_DISABLED141 142/*-----------------------------------------------------------------------------143 Global Data Definitions and Declarations144-----------------------------------------------------------------------------*/145 146#ifndef GRAPHICS_DISABLED147/* global display lists used to display proto and feature match information*/148static ScrollView *IntMatchWindow = nullptr;149static ScrollView *FeatureDisplayWindow = nullptr;150static ScrollView *ProtoDisplayWindow = nullptr;151#endif152 153/*-----------------------------------------------------------------------------154 Variables155-----------------------------------------------------------------------------*/156 157/* control knobs */158static INT_VAR(classify_num_cp_levels, 3, ""Number of Class Pruner Levels"");159static double_VAR(classify_cp_angle_pad_loose, 45.0, ""Class Pruner Angle Pad Loose"");160static double_VAR(classify_cp_angle_pad_medium, 20.0, ""Class Pruner Angle Pad Medium"");161static double_VAR(classify_cp_angle_pad_tight, 10.0, ""CLass Pruner Angle Pad Tight"");162static double_VAR(classify_cp_end_pad_loose, 0.5, ""Class Pruner End Pad Loose"");163static double_VAR(classify_cp_end_pad_medium, 0.5, ""Class Pruner End Pad Medium"");164static double_VAR(classify_cp_end_pad_tight, 0.5, ""Class Pruner End Pad Tight"");165static double_VAR(classify_cp_side_pad_loose, 2.5, ""Class Pruner Side Pad Loose"");166static double_VAR(classify_cp_side_pad_medium, 1.2, ""Class Pruner Side Pad Medium"");167static double_VAR(classify_cp_side_pad_tight, 0.6, ""Class Pruner Side Pad Tight"");168static double_VAR(classify_pp_angle_pad, 45.0, ""Proto Pruner Angle Pad"");169static double_VAR(classify_pp_end_pad, 0.5, ""Proto Prune End Pad"");170static double_VAR(classify_pp_side_pad, 2.5, ""Proto Pruner Side Pad"");171 172/**173 * This routine truncates Param to lie within the range174 * of Min-Max inclusive.175 *176 * @param Param parameter value to be truncated177 * @param Min, Max parameter limits (inclusive)178 *179 * @return Truncated parameter.180 */181static int TruncateParam(float Param, int Min, int Max) {182 int result;183 if (Param < Min) {184 result = Min;185 } else if (Param > Max) {186 result = Max;187 } else {188 result = static_cast<int>(std::floor(Param));189 }190 return result;191}192 193/*-----------------------------------------------------------------------------194 Public Code195-----------------------------------------------------------------------------*/196/// Builds a feature from an FCOORD for position with all the necessary197/// clipping and rounding.198INT_FEATURE_STRUCT::INT_FEATURE_STRUCT(const FCOORD &pos, uint8_t theta)199 : X(ClipToRange<int16_t>(static_cast<int16_t>(pos.x() + 0.5), 0, 255))200 , Y(ClipToRange<int16_t>(static_cast<int16_t>(pos.y() + 0.5), 0, 255))201 , Theta(theta)202 , CP_misses(0) {}203/** Builds a feature from ints with all the necessary clipping and casting. */204INT_FEATURE_STRUCT::INT_FEATURE_STRUCT(int x, int y, int theta)205 : X(static_cast<uint8_t>(ClipToRange<int>(x, 0, UINT8_MAX)))206 , Y(static_cast<uint8_t>(ClipToRange<int>(y, 0, UINT8_MAX)))207 , Theta(static_cast<uint8_t>(ClipToRange<int>(theta, 0, UINT8_MAX)))208 , CP_misses(0) {}209 210/**211 * This routine adds a new class structure to a set of212 * templates. Classes have to be added to Templates in213 * the order of increasing ClassIds.214 *215 * @param Templates templates to add new class to216 * @param ClassId class id to associate new class with217 * @param Class class data structure to add to templates218 *219 * Globals: none220 */221void AddIntClass(INT_TEMPLATES_STRUCT *Templates, CLASS_ID ClassId, INT_CLASS_STRUCT *Class) {222 int Pruner;223 224 assert(LegalClassId(ClassId));225 if (static_cast<unsigned>(ClassId) != Templates->NumClasses) {226 fprintf(stderr,227 ""Please make sure that classes are added to templates""228 "" in increasing order of ClassIds\\n"");229 exit(1);230 }231 ClassForClassId(Templates, ClassId) = Class;232 Templates->NumClasses++;233 234 if (Templates->NumClasses > MaxNumClassesIn(Templates)) {235 Pruner = Templates->NumClassPruners++;236 Templates->ClassPruners[Pruner] = new CLASS_PRUNER_STRUCT;237 memset(Templates->ClassPruners[Pruner], 0, sizeof(CLASS_PRUNER_STRUCT));238 }239} /* AddIntClass */240 241/**242 * This routine returns the index of the next free config243 * in Class.244 *245 * @param Class class to add new configuration to246 *247 * Globals: none248 *249 * @return Index of next free config.250 */251int AddIntConfig(INT_CLASS_STRUCT *Class) {252 int Index;253 254 assert(Class->NumConfigs < MAX_NUM_CONFIGS);255 256 Index = Class->NumConfigs++;257 Class->ConfigLengths[Index] = 0;258 return Index;259} /* AddIntConfig */260 261/**262 * This routine allocates the next free proto in Class and263 * returns its index.264 *265 * @param Class class to add new proto to266 *267 * Globals: none268 *269 * @return Proto index of new proto.270 */271int AddIntProto(INT_CLASS_STRUCT *Class) {272 if (Class->NumProtos >= MAX_NUM_PROTOS) {273 return (NO_PROTO);274 }275 276 int Index = Class->NumProtos++;277 278 if (Class->NumProtos > MaxNumIntProtosIn(Class)) {279 int ProtoSetId = Class->NumProtoSets++;280 auto ProtoSet = new PROTO_SET_STRUCT;281 Class->ProtoSets[ProtoSetId] = ProtoSet;282 memset(ProtoSet, 0, sizeof(*ProtoSet));283 284 /* reallocate space for the proto lengths and install in class */285 Class->ProtoLengths.resize(MaxNumIntProtosIn(Class));286 }287 288 /* initialize proto so its length is zero and it isn't in any configs */289 Class->ProtoLengths[Index] = 0;290 auto Proto = ProtoForProtoId(Class, Index);291 for (uint32_t *Word = Proto->Configs; Word < Proto->Configs + WERDS_PER_CONFIG_VEC; *Word++ = 0) {292 }293 294 return (Index);295}296 297/**298 * This routine adds Proto to the class pruning tables299 * for the specified class in Templates.300 *301 * Globals:302 * - classify_num_cp_levels number of levels used in the class pruner303 * @param Proto floating-pt proto to add to class pruner304 * @param ClassId class id corresponding to Proto305 * @param Templates set of templates containing class pruner306 */307void AddProtoToClassPruner(PROTO_STRUCT *Proto, CLASS_ID ClassId, INT_TEMPLATES_STRUCT *Templates)308#define MAX_LEVEL 2309{310 CLASS_PRUNER_STRUCT *Pruner;311 uint32_t ClassMask;312 uint32_t ClassCount;313 uint32_t WordIndex;314 int Level;315 float EndPad, SidePad, AnglePad;316 TABLE_FILLER TableFiller;317 FILL_SPEC FillSpec;318 319 Pruner = CPrunerFor(Templates, ClassId);320 WordIndex = CPrunerWordIndexFor(ClassId);321 ClassMask = CPrunerMaskFor(MAX_LEVEL, ClassId);322 323 for (Level = classify_num_cp_levels - 1; Level >= 0; Level--) {324 GetCPPadsForLevel(Level, &EndPad, &SidePad, &AnglePad);325 ClassCount = CPrunerMaskFor(Level, ClassId);326 InitTableFiller(EndPad, SidePad, AnglePad, Proto, &TableFiller);327 328 while (!FillerDone(&TableFiller)) {329 GetNextFill(&TableFiller, &FillSpec);330 DoFill(&FillSpec, Pruner, ClassMask, ClassCount, WordIndex);331 }332 }333} /* AddProtoToClassPruner */334 335/**336 * This routine updates the proto pruner lookup tables337 * for Class to include a new proto identified by ProtoId338 * and described by Proto.339 * @param Proto floating-pt proto to be added to proto pruner340 * @param ProtoId id of proto341 * @param Class integer class that contains desired proto pruner342 * @param debug debug flag343 * @note Globals: none344 */345void AddProtoToProtoPruner(PROTO_STRUCT *Proto, int ProtoId, INT_CLASS_STRUCT *Class, bool debug) {346 float X, Y, Length;347 float Pad;348 349 if (ProtoId >= Class->NumProtos) {350 tprintf(""AddProtoToProtoPruner:assert failed: %d < %d"", ProtoId, Class->NumProtos);351 }352 assert(ProtoId < Class->NumProtos);353 354 int Index = IndexForProto(ProtoId);355 auto ProtoSet = Class->ProtoSets[SetForProto(ProtoId)];356 357 float Angle = Proto->Angle;358#ifndef _WIN32359 assert(!std::isnan(Angle));360#endif361 362 FillPPCircularBits(ProtoSet->ProtoPruner[PRUNER_ANGLE], Index, Angle + ANGLE_SHIFT,363 classify_pp_angle_pad / 360.0, debug);364 365 Angle *= 2.0 * M_PI;366 Length = Proto->Length;367 368 X = Proto->X + X_SHIFT;369 Pad = std::max(fabs(std::cos(Angle)) * (Length / 2.0 + classify_pp_end_pad * GetPicoFeatureLength()),370 fabs(std::sin(Angle)) * (classify_pp_side_pad * GetPicoFeatureLength()));371 372 FillPPLinearBits(ProtoSet->ProtoPruner[PRUNER_X], Index, X, Pad, debug);373 374 Y = Proto->Y + Y_SHIFT;375 Pad = std::max(fabs(std::sin(Angle)) * (Length / 2.0 + classify_pp_end_pad * GetPicoFeatureLength()),376 fabs(std::cos(Angle)) * (classify_pp_side_pad * GetPicoFeatureLength()));377 378 FillPPLinearBits(ProtoSet->ProtoPruner[PRUNER_Y], Index, Y, Pad, debug);379} /* AddProtoToProtoPruner */380 381/**382 * Returns a quantized bucket for the given param shifted by offset,383 * notionally (param + offset) * num_buckets, but clipped and casted to the384 * appropriate type.385 */386uint8_t Bucket8For(float param, float offset, int num_buckets) {387 int bucket = IntCastRounded(MapParam(param, offset, num_buckets));388 return static_cast<uint8_t>(ClipToRange<int>(bucket, 0, num_buckets - 1));389}390uint16_t Bucket16For(float param, float offset, int num_buckets) {391 int bucket = IntCastRounded(MapParam(param, offset, num_buckets));392 return static_cast<uint16_t>(ClipToRange<int>(bucket, 0, num_buckets - 1));393}394 395/**396 * Returns a quantized bucket for the given circular param shifted by offset,397 * notionally (param + offset) * num_buckets, but modded and casted to the398 * appropriate type.399 */400uint8_t CircBucketFor(float param, float offset, int num_buckets) {401 int bucket = IntCastRounded(MapParam(param, offset, num_buckets));402 return static_cast<uint8_t>(Modulo(bucket, num_buckets));403} /* CircBucketFor */404 405#ifndef GRAPHICS_DISABLED406/**407 * This routine clears the global feature and proto408 * display lists.409 *410 * Globals:411 * - FeatureShapes display list for features412 * - ProtoShapes display list for protos413 */414void UpdateMatchDisplay() {415 if (IntMatchWindow != nullptr) {416 IntMatchWindow->Update();417 }418} /* ClearMatchDisplay */419#endif420 421/**422 * This operation updates the config vectors of all protos423 * in Class to indicate that the protos with 1's in Config424 * belong to a new configuration identified by ConfigId.425 * It is assumed that the length of the Config bit vector is426 * equal to the number of protos in Class.427 * @param Config config to be added to class428 * @param ConfigId id to be used for new config429 * @param Class class to add new config to430 */431void ConvertConfig(BIT_VECTOR Config, int ConfigId, INT_CLASS_STRUCT *Class) {432 int ProtoId;433 INT_PROTO_STRUCT *Proto;434 int TotalLength;435 436 for (ProtoId = 0, TotalLength = 0; ProtoId < Class->NumProtos; ProtoId++) {437 if (test_bit(Config, ProtoId)) {438 Proto = ProtoForProtoId(Class, ProtoId);439 SET_BIT(Proto->Configs, ConfigId);440 TotalLength += Class->ProtoLengths[ProtoId];441 }442 }443 Class->ConfigLengths[ConfigId] = TotalLength;444} /* ConvertConfig */445 446/**447 * This routine converts Proto to integer format and448 * installs it as ProtoId in Class.449 * @param Proto floating-pt proto to be converted to integer format450 * @param ProtoId id of proto451 * @param Class integer class to add converted proto to452 */453void Classify::ConvertProto(PROTO_STRUCT *Proto, int ProtoId, INT_CLASS_STRUCT *Class) {454 assert(ProtoId < Class->NumProtos);455 456 INT_PROTO_STRUCT *P = ProtoForProtoId(Class, ProtoId);457 458 float Param = Proto->A * 128;459 P->A = TruncateParam(Param, -128, 127);460 461 Param = -Proto->B * 256;462 P->B = TruncateParam(Param, 0, 255);463 464 Param = Proto->C * 128;465 P->C = TruncateParam(Param, -128, 127);466 467 Param = Proto->Angle * 256;468 if (Param < 0 || Param >= 256) {469 P->Angle = 0;470 } else {471 P->Angle = static_cast<uint8_t>(Param);472 }473 474 /* round proto length to nearest integer number of pico-features */475 Param = (Proto->Length / GetPicoFeatureLength()) + 0.5;476 Class->ProtoLengths[ProtoId] = TruncateParam(Param, 1, 255);477 if (classify_learning_debug_level >= 2) {478 tprintf(""Converted ffeat to (A=%d,B=%d,C=%d,L=%d)"", P->A, P->B, P->C,479 Class->ProtoLengths[ProtoId]);480 }481} /* ConvertProto */482 483/**484 * This routine converts from the old floating point format485 * to the new integer format.486 * @param FloatProtos prototypes in old floating pt format487 * @param target_unicharset the UNICHARSET to use488 * @return New set of training templates in integer format.489 * @note Globals: none490 */491INT_TEMPLATES_STRUCT *Classify::CreateIntTemplates(CLASSES FloatProtos,492 const UNICHARSET &target_unicharset) {493 CLASS_TYPE FClass;494 INT_CLASS_STRUCT *IClass;495 int ProtoId;496 int ConfigId;497 498 auto IntTemplates = new INT_TEMPLATES_STRUCT;499 500 for (unsigned ClassId = 0; ClassId < target_unicharset.size(); ClassId++) {501 FClass = &(FloatProtos[ClassId]);502 if (FClass->NumProtos == 0 && FClass->NumConfigs == 0 &&503 strcmp(target_unicharset.id_to_unichar(ClassId), "" "") != 0) {504 tprintf(""Warning: no protos/configs for %s in CreateIntTemplates()\\n"",505 target_unicharset.id_to_unichar(ClassId));506 }507 assert(UnusedClassIdIn(IntTemplates, ClassId));508 IClass = new INT_CLASS_STRUCT(FClass->NumProtos, FClass->NumConfigs);509 FontSet fs{FClass->font_set.size()};510 for (unsigned i = 0; i < fs.size(); ++i) {511 fs[i] = FClass->font_set.at(i);512 }513 if (this->fontset_table_.contains(fs)) {514 IClass->font_set_id = this->fontset_table_.get_index(fs);515 } else {516 IClass->font_set_id = this->fontset_table_.push_back(fs);517 }518 AddIntClass(IntTemplates, ClassId, IClass);519 520 for (ProtoId = 0; ProtoId < FClass->NumProtos; ProtoId++) {521 AddIntProto(IClass);522 ConvertProto(ProtoIn(FClass, ProtoId), ProtoId, IClass);523 AddProtoToProtoPruner(ProtoIn(FClass, ProtoId), ProtoId, IClass,524 classify_learning_debug_level >= 2);525 AddProtoToClassPruner(ProtoIn(FClass, ProtoId), ClassId, IntTemplates);526 }527 528 for (ConfigId = 0; ConfigId < FClass->NumConfigs; ConfigId++) {529 AddIntConfig(IClass);530 ConvertConfig(FClass->Configurations[ConfigId], ConfigId, IClass);531 }532 }533 return (IntTemplates);534} /* CreateIntTemplates */535 536#ifndef GRAPHICS_DISABLED537/**538 * This routine renders the specified feature into a539 * global display list.540 *541 * Globals:542 * - FeatureShapes global display list for features543 * @param Feature pico-feature to be displayed544 * @param Evidence best evidence for this feature (0-1)545 */546void DisplayIntFeature(const INT_FEATURE_STRUCT *Feature, float Evidence) {547 ScrollView::Color color = GetMatchColorFor(Evidence);548 RenderIntFeature(IntMatchWindow, Feature, color);549 if (FeatureDisplayWindow) {550 RenderIntFeature(FeatureDisplayWindow, Feature, color);551 }552} /* DisplayIntFeature */553 554/**555 * This routine renders the specified proto into a556 * global display list.557 *558 * Globals:559 * - ProtoShapes global display list for protos560 * @param Class class to take proto from561 * @param ProtoId id of proto in Class to be displayed562 * @param Evidence total evidence for proto (0-1)563 */564void DisplayIntProto(INT_CLASS_STRUCT *Class, PROTO_ID ProtoId, float Evidence) {565 ScrollView::Color color = GetMatchColorFor(Evidence);566 RenderIntProto(IntMatchWindow, Class, ProtoId, color);567 if (ProtoDisplayWindow) {568 RenderIntProto(ProtoDisplayWindow, Class, ProtoId, color);569 }570} /* DisplayIntProto */571#endif572 573/// This constructor creates a new integer class data structure574/// and returns it. Sufficient space is allocated575/// to handle the specified number of protos and configs.576/// @param MaxNumProtos number of protos to allocate space for577/// @param MaxNumConfigs number of configs to allocate space for578INT_CLASS_STRUCT::INT_CLASS_STRUCT(int MaxNumProtos, int MaxNumConfigs) :579 NumProtos(0),580 NumProtoSets((MaxNumProtos + PROTOS_PER_PROTO_SET - 1) / PROTOS_PER_PROTO_SET),581 NumConfigs(0),582 ProtoLengths(MaxNumIntProtosIn(this))583{584 assert(MaxNumConfigs <= MAX_NUM_CONFIGS);585 assert(NumProtoSets <= MAX_NUM_PROTO_SETS);586 587 for (int i = 0; i < NumProtoSets; i++) {588 /* allocate space for a proto set, install in class, and initialize */589 auto ProtoSet = new PROTO_SET_STRUCT;590 memset(ProtoSet, 0, sizeof(*ProtoSet));591 ProtoSets[i] = ProtoSet;592 593 /* allocate space for the proto lengths and install in class */594 }595 memset(ConfigLengths, 0, sizeof(ConfigLengths));596}597 598INT_CLASS_STRUCT::~INT_CLASS_STRUCT() {599 for (int i = 0; i < NumProtoSets; i++) {600 delete ProtoSets[i];601 }602}603 604/// This constructor allocates a new set of integer templates605/// initialized to hold 0 classes.606INT_TEMPLATES_STRUCT::INT_TEMPLATES_STRUCT() {607 NumClasses = 0;608 NumClassPruners = 0;609 610 for (int i = 0; i < MAX_NUM_CLASSES; i++) {611 ClassForClassId(this, i) = nullptr;612 }613}614 615INT_TEMPLATES_STRUCT::~INT_TEMPLATES_STRUCT() {616 for (unsigned i = 0; i < NumClasses; i++) {617 delete Class[i];618 }619 for (unsigned i = 0; i < NumClassPruners; i++) {620 delete ClassPruners[i];621 }622}623 624/**625 * This routine reads a set of integer templates from626 * File. File must already be open and must be in the627 * correct binary format.628 * @param fp open file to read templates from629 * @return Pointer to integer templates read from File.630 * @note Globals: none631 */632INT_TEMPLATES_STRUCT *Classify::ReadIntTemplates(TFile *fp) {633 int j, w, x, y, z;634 INT_TEMPLATES_STRUCT *Templates;635 CLASS_PRUNER_STRUCT *Pruner;636 INT_CLASS_STRUCT *Class;637 638 /* variables for conversion from older inttemp formats */639 int b, bit_number, last_cp_bit_number, new_b, new_i, new_w;640 CLASS_ID class_id, max_class_id;641 std::vector<CLASS_ID> ClassIdFor(MAX_NUM_CLASSES);642 std::vector<CLASS_PRUNER_STRUCT *> TempClassPruner(MAX_NUM_CLASS_PRUNERS);643 uint32_t SetBitsForMask = // word with NUM_BITS_PER_CLASS644 (1 << NUM_BITS_PER_CLASS) - 1; // set starting at bit 0645 uint32_t Mask, NewMask, ClassBits;646 unsigned MaxNumConfigs = MAX_NUM_CONFIGS;647 unsigned WerdsPerConfigVec = WERDS_PER_CONFIG_VEC;648 649 /* first read the high level template struct */650 Templates = new INT_TEMPLATES_STRUCT;651 // Read Templates in parts for 64 bit compatibility.652 uint32_t unicharset_size;653 if (fp->FReadEndian(&unicharset_size, sizeof(unicharset_size), 1) != 1) {654 tprintf(""Bad read of inttemp!\\n"");655 }656 int32_t version_id = 0;657 if (fp->FReadEndian(&version_id, sizeof(version_id), 1) != 1 ||658 fp->FReadEndian(&Templates->NumClassPruners, sizeof(Templates->NumClassPruners), 1) != 1) {659 tprintf(""Bad read of inttemp!\\n"");660 }661 if (version_id < 0) {662 // This file has a version id!663 version_id = -version_id;664 if (fp->FReadEndian(&Templates->NumClasses, sizeof(Templates->NumClasses), 1) != 1) {665 tprintf(""Bad read of inttemp!\\n"");666 }667 } else {668 Templates->NumClasses = version_id;669 }670 671 if (version_id < 3) {672 MaxNumConfigs = OLD_MAX_NUM_CONFIGS;673 WerdsPerConfigVec = OLD_WERDS_PER_CONFIG_VEC;674 }675 676 if (version_id < 2) {677 std::vector<int16_t> IndexFor(MAX_NUM_CLASSES);678 if (fp->FReadEndian(&IndexFor[0], sizeof(IndexFor[0]), unicharset_size) != unicharset_size) {679 tprintf(""Bad read of inttemp!\\n"");680 }681 if (fp->FReadEndian(&ClassIdFor[0], sizeof(ClassIdFor[0]), Templates->NumClasses) !=682 Templates->NumClasses) {683 tprintf(""Bad read of inttemp!\\n"");684 }685 }686 687 /* then read in the class pruners */688 const unsigned kNumBuckets = NUM_CP_BUCKETS * NUM_CP_BUCKETS * NUM_CP_BUCKETS * WERDS_PER_CP_VECTOR;689 for (unsigned i = 0; i < Templates->NumClassPruners; i++) {690 Pruner = new CLASS_PRUNER_STRUCT;691 if (fp->FReadEndian(Pruner, sizeof(Pruner->p[0][0][0][0]), kNumBuckets) != kNumBuckets) {692 tprintf(""Bad read of inttemp!\\n"");693 }694 if (version_id < 2) {695 TempClassPruner[i] = Pruner;696 } else {697 Templates->ClassPruners[i] = Pruner;698 }699 }700 701 /* fix class pruners if they came from an old version of inttemp */702 if (version_id < 2) {703 // Allocate enough class pruners to cover all the class ids.704 max_class_id = 0;705 for (unsigned i = 0; i < Templates->NumClasses; i++) {706 if (ClassIdFor[i] > max_class_id) {707 max_class_id = ClassIdFor[i];708 }709 }710 for (int i = 0; i <= CPrunerIdFor(max_class_id); i++) {711 Templates->ClassPruners[i] = new CLASS_PRUNER_STRUCT;712 memset(Templates->ClassPruners[i], 0, sizeof(CLASS_PRUNER_STRUCT));713 }714 // Convert class pruners from the old format (indexed by class index)715 // to the new format (indexed by class id).716 last_cp_bit_number = NUM_BITS_PER_CLASS * Templates->NumClasses - 1;717 for (unsigned i = 0; i < Templates->NumClassPruners; i++) {718 for (x = 0; x < NUM_CP_BUCKETS; x++) {719 for (y = 0; y < NUM_CP_BUCKETS; y++) {720 for (z = 0; z < NUM_CP_BUCKETS; z++) {721 for (w = 0; w < WERDS_PER_CP_VECTOR; w++) {722 if (TempClassPruner[i]->p[x][y][z][w] == 0) {723 continue;724 }725 for (b = 0; b < BITS_PER_WERD; b += NUM_BITS_PER_CLASS) {726 bit_number = i * BITS_PER_CP_VECTOR + w * BITS_PER_WERD + b;727 if (bit_number > last_cp_bit_number) {728 break; // the rest of the bits in this word are not used729 }730 class_id = ClassIdFor[bit_number / NUM_BITS_PER_CLASS];731 // Single out NUM_BITS_PER_CLASS bits relating to class_id.732 Mask = SetBitsForMask << b;733 ClassBits = TempClassPruner[i]->p[x][y][z][w] & Mask;734 // Move these bits to the new position in which they should735 // appear (indexed corresponding to the class_id).736 new_i = CPrunerIdFor(class_id);737 new_w = CPrunerWordIndexFor(class_id);738 new_b = CPrunerBitIndexFor(class_id) * NUM_BITS_PER_CLASS;739 if (new_b > b) {740 ClassBits <<= (new_b - b);741 } else {742 ClassBits >>= (b - new_b);743 }744 // Copy bits relating to class_id to the correct position745 // in Templates->ClassPruner.746 NewMask = SetBitsForMask << new_b;747 Templates->ClassPruners[new_i]->p[x][y][z][new_w] &= ~NewMask;748 Templates->ClassPruners[new_i]->p[x][y][z][new_w] |= ClassBits;749 }750 }751 }752 }753 }754 }755 for (unsigned i = 0; i < Templates->NumClassPruners; i++) {756 delete TempClassPruner[i];757 }758 }759 760 /* then read in each class */761 for (unsigned i = 0; i < Templates->NumClasses; i++) {762 /* first read in the high level struct for the class */763 Class = new INT_CLASS_STRUCT;764 if (fp->FReadEndian(&Class->NumProtos, sizeof(Class->NumProtos), 1) != 1 ||765 fp->FRead(&Class->NumProtoSets, sizeof(Class->NumProtoSets), 1) != 1 ||766 fp->FRead(&Class->NumConfigs, sizeof(Class->NumConfigs), 1) != 1) {767 tprintf(""Bad read of inttemp!\\n"");768 }769 if (version_id == 0) {770 // Only version 0 writes 5 pointless pointers to the file.771 for (j = 0; j < 5; ++j) {772 int32_t junk;773 if (fp->FRead(&junk, sizeof(junk), 1) != 1) {774 tprintf(""Bad read of inttemp!\\n"");775 }776 }777 }778 unsigned num_configs = version_id < 4 ? MaxNumConfigs : Class->NumConfigs;779 ASSERT_HOST(num_configs <= MaxNumConfigs);780 if (fp->FReadEndian(Class->ConfigLengths, sizeof(uint16_t), num_configs) != num_configs) {781 tprintf(""Bad read of inttemp!\\n"");782 }783 if (version_id < 2) {784 ClassForClassId(Templates, ClassIdFor[i]) = Class;785 } else {786 ClassForClassId(Templates, i) = Class;787 }788 789 /* then read in the proto lengths */790 Class->ProtoLengths.clear();791 if (MaxNumIntProtosIn(Class) > 0) {792 Class->ProtoLengths.resize(MaxNumIntProtosIn(Class));793 if (fp->FRead(&Class->ProtoLengths[0], sizeof(uint8_t), MaxNumIntProtosIn(Class)) !=794 MaxNumIntProtosIn(Class)) {795 tprintf(""Bad read of inttemp!\\n"");796 }797 }798 799 /* then read in the proto sets */800 for (j = 0; j < Class->NumProtoSets; j++) {801 auto ProtoSet = new PROTO_SET_STRUCT;802 unsigned num_buckets = NUM_PP_PARAMS * NUM_PP_BUCKETS * WERDS_PER_PP_VECTOR;803 if (fp->FReadEndian(&ProtoSet->ProtoPruner, sizeof(ProtoSet->ProtoPruner[0][0][0]),804 num_buckets) != num_buckets) {805 tprintf(""Bad read of inttemp!\\n"");806 }807 for (x = 0; x < PROTOS_PER_PROTO_SET; x++) {808 if (fp->FRead(&ProtoSet->Protos[x].A, sizeof(ProtoSet->Protos[x].A), 1) != 1 ||809 fp->FRead(&ProtoSet->Protos[x].B, sizeof(ProtoSet->Protos[x].B), 1) != 1 ||810 fp->FRead(&ProtoSet->Protos[x].C, sizeof(ProtoSet->Protos[x].C), 1) != 1 ||811 fp->FRead(&ProtoSet->Protos[x].Angle, sizeof(ProtoSet->Protos[x].Angle), 1) != 1) {812 tprintf(""Bad read of inttemp!\\n"");813 }814 if (fp->FReadEndian(&ProtoSet->Protos[x].Configs, sizeof(ProtoSet->Protos[x].Configs[0]),815 WerdsPerConfigVec) != WerdsPerConfigVec) {816 tprintf(""Bad read of inttemp!\\n"");817 }818 }819 Class->ProtoSets[j] = ProtoSet;820 }821 if (version_id < 4) {822 Class->font_set_id = -1;823 } else {824 fp->FReadEndian(&Class->font_set_id, sizeof(Class->font_set_id), 1);825 }826 }827 828 if (version_id < 2) {829 /* add an empty nullptr class with class id 0 */830 assert(UnusedClassIdIn(Templates, 0));831 ClassForClassId(Templates, 0) = new INT_CLASS_STRUCT(1, 1);832 ClassForClassId(Templates, 0)->font_set_id = -1;833 Templates->NumClasses++;834 /* make sure the classes are contiguous */835 for (unsigned i = 0; i < MAX_NUM_CLASSES; i++) {836 if (i < Templates->NumClasses) {837 if (ClassForClassId(Templates, i) == nullptr) {838 fprintf(stderr, ""Non-contiguous class ids in inttemp\\n"");839 exit(1);840 }841 } else {842 if (ClassForClassId(Templates, i) != nullptr) {843 fprintf(stderr, ""Class id %u exceeds NumClassesIn (Templates) %u\\n"", i,844 Templates->NumClasses);845 exit(1);846 }847 }848 }849 }850 if (version_id >= 4) {851 using namespace std::placeholders; // for _1, _2852 this->fontinfo_table_.read(fp, std::bind(read_info, _1, _2));853 if (version_id >= 5) {854 this->fontinfo_table_.read(fp, std::bind(read_spacing_info, _1, _2));855 }856 this->fontset_table_.read(fp, [](auto *f, auto *fs) { return f->DeSerialize(*fs); } );857 }858 859 return (Templates);860} /* ReadIntTemplates */861 862#ifndef GRAPHICS_DISABLED863/**864 * This routine sends the shapes in the global display865 * lists to the match debugger window.866 *867 * Globals:868 * - FeatureShapes display list containing feature matches869 * - ProtoShapes display list containing proto matches870 */871void Classify::ShowMatchDisplay() {872 InitIntMatchWindowIfReqd();873 if (ProtoDisplayWindow) {874 ProtoDisplayWindow->Clear();875 }876 if (FeatureDisplayWindow) {877 FeatureDisplayWindow->Clear();878 }879 ClearFeatureSpaceWindow(static_cast<NORM_METHOD>(static_cast<int>(classify_norm_method)),880 IntMatchWindow);881 IntMatchWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y, INT_MAX_X, INT_MAX_Y);882 if (ProtoDisplayWindow) {883 ProtoDisplayWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y, INT_MAX_X, INT_MAX_Y);884 }885 if (FeatureDisplayWindow) {886 FeatureDisplayWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y, INT_MAX_X, INT_MAX_Y);887 }888} /* ShowMatchDisplay */889 890/// Clears the given window and draws the featurespace guides for the891/// appropriate normalization method.892void ClearFeatureSpaceWindow(NORM_METHOD norm_method, ScrollView *window) {893 window->Clear();894 895 window->Pen(ScrollView::GREY);896 // Draw the feature space limit rectangle.897 window->Rectangle(0, 0, INT_MAX_X, INT_MAX_Y);898 if (norm_method == baseline) {899 window->SetCursor(0, INT_DESCENDER);900 window->DrawTo(INT_MAX_X, INT_DESCENDER);901 window->SetCursor(0, INT_BASELINE);902 window->DrawTo(INT_MAX_X, INT_BASELINE);903 window->SetCursor(0, INT_XHEIGHT);904 window->DrawTo(INT_MAX_X, INT_XHEIGHT);905 window->SetCursor(0, INT_CAPHEIGHT);906 window->DrawTo(INT_MAX_X, INT_CAPHEIGHT);907 } else {908 window->Rectangle(INT_XCENTER - INT_XRADIUS, INT_YCENTER - INT_YRADIUS,909 INT_XCENTER + INT_XRADIUS, INT_YCENTER + INT_YRADIUS);910 }911}912#endif913 914/**915 * This routine writes Templates to File. The format916 * is an efficient binary format. File must already be open917 * for writing.918 * @param File open file to write templates to919 * @param Templates templates to save into File920 * @param target_unicharset the UNICHARSET to use921 */922void Classify::WriteIntTemplates(FILE *File, INT_TEMPLATES_STRUCT *Templates,923 const UNICHARSET &target_unicharset) {924 INT_CLASS_STRUCT *Class;925 auto unicharset_size = target_unicharset.size();926 int version_id = -5; // When negated by the reader -1 becomes +1 etc.927 928 if (Templates->NumClasses != unicharset_size) {929 tprintf(930 ""Warning: executing WriteIntTemplates() with %d classes in""931 "" Templates, while target_unicharset size is %zu\\n"",932 Templates->NumClasses, unicharset_size);933 }934 935 /* first write the high level template struct */936 fwrite(&unicharset_size, sizeof(unicharset_size), 1, File);937 fwrite(&version_id, sizeof(version_id), 1, File);938 fwrite(&Templates->NumClassPruners, sizeof(Templates->NumClassPruners), 1, File);939 fwrite(&Templates->NumClasses, sizeof(Templates->NumClasses), 1, File);940 941 /* then write out the class pruners */942 for (unsigned i = 0; i < Templates->NumClassPruners; i++) {943 fwrite(Templates->ClassPruners[i], sizeof(CLASS_PRUNER_STRUCT), 1, File);944 }945 946 /* then write out each class */947 for (unsigned i = 0; i < Templates->NumClasses; i++) {948 Class = Templates->Class[i];949 950 /* first write out the high level struct for the class */951 fwrite(&Class->NumProtos, sizeof(Class->NumProtos), 1, File);952 fwrite(&Class->NumProtoSets, sizeof(Class->NumProtoSets), 1, File);953 ASSERT_HOST(Class->NumConfigs == this->fontset_table_.at(Class->font_set_id).size());954 fwrite(&Class->NumConfigs, sizeof(Class->NumConfigs), 1, File);955 for (int j = 0; j < Class->NumConfigs; ++j) {956 fwrite(&Class->ConfigLengths[j], sizeof(uint16_t), 1, File);957 }958 959 /* then write out the proto lengths */960 if (MaxNumIntProtosIn(Class) > 0) {961 fwrite(&Class->ProtoLengths[0], sizeof(uint8_t), MaxNumIntProtosIn(Class), File);962 }963 964 /* then write out the proto sets */965 for (int j = 0; j < Class->NumProtoSets; j++) {966 fwrite(Class->ProtoSets[j], sizeof(PROTO_SET_STRUCT), 1, File);967 }968 969 /* then write the fonts info */970 fwrite(&Class->font_set_id, sizeof(int), 1, File);971 }972 973 /* Write the fonts info tables */974 using namespace std::placeholders; // for _1, _2975 this->fontinfo_table_.write(File, std::bind(write_info, _1, _2));976 this->fontinfo_table_.write(File, std::bind(write_spacing_info, _1, _2));977 this->fontset_table_.write(File, std::bind(write_set, _1, _2));978} /* WriteIntTemplates */979 980/*-----------------------------------------------------------------------------981 Private Code982-----------------------------------------------------------------------------*/983/**984 * This routine returns the parameter value which985 * corresponds to the beginning of the specified bucket.986 * The bucket number should have been generated using the987 * BucketFor() function with parameters Offset and NumBuckets.988 * @param Bucket bucket whose start is to be computed989 * @param Offset offset used to map params to buckets990 * @param NumBuckets total number of buckets991 * @return Param value corresponding to start position of Bucket.992 * @note Globals: none993 */994float BucketStart(int Bucket, float Offset, int NumBuckets) {995 return static_cast<float>(Bucket) / NumBuckets - Offset;996 997} /* BucketStart */998 999/**1000 * This routine returns the parameter value which1001 * corresponds to the end of the specified bucket.1002 * The bucket number should have been generated using the1003 * BucketFor() function with parameters Offset and NumBuckets.1004 * @param Bucket bucket whose end is to be computed1005 * @param Offset offset used to map params to buckets1006 * @param NumBuckets total number of buckets1007 * @return Param value corresponding to end position of Bucket.1008 * @note Globals: none1009 */1010float BucketEnd(int Bucket, float Offset, int NumBuckets) {1011 return static_cast<float>(Bucket + 1) / NumBuckets - Offset;1012} /* BucketEnd */1013 1014/**1015 * This routine fills in the section of a class pruner1016 * corresponding to a single x value for a single proto of1017 * a class.1018 * @param FillSpec specifies which bits to fill in pruner1019 * @param Pruner class pruner to be filled1020 * @param ClassMask indicates which bits to change in each word1021 * @param ClassCount indicates what to change bits to1022 * @param WordIndex indicates which word to change1023 */1024void DoFill(FILL_SPEC *FillSpec, CLASS_PRUNER_STRUCT *Pruner, uint32_t ClassMask,1025 uint32_t ClassCount, uint32_t WordIndex) {1026 int X, Y, Angle;1027 uint32_t OldWord;1028 1029 X = FillSpec->X;1030 if (X < 0) {1031 X = 0;1032 }1033 if (X >= NUM_CP_BUCKETS) {1034 X = NUM_CP_BUCKETS - 1;1035 }1036 1037 if (FillSpec->YStart < 0) {1038 FillSpec->YStart = 0;1039 }1040 if (FillSpec->YEnd >= NUM_CP_BUCKETS) {1041 FillSpec->YEnd = NUM_CP_BUCKETS - 1;1042 }1043 1044 for (Y = FillSpec->YStart; Y <= FillSpec->YEnd; Y++) {1045 for (Angle = FillSpec->AngleStart;; CircularIncrement(Angle, NUM_CP_BUCKETS)) {1046 OldWord = Pruner->p[X][Y][Angle][WordIndex];1047 if (ClassCount > (OldWord & ClassMask)) {1048 OldWord &= ~ClassMask;1049 OldWord |= ClassCount;1050 Pruner->p[X][Y][Angle][WordIndex] = OldWord;1051 }1052 if (Angle == FillSpec->AngleEnd) {1053 break;1054 }1055 }1056 }1057} /* DoFill */1058 1059/**1060 * Return true if the specified table filler is done, i.e.1061 * if it has no more lines to fill.1062 * @param Filler table filler to check if done1063 * @return true if no more lines to fill, false otherwise.1064 * @note Globals: none1065 */1066bool FillerDone(TABLE_FILLER *Filler) {1067 FILL_SWITCH *Next;1068 1069 Next = &(Filler->Switch[Filler->NextSwitch]);1070 1071 return Filler->X > Next->X && Next->Type == LastSwitch;1072 1073} /* FillerDone */1074 1075/**1076 * This routine sets Bit in each bit vector whose1077 * bucket lies within the range Center +- Spread. The fill1078 * is done for a circular dimension, i.e. bucket 0 is adjacent1079 * to the last bucket. It is assumed that Center and Spread1080 * are expressed in a circular coordinate system whose range1081 * is 0 to 1.1082 * @param ParamTable table of bit vectors, one per param bucket1083 * @param Bit bit position in vectors to be filled1084 * @param Center center of filled area1085 * @param Spread spread of filled area1086 * @param debug debug flag1087 */1088void FillPPCircularBits(uint32_t ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], int Bit,1089 float Center, float Spread, bool debug) {1090 int i, FirstBucket, LastBucket;1091 1092 if (Spread > 0.5) {1093 Spread = 0.5;1094 }1095 1096 FirstBucket = static_cast<int>(std::floor((Center - Spread) * NUM_PP_BUCKETS));1097 if (FirstBucket < 0) {1098 FirstBucket += NUM_PP_BUCKETS;1099 }1100 1101 LastBucket = static_cast<int>(std::floor((Center + Spread) * NUM_PP_BUCKETS));1102 if (LastBucket >= NUM_PP_BUCKETS) {1103 LastBucket -= NUM_PP_BUCKETS;1104 }1105 if (debug) {1106 tprintf(""Circular fill from %d to %d"", FirstBucket, LastBucket);1107 }1108 for (i = FirstBucket; true; CircularIncrement(i, NUM_PP_BUCKETS)) {1109 SET_BIT(ParamTable[i], Bit);1110 1111 /* exit loop after we have set the bit for the last bucket */1112 if (i == LastBucket) {1113 break;1114 }1115 }1116 1117} /* FillPPCircularBits */1118 1119/**1120 * This routine sets Bit in each bit vector whose1121 * bucket lies within the range Center +- Spread. The fill1122 * is done for a linear dimension, i.e. there is no wrap-around1123 * for this dimension. It is assumed that Center and Spread1124 * are expressed in a linear coordinate system whose range1125 * is approximately 0 to 1. Values outside this range will1126 * be clipped.1127 * @param ParamTable table of bit vectors, one per param bucket1128 * @param Bit bit number being filled1129 * @param Center center of filled area1130 * @param Spread spread of filled area1131 * @param debug debug flag1132 */1133void FillPPLinearBits(uint32_t ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], int Bit,1134 float Center, float Spread, bool debug) {1135 int i, FirstBucket, LastBucket;1136 1137 FirstBucket = static_cast<int>(std::floor((Center - Spread) * NUM_PP_BUCKETS));1138 if (FirstBucket < 0) {1139 FirstBucket = 0;1140 }1141 1142 LastBucket = static_cast<int>(std::floor((Center + Spread) * NUM_PP_BUCKETS));1143 if (LastBucket >= NUM_PP_BUCKETS) {1144 LastBucket = NUM_PP_BUCKETS - 1;1145 }1146 1147 if (debug) {1148 tprintf(""Linear fill from %d to %d"", FirstBucket, LastBucket);1149 }1150 for (i = FirstBucket; i <= LastBucket; i++) {1151 SET_BIT(ParamTable[i], Bit);1152 }1153 1154} /* FillPPLinearBits */1155 1156/*---------------------------------------------------------------------------*/1157#ifndef GRAPHICS_DISABLED1158/**1159 * This routine prompts the user with Prompt and waits1160 * for the user to enter something in the debug window.1161 * @param Prompt prompt to print while waiting for input from window1162 * @param adaptive_on1163 * @param pretrained_on1164 * @param shape_id1165 * @return Character entered in the debug window.1166 * @note Globals: none1167 */1168CLASS_ID Classify::GetClassToDebug(const char *Prompt, bool *adaptive_on, bool *pretrained_on,1169 int *shape_id) {1170 tprintf(""%s\\n"", Prompt);1171 SVEvent *ev;1172 SVEventType ev_type;1173 int unichar_id = INVALID_UNICHAR_ID;1174 // Wait until a click or popup event.1175 do {1176 ev = IntMatchWindow->AwaitEvent(SVET_ANY);1177 ev_type = ev->type;1178 if (ev_type == SVET_POPUP) {1179 if (ev->command_id == IDA_SHAPE_INDEX) {1180 if (shape_table_ != nullptr) {1181 *shape_id = atoi(ev->parameter);1182 *adaptive_on = false;1183 *pretrained_on = true;1184 if (*shape_id >= 0 && static_cast<unsigned>(*shape_id) < shape_table_->NumShapes()) {1185 int font_id;1186 shape_table_->GetFirstUnicharAndFont(*shape_id, &unichar_id, &font_id);1187 tprintf(""Shape %d, first unichar=%d, font=%d\\n"", *shape_id, unichar_id, font_id);1188 return unichar_id;1189 }1190 tprintf(""Shape index '%s' not found in shape table\\n"", ev->parameter);1191 } else {1192 tprintf(""No shape table loaded!\\n"");1193 }1194 } else {1195 if (unicharset.contains_unichar(ev->parameter)) {1196 unichar_id = unicharset.unichar_to_id(ev->parameter);1197 if (ev->command_id == IDA_ADAPTIVE) {1198 *adaptive_on = true;1199 *pretrained_on = false;1200 *shape_id = -1;