|
tesseract 3.04.01
|
00001 /****************************************************************************** 00002 ** Filename: intproto.c 00003 ** Purpose: Definition of data structures for integer protos. 00004 ** Author: Dan Johnson 00005 ** History: Thu Feb 7 14:38:16 1991, DSJ, Created. 00006 ** 00007 ** (c) Copyright Hewlett-Packard Company, 1988. 00008 ** Licensed under the Apache License, Version 2.0 (the "License"); 00009 ** you may not use this file except in compliance with the License. 00010 ** You may obtain a copy of the License at 00011 ** http://www.apache.org/licenses/LICENSE-2.0 00012 ** Unless required by applicable law or agreed to in writing, software 00013 ** distributed under the License is distributed on an "AS IS" BASIS, 00014 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00015 ** See the License for the specific language governing permissions and 00016 ** limitations under the License. 00017 ******************************************************************************/ 00018 /*----------------------------------------------------------------------------- 00019 Include Files and Type Defines 00020 -----------------------------------------------------------------------------*/ 00021 00022 #include <math.h> 00023 #include <stdio.h> 00024 #include <assert.h> 00025 #ifdef __UNIX__ 00026 #include <unistd.h> 00027 #endif 00028 00029 #include "classify.h" 00030 #include "const.h" 00031 #include "emalloc.h" 00032 #include "fontinfo.h" 00033 #include "genericvector.h" 00034 #include "globals.h" 00035 #include "helpers.h" 00036 #include "intproto.h" 00037 #include "mfoutline.h" 00038 #include "ndminx.h" 00039 #include "picofeat.h" 00040 #include "points.h" 00041 #include "shapetable.h" 00042 #include "svmnode.h" 00043 00044 // Include automatically generated configuration file if running autoconf. 00045 #ifdef HAVE_CONFIG_H 00046 #include "config_auto.h" 00047 #endif 00048 00049 using tesseract::FontInfo; 00050 using tesseract::FontSet; 00051 using tesseract::FontSpacingInfo; 00052 00053 /* match debug display constants*/ 00054 #define PROTO_PRUNER_SCALE (4.0) 00055 00056 #define INT_DESCENDER (0.0 * INT_CHAR_NORM_RANGE) 00057 #define INT_BASELINE (0.25 * INT_CHAR_NORM_RANGE) 00058 #define INT_XHEIGHT (0.75 * INT_CHAR_NORM_RANGE) 00059 #define INT_CAPHEIGHT (1.0 * INT_CHAR_NORM_RANGE) 00060 00061 #define INT_XCENTER (0.5 * INT_CHAR_NORM_RANGE) 00062 #define INT_YCENTER (0.5 * INT_CHAR_NORM_RANGE) 00063 #define INT_XRADIUS (0.2 * INT_CHAR_NORM_RANGE) 00064 #define INT_YRADIUS (0.2 * INT_CHAR_NORM_RANGE) 00065 #define INT_MIN_X 0 00066 #define INT_MIN_Y 0 00067 #define INT_MAX_X INT_CHAR_NORM_RANGE 00068 #define INT_MAX_Y INT_CHAR_NORM_RANGE 00069 00071 #define HV_TOLERANCE (0.0025) /* approx 0.9 degrees */ 00072 00073 typedef enum 00074 { StartSwitch, EndSwitch, LastSwitch } 00075 SWITCH_TYPE; 00076 #define MAX_NUM_SWITCHES 3 00077 00078 typedef struct 00079 { 00080 SWITCH_TYPE Type; 00081 inT8 X, Y; 00082 inT16 YInit; 00083 inT16 Delta; 00084 } 00085 00086 00087 FILL_SWITCH; 00088 00089 typedef struct 00090 { 00091 uinT8 NextSwitch; 00092 uinT8 AngleStart, AngleEnd; 00093 inT8 X; 00094 inT16 YStart, YEnd; 00095 inT16 StartDelta, EndDelta; 00096 FILL_SWITCH Switch[MAX_NUM_SWITCHES]; 00097 } 00098 00099 00100 TABLE_FILLER; 00101 00102 typedef struct 00103 { 00104 inT8 X; 00105 inT8 YStart, YEnd; 00106 uinT8 AngleStart, AngleEnd; 00107 } 00108 00109 00110 FILL_SPEC; 00111 00112 00113 /* constants for conversion from old inttemp format */ 00114 #define OLD_MAX_NUM_CONFIGS 32 00115 #define OLD_WERDS_PER_CONFIG_VEC ((OLD_MAX_NUM_CONFIGS + BITS_PER_WERD - 1) /\ 00116 BITS_PER_WERD) 00117 00118 /*----------------------------------------------------------------------------- 00119 Macros 00120 -----------------------------------------------------------------------------*/ 00122 #define CircularIncrement(i,r) (((i) < (r) - 1)?((i)++):((i) = 0)) 00123 00125 #define MapParam(P,O,N) (floor (((P) + (O)) * (N))) 00126 00127 /*--------------------------------------------------------------------------- 00128 Private Function Prototypes 00129 ----------------------------------------------------------------------------*/ 00130 FLOAT32 BucketStart(int Bucket, FLOAT32 Offset, int NumBuckets); 00131 00132 FLOAT32 BucketEnd(int Bucket, FLOAT32 Offset, int NumBuckets); 00133 00134 void DoFill(FILL_SPEC *FillSpec, 00135 CLASS_PRUNER_STRUCT* Pruner, 00136 register uinT32 ClassMask, 00137 register uinT32 ClassCount, 00138 register uinT32 WordIndex); 00139 00140 BOOL8 FillerDone(TABLE_FILLER *Filler); 00141 00142 void FillPPCircularBits(uinT32 00143 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], 00144 int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug); 00145 00146 void FillPPLinearBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], 00147 int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug); 00148 00149 void GetCPPadsForLevel(int Level, 00150 FLOAT32 *EndPad, 00151 FLOAT32 *SidePad, 00152 FLOAT32 *AnglePad); 00153 00154 ScrollView::Color GetMatchColorFor(FLOAT32 Evidence); 00155 00156 void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill); 00157 00158 void InitTableFiller(FLOAT32 EndPad, 00159 FLOAT32 SidePad, 00160 FLOAT32 AnglePad, 00161 PROTO Proto, 00162 TABLE_FILLER *Filler); 00163 00164 #ifndef GRAPHICS_DISABLED 00165 void RenderIntFeature(ScrollView *window, const INT_FEATURE_STRUCT* Feature, 00166 ScrollView::Color color); 00167 00168 void RenderIntProto(ScrollView *window, 00169 INT_CLASS Class, 00170 PROTO_ID ProtoId, 00171 ScrollView::Color color); 00172 #endif // GRAPHICS_DISABLED 00173 00174 int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id); 00175 00176 /*----------------------------------------------------------------------------- 00177 Global Data Definitions and Declarations 00178 -----------------------------------------------------------------------------*/ 00179 00180 /* global display lists used to display proto and feature match information*/ 00181 ScrollView *IntMatchWindow = NULL; 00182 ScrollView *FeatureDisplayWindow = NULL; 00183 ScrollView *ProtoDisplayWindow = NULL; 00184 00185 /*----------------------------------------------------------------------------- 00186 Variables 00187 -----------------------------------------------------------------------------*/ 00188 00189 /* control knobs */ 00190 INT_VAR(classify_num_cp_levels, 3, "Number of Class Pruner Levels"); 00191 double_VAR(classify_cp_angle_pad_loose, 45.0, 00192 "Class Pruner Angle Pad Loose"); 00193 double_VAR(classify_cp_angle_pad_medium, 20.0, 00194 "Class Pruner Angle Pad Medium"); 00195 double_VAR(classify_cp_angle_pad_tight, 10.0, 00196 "CLass Pruner Angle Pad Tight"); 00197 double_VAR(classify_cp_end_pad_loose, 0.5, "Class Pruner End Pad Loose"); 00198 double_VAR(classify_cp_end_pad_medium, 0.5, "Class Pruner End Pad Medium"); 00199 double_VAR(classify_cp_end_pad_tight, 0.5, "Class Pruner End Pad Tight"); 00200 double_VAR(classify_cp_side_pad_loose, 2.5, "Class Pruner Side Pad Loose"); 00201 double_VAR(classify_cp_side_pad_medium, 1.2, "Class Pruner Side Pad Medium"); 00202 double_VAR(classify_cp_side_pad_tight, 0.6, "Class Pruner Side Pad Tight"); 00203 double_VAR(classify_pp_angle_pad, 45.0, "Proto Pruner Angle Pad"); 00204 double_VAR(classify_pp_end_pad, 0.5, "Proto Prune End Pad"); 00205 double_VAR(classify_pp_side_pad, 2.5, "Proto Pruner Side Pad"); 00206 00207 /*----------------------------------------------------------------------------- 00208 Public Code 00209 -----------------------------------------------------------------------------*/ 00212 INT_FEATURE_STRUCT::INT_FEATURE_STRUCT(const FCOORD& pos, uinT8 theta) 00213 : X(ClipToRange<inT16>(static_cast<inT16>(pos.x() + 0.5), 0, 255)), 00214 Y(ClipToRange<inT16>(static_cast<inT16>(pos.y() + 0.5), 0, 255)), 00215 Theta(theta), 00216 CP_misses(0) { 00217 } 00219 INT_FEATURE_STRUCT::INT_FEATURE_STRUCT(int x, int y, int theta) 00220 : X(static_cast<uinT8>(ClipToRange(x, 0, MAX_UINT8))), 00221 Y(static_cast<uinT8>(ClipToRange(y, 0, MAX_UINT8))), 00222 Theta(static_cast<uinT8>(ClipToRange(theta, 0, MAX_UINT8))), 00223 CP_misses(0) { 00224 } 00225 00240 void AddIntClass(INT_TEMPLATES Templates, CLASS_ID ClassId, INT_CLASS Class) { 00241 int Pruner; 00242 00243 assert (LegalClassId (ClassId)); 00244 if (ClassId != Templates->NumClasses) { 00245 fprintf(stderr, "Please make sure that classes are added to templates"); 00246 fprintf(stderr, " in increasing order of ClassIds\n"); 00247 exit(1); 00248 } 00249 ClassForClassId (Templates, ClassId) = Class; 00250 Templates->NumClasses++; 00251 00252 if (Templates->NumClasses > MaxNumClassesIn (Templates)) { 00253 Pruner = Templates->NumClassPruners++; 00254 Templates->ClassPruners[Pruner] = new CLASS_PRUNER_STRUCT; 00255 memset(Templates->ClassPruners[Pruner], 0, sizeof(CLASS_PRUNER_STRUCT)); 00256 } 00257 } /* AddIntClass */ 00258 00259 00272 int AddIntConfig(INT_CLASS Class) { 00273 int Index; 00274 00275 assert(Class->NumConfigs < MAX_NUM_CONFIGS); 00276 00277 Index = Class->NumConfigs++; 00278 Class->ConfigLengths[Index] = 0; 00279 return Index; 00280 } /* AddIntConfig */ 00281 00282 00295 int AddIntProto(INT_CLASS Class) { 00296 int Index; 00297 int ProtoSetId; 00298 PROTO_SET ProtoSet; 00299 INT_PROTO Proto; 00300 uinT32 *Word; 00301 00302 if (Class->NumProtos >= MAX_NUM_PROTOS) 00303 return (NO_PROTO); 00304 00305 Index = Class->NumProtos++; 00306 00307 if (Class->NumProtos > MaxNumIntProtosIn(Class)) { 00308 ProtoSetId = Class->NumProtoSets++; 00309 00310 ProtoSet = (PROTO_SET) Emalloc(sizeof(PROTO_SET_STRUCT)); 00311 Class->ProtoSets[ProtoSetId] = ProtoSet; 00312 memset(ProtoSet, 0, sizeof(*ProtoSet)); 00313 00314 /* reallocate space for the proto lengths and install in class */ 00315 Class->ProtoLengths = 00316 (uinT8 *)Erealloc(Class->ProtoLengths, 00317 MaxNumIntProtosIn(Class) * sizeof(uinT8)); 00318 memset(&Class->ProtoLengths[Index], 0, 00319 sizeof(*Class->ProtoLengths) * (MaxNumIntProtosIn(Class) - Index)); 00320 } 00321 00322 /* initialize proto so its length is zero and it isn't in any configs */ 00323 Class->ProtoLengths[Index] = 0; 00324 Proto = ProtoForProtoId (Class, Index); 00325 for (Word = Proto->Configs; 00326 Word < Proto->Configs + WERDS_PER_CONFIG_VEC; *Word++ = 0); 00327 00328 return (Index); 00329 00330 } 00331 00332 00346 void AddProtoToClassPruner (PROTO Proto, CLASS_ID ClassId, 00347 INT_TEMPLATES Templates) 00348 #define MAX_LEVEL 2 00349 { 00350 CLASS_PRUNER_STRUCT* Pruner; 00351 uinT32 ClassMask; 00352 uinT32 ClassCount; 00353 uinT32 WordIndex; 00354 int Level; 00355 FLOAT32 EndPad, SidePad, AnglePad; 00356 TABLE_FILLER TableFiller; 00357 FILL_SPEC FillSpec; 00358 00359 Pruner = CPrunerFor (Templates, ClassId); 00360 WordIndex = CPrunerWordIndexFor (ClassId); 00361 ClassMask = CPrunerMaskFor (MAX_LEVEL, ClassId); 00362 00363 for (Level = classify_num_cp_levels - 1; Level >= 0; Level--) { 00364 GetCPPadsForLevel(Level, &EndPad, &SidePad, &AnglePad); 00365 ClassCount = CPrunerMaskFor (Level, ClassId); 00366 InitTableFiller(EndPad, SidePad, AnglePad, Proto, &TableFiller); 00367 00368 while (!FillerDone (&TableFiller)) { 00369 GetNextFill(&TableFiller, &FillSpec); 00370 DoFill(&FillSpec, Pruner, ClassMask, ClassCount, WordIndex); 00371 } 00372 } 00373 } /* AddProtoToClassPruner */ 00374 00375 00389 void AddProtoToProtoPruner(PROTO Proto, int ProtoId, 00390 INT_CLASS Class, bool debug) { 00391 FLOAT32 Angle, X, Y, Length; 00392 FLOAT32 Pad; 00393 int Index; 00394 PROTO_SET ProtoSet; 00395 00396 if (ProtoId >= Class->NumProtos) 00397 cprintf("AddProtoToProtoPruner:assert failed: %d < %d", 00398 ProtoId, Class->NumProtos); 00399 assert(ProtoId < Class->NumProtos); 00400 00401 Index = IndexForProto (ProtoId); 00402 ProtoSet = Class->ProtoSets[SetForProto (ProtoId)]; 00403 00404 Angle = Proto->Angle; 00405 #ifndef _WIN32 00406 assert(!isnan(Angle)); 00407 #endif 00408 00409 FillPPCircularBits (ProtoSet->ProtoPruner[PRUNER_ANGLE], Index, 00410 Angle + ANGLE_SHIFT, classify_pp_angle_pad / 360.0, 00411 debug); 00412 00413 Angle *= 2.0 * PI; 00414 Length = Proto->Length; 00415 00416 X = Proto->X + X_SHIFT; 00417 Pad = MAX (fabs (cos (Angle)) * (Length / 2.0 + 00418 classify_pp_end_pad * 00419 GetPicoFeatureLength ()), 00420 fabs (sin (Angle)) * (classify_pp_side_pad * 00421 GetPicoFeatureLength ())); 00422 00423 FillPPLinearBits(ProtoSet->ProtoPruner[PRUNER_X], Index, X, Pad, debug); 00424 00425 Y = Proto->Y + Y_SHIFT; 00426 Pad = MAX (fabs (sin (Angle)) * (Length / 2.0 + 00427 classify_pp_end_pad * 00428 GetPicoFeatureLength ()), 00429 fabs (cos (Angle)) * (classify_pp_side_pad * 00430 GetPicoFeatureLength ())); 00431 00432 FillPPLinearBits(ProtoSet->ProtoPruner[PRUNER_Y], Index, Y, Pad, debug); 00433 } /* AddProtoToProtoPruner */ 00434 00435 00441 uinT8 Bucket8For(FLOAT32 param, FLOAT32 offset, int num_buckets) { 00442 int bucket = IntCastRounded(MapParam(param, offset, num_buckets)); 00443 return static_cast<uinT8>(ClipToRange(bucket, 0, num_buckets - 1)); 00444 } 00445 uinT16 Bucket16For(FLOAT32 param, FLOAT32 offset, int num_buckets) { 00446 int bucket = IntCastRounded(MapParam(param, offset, num_buckets)); 00447 return static_cast<uinT16>(ClipToRange(bucket, 0, num_buckets - 1)); 00448 } 00449 00455 uinT8 CircBucketFor(FLOAT32 param, FLOAT32 offset, int num_buckets) { 00456 int bucket = IntCastRounded(MapParam(param, offset, num_buckets)); 00457 return static_cast<uinT8>(Modulo(bucket, num_buckets)); 00458 } /* CircBucketFor */ 00459 00460 00461 #ifndef GRAPHICS_DISABLED 00462 00473 void UpdateMatchDisplay() { 00474 if (IntMatchWindow != NULL) 00475 IntMatchWindow->Update(); 00476 } /* ClearMatchDisplay */ 00477 #endif 00478 00493 void ConvertConfig(BIT_VECTOR Config, int ConfigId, INT_CLASS Class) { 00494 int ProtoId; 00495 INT_PROTO Proto; 00496 int TotalLength; 00497 00498 for (ProtoId = 0, TotalLength = 0; 00499 ProtoId < Class->NumProtos; ProtoId++) { 00500 if (test_bit(Config, ProtoId)) { 00501 Proto = ProtoForProtoId(Class, ProtoId); 00502 SET_BIT(Proto->Configs, ConfigId); 00503 TotalLength += Class->ProtoLengths[ProtoId]; 00504 } 00505 } 00506 Class->ConfigLengths[ConfigId] = TotalLength; 00507 } /* ConvertConfig */ 00508 00509 00510 namespace tesseract { 00522 void Classify::ConvertProto(PROTO Proto, int ProtoId, INT_CLASS Class) { 00523 INT_PROTO P; 00524 FLOAT32 Param; 00525 00526 assert(ProtoId < Class->NumProtos); 00527 00528 P = ProtoForProtoId(Class, ProtoId); 00529 00530 Param = Proto->A * 128; 00531 P->A = TruncateParam(Param, -128, 127, NULL); 00532 00533 Param = -Proto->B * 256; 00534 P->B = TruncateParam(Param, 0, 255, NULL); 00535 00536 Param = Proto->C * 128; 00537 P->C = TruncateParam(Param, -128, 127, NULL); 00538 00539 Param = Proto->Angle * 256; 00540 if (Param < 0 || Param >= 256) 00541 P->Angle = 0; 00542 else 00543 P->Angle = (uinT8) Param; 00544 00545 /* round proto length to nearest integer number of pico-features */ 00546 Param = (Proto->Length / GetPicoFeatureLength()) + 0.5; 00547 Class->ProtoLengths[ProtoId] = TruncateParam(Param, 1, 255, NULL); 00548 if (classify_learning_debug_level >= 2) 00549 cprintf("Converted ffeat to (A=%d,B=%d,C=%d,L=%d)", 00550 P->A, P->B, P->C, Class->ProtoLengths[ProtoId]); 00551 } /* ConvertProto */ 00552 00553 00564 INT_TEMPLATES Classify::CreateIntTemplates(CLASSES FloatProtos, 00565 const UNICHARSET& 00566 target_unicharset) { 00567 INT_TEMPLATES IntTemplates; 00568 CLASS_TYPE FClass; 00569 INT_CLASS IClass; 00570 int ClassId; 00571 int ProtoId; 00572 int ConfigId; 00573 00574 IntTemplates = NewIntTemplates(); 00575 00576 for (ClassId = 0; ClassId < target_unicharset.size(); ClassId++) { 00577 FClass = &(FloatProtos[ClassId]); 00578 if (FClass->NumProtos == 0 && FClass->NumConfigs == 0 && 00579 strcmp(target_unicharset.id_to_unichar(ClassId), " ") != 0) { 00580 cprintf("Warning: no protos/configs for %s in CreateIntTemplates()\n", 00581 target_unicharset.id_to_unichar(ClassId)); 00582 } 00583 assert(UnusedClassIdIn(IntTemplates, ClassId)); 00584 IClass = NewIntClass(FClass->NumProtos, FClass->NumConfigs); 00585 FontSet fs; 00586 fs.size = FClass->font_set.size(); 00587 fs.configs = new int[fs.size]; 00588 for (int i = 0; i < fs.size; ++i) { 00589 fs.configs[i] = FClass->font_set.get(i); 00590 } 00591 if (this->fontset_table_.contains(fs)) { 00592 IClass->font_set_id = this->fontset_table_.get_id(fs); 00593 delete[] fs.configs; 00594 } else { 00595 IClass->font_set_id = this->fontset_table_.push_back(fs); 00596 } 00597 AddIntClass(IntTemplates, ClassId, IClass); 00598 00599 for (ProtoId = 0; ProtoId < FClass->NumProtos; ProtoId++) { 00600 AddIntProto(IClass); 00601 ConvertProto(ProtoIn(FClass, ProtoId), ProtoId, IClass); 00602 AddProtoToProtoPruner(ProtoIn(FClass, ProtoId), ProtoId, IClass, 00603 classify_learning_debug_level >= 2); 00604 AddProtoToClassPruner(ProtoIn(FClass, ProtoId), ClassId, IntTemplates); 00605 } 00606 00607 for (ConfigId = 0; ConfigId < FClass->NumConfigs; ConfigId++) { 00608 AddIntConfig(IClass); 00609 ConvertConfig(FClass->Configurations[ConfigId], ConfigId, IClass); 00610 } 00611 } 00612 return (IntTemplates); 00613 } /* CreateIntTemplates */ 00614 } // namespace tesseract 00615 00616 00617 #ifndef GRAPHICS_DISABLED 00618 00630 void DisplayIntFeature(const INT_FEATURE_STRUCT* Feature, FLOAT32 Evidence) { 00631 ScrollView::Color color = GetMatchColorFor(Evidence); 00632 RenderIntFeature(IntMatchWindow, Feature, color); 00633 if (FeatureDisplayWindow) { 00634 RenderIntFeature(FeatureDisplayWindow, Feature, color); 00635 } 00636 } /* DisplayIntFeature */ 00637 00638 00652 void DisplayIntProto(INT_CLASS Class, PROTO_ID ProtoId, FLOAT32 Evidence) { 00653 ScrollView::Color color = GetMatchColorFor(Evidence); 00654 RenderIntProto(IntMatchWindow, Class, ProtoId, color); 00655 if (ProtoDisplayWindow) { 00656 RenderIntProto(ProtoDisplayWindow, Class, ProtoId, color); 00657 } 00658 } /* DisplayIntProto */ 00659 #endif 00660 00672 INT_CLASS NewIntClass(int MaxNumProtos, int MaxNumConfigs) { 00673 INT_CLASS Class; 00674 PROTO_SET ProtoSet; 00675 int i; 00676 00677 assert(MaxNumConfigs <= MAX_NUM_CONFIGS); 00678 00679 Class = (INT_CLASS) Emalloc(sizeof(INT_CLASS_STRUCT)); 00680 Class->NumProtoSets = ((MaxNumProtos + PROTOS_PER_PROTO_SET - 1) / 00681 PROTOS_PER_PROTO_SET); 00682 00683 assert(Class->NumProtoSets <= MAX_NUM_PROTO_SETS); 00684 00685 Class->NumProtos = 0; 00686 Class->NumConfigs = 0; 00687 00688 for (i = 0; i < Class->NumProtoSets; i++) { 00689 /* allocate space for a proto set, install in class, and initialize */ 00690 ProtoSet = (PROTO_SET) Emalloc(sizeof(PROTO_SET_STRUCT)); 00691 memset(ProtoSet, 0, sizeof(*ProtoSet)); 00692 Class->ProtoSets[i] = ProtoSet; 00693 00694 /* allocate space for the proto lengths and install in class */ 00695 } 00696 if (MaxNumIntProtosIn (Class) > 0) { 00697 Class->ProtoLengths = 00698 (uinT8 *)Emalloc(MaxNumIntProtosIn (Class) * sizeof (uinT8)); 00699 memset(Class->ProtoLengths, 0, 00700 MaxNumIntProtosIn(Class) * sizeof(*Class->ProtoLengths)); 00701 } else { 00702 Class->ProtoLengths = NULL; 00703 } 00704 memset(Class->ConfigLengths, 0, sizeof(Class->ConfigLengths)); 00705 00706 return (Class); 00707 00708 } /* NewIntClass */ 00709 00710 00711 void free_int_class(INT_CLASS int_class) { 00712 int i; 00713 00714 for (i = 0; i < int_class->NumProtoSets; i++) { 00715 Efree (int_class->ProtoSets[i]); 00716 } 00717 if (int_class->ProtoLengths != NULL) { 00718 Efree (int_class->ProtoLengths); 00719 } 00720 Efree(int_class); 00721 } 00722 00723 00732 INT_TEMPLATES NewIntTemplates() { 00733 INT_TEMPLATES T; 00734 int i; 00735 00736 T = (INT_TEMPLATES) Emalloc (sizeof (INT_TEMPLATES_STRUCT)); 00737 T->NumClasses = 0; 00738 T->NumClassPruners = 0; 00739 00740 for (i = 0; i < MAX_NUM_CLASSES; i++) 00741 ClassForClassId (T, i) = NULL; 00742 00743 return (T); 00744 } /* NewIntTemplates */ 00745 00746 00747 /*---------------------------------------------------------------------------*/ 00748 void free_int_templates(INT_TEMPLATES templates) { 00749 int i; 00750 00751 for (i = 0; i < templates->NumClasses; i++) 00752 free_int_class(templates->Class[i]); 00753 for (i = 0; i < templates->NumClassPruners; i++) 00754 delete templates->ClassPruners[i]; 00755 Efree(templates); 00756 } 00757 00758 00759 namespace tesseract { 00770 INT_TEMPLATES Classify::ReadIntTemplates(FILE *File) { 00771 int i, j, w, x, y, z; 00772 BOOL8 swap; 00773 int nread; 00774 int unicharset_size; 00775 int version_id = 0; 00776 INT_TEMPLATES Templates; 00777 CLASS_PRUNER_STRUCT* Pruner; 00778 INT_CLASS Class; 00779 uinT8 *Lengths; 00780 PROTO_SET ProtoSet; 00781 00782 /* variables for conversion from older inttemp formats */ 00783 int b, bit_number, last_cp_bit_number, new_b, new_i, new_w; 00784 CLASS_ID class_id, max_class_id; 00785 inT16 *IndexFor = new inT16[MAX_NUM_CLASSES]; 00786 CLASS_ID *ClassIdFor = new CLASS_ID[MAX_NUM_CLASSES]; 00787 CLASS_PRUNER_STRUCT **TempClassPruner = 00788 new CLASS_PRUNER_STRUCT*[MAX_NUM_CLASS_PRUNERS]; 00789 uinT32 SetBitsForMask = // word with NUM_BITS_PER_CLASS 00790 (1 << NUM_BITS_PER_CLASS) - 1; // set starting at bit 0 00791 uinT32 Mask, NewMask, ClassBits; 00792 int MaxNumConfigs = MAX_NUM_CONFIGS; 00793 int WerdsPerConfigVec = WERDS_PER_CONFIG_VEC; 00794 00795 /* first read the high level template struct */ 00796 Templates = NewIntTemplates(); 00797 // Read Templates in parts for 64 bit compatibility. 00798 if (fread(&unicharset_size, sizeof(int), 1, File) != 1) 00799 cprintf("Bad read of inttemp!\n"); 00800 if (fread(&Templates->NumClasses, 00801 sizeof(Templates->NumClasses), 1, File) != 1 || 00802 fread(&Templates->NumClassPruners, 00803 sizeof(Templates->NumClassPruners), 1, File) != 1) 00804 cprintf("Bad read of inttemp!\n"); 00805 // Swap status is determined automatically. 00806 swap = Templates->NumClassPruners < 0 || 00807 Templates->NumClassPruners > MAX_NUM_CLASS_PRUNERS; 00808 if (swap) { 00809 Reverse32(&Templates->NumClassPruners); 00810 Reverse32(&Templates->NumClasses); 00811 Reverse32(&unicharset_size); 00812 } 00813 if (Templates->NumClasses < 0) { 00814 // This file has a version id! 00815 version_id = -Templates->NumClasses; 00816 if (fread(&Templates->NumClasses, sizeof(Templates->NumClasses), 00817 1, File) != 1) 00818 cprintf("Bad read of inttemp!\n"); 00819 if (swap) 00820 Reverse32(&Templates->NumClasses); 00821 } 00822 00823 if (version_id < 3) { 00824 MaxNumConfigs = OLD_MAX_NUM_CONFIGS; 00825 WerdsPerConfigVec = OLD_WERDS_PER_CONFIG_VEC; 00826 } 00827 00828 if (version_id < 2) { 00829 for (i = 0; i < unicharset_size; ++i) { 00830 if (fread(&IndexFor[i], sizeof(inT16), 1, File) != 1) 00831 cprintf("Bad read of inttemp!\n"); 00832 } 00833 for (i = 0; i < Templates->NumClasses; ++i) { 00834 if (fread(&ClassIdFor[i], sizeof(CLASS_ID), 1, File) != 1) 00835 cprintf("Bad read of inttemp!\n"); 00836 } 00837 if (swap) { 00838 for (i = 0; i < Templates->NumClasses; i++) 00839 Reverse16(&IndexFor[i]); 00840 for (i = 0; i < Templates->NumClasses; i++) 00841 Reverse32(&ClassIdFor[i]); 00842 } 00843 } 00844 00845 /* then read in the class pruners */ 00846 for (i = 0; i < Templates->NumClassPruners; i++) { 00847 Pruner = new CLASS_PRUNER_STRUCT; 00848 if ((nread = 00849 fread(Pruner, 1, sizeof(CLASS_PRUNER_STRUCT), 00850 File)) != sizeof(CLASS_PRUNER_STRUCT)) 00851 cprintf("Bad read of inttemp!\n"); 00852 if (swap) { 00853 for (x = 0; x < NUM_CP_BUCKETS; x++) { 00854 for (y = 0; y < NUM_CP_BUCKETS; y++) { 00855 for (z = 0; z < NUM_CP_BUCKETS; z++) { 00856 for (w = 0; w < WERDS_PER_CP_VECTOR; w++) { 00857 Reverse32(&Pruner->p[x][y][z][w]); 00858 } 00859 } 00860 } 00861 } 00862 } 00863 if (version_id < 2) { 00864 TempClassPruner[i] = Pruner; 00865 } else { 00866 Templates->ClassPruners[i] = Pruner; 00867 } 00868 } 00869 00870 /* fix class pruners if they came from an old version of inttemp */ 00871 if (version_id < 2) { 00872 // Allocate enough class pruners to cover all the class ids. 00873 max_class_id = 0; 00874 for (i = 0; i < Templates->NumClasses; i++) 00875 if (ClassIdFor[i] > max_class_id) 00876 max_class_id = ClassIdFor[i]; 00877 for (i = 0; i <= CPrunerIdFor(max_class_id); i++) { 00878 Templates->ClassPruners[i] = new CLASS_PRUNER_STRUCT; 00879 memset(Templates->ClassPruners[i], 0, sizeof(CLASS_PRUNER_STRUCT)); 00880 } 00881 // Convert class pruners from the old format (indexed by class index) 00882 // to the new format (indexed by class id). 00883 last_cp_bit_number = NUM_BITS_PER_CLASS * Templates->NumClasses - 1; 00884 for (i = 0; i < Templates->NumClassPruners; i++) { 00885 for (x = 0; x < NUM_CP_BUCKETS; x++) 00886 for (y = 0; y < NUM_CP_BUCKETS; y++) 00887 for (z = 0; z < NUM_CP_BUCKETS; z++) 00888 for (w = 0; w < WERDS_PER_CP_VECTOR; w++) { 00889 if (TempClassPruner[i]->p[x][y][z][w] == 0) 00890 continue; 00891 for (b = 0; b < BITS_PER_WERD; b += NUM_BITS_PER_CLASS) { 00892 bit_number = i * BITS_PER_CP_VECTOR + w * BITS_PER_WERD + b; 00893 if (bit_number > last_cp_bit_number) 00894 break; // the rest of the bits in this word are not used 00895 class_id = ClassIdFor[bit_number / NUM_BITS_PER_CLASS]; 00896 // Single out NUM_BITS_PER_CLASS bits relating to class_id. 00897 Mask = SetBitsForMask << b; 00898 ClassBits = TempClassPruner[i]->p[x][y][z][w] & Mask; 00899 // Move these bits to the new position in which they should 00900 // appear (indexed corresponding to the class_id). 00901 new_i = CPrunerIdFor(class_id); 00902 new_w = CPrunerWordIndexFor(class_id); 00903 new_b = CPrunerBitIndexFor(class_id) * NUM_BITS_PER_CLASS; 00904 if (new_b > b) { 00905 ClassBits <<= (new_b - b); 00906 } else { 00907 ClassBits >>= (b - new_b); 00908 } 00909 // Copy bits relating to class_id to the correct position 00910 // in Templates->ClassPruner. 00911 NewMask = SetBitsForMask << new_b; 00912 Templates->ClassPruners[new_i]->p[x][y][z][new_w] &= ~NewMask; 00913 Templates->ClassPruners[new_i]->p[x][y][z][new_w] |= ClassBits; 00914 } 00915 } 00916 } 00917 for (i = 0; i < Templates->NumClassPruners; i++) { 00918 delete TempClassPruner[i]; 00919 } 00920 } 00921 00922 /* then read in each class */ 00923 for (i = 0; i < Templates->NumClasses; i++) { 00924 /* first read in the high level struct for the class */ 00925 Class = (INT_CLASS) Emalloc (sizeof (INT_CLASS_STRUCT)); 00926 if (fread(&Class->NumProtos, sizeof(Class->NumProtos), 1, File) != 1 || 00927 fread(&Class->NumProtoSets, sizeof(Class->NumProtoSets), 1, File) != 1 || 00928 fread(&Class->NumConfigs, sizeof(Class->NumConfigs), 1, File) != 1) 00929 cprintf ("Bad read of inttemp!\n"); 00930 if (version_id == 0) { 00931 // Only version 0 writes 5 pointless pointers to the file. 00932 for (j = 0; j < 5; ++j) { 00933 int junk; 00934 if (fread(&junk, sizeof(junk), 1, File) != 1) 00935 cprintf ("Bad read of inttemp!\n"); 00936 } 00937 } 00938 if (version_id < 4) { 00939 for (j = 0; j < MaxNumConfigs; ++j) { 00940 if (fread(&Class->ConfigLengths[j], sizeof(uinT16), 1, File) != 1) 00941 cprintf ("Bad read of inttemp!\n"); 00942 } 00943 if (swap) { 00944 Reverse16(&Class->NumProtos); 00945 for (j = 0; j < MaxNumConfigs; j++) 00946 Reverse16(&Class->ConfigLengths[j]); 00947 } 00948 } else { 00949 ASSERT_HOST(Class->NumConfigs < MaxNumConfigs); 00950 for (j = 0; j < Class->NumConfigs; ++j) { 00951 if (fread(&Class->ConfigLengths[j], sizeof(uinT16), 1, File) != 1) 00952 cprintf ("Bad read of inttemp!\n"); 00953 } 00954 if (swap) { 00955 Reverse16(&Class->NumProtos); 00956 for (j = 0; j < MaxNumConfigs; j++) 00957 Reverse16(&Class->ConfigLengths[j]); 00958 } 00959 } 00960 if (version_id < 2) { 00961 ClassForClassId (Templates, ClassIdFor[i]) = Class; 00962 } else { 00963 ClassForClassId (Templates, i) = Class; 00964 } 00965 00966 /* then read in the proto lengths */ 00967 Lengths = NULL; 00968 if (MaxNumIntProtosIn (Class) > 0) { 00969 Lengths = (uinT8 *)Emalloc(sizeof(uinT8) * MaxNumIntProtosIn(Class)); 00970 if ((nread = 00971 fread((char *)Lengths, sizeof(uinT8), 00972 MaxNumIntProtosIn(Class), File)) != MaxNumIntProtosIn (Class)) 00973 cprintf ("Bad read of inttemp!\n"); 00974 } 00975 Class->ProtoLengths = Lengths; 00976 00977 /* then read in the proto sets */ 00978 for (j = 0; j < Class->NumProtoSets; j++) { 00979 ProtoSet = (PROTO_SET)Emalloc(sizeof(PROTO_SET_STRUCT)); 00980 if (version_id < 3) { 00981 if ((nread = 00982 fread((char *) &ProtoSet->ProtoPruner, 1, 00983 sizeof(PROTO_PRUNER), File)) != sizeof(PROTO_PRUNER)) 00984 cprintf("Bad read of inttemp!\n"); 00985 for (x = 0; x < PROTOS_PER_PROTO_SET; x++) { 00986 if ((nread = fread((char *) &ProtoSet->Protos[x].A, 1, 00987 sizeof(inT8), File)) != sizeof(inT8) || 00988 (nread = fread((char *) &ProtoSet->Protos[x].B, 1, 00989 sizeof(uinT8), File)) != sizeof(uinT8) || 00990 (nread = fread((char *) &ProtoSet->Protos[x].C, 1, 00991 sizeof(inT8), File)) != sizeof(inT8) || 00992 (nread = fread((char *) &ProtoSet->Protos[x].Angle, 1, 00993 sizeof(uinT8), File)) != sizeof(uinT8)) 00994 cprintf("Bad read of inttemp!\n"); 00995 for (y = 0; y < WerdsPerConfigVec; y++) 00996 if ((nread = fread((char *) &ProtoSet->Protos[x].Configs[y], 1, 00997 sizeof(uinT32), File)) != sizeof(uinT32)) 00998 cprintf("Bad read of inttemp!\n"); 00999 } 01000 } else { 01001 if ((nread = 01002 fread((char *) ProtoSet, 1, sizeof(PROTO_SET_STRUCT), 01003 File)) != sizeof(PROTO_SET_STRUCT)) 01004 cprintf("Bad read of inttemp!\n"); 01005 } 01006 if (swap) { 01007 for (x = 0; x < NUM_PP_PARAMS; x++) 01008 for (y = 0; y < NUM_PP_BUCKETS; y++) 01009 for (z = 0; z < WERDS_PER_PP_VECTOR; z++) 01010 Reverse32(&ProtoSet->ProtoPruner[x][y][z]); 01011 for (x = 0; x < PROTOS_PER_PROTO_SET; x++) 01012 for (y = 0; y < WerdsPerConfigVec; y++) 01013 Reverse32(&ProtoSet->Protos[x].Configs[y]); 01014 } 01015 Class->ProtoSets[j] = ProtoSet; 01016 } 01017 if (version_id < 4) 01018 Class->font_set_id = -1; 01019 else { 01020 fread(&Class->font_set_id, sizeof(int), 1, File); 01021 if (swap) 01022 Reverse32(&Class->font_set_id); 01023 } 01024 } 01025 01026 if (version_id < 2) { 01027 /* add an empty NULL class with class id 0 */ 01028 assert(UnusedClassIdIn (Templates, 0)); 01029 ClassForClassId (Templates, 0) = NewIntClass (1, 1); 01030 ClassForClassId (Templates, 0)->font_set_id = -1; 01031 Templates->NumClasses++; 01032 /* make sure the classes are contiguous */ 01033 for (i = 0; i < MAX_NUM_CLASSES; i++) { 01034 if (i < Templates->NumClasses) { 01035 if (ClassForClassId (Templates, i) == NULL) { 01036 fprintf(stderr, "Non-contiguous class ids in inttemp\n"); 01037 exit(1); 01038 } 01039 } else { 01040 if (ClassForClassId (Templates, i) != NULL) { 01041 fprintf(stderr, "Class id %d exceeds NumClassesIn (Templates) %d\n", 01042 i, Templates->NumClasses); 01043 exit(1); 01044 } 01045 } 01046 } 01047 } 01048 if (version_id >= 4) { 01049 this->fontinfo_table_.read(File, NewPermanentTessCallback(read_info), swap); 01050 if (version_id >= 5) { 01051 this->fontinfo_table_.read(File, 01052 NewPermanentTessCallback(read_spacing_info), 01053 swap); 01054 } 01055 this->fontset_table_.read(File, NewPermanentTessCallback(read_set), swap); 01056 } 01057 01058 // Clean up. 01059 delete[] IndexFor; 01060 delete[] ClassIdFor; 01061 delete[] TempClassPruner; 01062 01063 return (Templates); 01064 } /* ReadIntTemplates */ 01065 01066 01067 #ifndef GRAPHICS_DISABLED 01068 01079 void Classify::ShowMatchDisplay() { 01080 InitIntMatchWindowIfReqd(); 01081 if (ProtoDisplayWindow) { 01082 ProtoDisplayWindow->Clear(); 01083 } 01084 if (FeatureDisplayWindow) { 01085 FeatureDisplayWindow->Clear(); 01086 } 01087 ClearFeatureSpaceWindow( 01088 static_cast<NORM_METHOD>(static_cast<int>(classify_norm_method)), 01089 IntMatchWindow); 01090 IntMatchWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y, 01091 INT_MAX_X, INT_MAX_Y); 01092 if (ProtoDisplayWindow) { 01093 ProtoDisplayWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y, 01094 INT_MAX_X, INT_MAX_Y); 01095 } 01096 if (FeatureDisplayWindow) { 01097 FeatureDisplayWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y, 01098 INT_MAX_X, INT_MAX_Y); 01099 } 01100 } /* ShowMatchDisplay */ 01101 01104 void ClearFeatureSpaceWindow(NORM_METHOD norm_method, ScrollView* window) { 01105 window->Clear(); 01106 01107 window->Pen(ScrollView::GREY); 01108 // Draw the feature space limit rectangle. 01109 window->Rectangle(0, 0, INT_MAX_X, INT_MAX_Y); 01110 if (norm_method == baseline) { 01111 window->SetCursor(0, INT_DESCENDER); 01112 window->DrawTo(INT_MAX_X, INT_DESCENDER); 01113 window->SetCursor(0, INT_BASELINE); 01114 window->DrawTo(INT_MAX_X, INT_BASELINE); 01115 window->SetCursor(0, INT_XHEIGHT); 01116 window->DrawTo(INT_MAX_X, INT_XHEIGHT); 01117 window->SetCursor(0, INT_CAPHEIGHT); 01118 window->DrawTo(INT_MAX_X, INT_CAPHEIGHT); 01119 } else { 01120 window->Rectangle(INT_XCENTER - INT_XRADIUS, INT_YCENTER - INT_YRADIUS, 01121 INT_XCENTER + INT_XRADIUS, INT_YCENTER + INT_YRADIUS); 01122 } 01123 } 01124 #endif 01125 01138 void Classify::WriteIntTemplates(FILE *File, INT_TEMPLATES Templates, 01139 const UNICHARSET& target_unicharset) { 01140 int i, j; 01141 INT_CLASS Class; 01142 int unicharset_size = target_unicharset.size(); 01143 int version_id = -5; // When negated by the reader -1 becomes +1 etc. 01144 01145 if (Templates->NumClasses != unicharset_size) { 01146 cprintf("Warning: executing WriteIntTemplates() with %d classes in" 01147 " Templates, while target_unicharset size is %d\n", 01148 Templates->NumClasses, unicharset_size); 01149 } 01150 01151 /* first write the high level template struct */ 01152 fwrite(&unicharset_size, sizeof(unicharset_size), 1, File); 01153 fwrite(&version_id, sizeof(version_id), 1, File); 01154 fwrite(&Templates->NumClassPruners, sizeof(Templates->NumClassPruners), 01155 1, File); 01156 fwrite(&Templates->NumClasses, sizeof(Templates->NumClasses), 1, File); 01157 01158 /* then write out the class pruners */ 01159 for (i = 0; i < Templates->NumClassPruners; i++) 01160 fwrite(Templates->ClassPruners[i], 01161 sizeof(CLASS_PRUNER_STRUCT), 1, File); 01162 01163 /* then write out each class */ 01164 for (i = 0; i < Templates->NumClasses; i++) { 01165 Class = Templates->Class[i]; 01166 01167 /* first write out the high level struct for the class */ 01168 fwrite(&Class->NumProtos, sizeof(Class->NumProtos), 1, File); 01169 fwrite(&Class->NumProtoSets, sizeof(Class->NumProtoSets), 1, File); 01170 ASSERT_HOST(Class->NumConfigs == this->fontset_table_.get(Class->font_set_id).size); 01171 fwrite(&Class->NumConfigs, sizeof(Class->NumConfigs), 1, File); 01172 for (j = 0; j < Class->NumConfigs; ++j) { 01173 fwrite(&Class->ConfigLengths[j], sizeof(uinT16), 1, File); 01174 } 01175 01176 /* then write out the proto lengths */ 01177 if (MaxNumIntProtosIn (Class) > 0) { 01178 fwrite ((char *) (Class->ProtoLengths), sizeof (uinT8), 01179 MaxNumIntProtosIn (Class), File); 01180 } 01181 01182 /* then write out the proto sets */ 01183 for (j = 0; j < Class->NumProtoSets; j++) 01184 fwrite ((char *) Class->ProtoSets[j], 01185 sizeof (PROTO_SET_STRUCT), 1, File); 01186 01187 /* then write the fonts info */ 01188 fwrite(&Class->font_set_id, sizeof(int), 1, File); 01189 } 01190 01191 /* Write the fonts info tables */ 01192 this->fontinfo_table_.write(File, NewPermanentTessCallback(write_info)); 01193 this->fontinfo_table_.write(File, 01194 NewPermanentTessCallback(write_spacing_info)); 01195 this->fontset_table_.write(File, NewPermanentTessCallback(write_set)); 01196 } /* WriteIntTemplates */ 01197 } // namespace tesseract 01198 01199 01200 /*----------------------------------------------------------------------------- 01201 Private Code 01202 -----------------------------------------------------------------------------*/ 01216 FLOAT32 BucketStart(int Bucket, FLOAT32 Offset, int NumBuckets) { 01217 return (((FLOAT32) Bucket / NumBuckets) - Offset); 01218 01219 } /* BucketStart */ 01220 01221 01235 FLOAT32 BucketEnd(int Bucket, FLOAT32 Offset, int NumBuckets) { 01236 return (((FLOAT32) (Bucket + 1) / NumBuckets) - Offset); 01237 } /* BucketEnd */ 01238 01239 01254 void DoFill(FILL_SPEC *FillSpec, 01255 CLASS_PRUNER_STRUCT* Pruner, 01256 register uinT32 ClassMask, 01257 register uinT32 ClassCount, 01258 register uinT32 WordIndex) { 01259 int X, Y, Angle; 01260 uinT32 OldWord; 01261 01262 X = FillSpec->X; 01263 if (X < 0) 01264 X = 0; 01265 if (X >= NUM_CP_BUCKETS) 01266 X = NUM_CP_BUCKETS - 1; 01267 01268 if (FillSpec->YStart < 0) 01269 FillSpec->YStart = 0; 01270 if (FillSpec->YEnd >= NUM_CP_BUCKETS) 01271 FillSpec->YEnd = NUM_CP_BUCKETS - 1; 01272 01273 for (Y = FillSpec->YStart; Y <= FillSpec->YEnd; Y++) 01274 for (Angle = FillSpec->AngleStart; 01275 TRUE; CircularIncrement (Angle, NUM_CP_BUCKETS)) { 01276 OldWord = Pruner->p[X][Y][Angle][WordIndex]; 01277 if (ClassCount > (OldWord & ClassMask)) { 01278 OldWord &= ~ClassMask; 01279 OldWord |= ClassCount; 01280 Pruner->p[X][Y][Angle][WordIndex] = OldWord; 01281 } 01282 if (Angle == FillSpec->AngleEnd) 01283 break; 01284 } 01285 } /* DoFill */ 01286 01287 01297 BOOL8 FillerDone(TABLE_FILLER *Filler) { 01298 FILL_SWITCH *Next; 01299 01300 Next = &(Filler->Switch[Filler->NextSwitch]); 01301 01302 if (Filler->X > Next->X && Next->Type == LastSwitch) 01303 return (TRUE); 01304 else 01305 return (FALSE); 01306 01307 } /* FillerDone */ 01308 01309 01327 void FillPPCircularBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], 01328 int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug) { 01329 int i, FirstBucket, LastBucket; 01330 01331 if (Spread > 0.5) 01332 Spread = 0.5; 01333 01334 FirstBucket = (int) floor ((Center - Spread) * NUM_PP_BUCKETS); 01335 if (FirstBucket < 0) 01336 FirstBucket += NUM_PP_BUCKETS; 01337 01338 LastBucket = (int) floor ((Center + Spread) * NUM_PP_BUCKETS); 01339 if (LastBucket >= NUM_PP_BUCKETS) 01340 LastBucket -= NUM_PP_BUCKETS; 01341 if (debug) tprintf("Circular fill from %d to %d", FirstBucket, LastBucket); 01342 for (i = FirstBucket; TRUE; CircularIncrement (i, NUM_PP_BUCKETS)) { 01343 SET_BIT (ParamTable[i], Bit); 01344 01345 /* exit loop after we have set the bit for the last bucket */ 01346 if (i == LastBucket) 01347 break; 01348 } 01349 01350 } /* FillPPCircularBits */ 01351 01352 01371 void FillPPLinearBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR], 01372 int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug) { 01373 int i, FirstBucket, LastBucket; 01374 01375 FirstBucket = (int) floor ((Center - Spread) * NUM_PP_BUCKETS); 01376 if (FirstBucket < 0) 01377 FirstBucket = 0; 01378 01379 LastBucket = (int) floor ((Center + Spread) * NUM_PP_BUCKETS); 01380 if (LastBucket >= NUM_PP_BUCKETS) 01381 LastBucket = NUM_PP_BUCKETS - 1; 01382 01383 if (debug) tprintf("Linear fill from %d to %d", FirstBucket, LastBucket); 01384 for (i = FirstBucket; i <= LastBucket; i++) 01385 SET_BIT (ParamTable[i], Bit); 01386 01387 } /* FillPPLinearBits */ 01388 01389 01390 /*---------------------------------------------------------------------------*/ 01391 #ifndef GRAPHICS_DISABLED 01392 namespace tesseract { 01405 CLASS_ID Classify::GetClassToDebug(const char *Prompt, bool* adaptive_on, 01406 bool* pretrained_on, int* shape_id) { 01407 tprintf("%s\n", Prompt); 01408 SVEvent* ev; 01409 SVEventType ev_type; 01410 int unichar_id = INVALID_UNICHAR_ID; 01411 // Wait until a click or popup event. 01412 do { 01413 ev = IntMatchWindow->AwaitEvent(SVET_ANY); 01414 ev_type = ev->type; 01415 if (ev_type == SVET_POPUP) { 01416 if (ev->command_id == IDA_SHAPE_INDEX) { 01417 if (shape_table_ != NULL) { 01418 *shape_id = atoi(ev->parameter); 01419 *adaptive_on = false; 01420 *pretrained_on = true; 01421 if (*shape_id >= 0 && *shape_id < shape_table_->NumShapes()) { 01422 int font_id; 01423 shape_table_->GetFirstUnicharAndFont(*shape_id, &unichar_id, 01424 &font_id); 01425 tprintf("Shape %d, first unichar=%d, font=%d\n", 01426 *shape_id, unichar_id, font_id); 01427 return unichar_id; 01428 } 01429 tprintf("Shape index '%s' not found in shape table\n", ev->parameter); 01430 } else { 01431 tprintf("No shape table loaded!\n"); 01432 } 01433 } else { 01434 if (unicharset.contains_unichar(ev->parameter)) { 01435 unichar_id = unicharset.unichar_to_id(ev->parameter); 01436 if (ev->command_id == IDA_ADAPTIVE) { 01437 *adaptive_on = true; 01438 *pretrained_on = false; 01439 *shape_id = -1; 01440 } else if (ev->command_id == IDA_STATIC) { 01441 *adaptive_on = false; 01442 *pretrained_on = true; 01443 } else { 01444 *adaptive_on = true; 01445 *pretrained_on = true; 01446 } 01447 if (ev->command_id == IDA_ADAPTIVE || shape_table_ == NULL) { 01448 *shape_id = -1; 01449 return unichar_id; 01450 } 01451 for (int s = 0; s < shape_table_->NumShapes(); ++s) { 01452 if (shape_table_->GetShape(s).ContainsUnichar(unichar_id)) { 01453 tprintf("%s\n", shape_table_->DebugStr(s).string()); 01454 } 01455 } 01456 } else { 01457 tprintf("Char class '%s' not found in unicharset", 01458 ev->parameter); 01459 } 01460 } 01461 } 01462 delete ev; 01463 } while (ev_type != SVET_CLICK); 01464 return 0; 01465 } /* GetClassToDebug */ 01466 01467 } // namespace tesseract 01468 #endif 01469 01485 void GetCPPadsForLevel(int Level, 01486 FLOAT32 *EndPad, 01487 FLOAT32 *SidePad, 01488 FLOAT32 *AnglePad) { 01489 switch (Level) { 01490 case 0: 01491 *EndPad = classify_cp_end_pad_loose * GetPicoFeatureLength (); 01492 *SidePad = classify_cp_side_pad_loose * GetPicoFeatureLength (); 01493 *AnglePad = classify_cp_angle_pad_loose / 360.0; 01494 break; 01495 01496 case 1: 01497 *EndPad = classify_cp_end_pad_medium * GetPicoFeatureLength (); 01498 *SidePad = classify_cp_side_pad_medium * GetPicoFeatureLength (); 01499 *AnglePad = classify_cp_angle_pad_medium / 360.0; 01500 break; 01501 01502 case 2: 01503 *EndPad = classify_cp_end_pad_tight * GetPicoFeatureLength (); 01504 *SidePad = classify_cp_side_pad_tight * GetPicoFeatureLength (); 01505 *AnglePad = classify_cp_angle_pad_tight / 360.0; 01506 break; 01507 01508 default: 01509 *EndPad = classify_cp_end_pad_tight * GetPicoFeatureLength (); 01510 *SidePad = classify_cp_side_pad_tight * GetPicoFeatureLength (); 01511 *AnglePad = classify_cp_angle_pad_tight / 360.0; 01512 break; 01513 } 01514 if (*AnglePad > 0.5) 01515 *AnglePad = 0.5; 01516 01517 } /* GetCPPadsForLevel */ 01518 01519 01527 ScrollView::Color GetMatchColorFor(FLOAT32 Evidence) { 01528 assert (Evidence >= 0.0); 01529 assert (Evidence <= 1.0); 01530 01531 if (Evidence >= 0.90) 01532 return ScrollView::WHITE; 01533 else if (Evidence >= 0.75) 01534 return ScrollView::GREEN; 01535 else if (Evidence >= 0.50) 01536 return ScrollView::RED; 01537 else 01538 return ScrollView::BLUE; 01539 } /* GetMatchColorFor */ 01540 01541 01554 void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill) { 01555 FILL_SWITCH *Next; 01556 01557 /* compute the fill assuming no switches will be encountered */ 01558 Fill->AngleStart = Filler->AngleStart; 01559 Fill->AngleEnd = Filler->AngleEnd; 01560 Fill->X = Filler->X; 01561 Fill->YStart = Filler->YStart >> 8; 01562 Fill->YEnd = Filler->YEnd >> 8; 01563 01564 /* update the fill info and the filler for ALL switches at this X value */ 01565 Next = &(Filler->Switch[Filler->NextSwitch]); 01566 while (Filler->X >= Next->X) { 01567 Fill->X = Filler->X = Next->X; 01568 if (Next->Type == StartSwitch) { 01569 Fill->YStart = Next->Y; 01570 Filler->StartDelta = Next->Delta; 01571 Filler->YStart = Next->YInit; 01572 } 01573 else if (Next->Type == EndSwitch) { 01574 Fill->YEnd = Next->Y; 01575 Filler->EndDelta = Next->Delta; 01576 Filler->YEnd = Next->YInit; 01577 } 01578 else { /* Type must be LastSwitch */ 01579 break; 01580 } 01581 Filler->NextSwitch++; 01582 Next = &(Filler->Switch[Filler->NextSwitch]); 01583 } 01584 01585 /* prepare the filler for the next call to this routine */ 01586 Filler->X++; 01587 Filler->YStart += Filler->StartDelta; 01588 Filler->YEnd += Filler->EndDelta; 01589 01590 } /* GetNextFill */ 01591 01592 01607 void InitTableFiller (FLOAT32 EndPad, FLOAT32 SidePad, 01608 FLOAT32 AnglePad, PROTO Proto, TABLE_FILLER * Filler) 01609 #define XS X_SHIFT 01610 #define YS Y_SHIFT 01611 #define AS ANGLE_SHIFT 01612 #define NB NUM_CP_BUCKETS 01613 { 01614 FLOAT32 Angle; 01615 FLOAT32 X, Y, HalfLength; 01616 FLOAT32 Cos, Sin; 01617 FLOAT32 XAdjust, YAdjust; 01618 FPOINT Start, Switch1, Switch2, End; 01619 int S1 = 0; 01620 int S2 = 1; 01621 01622 Angle = Proto->Angle; 01623 X = Proto->X; 01624 Y = Proto->Y; 01625 HalfLength = Proto->Length / 2.0; 01626 01627 Filler->AngleStart = CircBucketFor(Angle - AnglePad, AS, NB); 01628 Filler->AngleEnd = CircBucketFor(Angle + AnglePad, AS, NB); 01629 Filler->NextSwitch = 0; 01630 01631 if (fabs (Angle - 0.0) < HV_TOLERANCE || fabs (Angle - 0.5) < HV_TOLERANCE) { 01632 /* horizontal proto - handle as special case */ 01633 Filler->X = Bucket8For(X - HalfLength - EndPad, XS, NB); 01634 Filler->YStart = Bucket16For(Y - SidePad, YS, NB * 256); 01635 Filler->YEnd = Bucket16For(Y + SidePad, YS, NB * 256); 01636 Filler->StartDelta = 0; 01637 Filler->EndDelta = 0; 01638 Filler->Switch[0].Type = LastSwitch; 01639 Filler->Switch[0].X = Bucket8For(X + HalfLength + EndPad, XS, NB); 01640 } else if (fabs(Angle - 0.25) < HV_TOLERANCE || 01641 fabs(Angle - 0.75) < HV_TOLERANCE) { 01642 /* vertical proto - handle as special case */ 01643 Filler->X = Bucket8For(X - SidePad, XS, NB); 01644 Filler->YStart = Bucket16For(Y - HalfLength - EndPad, YS, NB * 256); 01645 Filler->YEnd = Bucket16For(Y + HalfLength + EndPad, YS, NB * 256); 01646 Filler->StartDelta = 0; 01647 Filler->EndDelta = 0; 01648 Filler->Switch[0].Type = LastSwitch; 01649 Filler->Switch[0].X = Bucket8For(X + SidePad, XS, NB); 01650 } else { 01651 /* diagonal proto */ 01652 01653 if ((Angle > 0.0 && Angle < 0.25) || (Angle > 0.5 && Angle < 0.75)) { 01654 /* rising diagonal proto */ 01655 Angle *= 2.0 * PI; 01656 Cos = fabs(cos(Angle)); 01657 Sin = fabs(sin(Angle)); 01658 01659 /* compute the positions of the corners of the acceptance region */ 01660 Start.x = X - (HalfLength + EndPad) * Cos - SidePad * Sin; 01661 Start.y = Y - (HalfLength + EndPad) * Sin + SidePad * Cos; 01662 End.x = 2.0 * X - Start.x; 01663 End.y = 2.0 * Y - Start.y; 01664 Switch1.x = X - (HalfLength + EndPad) * Cos + SidePad * Sin; 01665 Switch1.y = Y - (HalfLength + EndPad) * Sin - SidePad * Cos; 01666 Switch2.x = 2.0 * X - Switch1.x; 01667 Switch2.y = 2.0 * Y - Switch1.y; 01668 01669 if (Switch1.x > Switch2.x) { 01670 S1 = 1; 01671 S2 = 0; 01672 } 01673 01674 /* translate into bucket positions and deltas */ 01675 Filler->X = Bucket8For(Start.x, XS, NB); 01676 Filler->StartDelta = -(inT16) ((Cos / Sin) * 256); 01677 Filler->EndDelta = (inT16) ((Sin / Cos) * 256); 01678 01679 XAdjust = BucketEnd(Filler->X, XS, NB) - Start.x; 01680 YAdjust = XAdjust * Cos / Sin; 01681 Filler->YStart = Bucket16For(Start.y - YAdjust, YS, NB * 256); 01682 YAdjust = XAdjust * Sin / Cos; 01683 Filler->YEnd = Bucket16For(Start.y + YAdjust, YS, NB * 256); 01684 01685 Filler->Switch[S1].Type = StartSwitch; 01686 Filler->Switch[S1].X = Bucket8For(Switch1.x, XS, NB); 01687 Filler->Switch[S1].Y = Bucket8For(Switch1.y, YS, NB); 01688 XAdjust = Switch1.x - BucketStart(Filler->Switch[S1].X, XS, NB); 01689 YAdjust = XAdjust * Sin / Cos; 01690 Filler->Switch[S1].YInit = Bucket16For(Switch1.y - YAdjust, YS, NB * 256); 01691 Filler->Switch[S1].Delta = Filler->EndDelta; 01692 01693 Filler->Switch[S2].Type = EndSwitch; 01694 Filler->Switch[S2].X = Bucket8For(Switch2.x, XS, NB); 01695 Filler->Switch[S2].Y = Bucket8For(Switch2.y, YS, NB); 01696 XAdjust = Switch2.x - BucketStart(Filler->Switch[S2].X, XS, NB); 01697 YAdjust = XAdjust * Cos / Sin; 01698 Filler->Switch[S2].YInit = Bucket16For(Switch2.y + YAdjust, YS, NB * 256); 01699 Filler->Switch[S2].Delta = Filler->StartDelta; 01700 01701 Filler->Switch[2].Type = LastSwitch; 01702 Filler->Switch[2].X = Bucket8For(End.x, XS, NB); 01703 } else { 01704 /* falling diagonal proto */ 01705 Angle *= 2.0 * PI; 01706 Cos = fabs(cos(Angle)); 01707 Sin = fabs(sin(Angle)); 01708 01709 /* compute the positions of the corners of the acceptance region */ 01710 Start.x = X - (HalfLength + EndPad) * Cos - SidePad * Sin; 01711 Start.y = Y + (HalfLength + EndPad) * Sin - SidePad * Cos; 01712 End.x = 2.0 * X - Start.x; 01713 End.y = 2.0 * Y - Start.y; 01714 Switch1.x = X - (HalfLength + EndPad) * Cos + SidePad * Sin; 01715 Switch1.y = Y + (HalfLength + EndPad) * Sin + SidePad * Cos; 01716 Switch2.x = 2.0 * X - Switch1.x; 01717 Switch2.y = 2.0 * Y - Switch1.y; 01718 01719 if (Switch1.x > Switch2.x) { 01720 S1 = 1; 01721 S2 = 0; 01722 } 01723 01724 /* translate into bucket positions and deltas */ 01725 Filler->X = Bucket8For(Start.x, XS, NB); 01726 Filler->StartDelta = -(inT16) ((Sin / Cos) * 256); 01727 Filler->EndDelta = (inT16) ((Cos / Sin) * 256); 01728 01729 XAdjust = BucketEnd(Filler->X, XS, NB) - Start.x; 01730 YAdjust = XAdjust * Sin / Cos; 01731 Filler->YStart = Bucket16For(Start.y - YAdjust, YS, NB * 256); 01732 YAdjust = XAdjust * Cos / Sin; 01733 Filler->YEnd = Bucket16For(Start.y + YAdjust, YS, NB * 256); 01734 01735 Filler->Switch[S1].Type = EndSwitch; 01736 Filler->Switch[S1].X = Bucket8For(Switch1.x, XS, NB); 01737 Filler->Switch[S1].Y = Bucket8For(Switch1.y, YS, NB); 01738 XAdjust = Switch1.x - BucketStart(Filler->Switch[S1].X, XS, NB); 01739 YAdjust = XAdjust * Sin / Cos; 01740 Filler->Switch[S1].YInit = Bucket16For(Switch1.y + YAdjust, YS, NB * 256); 01741 Filler->Switch[S1].Delta = Filler->StartDelta; 01742 01743 Filler->Switch[S2].Type = StartSwitch; 01744 Filler->Switch[S2].X = Bucket8For(Switch2.x, XS, NB); 01745 Filler->Switch[S2].Y = Bucket8For(Switch2.y, YS, NB); 01746 XAdjust = Switch2.x - BucketStart(Filler->Switch[S2].X, XS, NB); 01747 YAdjust = XAdjust * Cos / Sin; 01748 Filler->Switch[S2].YInit = Bucket16For(Switch2.y - YAdjust, YS, NB * 256); 01749 Filler->Switch[S2].Delta = Filler->EndDelta; 01750 01751 Filler->Switch[2].Type = LastSwitch; 01752 Filler->Switch[2].X = Bucket8For(End.x, XS, NB); 01753 } 01754 } 01755 } /* InitTableFiller */ 01756 01757 01758 /*---------------------------------------------------------------------------*/ 01759 #ifndef GRAPHICS_DISABLED 01760 01770 void RenderIntFeature(ScrollView *window, const INT_FEATURE_STRUCT* Feature, 01771 ScrollView::Color color) { 01772 FLOAT32 X, Y, Dx, Dy, Length; 01773 01774 window->Pen(color); 01775 assert(Feature != NULL); 01776 assert(color != 0); 01777 01778 X = Feature->X; 01779 Y = Feature->Y; 01780 Length = GetPicoFeatureLength() * 0.7 * INT_CHAR_NORM_RANGE; 01781 // The -PI has no significant effect here, but the value of Theta is computed 01782 // using BinaryAnglePlusPi in intfx.cpp. 01783 Dx = (Length / 2.0) * cos((Feature->Theta / 256.0) * 2.0 * PI - PI); 01784 Dy = (Length / 2.0) * sin((Feature->Theta / 256.0) * 2.0 * PI - PI); 01785 01786 window->SetCursor(X, Y); 01787 window->DrawTo(X + Dx, Y + Dy); 01788 } /* RenderIntFeature */ 01789 01790 01807 void RenderIntProto(ScrollView *window, 01808 INT_CLASS Class, 01809 PROTO_ID ProtoId, 01810 ScrollView::Color color) { 01811 PROTO_SET ProtoSet; 01812 INT_PROTO Proto; 01813 int ProtoSetIndex; 01814 int ProtoWordIndex; 01815 FLOAT32 Length; 01816 int Xmin, Xmax, Ymin, Ymax; 01817 FLOAT32 X, Y, Dx, Dy; 01818 uinT32 ProtoMask; 01819 int Bucket; 01820 01821 assert(ProtoId >= 0); 01822 assert(Class != NULL); 01823 assert(ProtoId < Class->NumProtos); 01824 assert(color != 0); 01825 window->Pen(color); 01826 01827 ProtoSet = Class->ProtoSets[SetForProto(ProtoId)]; 01828 ProtoSetIndex = IndexForProto(ProtoId); 01829 Proto = &(ProtoSet->Protos[ProtoSetIndex]); 01830 Length = (Class->ProtoLengths[ProtoId] * 01831 GetPicoFeatureLength() * INT_CHAR_NORM_RANGE); 01832 ProtoMask = PPrunerMaskFor(ProtoId); 01833 ProtoWordIndex = PPrunerWordIndexFor(ProtoId); 01834 01835 // find the x and y extent of the proto from the proto pruning table 01836 Xmin = Ymin = NUM_PP_BUCKETS; 01837 Xmax = Ymax = 0; 01838 for (Bucket = 0; Bucket < NUM_PP_BUCKETS; Bucket++) { 01839 if (ProtoMask & ProtoSet->ProtoPruner[PRUNER_X][Bucket][ProtoWordIndex]) { 01840 UpdateRange(Bucket, &Xmin, &Xmax); 01841 } 01842 01843 if (ProtoMask & ProtoSet->ProtoPruner[PRUNER_Y][Bucket][ProtoWordIndex]) { 01844 UpdateRange(Bucket, &Ymin, &Ymax); 01845 } 01846 } 01847 X = (Xmin + Xmax + 1) / 2.0 * PROTO_PRUNER_SCALE; 01848 Y = (Ymin + Ymax + 1) / 2.0 * PROTO_PRUNER_SCALE; 01849 // The -PI has no significant effect here, but the value of Theta is computed 01850 // using BinaryAnglePlusPi in intfx.cpp. 01851 Dx = (Length / 2.0) * cos((Proto->Angle / 256.0) * 2.0 * PI - PI); 01852 Dy = (Length / 2.0) * sin((Proto->Angle / 256.0) * 2.0 * PI - PI); 01853 01854 window->SetCursor(X - Dx, Y - Dy); 01855 window->DrawTo(X + Dx, Y + Dy); 01856 } /* RenderIntProto */ 01857 #endif 01858 01874 int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id) { 01875 if (Param < Min) { 01876 if (Id) 01877 cprintf("Warning: Param %s truncated from %f to %d!\n", 01878 Id, Param, Min); 01879 Param = Min; 01880 } else if (Param > Max) { 01881 if (Id) 01882 cprintf("Warning: Param %s truncated from %f to %d!\n", 01883 Id, Param, Max); 01884 Param = Max; 01885 } 01886 return static_cast<int>(floor(Param)); 01887 } /* TruncateParam */ 01888 01889 01890 #ifndef GRAPHICS_DISABLED 01891 01895 void InitIntMatchWindowIfReqd() { 01896 if (IntMatchWindow == NULL) { 01897 IntMatchWindow = CreateFeatureSpaceWindow("IntMatchWindow", 50, 200); 01898 SVMenuNode* popup_menu = new SVMenuNode(); 01899 01900 popup_menu->AddChild("Debug Adapted classes", IDA_ADAPTIVE, 01901 "x", "Class to debug"); 01902 popup_menu->AddChild("Debug Static classes", IDA_STATIC, 01903 "x", "Class to debug"); 01904 popup_menu->AddChild("Debug Both", IDA_BOTH, 01905 "x", "Class to debug"); 01906 popup_menu->AddChild("Debug Shape Index", IDA_SHAPE_INDEX, 01907 "0", "Index to debug"); 01908 popup_menu->BuildMenu(IntMatchWindow, false); 01909 } 01910 } 01911 01916 void InitProtoDisplayWindowIfReqd() { 01917 if (ProtoDisplayWindow == NULL) { 01918 ProtoDisplayWindow = CreateFeatureSpaceWindow("ProtoDisplayWindow", 01919 550, 200); 01920 } 01921 } 01922 01927 void InitFeatureDisplayWindowIfReqd() { 01928 if (FeatureDisplayWindow == NULL) { 01929 FeatureDisplayWindow = CreateFeatureSpaceWindow("FeatureDisplayWindow", 01930 50, 700); 01931 } 01932 } 01933 01936 ScrollView* CreateFeatureSpaceWindow(const char* name, int xpos, int ypos) { 01937 return new ScrollView(name, xpos, ypos, 520, 520, 260, 260, true); 01938 } 01939 #endif // GRAPHICS_DISABLED