Penlets.com provides resources for users and developers of the Pulse smart pen from LiveScribe.
Subscribe: RSS Feed - Developers Group
Tutorial
Using Intelligent Character Recognition (ICR)
Tutorial by Robert HansonPage 1 » Page 2
Next, lets add the
initICRContext
and
destroyICRContext
methods. This is where we start to get to the heart of what we are
trying to accomplish.
private void initICRContext ()
{
this.icrContext = this.context.getICRContext(1000, this); /* [1] */
Resource[] resources = { /* [2] */
this.icrContext.getDefaultAlphabetKnowledgeResource(),
this.icrContext.createSKSystemResource(ICRContext.SYSRES_SK_ALPHA),
this.icrContext.createLKSystemResource(ICRContext.SYSRES_LK_WORDLIST_100K)
};
this.icrContext.addResourceSet(resources);
this.context.addStrokeListener(this); /* [3] */
}
private void destroyICRContext ()
{
icrContext.dispose(); /* [4] */
icrContext = null;
}
So here we have our two methods. In the
initICRContext
method we start by getting an ICR Context
[1]. We do this by calling
getICRContext
on the
context
field, which resides in our super-class
Penlet
. The context field is a protected field of type
PenletContext
. The two arguments passed to
getICRContext
are a timeout and an instance of
HWRListener
. The timeout is used to detect writing pauses, and in this case we
are indicating that 1000 milliseconds (1 second) constituted a pause.
The second argument is a HWRListener that will accept hand-writing
recognition events, namely word results and detected pauses. Note
that we pass
this
as the second argument, implying that our class will need to extend
HWRListener
. Indeed it will, and we will show that shortly.
Next we need to define the resources [2] that we want to use and pass them to the ICR Context. This is where the "AK" (alphabet knowledge), "SK" (subset knowledge), and "LK" (lexicon knowledge) resources that we discussed fit in. Here we tell the recognition engine to use the default alphabet, including only alpha characters (letters), and to use the built-in 100,000 word lexicon.
Now as I mentioned earlier, WE need to feed the ICR Context with pen
strokes, it won't do it on its own. So we need to also register a
stroke listener
[3] that will capture pen strokes and pass them on
to the ICR Context. Here we pass
this
, implying that our class will also implement the
StrokeListener
interface, and indeed it will.
In comparison, the
destroyICRContext
[4] is kinda boring. Here we simply dispose of the
ICR Context and set the field to null.
Next up we want to have out class implement the
StrokeListener
interface so that it can pass strokes to the ICR Context.
public class ICRDemo extends Penlet
implements StrokeListener
{
...
public void strokeCreated (long startTime, Region region,
PageInstance pageInstance)
{
this.icrContext.addStroke(pageInstance, startTime);
}
}
Well, I think the code speaks for itself. Nothing complicated, just passing along the stroke data to the ICR Context as we receive it.
Now we are close to the end, and have only one loose end to finish.
We need our class to implement the
HWRListener
interface so that we can be notified when the recognition engine
matches the strokes to words.
public class ICRDemo extends Penlet
implements StrokeListener, HWRListener
{
...
/* [1] */
public void hwrResult (long time, String result)
{
this.label.draw(result);
}
/* [2] */
public void hwrUserPause (long time, String result)
{
this.icrContext.clearStrokes();
this.label.draw(result + " :)", true);
}
public void hwrCrossingOut (long time, String result) { }
public void hwrError (long time, String error) { }
}
There are four methods that make up the
HWRListener
interface, of which we only provide code for two. In the
hwrResult
[1] method we update the display with the value of
the
result
parameter. The result is the recognition engine's best guess based on
the strokes that have been fed to it. If you watch the display as you
write you will see that partial words result in bad guesses, and as
each new stroke is added it updates its guess.
You might recall that when we created the ICR Context, we defined a
timeout of 1000 milliseconds (or 1 second). When the user stops
writing for this one second, the
hwrUserPause
method
[2] is called. We equate a pause with an indication
that the user has finished writing the word. To indicate the pause in
the pen's display we show the result with a smiley face. In addition
to this we clear the collected pen strokes from the ICR Context so
that the user can write a new word.
Next Steps
Once you get this application running on your pen you should
experiment with the various AK, SK, and LK resources available. Some
of these are defined at getters in the
ICRContext
class, like
getDefaultAlphabetKnowledgeResource
and
getMultiWordDefaultResourceSet
. Others are defined as constants of the
ICRContext
class and added via the
create*KSystemResource
methods. These include
SYSRES_LK_BIGRAM_100K
an alternate 100K lexicon using a Bigram frequency table (
Bigram at Wikipedia),
SYSRES_LK_LMEM
for cursive writing, and
SYSRES_LK_OUT_OF_LEXICON
allows recognition of words not in the lexicon. Try combining them in
various ways, and even try creating your own lexicon files, which
will be covered in a separate tutorial.
Happy coding!
Page 1 » Page 2
Comments (View) blog comments powered by Disqus
Project Information
Tested for use with: PreRelease-SDK 0.6
Download the source code for this tutorial.
Download Source Code (zip)
New Tutorials
Using a Shared Library
Learn how to create a library of utils that you can
share between projects.
Capturing Drawn Shapes
Learn how to capture shapes drawn by the pen and determine relationships.
Penlets 101
Never written a penlet before? Then start here with Penlets 101!
Creating a Custom Vocabulary
Learn how to create a custom vocabulary for your ICR applications.
Using Properties Files
J2ME lacks a Properties class. In this tutorial we roll our own,
along with split and chomp functions.