| /****************************************************************************** |
| ** Filename: MergeNF.c |
| ** Purpose: Program for merging similar nano-feature protos |
| ** Author: Dan Johnson |
| ** History: Wed Nov 21 09:55:23 1990, DSJ, Created. |
| ** |
| ** (c) Copyright Hewlett-Packard Company, 1988. |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| ******************************************************************************/ |
| /**---------------------------------------------------------------------------- |
| Include Files and Type Defines |
| ----------------------------------------------------------------------------**/ |
| #include "mergenf.h" |
| #include "general.h" |
| #include "efio.h" |
| #include "clusttool.h" |
| #include "cluster.h" |
| #include "oldlist.h" |
| #include "protos.h" |
| #include "ndminx.h" |
| #include "ocrfeatures.h" |
| #include "debug.h" |
| #include "const.h" |
| #include "featdefs.h" |
| #include "intproto.h" |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <math.h> |
| |
| |
| /**---------------------------------------------------------------------------- |
| Variables |
| -----------------------------------------------------------------------------**/ |
| /*-------------------once in subfeat---------------------------------*/ |
| make_float_var (AngleMatchScale, 1.0, MakeAngleMatchScale, |
| 7, 2, SetAngleMatchScale, "Angle Match Scale ...") |
| |
| make_float_var (SimilarityMidpoint, 0.0075, MakeSimilarityMidpoint, |
| 7, 3, SetSimilarityMidpoint, "Similarity Midpoint ...") |
| |
| make_float_var (SimilarityCurl, 2.0, MakeSimilarityCurl, |
| 7, 4, SetSimilarityCurl, "Similarity Curl ...") |
| |
| /*-----------------------------once in fasttrain----------------------------------*/ |
| make_float_var (TangentBBoxPad, 0.5, MakeTangentBBoxPad, |
| 15, 3, SetTangentBBoxPad, "Tangent bounding box pad ...") |
| |
| make_float_var (OrthogonalBBoxPad, 2.5, MakeOrthogonalBBoxPad, |
| 15, 4, SetOrthogonalBBoxPad, "Orthogonal bounding box pad ...") |
| |
| make_float_var (AnglePad, 45.0, MakeAnglePad, |
| 15, 5, SetAnglePad, "Angle pad ...") |
| |
| /**---------------------------------------------------------------------------- |
| Global Data Definitions and Declarations |
| ----------------------------------------------------------------------------**/ |
| //int row_number; /* kludge due to linking problems */ |
| |
| /**---------------------------------------------------------------------------- |
| Public Code |
| ----------------------------------------------------------------------------**/ |
| /*---------------------------------------------------------------------------*/ |
| FLOAT32 CompareProtos ( |
| PROTO p1, |
| PROTO p2) |
| |
| /* |
| ** Parameters: |
| ** p1, p2 protos to be compared |
| ** Globals: none |
| ** Operation: Compare protos p1 and p2 and return an estimate of the |
| ** worst evidence rating that will result for any part of p1 |
| ** that is compared to p2. In other words, if p1 were broken |
| ** into pico-features and each pico-feature was matched to p2, |
| ** what is the worst evidence rating that will be achieved for |
| ** any pico-feature. |
| ** Return: Worst possible result when matching p1 to p2. |
| ** Exceptions: none |
| ** History: Mon Nov 26 08:27:53 1990, DSJ, Created. |
| */ |
| |
| { |
| FEATURE Feature; |
| FLOAT32 WorstEvidence = WORST_EVIDENCE; |
| FLOAT32 Evidence; |
| FLOAT32 Angle, Length; |
| |
| /* if p1 and p2 are not close in length, don't let them match */ |
| Length = fabs (ProtoLength (p1) - ProtoLength (p2)); |
| if (Length > MAX_LENGTH_MISMATCH) |
| return (0.0); |
| |
| /* create a dummy pico-feature to be used for comparisons */ |
| Feature = NewFeature (&PicoFeatDesc); |
| ParamOf (Feature, PicoFeatDir) = ProtoAngle (p1); |
| |
| /* convert angle to radians */ |
| Angle = ProtoAngle (p1) * 2.0 * PI; |
| |
| /* find distance from center of p1 to 1/2 picofeat from end */ |
| Length = ProtoLength (p1) / 2.0 - GetPicoFeatureLength () / 2.0; |
| if (Length < 0) Length = 0; |
| |
| /* set the dummy pico-feature at one end of p1 and match it to p2 */ |
| ParamOf (Feature, PicoFeatX) = ProtoX (p1) + cos (Angle) * Length; |
| ParamOf (Feature, PicoFeatY) = ProtoY (p1) + sin (Angle) * Length; |
| if (DummyFastMatch (Feature, p2)) |
| { |
| Evidence = SubfeatureEvidence (Feature, p2); |
| if (Evidence < WorstEvidence) |
| WorstEvidence = Evidence; |
| } |
| else |
| { |
| FreeFeature (Feature); |
| return (0.0); |
| } |
| |
| /* set the dummy pico-feature at the other end of p1 and match it to p2 */ |
| ParamOf (Feature, PicoFeatX) = ProtoX (p1) - cos (Angle) * Length; |
| ParamOf (Feature, PicoFeatY) = ProtoY (p1) - sin (Angle) * Length; |
| if (DummyFastMatch (Feature, p2)) |
| { |
| Evidence = SubfeatureEvidence (Feature, p2); |
| if (Evidence < WorstEvidence) |
| WorstEvidence = Evidence; |
| } |
| else |
| { |
| FreeFeature (Feature); |
| return (0.0); |
| } |
| |
| FreeFeature (Feature); |
| return (WorstEvidence); |
| |
| } /* CompareProtos */ |
| |
| /*---------------------------------------------------------------------------*/ |
| void ComputeMergedProto ( |
| PROTO p1, |
| PROTO p2, |
| FLOAT32 w1, |
| FLOAT32 w2, |
| PROTO MergedProto) |
| |
| /* |
| ** Parameters: |
| ** p1, p2 protos to be merged |
| ** w1, w2 weight of each proto |
| ** MergedProto place to put resulting merged proto |
| ** Globals: none |
| ** Operation: This routine computes a proto which is the weighted |
| ** average of protos p1 and p2. The new proto is returned |
| ** in MergedProto. |
| ** Return: none (results are returned in MergedProto) |
| ** Exceptions: none |
| ** History: Mon Nov 26 08:15:08 1990, DSJ, Created. |
| */ |
| |
| { |
| FLOAT32 TotalWeight; |
| |
| TotalWeight = w1 + w2; |
| w1 /= TotalWeight; |
| w2 /= TotalWeight; |
| |
| ProtoX (MergedProto) = ProtoX (p1) * w1 + ProtoX (p2) * w2; |
| ProtoY (MergedProto) = ProtoY (p1) * w1 + ProtoY (p2) * w2; |
| ProtoLength (MergedProto) = ProtoLength (p1) * w1 + ProtoLength (p2) * w2; |
| ProtoAngle (MergedProto) = ProtoAngle (p1) * w1 + ProtoAngle (p2) * w2; |
| FillABC (MergedProto); |
| |
| } /* ComputeMergedProto */ |
| |
| /*---------------------------------------------------------------------------*/ |
| int FindClosestExistingProto ( |
| CLASS_TYPE Class, |
| int NumMerged[], |
| PROTOTYPE *Prototype) |
| |
| /* |
| ** Parameters: |
| ** Class class to search for matching old proto in |
| ** NumMerged[] # of protos merged into each proto of Class |
| ** Prototype new proto to find match for |
| ** Globals: none |
| ** Operation: This routine searches thru all of the prototypes in |
| ** Class and returns the id of the proto which would provide |
| ** the best approximation of Prototype. If no close |
| ** approximation can be found, NO_PROTO is returned. |
| ** Return: Id of closest proto in Class or NO_PROTO. |
| ** Exceptions: none |
| ** History: Sat Nov 24 11:42:58 1990, DSJ, Created. |
| */ |
| |
| { |
| PROTO_STRUCT NewProto; |
| PROTO_STRUCT MergedProto; |
| int Pid; |
| PROTO Proto; |
| int BestProto; |
| FLOAT32 BestMatch; |
| FLOAT32 Match, OldMatch, NewMatch; |
| |
| MakeNewFromOld (&NewProto, Prototype); |
| |
| BestProto = NO_PROTO; |
| BestMatch = WORST_MATCH_ALLOWED; |
| for (Pid = 0; Pid < NumProtosIn (Class); Pid++) |
| { |
| Proto = ProtoIn (Class, Pid); |
| ComputeMergedProto (Proto, &NewProto, |
| (FLOAT32) NumMerged[Pid], 1.0, &MergedProto); |
| OldMatch = CompareProtos (Proto, &MergedProto); |
| NewMatch = CompareProtos (&NewProto, &MergedProto); |
| Match = MIN (OldMatch, NewMatch); |
| if (Match > BestMatch) |
| { |
| BestProto = Pid; |
| BestMatch = Match; |
| } |
| } |
| return (BestProto); |
| |
| } /* FindClosestExistingProto */ |
| |
| /*---------------------------------------------------------------------------*/ |
| void MakeNewFromOld ( |
| PROTO New, |
| PROTOTYPE *Old) |
| |
| /* |
| ** Parameters: |
| ** New new proto to be filled in |
| ** Old old proto to be converted |
| ** Globals: none |
| ** Operation: This fills in the fields of the New proto based on the |
| ** fields of the Old proto. |
| ** Return: none |
| ** Exceptions: none |
| ** History: Mon Nov 26 09:45:39 1990, DSJ, Created. |
| */ |
| |
| { |
| ProtoX (New) = CenterX (Old->Mean); |
| ProtoY (New) = CenterY (Old->Mean); |
| ProtoLength (New) = LengthOf (Old->Mean); |
| ProtoAngle (New) = OrientationOf (Old->Mean); |
| FillABC (New); |
| |
| } /* MakeNewFromOld */ |
| |
| /*-------------------once in subfeat---------------------------------*/ |
| /********************************************************************** |
| * InitSubfeatureVars |
| * |
| * Create and set up all menus and variables needed for this file. |
| **********************************************************************/ |
| void InitSubfeatureVars () |
| { |
| MakeAngleMatchScale (); |
| MakeSimilarityCurl (); |
| MakeSimilarityMidpoint (); |
| } |
| |
| |
| /********************************************************************** |
| * SubfeatureEvidence |
| * |
| * Compare a feature to a prototype. Print the result. |
| **********************************************************************/ |
| FLOAT32 SubfeatureEvidence ( |
| FEATURE Feature, |
| PROTO Proto) |
| { |
| float Distance; |
| float Dangle; |
| |
| Dangle = ProtoAngle (Proto) - ParamOf(Feature, PicoFeatDir); |
| if (Dangle < -0.5) Dangle += 1.0; |
| if (Dangle > 0.5) Dangle -= 1.0; |
| Dangle *= AngleMatchScale; |
| |
| Distance = CoefficientA (Proto) * ParamOf(Feature, PicoFeatX) + |
| CoefficientB (Proto) * ParamOf(Feature, PicoFeatY) + |
| CoefficientC (Proto); |
| |
| return (EvidenceOf (Distance * Distance + Dangle * Dangle)); |
| } |
| |
| /********************************************************************** |
| * EvidenceOf |
| * |
| * Return the new type of evidence number corresponding to this |
| * distance value. This number is no longer based on the chi squared |
| * approximation. The equation that represents the transform is: |
| * 1 / (1 + (sim / midpoint) ^ curl) |
| **********************************************************************/ |
| FLOAT32 EvidenceOf ( |
| register FLOAT32 Similarity) |
| { |
| |
| Similarity /= SimilarityMidpoint; |
| |
| if (SimilarityCurl == 3) |
| Similarity = Similarity * Similarity * Similarity; |
| else if (SimilarityCurl == 2) |
| Similarity = Similarity * Similarity; |
| else |
| Similarity = pow (Similarity, SimilarityCurl); |
| |
| return (1.0 / (1.0 + Similarity)); |
| } |
| |
| /*-----------------------------once in fasttrain----------------------------------*/ |
| void InitFastTrainerVars () |
| /* |
| ** Parameters: none |
| ** Globals: none |
| ** Operation: This routine initializes all of the control variables |
| ** for the fast trainer. |
| ** Return: none |
| ** Exceptions: none |
| ** History: Mon Nov 12 13:27:35 1990, DSJ, Created. |
| */ |
| |
| { |
| MakeTangentBBoxPad (); |
| MakeOrthogonalBBoxPad (); |
| MakeAnglePad (); |
| |
| } /* InitFastTrainerVars */ |
| |
| /*---------------------------------------------------------------------------*/ |
| BOOL8 DummyFastMatch ( |
| FEATURE Feature, |
| PROTO Proto) |
| |
| /* |
| ** Parameters: |
| ** Feature feature to be "fast matched" to proto |
| ** Proto proto being "fast matched" against |
| ** Globals: |
| ** TangentBBoxPad bounding box pad tangent to proto |
| ** OrthogonalBBoxPad bounding box pad orthogonal to proto |
| ** Operation: This routine returns TRUE if Feature would be matched |
| ** by a fast match table built from Proto. |
| ** Return: TRUE if feature could match Proto. |
| ** Exceptions: none |
| ** History: Wed Nov 14 17:19:58 1990, DSJ, Created. |
| */ |
| |
| { |
| FRECT BoundingBox; |
| FLOAT32 MaxAngleError; |
| FLOAT32 AngleError; |
| |
| MaxAngleError = AnglePad / 360.0; |
| AngleError = fabs (ProtoAngle (Proto) - ParamOf (Feature, PicoFeatDir)); |
| if (AngleError > 0.5) |
| AngleError = 1.0 - AngleError; |
| |
| if (AngleError > MaxAngleError) |
| return (FALSE); |
| |
| ComputePaddedBoundingBox (Proto, |
| TangentBBoxPad * GetPicoFeatureLength (), |
| OrthogonalBBoxPad * GetPicoFeatureLength (), |
| &BoundingBox); |
| |
| return (PointInside (&BoundingBox, |
| ParamOf (Feature, PicoFeatX), |
| ParamOf (Feature, PicoFeatY))); |
| |
| } /* DummyFastMatch */ |
| |
| /*----------------------------------------------------------------------------*/ |
| void ComputePaddedBoundingBox ( |
| PROTO Proto, |
| FLOAT32 TangentPad, |
| FLOAT32 OrthogonalPad, |
| FRECT *BoundingBox) |
| |
| /* |
| ** Parameters: |
| ** Proto proto to compute bounding box for |
| ** TangentPad amount of pad to add in direction of segment |
| ** OrthogonalPad amount of pad to add orthogonal to segment |
| ** BoundingBox place to put results |
| ** Globals: none |
| ** Operation: This routine computes a bounding box that encloses the |
| ** specified proto along with some padding. The |
| ** amount of padding is specified as separate distances |
| ** in the tangential and orthogonal directions. |
| ** Return: none (results are returned in BoundingBox) |
| ** Exceptions: none |
| ** History: Wed Nov 14 14:55:30 1990, DSJ, Created. |
| */ |
| |
| { |
| FLOAT32 Pad, Length, Angle; |
| FLOAT32 CosOfAngle, SinOfAngle; |
| |
| Length = ProtoLength (Proto) / 2.0 + TangentPad; |
| Angle = ProtoAngle (Proto) * 2.0 * PI; |
| CosOfAngle = fabs (cos (Angle)); |
| SinOfAngle = fabs (sin (Angle)); |
| |
| Pad = MAX (CosOfAngle * Length, SinOfAngle * OrthogonalPad); |
| BoundingBox->MinX = ProtoX (Proto) - Pad; |
| BoundingBox->MaxX = ProtoX (Proto) + Pad; |
| |
| Pad = MAX (SinOfAngle * Length, CosOfAngle * OrthogonalPad); |
| BoundingBox->MinY = ProtoY (Proto) - Pad; |
| BoundingBox->MaxY = ProtoY (Proto) + Pad; |
| |
| } /* ComputePaddedBoundingBox */ |
| |
| /*--------------------------------------------------------------------------*/ |
| BOOL8 PointInside ( |
| FRECT *Rectangle, |
| FLOAT32 X, |
| FLOAT32 Y) |
| |
| /* |
| ** Parameters: |
| ** Globals: none |
| ** Operation: Return TRUE if point (X,Y) is inside of Rectangle. |
| ** Return: Return TRUE if point (X,Y) is inside of Rectangle. |
| ** Exceptions: none |
| ** History: Wed Nov 14 17:26:35 1990, DSJ, Created. |
| */ |
| |
| { |
| if (X < Rectangle->MinX) return (FALSE); |
| if (X > Rectangle->MaxX) return (FALSE); |
| if (Y < Rectangle->MinY) return (FALSE); |
| if (Y > Rectangle->MaxY) return (FALSE); |
| return (TRUE); |
| |
| } /* PointInside */ |