Well, first of all, let me apologize for not emailing you the RECOG.C file.
The email account I have access to (it's not mine, which is part of the
problem) is for some reason unable to transfer files from PCs.  Or at least
that's what I have to conclude.  Something to do with a minimal size linkup
to the mainframe.  More than a little irritating.
So, here you have the executable (called FINALREC.EXE) and RECOG.C.
Compiled with Borland C++, but I hope that it will compile OK with Microsoft
as well.

My engine relies on fuzzy logic.  It sets up an array of attributes,
rangeing in value from 1 to 100 for each stroke.  At the moment it uses
55 different attributes per stroke.  Also, there are a few covering the
whole gesture.  The choice of values to use is the crucial part.  It was
pretty easy to get about 80% of the letters.  90% wasn't too bad, either.
But every percentage point beyond that was an uphill battle.  A few things
I had considered perfectly reasonable turned out actually to be detrimental
to my end results.  In particular, any calculations to do with the angles
and the changes in those angles (first and second derivatives) seemed to
make the results worse rather than better.  If anyone has any ideas about
tweaking the performance even further: You're welcome to try.  All it takes
is adding or removing entries in the lists in strokeanalyze() and
gestanalyze(), and changeing NUM_STROKE_VARS and NUM_GEST_VARS.

One other thing that has a surprizing effect on the recognition accuracy is
the simplify() routine.  At least the way I have written the rest of the
program, simplifying any more than a minimal amount has negative effects on
the recognition.  On the other hand, going through *all* the points sampled
does slow things down quite a bit.  I went for higher accuracy, and an
admittedly pretty slow recognizer.  It seems to be on average about 1/3 the
speed of the sample RECOG.C file that came with the rest of the sources.
Not great.  In fact, bad enough so that one could probably tack the two
together and get slightly better results for a minimal speed decrease.
Though I don't think it would be worth it.

I did decide to average all the results together for each individual letter.
This does make things a lot faster than they could be.  But it also means
that one severely wonky sample character will not only throw off chances of
recognizing that particular letter, but may very well make it look enough
like another to cause confusion there.  Strangely enough, I tried quite a
few more (or usually less) fancy statisical techniques.  All of which did
nothing at all for me.  Most made the whole thing much worse.  Like trying
to discard extreme values when averageing.  And including the variance or
standard deviation in the calculations.  Or just squaring the errors found
in weighting().  Nothing.  So what you have here is, in fact, a rather cut
back version of what I had originally planned.  But it works better.  Or
so it seems.  Weird.

One improvement I haven't tried, and which might make a bit of a difference,
is to have an array of weighting values for the different attributes.  For
instance, one could make the integrals twice as "heavy" as the relative
positions of the beginning and end points, if it seemed that they gave more
consistent results.  Or compensate for extreme aspect ratios in some.  An
example would be: the degree of "leftness" of the end points would be much
less relevant in a vertical line, which when drawn well would be only one
or two pixels wide, since making it even *slightly* diagonal one way or
another would make an extreme difference in how far left the point was.
This is sort of what I was trying to compensate for by using the variance.
Sadly, it didn't work.

In order to really use more (or rather, less) than halves (FRACTION > 2),
quite a lot of change need to be made, though they could be fairly effective
I would imagine.  The only problem would be the extra time taken wouldn't be
worth it.  As it stands, it's probably already unacceptably slow.  The only
comfort I can take is that since it hardly needs (in fact, doesn't like)
simplification, it might be fairly good to adapt to a real-time situation.
Then again...

It's quite possible to give it data that will crash things.  One possibility
would be to have too many points in the sampled character.  The simplify()
function will quite happily write beyond the specified MAX_N points.  Also,
it is vaguely possible that something *might* by chance add up to exactly
zero at some point.  At least this was a problem when I had the code dealing
with angles still operating.  I don't believe that this can happen with the
engine as it stands at the moment, though.  One major problem I haven't
dealt with is someone writing the same letter with different numbers of
strokes.  This could be looked at as a major design flaw, since it will
definitely cause problems if it ever occurs.  I don't expect this to be a
real cause for concern, however.  As long as it is trained to recognize only
one single person's handwriting at a time, I imagine most people are
consistent enough not to do that.  (If they do, well then it serves them
right if the computer hangs horribly :-)  In fact, by ignoring all samples
with a different number of strokes, things are quite a bit faster than they
could be.  Also, note that since 'E' is the only 4-stroke character, it is
*never* guessed wrong.  Well, them's the breaks.

Well, what more can I say?  It's slow.  But it's ok.  By averageing all the
learned values, the speed is constant no matter how much data it is trained
with.  On the other hand, so is the accuracy.  Much more than 3 or 4 of each
character will probably not be worth while.  Ideally (with a computer 2 to 3
years in the future) I would apply the same method but have each trained set
of values *plus* an averaged set (or two, one with extreme values removed)
and operate from those results.  But for now.  This is will have to be good
enough.

Since I only had the two sample datafiles to work with (I don't trust myself
to create lots of characters with only a mouse), I tried to strike a balance
between doing well at recognizing each one.  I tended to lean towards making
it recognize more of the really lousy letters (my handwriting is pretty
abominable, so I think that's quite important).  The CLEAN.DAT samples are
unrealistically clean.  And although the other ones aren't exactly that
realistically badly written, they had to substitue for sloppy writing.  If
the samples were really going to be so well executed all the time, then
things could be speeded up without much penalty by making the simplify()
function more elaborate.  But that really worsened the results for the dirty
input, so I went the other way.

Fine.  I'll shut up now.

I look forward to seeing the results in the December issue.
Or do I get to know earlier?
Anyway, thanks for your attention and patience.



										Philipp (SAM) Hanes

                                        Seitenstettengasse 5/16
                                        A-1010 Vienna
                                        AUSTRIA








