blob: 90c871625109ba24201b7432adf6e27817319ce0 [file] [log] [blame]
/******************************************************************************
** Filename: mfx.c
** Purpose: Micro feature extraction routines
** Author: Dan Johnson
** History: 7/21/89, 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 "mfdefs.h"
#include "mfoutline.h"
#include "clusttool.h" //NEEDED
#include "const.h"
#include "intfx.h"
#include "varable.h"
#include <math.h>
/**----------------------------------------------------------------------------
Variables
----------------------------------------------------------------------------**/
/* old numbers corresponded to 10.0 degrees and 80.0 degrees */
double_VAR(classify_min_slope, 0.414213562,
"Slope below which lines are called horizontal");
double_VAR(classify_max_slope, 2.414213562,
"Slope above which lines are called vertical");
double_VAR(classify_noise_segment_length, 0.00,
"Length below which outline segments are treated as noise");
/**----------------------------------------------------------------------------
Macros
----------------------------------------------------------------------------**/
/* miscellaneous macros */
#define NormalizeAngle(A) ( (((A)<0)?((A)+2*PI):(A)) / (2*PI) )
/*----------------------------------------------------------------------------
Private Function Prototypes
-----------------------------------------------------------------------------*/
void ComputeBulges(MFOUTLINE Start, MFOUTLINE End, MICROFEATURE MicroFeature);
FLOAT32 ComputeOrientation(MFEDGEPT *Start, MFEDGEPT *End);
MICROFEATURES ConvertToMicroFeatures(MFOUTLINE Outline,
MICROFEATURES MicroFeatures);
MICROFEATURE ExtractMicroFeature(MFOUTLINE Start, MFOUTLINE End);
void SmearBulges(MICROFEATURES MicroFeatures, FLOAT32 XScale, FLOAT32 YScale);
/**----------------------------------------------------------------------------
Public Code
----------------------------------------------------------------------------**/
/*---------------------------------------------------------------------------*/
CHAR_FEATURES BlobMicroFeatures(TBLOB *Blob, LINE_STATS *LineStats) {
/*
** Parameters:
** Blob blob to extract micro-features from
** LineStats statistics for text line normalization
** Operation:
** This routine extracts micro-features from the specified
** blob and returns a list of the micro-features. All
** micro-features are normalized according to the specified
** line statistics.
** Return: List of micro-features extracted from the blob.
** Exceptions: none
** History: 7/21/89, DSJ, Created.
*/
MICROFEATURES MicroFeatures = NIL;
FLOAT32 XScale, YScale;
LIST Outlines;
LIST RemainingOutlines;
MFOUTLINE Outline;
INT_FEATURE_ARRAY blfeatures;
INT_FEATURE_ARRAY cnfeatures;
INT_FX_RESULT_STRUCT results;
if (Blob != NULL) {
Outlines = ConvertBlob (Blob);
// NormalizeOutlines(Outlines, LineStats, &XScale, &YScale);
if (!ExtractIntFeat(Blob, blfeatures, cnfeatures, &results))
return NULL;
XScale = 0.2f / results.Ry;
YScale = 0.2f / results.Rx;
RemainingOutlines = Outlines;
iterate(RemainingOutlines) {
Outline = (MFOUTLINE) first_node (RemainingOutlines);
CharNormalizeOutline (Outline,
results.Xmean, results.Ymean,
XScale, YScale);
}
RemainingOutlines = Outlines;
iterate(RemainingOutlines) {
Outline = (MFOUTLINE) first_node (RemainingOutlines);
FindDirectionChanges(Outline, classify_min_slope, classify_max_slope);
FilterEdgeNoise(Outline, classify_noise_segment_length);
MarkDirectionChanges(Outline);
SmearExtremities(Outline, XScale, YScale);
MicroFeatures = ConvertToMicroFeatures (Outline, MicroFeatures);
}
SmearBulges(MicroFeatures, XScale, YScale);
FreeOutlines(Outlines);
}
return ((CHAR_FEATURES) MicroFeatures);
} /* BlobMicroFeatures */
/**----------------------------------------------------------------------------
Private Macros
----------------------------------------------------------------------------**/
/**********************************************************************
* angle_of
*
* Return the angle of the line between two points.
**********************************************************************/
#define angle_of(x1,y1,x2,y2) \
((x2-x1) ? \
(atan2 (y2-y1, x2-x1)) : \
((y2<y1) ? (- PI / 2.0) : (PI / 2.0))) \
/**********************************************************************
* scale_angle
*
* Make sure that the angle is non-negative. Scale it to the right
* amount.
**********************************************************************/
#define scale_angle(x) \
(((x<0) ? (2.0 * PI + x) : (x)) * 0.5 / PI) \
/*---------------------------------------------------------------------------
Private Code
---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void ComputeBulges(MFOUTLINE Start, MFOUTLINE End, MICROFEATURE MicroFeature) {
/*
** Parameters:
** Start starting point of micro-feature
** End ending point of micro-feature
** MicroFeature micro-feature whose bulges are to be computed
** Globals: none
** Operation:
** This routine computes the size of the "bulges" of the
** specified micro-feature. The bulges are the deviations
** of the micro-features from a straight line at the 1/3
** and 2/3 points along the straight line approximation of
** the micro-feature. The size of each bulge is normalized
** to the range -0.5 to 0.5. A positive bulge indicates a
** deviation in the counterclockwise direction and vice versa.
** A size of 0.5 (+ or -) corresponds to the largest bulge that
** could ever occur for the given feature independent of
** orientation. This routine assumes that Start
** and End are not the same point. It also assumes that the
** orientation and length parameters of the micro-feature
** have already been computed.
** Return: none
** Exceptions: none
** History: 7/27/89, DSJ, Created.
*/
MATRIX_2D Matrix;
MFEDGEPT *Origin;
MFOUTLINE SegmentStart, SegmentEnd;
FPOINT CurrentPoint, LastPoint;
FLOAT32 BulgePosition;
/* check for simple case */
if (End == NextPointAfter (Start))
MicroFeature[FIRSTBULGE] = MicroFeature[SECONDBULGE] = 0;
else {
Origin = PointAt (Start);
InitMatrix(&Matrix);
RotateMatrix (&Matrix, MicroFeature[ORIENTATION] * -2.0 * PI);
TranslateMatrix (&Matrix, -Origin->Point.x, -Origin->Point.y);
SegmentEnd = Start;
CurrentPoint.x = 0.0f;
CurrentPoint.y = 0.0f;
BulgePosition = MicroFeature[MFLENGTH] / 3;
LastPoint = CurrentPoint;
while (CurrentPoint.x < BulgePosition) {
SegmentStart = SegmentEnd;
SegmentEnd = NextPointAfter (SegmentStart);
LastPoint = CurrentPoint;
MapPoint(&Matrix, PointAt(SegmentEnd)->Point, &CurrentPoint);
}
MicroFeature[FIRSTBULGE] =
XIntersectionOf(LastPoint, CurrentPoint, BulgePosition);
BulgePosition *= 2;
// Prevents from copying the points before computing the bulge if
// CurrentPoint will not change. (Which would cause to output nan
// for the SecondBulge.)
if (CurrentPoint.x < BulgePosition)
LastPoint = CurrentPoint;
while (CurrentPoint.x < BulgePosition) {
SegmentStart = SegmentEnd;
SegmentEnd = NextPointAfter (SegmentStart);
LastPoint = CurrentPoint;
MapPoint(&Matrix, PointAt(SegmentEnd)->Point, &CurrentPoint);
}
MicroFeature[SECONDBULGE] =
XIntersectionOf(LastPoint, CurrentPoint, BulgePosition);
MicroFeature[FIRSTBULGE] /= BULGENORMALIZER * MicroFeature[MFLENGTH];
MicroFeature[SECONDBULGE] /= BULGENORMALIZER * MicroFeature[MFLENGTH];
}
} /* ComputeBulges */
/*---------------------------------------------------------------------------*/
FLOAT32 ComputeOrientation(MFEDGEPT *Start, MFEDGEPT *End) {
/*
** Parameters:
** Start starting edge point of micro-feature
** End ending edge point of micro-feature
** Globals: none
** Operation:
** This routine computes the orientation parameter of the
** specified micro-feature. The orientation is the angle of
** the vector from Start to End. It is normalized to a number
** between 0 and 1 where 0 corresponds to 0 degrees and 1
** corresponds to 360 degrees. The actual range is [0,1), i.e.
** 1 is excluded from the range (since it is actual the
** same orientation as 0). This routine assumes that Start
** and End are not the same point.
** Return: Orientation parameter for the specified micro-feature.
** Exceptions: none
** History: 7/27/89, DSJ, Created.
*/
FLOAT32 Orientation;
Orientation = NormalizeAngle (AngleFrom (Start->Point, End->Point));
/* ensure that round-off errors do not put circular param out of range */
if ((Orientation < 0) || (Orientation >= 1))
Orientation = 0;
return (Orientation);
} /* ComputeOrientation */
/*---------------------------------------------------------------------------*/
MICROFEATURES ConvertToMicroFeatures(MFOUTLINE Outline,
MICROFEATURES MicroFeatures) {
/*
** Parameters:
** Outline outline to extract micro-features from
** MicroFeatures list of micro-features to add to
** Globals: none
** Operation:
** This routine
** Return: List of micro-features with new features added to front.
** Exceptions: none
** History: 7/26/89, DSJ, Created.
*/
MFOUTLINE Current;
MFOUTLINE Last;
MFOUTLINE First;
MICROFEATURE NewFeature;
if (DegenerateOutline (Outline))
return (MicroFeatures);
First = NextExtremity (Outline);
Last = First;
do {
Current = NextExtremity (Last);
NewFeature = ExtractMicroFeature (Last, Current);
if (NewFeature != NULL)
MicroFeatures = push (MicroFeatures, NewFeature);
Last = Current;
}
while (Last != First);
return (MicroFeatures);
} /* ConvertToMicroFeatures */
/*---------------------------------------------------------------------------*/
MICROFEATURE ExtractMicroFeature(MFOUTLINE Start, MFOUTLINE End) {
/*
** Parameters:
** Start starting point of micro-feature
** End ending point of micro-feature
** Globals: none
** Operation:
** This routine computes the feature parameters which describe
** the micro-feature that starts and Start and ends at End.
** A new micro-feature is allocated, filled with the feature
** parameters, and returned. The routine assumes that
** Start and End are not the same point. If they are the
** same point, NULL is returned, a warning message is
** printed, and the current outline is dumped to stdout.
** Return: New micro-feature or NULL if the feature was rejected.
** Exceptions: none
** History: 7/26/89, DSJ, Created.
** 11/17/89, DSJ, Added handling for Start and End same point.
*/
MICROFEATURE NewFeature;
MFEDGEPT *P1, *P2;
P1 = PointAt (Start);
P2 = PointAt (End);
NewFeature = NewMicroFeature ();
NewFeature[XPOSITION] = AverageOf (P1->Point.x, P2->Point.x);
NewFeature[YPOSITION] = AverageOf (P1->Point.y, P2->Point.y);
NewFeature[MFLENGTH] = DistanceBetween (P1->Point, P2->Point);
NewFeature[ORIENTATION] = NormalizedAngleFrom(&P1->Point, &P2->Point, 1.0);
ComputeBulges(Start, End, NewFeature);
return (NewFeature);
} /* ExtractMicroFeature */
/*---------------------------------------------------------------------------*/
void SmearBulges(MICROFEATURES MicroFeatures, FLOAT32 XScale, FLOAT32 YScale) {
/*
** Parameters:
** MicroFeatures features to be smeared
** XScale # of normalized units per pixel in x dir
** YScale # of normalized units per pixel in y dir
** Globals: none
** Operation: Add a random amount to each bulge parameter of each
** feature. The amount added is between -0.5 pixels and
** 0.5 pixels. This is done to prevent the prototypes
** generated in training from being unrealistically tight.
** Return: none
** Exceptions: none
** History: Thu Jun 28 18:03:38 1990, DSJ, Created.
*/
MICROFEATURE MicroFeature;
FLOAT32 MinSmear;
FLOAT32 MaxSmear;
FLOAT32 Cos, Sin;
FLOAT32 Scale;
iterate(MicroFeatures) {
MicroFeature = NextFeatureOf (MicroFeatures);
Cos = fabs(cos(2.0 * PI * MicroFeature[ORIENTATION]));
Sin = fabs(sin(2.0 * PI * MicroFeature[ORIENTATION]));
Scale = YScale * Cos + XScale * Sin;
MinSmear = -0.5 * Scale / (BULGENORMALIZER * MicroFeature[MFLENGTH]);
MaxSmear = 0.5 * Scale / (BULGENORMALIZER * MicroFeature[MFLENGTH]);
MicroFeature[FIRSTBULGE] += UniformRandomNumber (MinSmear, MaxSmear);
MicroFeature[SECONDBULGE] += UniformRandomNumber (MinSmear, MaxSmear);
}
} /* SmearBulges */