1*80ee5cbfSDavid du Colombier /*
2*80ee5cbfSDavid du Colombier * li_recognizer.c
3*80ee5cbfSDavid du Colombier *
4*80ee5cbfSDavid du Colombier * Copyright 2000 Compaq Computer Corporation.
5*80ee5cbfSDavid du Colombier * Copying or modifying this code for any purpose is permitted,
6*80ee5cbfSDavid du Colombier * provided that this copyright notice is preserved in its entirety
7*80ee5cbfSDavid du Colombier * in all copies or modifications.
8*80ee5cbfSDavid du Colombier * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR
9*80ee5cbfSDavid du Colombier * IMPLIED, AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR
10*80ee5cbfSDavid du Colombier *
11*80ee5cbfSDavid du Colombier *
12*80ee5cbfSDavid du Colombier * Adapted from cmu_recognizer.c by Jay Kistler.
13*80ee5cbfSDavid du Colombier *
14*80ee5cbfSDavid du Colombier * Where is the CMU copyright???? Gotta track it down - Jim Gettys
15*80ee5cbfSDavid du Colombier *
16*80ee5cbfSDavid du Colombier * Credit to Dean Rubine, Jim Kempf, and Ari Rapkin.
17*80ee5cbfSDavid du Colombier */
18*80ee5cbfSDavid du Colombier
19*80ee5cbfSDavid du Colombier #include <u.h>
20*80ee5cbfSDavid du Colombier #include <libc.h>
21*80ee5cbfSDavid du Colombier #include <stdio.h>
22*80ee5cbfSDavid du Colombier #include <draw.h>
23*80ee5cbfSDavid du Colombier #include <scribble.h>
24*80ee5cbfSDavid du Colombier #include "scribbleimpl.h"
25*80ee5cbfSDavid du Colombier
26*80ee5cbfSDavid du Colombier #include "hre_internal.h"
27*80ee5cbfSDavid du Colombier #include "li_recognizer_internal.h"
28*80ee5cbfSDavid du Colombier
29*80ee5cbfSDavid du Colombier int lidebug = 0;
30*80ee5cbfSDavid du Colombier
31*80ee5cbfSDavid du Colombier #define LI_MAGIC 0xACCBADDD
32*80ee5cbfSDavid du Colombier
33*80ee5cbfSDavid du Colombier #define CHECK_LI_MAGIC(_a) \
34*80ee5cbfSDavid du Colombier ((_a) != nil && ((li_recognizer*)(_a))->li_magic == LI_MAGIC)
35*80ee5cbfSDavid du Colombier
36*80ee5cbfSDavid du Colombier
37*80ee5cbfSDavid du Colombier static void lialg_initialize(rClassifier *);
38*80ee5cbfSDavid du Colombier static int lialg_read_classifier_digest(rClassifier *);
39*80ee5cbfSDavid du Colombier static int lialg_canonicalize_examples(rClassifier *);
40*80ee5cbfSDavid du Colombier static char *lialg_recognize_stroke(rClassifier *, point_list *);
41*80ee5cbfSDavid du Colombier static void lialg_compute_lpf_parameters(void);
42*80ee5cbfSDavid du Colombier
43*80ee5cbfSDavid du Colombier
44*80ee5cbfSDavid du Colombier char* li_err_msg = nil;
45*80ee5cbfSDavid du Colombier
46*80ee5cbfSDavid du Colombier #define bcopy(s1,s2,n) memmove(s2,s1,n)
47*80ee5cbfSDavid du Colombier
48*80ee5cbfSDavid du Colombier /*Freeing classifier*/
49*80ee5cbfSDavid du Colombier
50*80ee5cbfSDavid du Colombier static void
51*80ee5cbfSDavid du Colombier free_rClassifier(rClassifier* rc);
52*80ee5cbfSDavid du Colombier
53*80ee5cbfSDavid du Colombier /*
54*80ee5cbfSDavid du Colombier * Point List Support
55*80ee5cbfSDavid du Colombier */
56*80ee5cbfSDavid du Colombier
57*80ee5cbfSDavid du Colombier static point_list*
add_example(point_list * l,int npts,pen_point * pts)58*80ee5cbfSDavid du Colombier add_example(point_list* l,int npts,pen_point* pts)
59*80ee5cbfSDavid du Colombier {
60*80ee5cbfSDavid du Colombier pen_point* lpts = mallocz(npts*sizeof(pen_point), 1);
61*80ee5cbfSDavid du Colombier point_list *p = malloc(sizeof(point_list));
62*80ee5cbfSDavid du Colombier
63*80ee5cbfSDavid du Colombier p->npts = npts;
64*80ee5cbfSDavid du Colombier p->pts = lpts;
65*80ee5cbfSDavid du Colombier p->next = l; /*Order doesn't matter, so we stick on end.*/
66*80ee5cbfSDavid du Colombier
67*80ee5cbfSDavid du Colombier /*Copy points.*/
68*80ee5cbfSDavid du Colombier
69*80ee5cbfSDavid du Colombier bcopy(pts, lpts, npts * sizeof(pen_point));
70*80ee5cbfSDavid du Colombier
71*80ee5cbfSDavid du Colombier return(p);
72*80ee5cbfSDavid du Colombier }
73*80ee5cbfSDavid du Colombier
74*80ee5cbfSDavid du Colombier
75*80ee5cbfSDavid du Colombier static void
delete_examples(point_list * l)76*80ee5cbfSDavid du Colombier delete_examples(point_list* l)
77*80ee5cbfSDavid du Colombier {
78*80ee5cbfSDavid du Colombier point_list* p;
79*80ee5cbfSDavid du Colombier
80*80ee5cbfSDavid du Colombier for( ; l != nil; l = p ) {
81*80ee5cbfSDavid du Colombier p = l->next;
82*80ee5cbfSDavid du Colombier free(l->pts);
83*80ee5cbfSDavid du Colombier free(l);
84*80ee5cbfSDavid du Colombier }
85*80ee5cbfSDavid du Colombier }
86*80ee5cbfSDavid du Colombier
87*80ee5cbfSDavid du Colombier /*
88*80ee5cbfSDavid du Colombier * Local functions
89*80ee5cbfSDavid du Colombier */
90*80ee5cbfSDavid du Colombier
91*80ee5cbfSDavid du Colombier /*
92*80ee5cbfSDavid du Colombier * recognize_internal-Form Vector, use Classifier to classify, return char.
93*80ee5cbfSDavid du Colombier */
94*80ee5cbfSDavid du Colombier
95*80ee5cbfSDavid du Colombier static char*
recognize_internal(rClassifier * rec,Stroke * str,int *)96*80ee5cbfSDavid du Colombier recognize_internal(rClassifier* rec, Stroke* str, int*)
97*80ee5cbfSDavid du Colombier {
98*80ee5cbfSDavid du Colombier char *res;
99*80ee5cbfSDavid du Colombier point_list *stroke;
100*80ee5cbfSDavid du Colombier
101*80ee5cbfSDavid du Colombier stroke = add_example(nil, str->npts, str->pts);
102*80ee5cbfSDavid du Colombier if (stroke == nil) return(nil);
103*80ee5cbfSDavid du Colombier
104*80ee5cbfSDavid du Colombier res = lialg_recognize_stroke(rec, stroke);
105*80ee5cbfSDavid du Colombier
106*80ee5cbfSDavid du Colombier delete_examples(stroke);
107*80ee5cbfSDavid du Colombier return(res);
108*80ee5cbfSDavid du Colombier }
109*80ee5cbfSDavid du Colombier
110*80ee5cbfSDavid du Colombier /*
111*80ee5cbfSDavid du Colombier * file_path-Construct pathname, check for proper extension.
112*80ee5cbfSDavid du Colombier */
113*80ee5cbfSDavid du Colombier
114*80ee5cbfSDavid du Colombier static int
file_path(char * dir,char * filename,char * pathname)115*80ee5cbfSDavid du Colombier file_path(char* dir,char* filename,char* pathname)
116*80ee5cbfSDavid du Colombier {
117*80ee5cbfSDavid du Colombier char* dot;
118*80ee5cbfSDavid du Colombier
119*80ee5cbfSDavid du Colombier /*Check for proper extension on file name.*/
120*80ee5cbfSDavid du Colombier
121*80ee5cbfSDavid du Colombier dot = strrchr(filename,'.');
122*80ee5cbfSDavid du Colombier
123*80ee5cbfSDavid du Colombier if( dot == nil ) {
124*80ee5cbfSDavid du Colombier return(-1);
125*80ee5cbfSDavid du Colombier }
126*80ee5cbfSDavid du Colombier
127*80ee5cbfSDavid du Colombier /*Determine whether a gesture or character classifier.*/
128*80ee5cbfSDavid du Colombier
129*80ee5cbfSDavid du Colombier if( strcmp(dot,LI_CLASSIFIER_EXTENSION) != 0 ) {
130*80ee5cbfSDavid du Colombier return(-1);
131*80ee5cbfSDavid du Colombier }
132*80ee5cbfSDavid du Colombier
133*80ee5cbfSDavid du Colombier /*Concatenate directory and filename into pathname.*/
134*80ee5cbfSDavid du Colombier
135*80ee5cbfSDavid du Colombier strcpy(pathname,dir);
136*80ee5cbfSDavid du Colombier strcat(pathname,"/");
137*80ee5cbfSDavid du Colombier strcat(pathname,filename);
138*80ee5cbfSDavid du Colombier
139*80ee5cbfSDavid du Colombier return(0);
140*80ee5cbfSDavid du Colombier }
141*80ee5cbfSDavid du Colombier
142*80ee5cbfSDavid du Colombier /*read_classifier_points-Read points so classifier can be extended.*/
143*80ee5cbfSDavid du Colombier
144*80ee5cbfSDavid du Colombier static int
read_classifier_points(FILE * fd,int nclss,point_list ** ex,char ** cnames)145*80ee5cbfSDavid du Colombier read_classifier_points(FILE* fd,int nclss,point_list** ex,char** cnames)
146*80ee5cbfSDavid du Colombier {
147*80ee5cbfSDavid du Colombier int i,j,k;
148*80ee5cbfSDavid du Colombier char buf[BUFSIZ];
149*80ee5cbfSDavid du Colombier int nex = 0;
150*80ee5cbfSDavid du Colombier char* names[MAXSCLASSES];
151*80ee5cbfSDavid du Colombier point_list* examples[MAXSCLASSES];
152*80ee5cbfSDavid du Colombier pen_point* pts;
153*80ee5cbfSDavid du Colombier int npts;
154*80ee5cbfSDavid du Colombier
155*80ee5cbfSDavid du Colombier /*Initialize*/
156*80ee5cbfSDavid du Colombier
157*80ee5cbfSDavid du Colombier for( i = 0; i < MAXSCLASSES; i++ ) {
158*80ee5cbfSDavid du Colombier names[i] = nil;
159*80ee5cbfSDavid du Colombier examples[i] = nil;
160*80ee5cbfSDavid du Colombier }
161*80ee5cbfSDavid du Colombier
162*80ee5cbfSDavid du Colombier /*Go thru classes.*/
163*80ee5cbfSDavid du Colombier
164*80ee5cbfSDavid du Colombier for( k = 0; k < nclss; k++ ) {
165*80ee5cbfSDavid du Colombier
166*80ee5cbfSDavid du Colombier /*Read class name and number of examples.*/
167*80ee5cbfSDavid du Colombier
168*80ee5cbfSDavid du Colombier if( fscanf(fd,"%d %s",&nex,buf) != 2 )
169*80ee5cbfSDavid du Colombier goto unallocate;
170*80ee5cbfSDavid du Colombier
171*80ee5cbfSDavid du Colombier /*Save class name*/
172*80ee5cbfSDavid du Colombier
173*80ee5cbfSDavid du Colombier names[k] = strdup(buf);
174*80ee5cbfSDavid du Colombier
175*80ee5cbfSDavid du Colombier /*Read examples.*/
176*80ee5cbfSDavid du Colombier
177*80ee5cbfSDavid du Colombier for( i = 0; i < nex; i++ ) {
178*80ee5cbfSDavid du Colombier
179*80ee5cbfSDavid du Colombier /*Read number of points.*/
180*80ee5cbfSDavid du Colombier
181*80ee5cbfSDavid du Colombier if( fscanf(fd,"%d",&npts) != 1 )
182*80ee5cbfSDavid du Colombier goto unallocate; /*Boy would I like exceptions!*/
183*80ee5cbfSDavid du Colombier
184*80ee5cbfSDavid du Colombier /*Allocate array for points.*/
185*80ee5cbfSDavid du Colombier
186*80ee5cbfSDavid du Colombier if( (pts = mallocz(npts*sizeof(pen_point), 1)) == nil )
187*80ee5cbfSDavid du Colombier goto unallocate;
188*80ee5cbfSDavid du Colombier
189*80ee5cbfSDavid du Colombier /*Read in points.*/
190*80ee5cbfSDavid du Colombier
191*80ee5cbfSDavid du Colombier for( j = 0; j < npts; j++ ) {
192*80ee5cbfSDavid du Colombier int x,y;
193*80ee5cbfSDavid du Colombier if( fscanf(fd,"%d %d",&x,&y) != 2 ) {
194*80ee5cbfSDavid du Colombier delete_pen_point_array(pts);
195*80ee5cbfSDavid du Colombier goto unallocate;
196*80ee5cbfSDavid du Colombier }
197*80ee5cbfSDavid du Colombier pts[j].Point = Pt(x, y);
198*80ee5cbfSDavid du Colombier }
199*80ee5cbfSDavid du Colombier
200*80ee5cbfSDavid du Colombier /*Add example*/
201*80ee5cbfSDavid du Colombier
202*80ee5cbfSDavid du Colombier if( (examples[k] = add_example(examples[k],npts,pts)) == nil ) {
203*80ee5cbfSDavid du Colombier delete_pen_point_array(pts);
204*80ee5cbfSDavid du Colombier goto unallocate;
205*80ee5cbfSDavid du Colombier }
206*80ee5cbfSDavid du Colombier
207*80ee5cbfSDavid du Colombier delete_pen_point_array(pts);
208*80ee5cbfSDavid du Colombier
209*80ee5cbfSDavid du Colombier }
210*80ee5cbfSDavid du Colombier }
211*80ee5cbfSDavid du Colombier
212*80ee5cbfSDavid du Colombier /* ari -- end of list of classes */
213*80ee5cbfSDavid du Colombier /* fprint(2, "]\n"); */
214*80ee5cbfSDavid du Colombier
215*80ee5cbfSDavid du Colombier /*Transfer to recognizer.*/
216*80ee5cbfSDavid du Colombier
217*80ee5cbfSDavid du Colombier bcopy(examples,ex,sizeof(examples));
218*80ee5cbfSDavid du Colombier bcopy(names,cnames,sizeof(names));
219*80ee5cbfSDavid du Colombier
220*80ee5cbfSDavid du Colombier return(0);
221*80ee5cbfSDavid du Colombier
222*80ee5cbfSDavid du Colombier /*Error. Deallocate memory and return.*/
223*80ee5cbfSDavid du Colombier
224*80ee5cbfSDavid du Colombier unallocate:
225*80ee5cbfSDavid du Colombier for( ; k >= 0; k-- ) {
226*80ee5cbfSDavid du Colombier delete_examples(examples[k]);
227*80ee5cbfSDavid du Colombier free(names[k]);
228*80ee5cbfSDavid du Colombier }
229*80ee5cbfSDavid du Colombier
230*80ee5cbfSDavid du Colombier return(-1);
231*80ee5cbfSDavid du Colombier }
232*80ee5cbfSDavid du Colombier
233*80ee5cbfSDavid du Colombier /*read_classifier-Read a classifier file.*/
234*80ee5cbfSDavid du Colombier
read_classifier(FILE * fd,rClassifier * rc)235*80ee5cbfSDavid du Colombier static int read_classifier(FILE* fd,rClassifier* rc)
236*80ee5cbfSDavid du Colombier {
237*80ee5cbfSDavid du Colombier
238*80ee5cbfSDavid du Colombier li_err_msg = nil;
239*80ee5cbfSDavid du Colombier
240*80ee5cbfSDavid du Colombier /*Read in classifier file.*/
241*80ee5cbfSDavid du Colombier
242*80ee5cbfSDavid du Colombier if(fscanf(fd, "%d", &rc->nclasses) != 1)
243*80ee5cbfSDavid du Colombier return -1;
244*80ee5cbfSDavid du Colombier
245*80ee5cbfSDavid du Colombier /*Read in the example points, so classifier can be extended.*/
246*80ee5cbfSDavid du Colombier
247*80ee5cbfSDavid du Colombier if( read_classifier_points(fd,rc->nclasses,rc->ex,rc->cnames) != 0 )
248*80ee5cbfSDavid du Colombier return -1;
249*80ee5cbfSDavid du Colombier
250*80ee5cbfSDavid du Colombier return(0);
251*80ee5cbfSDavid du Colombier }
252*80ee5cbfSDavid du Colombier
253*80ee5cbfSDavid du Colombier /*
254*80ee5cbfSDavid du Colombier * Extension Functions
255*80ee5cbfSDavid du Colombier */
256*80ee5cbfSDavid du Colombier
257*80ee5cbfSDavid du Colombier /* getClasses and clearState are by Ari */
258*80ee5cbfSDavid du Colombier
259*80ee5cbfSDavid du Colombier static int
recognizer_getClasses(recognizer r,char *** list,int * nc)260*80ee5cbfSDavid du Colombier recognizer_getClasses (recognizer r, char ***list, int *nc)
261*80ee5cbfSDavid du Colombier {
262*80ee5cbfSDavid du Colombier int i, nclasses;
263*80ee5cbfSDavid du Colombier li_recognizer* rec;
264*80ee5cbfSDavid du Colombier char **ret;
265*80ee5cbfSDavid du Colombier
266*80ee5cbfSDavid du Colombier rec = (li_recognizer*)r->recognizer_specific;
267*80ee5cbfSDavid du Colombier
268*80ee5cbfSDavid du Colombier /*Check for LI recognizer.*/
269*80ee5cbfSDavid du Colombier
270*80ee5cbfSDavid du Colombier if( !CHECK_LI_MAGIC(rec) ) {
271*80ee5cbfSDavid du Colombier li_err_msg = "Not a LI recognizer";
272*80ee5cbfSDavid du Colombier return(-1);
273*80ee5cbfSDavid du Colombier }
274*80ee5cbfSDavid du Colombier
275*80ee5cbfSDavid du Colombier *nc = nclasses = rec->li_rc.nclasses;
276*80ee5cbfSDavid du Colombier ret = malloc(nclasses*sizeof(char*));
277*80ee5cbfSDavid du Colombier
278*80ee5cbfSDavid du Colombier for (i = 0; i < nclasses; i++)
279*80ee5cbfSDavid du Colombier ret[i] = rec->li_rc.cnames[i]; /* only the 1st char of the cname */
280*80ee5cbfSDavid du Colombier *list = ret;
281*80ee5cbfSDavid du Colombier return 0;
282*80ee5cbfSDavid du Colombier }
283*80ee5cbfSDavid du Colombier
284*80ee5cbfSDavid du Colombier static int
recognizer_clearState(recognizer)285*80ee5cbfSDavid du Colombier recognizer_clearState (recognizer)
286*80ee5cbfSDavid du Colombier {
287*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
288*80ee5cbfSDavid du Colombier
289*80ee5cbfSDavid du Colombier li_err_msg = "Clearing state is not supported by the LI recognizer";
290*80ee5cbfSDavid du Colombier
291*80ee5cbfSDavid du Colombier return(-1);
292*80ee5cbfSDavid du Colombier }
293*80ee5cbfSDavid du Colombier
isa_li(recognizer r)294*80ee5cbfSDavid du Colombier static bool isa_li(recognizer r)
295*80ee5cbfSDavid du Colombier { return(CHECK_LI_MAGIC(r)); }
296*80ee5cbfSDavid du Colombier
297*80ee5cbfSDavid du Colombier static int
recognizer_train(recognizer,rc *,uint,Stroke *,rec_element *,bool)298*80ee5cbfSDavid du Colombier recognizer_train(recognizer, rc*, uint, Stroke*, rec_element*, bool)
299*80ee5cbfSDavid du Colombier {
300*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
301*80ee5cbfSDavid du Colombier
302*80ee5cbfSDavid du Colombier li_err_msg = "Training is not supported by the LI recognizer";
303*80ee5cbfSDavid du Colombier
304*80ee5cbfSDavid du Colombier return(-1);
305*80ee5cbfSDavid du Colombier }
306*80ee5cbfSDavid du Colombier
307*80ee5cbfSDavid du Colombier int
li_recognizer_get_example(recognizer r,int class,int instance,char ** name,pen_point ** points,int * npts)308*80ee5cbfSDavid du Colombier li_recognizer_get_example (recognizer r,
309*80ee5cbfSDavid du Colombier int class,
310*80ee5cbfSDavid du Colombier int instance,
311*80ee5cbfSDavid du Colombier char **name,
312*80ee5cbfSDavid du Colombier pen_point **points,
313*80ee5cbfSDavid du Colombier int *npts)
314*80ee5cbfSDavid du Colombier {
315*80ee5cbfSDavid du Colombier li_recognizer *rec = (li_recognizer*)r->recognizer_specific;
316*80ee5cbfSDavid du Colombier int nclasses = rec->li_rc.nclasses;
317*80ee5cbfSDavid du Colombier point_list *pl;
318*80ee5cbfSDavid du Colombier
319*80ee5cbfSDavid du Colombier if( !CHECK_LI_MAGIC(rec) ) {
320*80ee5cbfSDavid du Colombier li_err_msg = "Not a LI recognizer";
321*80ee5cbfSDavid du Colombier return(-1);
322*80ee5cbfSDavid du Colombier }
323*80ee5cbfSDavid du Colombier if (class > nclasses)
324*80ee5cbfSDavid du Colombier return -1;
325*80ee5cbfSDavid du Colombier pl = rec->li_rc.canonex[class];
326*80ee5cbfSDavid du Colombier while (instance && pl)
327*80ee5cbfSDavid du Colombier {
328*80ee5cbfSDavid du Colombier pl = pl->next;
329*80ee5cbfSDavid du Colombier instance--;
330*80ee5cbfSDavid du Colombier }
331*80ee5cbfSDavid du Colombier if (!pl)
332*80ee5cbfSDavid du Colombier return -1;
333*80ee5cbfSDavid du Colombier *name = rec->li_rc.cnames[class];
334*80ee5cbfSDavid du Colombier *points = pl->pts;
335*80ee5cbfSDavid du Colombier *npts = pl->npts;
336*80ee5cbfSDavid du Colombier return pl->npts; /* I hope [sjm] */
337*80ee5cbfSDavid du Colombier }
338*80ee5cbfSDavid du Colombier
339*80ee5cbfSDavid du Colombier /*
340*80ee5cbfSDavid du Colombier * API Functions
341*80ee5cbfSDavid du Colombier */
342*80ee5cbfSDavid du Colombier
343*80ee5cbfSDavid du Colombier
344*80ee5cbfSDavid du Colombier /*li_recognizer_load-Load a classifier file.*/
345*80ee5cbfSDavid du Colombier
li_recognizer_load(recognizer r,char * dir,char * filename)346*80ee5cbfSDavid du Colombier static int li_recognizer_load(recognizer r, char* dir, char* filename)
347*80ee5cbfSDavid du Colombier {
348*80ee5cbfSDavid du Colombier FILE *fd;
349*80ee5cbfSDavid du Colombier char* pathname;
350*80ee5cbfSDavid du Colombier li_recognizer* rec;
351*80ee5cbfSDavid du Colombier rClassifier* rc;
352*80ee5cbfSDavid du Colombier
353*80ee5cbfSDavid du Colombier rec = (li_recognizer*)r->recognizer_specific;
354*80ee5cbfSDavid du Colombier
355*80ee5cbfSDavid du Colombier /*Make sure recognizer's OK*/
356*80ee5cbfSDavid du Colombier
357*80ee5cbfSDavid du Colombier if( !CHECK_LI_MAGIC(rec) ) {
358*80ee5cbfSDavid du Colombier li_err_msg = "Not a LI recognizer";
359*80ee5cbfSDavid du Colombier return(-1);
360*80ee5cbfSDavid du Colombier }
361*80ee5cbfSDavid du Colombier
362*80ee5cbfSDavid du Colombier rc = &(rec->li_rc);
363*80ee5cbfSDavid du Colombier
364*80ee5cbfSDavid du Colombier /*Check parameters.*/
365*80ee5cbfSDavid du Colombier
366*80ee5cbfSDavid du Colombier if( filename == nil ) {
367*80ee5cbfSDavid du Colombier li_err_msg = "Invalid parameters";
368*80ee5cbfSDavid du Colombier return(-1);
369*80ee5cbfSDavid du Colombier }
370*80ee5cbfSDavid du Colombier
371*80ee5cbfSDavid du Colombier /*We let the directory be null.*/
372*80ee5cbfSDavid du Colombier
373*80ee5cbfSDavid du Colombier if( dir == nil || (int)strlen(dir) <= 0 ) {
374*80ee5cbfSDavid du Colombier dir = ".";
375*80ee5cbfSDavid du Colombier }
376*80ee5cbfSDavid du Colombier
377*80ee5cbfSDavid du Colombier if(0)fprint(2, "dir = %s, filename = %s\n",
378*80ee5cbfSDavid du Colombier dir, filename);
379*80ee5cbfSDavid du Colombier
380*80ee5cbfSDavid du Colombier /*Make full pathname and check filename*/
381*80ee5cbfSDavid du Colombier
382*80ee5cbfSDavid du Colombier pathname = malloc((strlen(dir) + strlen(filename) + 2)*sizeof(char));
383*80ee5cbfSDavid du Colombier if( file_path(dir, filename, pathname) == -1 ) {
384*80ee5cbfSDavid du Colombier free(pathname);
385*80ee5cbfSDavid du Colombier li_err_msg = "Not a LI recognizer classifier file";
386*80ee5cbfSDavid du Colombier return -1;
387*80ee5cbfSDavid du Colombier }
388*80ee5cbfSDavid du Colombier
389*80ee5cbfSDavid du Colombier /* Try to short-circuit the full classifier-file processing. */
390*80ee5cbfSDavid du Colombier rc->file_name = pathname;
391*80ee5cbfSDavid du Colombier if (lialg_read_classifier_digest(rc) == 0)
392*80ee5cbfSDavid du Colombier return(0);
393*80ee5cbfSDavid du Colombier rc->file_name = nil;
394*80ee5cbfSDavid du Colombier
395*80ee5cbfSDavid du Colombier /*Open the file*/
396*80ee5cbfSDavid du Colombier
397*80ee5cbfSDavid du Colombier if( (fd = fopen(pathname,"r")) == nil ) {
398*80ee5cbfSDavid du Colombier li_err_msg = "Can't open classifier file";
399*80ee5cbfSDavid du Colombier if(0)fprint(2, "Can't open %s.\n", pathname);
400*80ee5cbfSDavid du Colombier free(pathname);
401*80ee5cbfSDavid du Colombier return(-1);
402*80ee5cbfSDavid du Colombier }
403*80ee5cbfSDavid du Colombier
404*80ee5cbfSDavid du Colombier /*If rClassifier is OK, then delete it first.*/
405*80ee5cbfSDavid du Colombier
406*80ee5cbfSDavid du Colombier if( rc->file_name != nil ) {
407*80ee5cbfSDavid du Colombier free_rClassifier(rc);
408*80ee5cbfSDavid du Colombier }
409*80ee5cbfSDavid du Colombier
410*80ee5cbfSDavid du Colombier /*Read classifier.*/
411*80ee5cbfSDavid du Colombier
412*80ee5cbfSDavid du Colombier if( read_classifier(fd,rc) < 0 ) {
413*80ee5cbfSDavid du Colombier free(pathname);
414*80ee5cbfSDavid du Colombier return(-1);
415*80ee5cbfSDavid du Colombier }
416*80ee5cbfSDavid du Colombier
417*80ee5cbfSDavid du Colombier /*Close file.*/
418*80ee5cbfSDavid du Colombier
419*80ee5cbfSDavid du Colombier fclose(fd);
420*80ee5cbfSDavid du Colombier
421*80ee5cbfSDavid du Colombier /*Add classifier name.*/
422*80ee5cbfSDavid du Colombier
423*80ee5cbfSDavid du Colombier rc->file_name = pathname;
424*80ee5cbfSDavid du Colombier
425*80ee5cbfSDavid du Colombier /* Canonicalize examples. */
426*80ee5cbfSDavid du Colombier if (lialg_canonicalize_examples(rc) != 0) {
427*80ee5cbfSDavid du Colombier free(pathname);
428*80ee5cbfSDavid du Colombier rc->file_name = nil;
429*80ee5cbfSDavid du Colombier return -1;
430*80ee5cbfSDavid du Colombier }
431*80ee5cbfSDavid du Colombier
432*80ee5cbfSDavid du Colombier return(0);
433*80ee5cbfSDavid du Colombier }
434*80ee5cbfSDavid du Colombier
435*80ee5cbfSDavid du Colombier /*li_recognizer_save-Save a classifier file.*/
436*80ee5cbfSDavid du Colombier
li_recognizer_save(recognizer,char *,char *)437*80ee5cbfSDavid du Colombier static int li_recognizer_save(recognizer, char*, char*)
438*80ee5cbfSDavid du Colombier {
439*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
440*80ee5cbfSDavid du Colombier
441*80ee5cbfSDavid du Colombier li_err_msg = "Saving is not supported by the LI recognizer";
442*80ee5cbfSDavid du Colombier return -1;
443*80ee5cbfSDavid du Colombier }
444*80ee5cbfSDavid du Colombier
445*80ee5cbfSDavid du Colombier static wordset
li_recognizer_load_dictionary(recognizer,char *,char *)446*80ee5cbfSDavid du Colombier li_recognizer_load_dictionary(recognizer, char*, char*)
447*80ee5cbfSDavid du Colombier {
448*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
449*80ee5cbfSDavid du Colombier
450*80ee5cbfSDavid du Colombier li_err_msg = "Dictionaries are not supported by the LI recognizer";
451*80ee5cbfSDavid du Colombier return nil;
452*80ee5cbfSDavid du Colombier }
453*80ee5cbfSDavid du Colombier
454*80ee5cbfSDavid du Colombier static int
li_recognizer_save_dictionary(recognizer,char *,char *,wordset)455*80ee5cbfSDavid du Colombier li_recognizer_save_dictionary(recognizer, char*, char*, wordset)
456*80ee5cbfSDavid du Colombier {
457*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
458*80ee5cbfSDavid du Colombier li_err_msg = "Dictionaries are not supported by the LI recognizer";
459*80ee5cbfSDavid du Colombier return -1;
460*80ee5cbfSDavid du Colombier }
461*80ee5cbfSDavid du Colombier
462*80ee5cbfSDavid du Colombier static int
li_recognizer_free_dictionary(recognizer,wordset)463*80ee5cbfSDavid du Colombier li_recognizer_free_dictionary(recognizer, wordset)
464*80ee5cbfSDavid du Colombier {
465*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
466*80ee5cbfSDavid du Colombier
467*80ee5cbfSDavid du Colombier li_err_msg = "Dictionaries are not supported by the LI recognizer";
468*80ee5cbfSDavid du Colombier
469*80ee5cbfSDavid du Colombier return -1;
470*80ee5cbfSDavid du Colombier
471*80ee5cbfSDavid du Colombier }
472*80ee5cbfSDavid du Colombier
473*80ee5cbfSDavid du Colombier static int
li_recognizer_add_to_dictionary(recognizer,letterset *,wordset)474*80ee5cbfSDavid du Colombier li_recognizer_add_to_dictionary(recognizer, letterset*, wordset)
475*80ee5cbfSDavid du Colombier {
476*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
477*80ee5cbfSDavid du Colombier li_err_msg = "Dictionaries are not supported by the LI recognizer";
478*80ee5cbfSDavid du Colombier return -1;
479*80ee5cbfSDavid du Colombier }
480*80ee5cbfSDavid du Colombier
481*80ee5cbfSDavid du Colombier static int
li_recognizer_delete_from_dictionary(recognizer,letterset *,wordset)482*80ee5cbfSDavid du Colombier li_recognizer_delete_from_dictionary(recognizer, letterset*, wordset)
483*80ee5cbfSDavid du Colombier {
484*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
485*80ee5cbfSDavid du Colombier li_err_msg = "Dictionaries are not supported by the LI recognizer";
486*80ee5cbfSDavid du Colombier return -1;
487*80ee5cbfSDavid du Colombier }
488*80ee5cbfSDavid du Colombier
489*80ee5cbfSDavid du Colombier static char*
li_recognizer_error(recognizer rec)490*80ee5cbfSDavid du Colombier li_recognizer_error(recognizer rec)
491*80ee5cbfSDavid du Colombier {
492*80ee5cbfSDavid du Colombier char* ret = li_err_msg;
493*80ee5cbfSDavid du Colombier
494*80ee5cbfSDavid du Colombier /*Check for LI recognizer.*/
495*80ee5cbfSDavid du Colombier
496*80ee5cbfSDavid du Colombier if( !CHECK_LI_MAGIC(rec->recognizer_specific) ) {
497*80ee5cbfSDavid du Colombier li_err_msg = "Not a LI recognizer";
498*80ee5cbfSDavid du Colombier return nil;
499*80ee5cbfSDavid du Colombier }
500*80ee5cbfSDavid du Colombier li_err_msg = nil;
501*80ee5cbfSDavid du Colombier return ret;
502*80ee5cbfSDavid du Colombier }
503*80ee5cbfSDavid du Colombier
504*80ee5cbfSDavid du Colombier static int
li_recognizer_clear(recognizer r,bool)505*80ee5cbfSDavid du Colombier li_recognizer_clear(recognizer r, bool)
506*80ee5cbfSDavid du Colombier {
507*80ee5cbfSDavid du Colombier li_recognizer* rec;
508*80ee5cbfSDavid du Colombier
509*80ee5cbfSDavid du Colombier rec = (li_recognizer*)r->recognizer_specific;
510*80ee5cbfSDavid du Colombier /*Check for LI recognizer.*/
511*80ee5cbfSDavid du Colombier if( !CHECK_LI_MAGIC(rec) ) {
512*80ee5cbfSDavid du Colombier li_err_msg = "Not a LI recognizer";
513*80ee5cbfSDavid du Colombier return 0;
514*80ee5cbfSDavid du Colombier }
515*80ee5cbfSDavid du Colombier return 0;
516*80ee5cbfSDavid du Colombier }
517*80ee5cbfSDavid du Colombier
518*80ee5cbfSDavid du Colombier static int
li_recognizer_set_context(recognizer,rc *)519*80ee5cbfSDavid du Colombier li_recognizer_set_context(recognizer, rc*)
520*80ee5cbfSDavid du Colombier {
521*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
522*80ee5cbfSDavid du Colombier li_err_msg = "Contexts are not supported by the LI recognizer";
523*80ee5cbfSDavid du Colombier return -1;
524*80ee5cbfSDavid du Colombier }
525*80ee5cbfSDavid du Colombier
526*80ee5cbfSDavid du Colombier static rc*
li_recognizer_get_context(recognizer)527*80ee5cbfSDavid du Colombier li_recognizer_get_context(recognizer)
528*80ee5cbfSDavid du Colombier {
529*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
530*80ee5cbfSDavid du Colombier li_err_msg = "Contexts are not supported by the LI recognizer";
531*80ee5cbfSDavid du Colombier return nil;
532*80ee5cbfSDavid du Colombier }
533*80ee5cbfSDavid du Colombier
534*80ee5cbfSDavid du Colombier static int
li_recognizer_get_buffer(recognizer,uint *,Stroke **)535*80ee5cbfSDavid du Colombier li_recognizer_get_buffer(recognizer, uint*, Stroke**)
536*80ee5cbfSDavid du Colombier {
537*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
538*80ee5cbfSDavid du Colombier li_err_msg = "Buffer get/set are not supported by the LI recognizer";
539*80ee5cbfSDavid du Colombier return -1;
540*80ee5cbfSDavid du Colombier }
541*80ee5cbfSDavid du Colombier
542*80ee5cbfSDavid du Colombier static int
li_recognizer_set_buffer(recognizer,uint,Stroke *)543*80ee5cbfSDavid du Colombier li_recognizer_set_buffer(recognizer, uint, Stroke*)
544*80ee5cbfSDavid du Colombier {
545*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
546*80ee5cbfSDavid du Colombier li_err_msg = "Buffer get/set are not supported by the LI recognizer";
547*80ee5cbfSDavid du Colombier return -1;
548*80ee5cbfSDavid du Colombier }
549*80ee5cbfSDavid du Colombier
550*80ee5cbfSDavid du Colombier static int
li_recognizer_translate(recognizer r,uint ncs,Stroke * tps,bool,int * nret,rec_alternative ** ret)551*80ee5cbfSDavid du Colombier li_recognizer_translate(recognizer r, uint ncs, Stroke* tps, bool, int* nret, rec_alternative** ret)
552*80ee5cbfSDavid du Colombier {
553*80ee5cbfSDavid du Colombier char* clss;
554*80ee5cbfSDavid du Colombier li_recognizer* rec;
555*80ee5cbfSDavid du Colombier int conf;
556*80ee5cbfSDavid du Colombier rClassifier* rc;
557*80ee5cbfSDavid du Colombier
558*80ee5cbfSDavid du Colombier rec = (li_recognizer*)r->recognizer_specific;
559*80ee5cbfSDavid du Colombier
560*80ee5cbfSDavid du Colombier *nret = 0;
561*80ee5cbfSDavid du Colombier *ret = nil;
562*80ee5cbfSDavid du Colombier
563*80ee5cbfSDavid du Colombier /*Check for LI recognizer.*/
564*80ee5cbfSDavid du Colombier
565*80ee5cbfSDavid du Colombier if( !CHECK_LI_MAGIC(rec) ) {
566*80ee5cbfSDavid du Colombier li_err_msg = "Not a LI recognizer";
567*80ee5cbfSDavid du Colombier return(-1);
568*80ee5cbfSDavid du Colombier }
569*80ee5cbfSDavid du Colombier
570*80ee5cbfSDavid du Colombier rc = &(rec->li_rc);
571*80ee5cbfSDavid du Colombier
572*80ee5cbfSDavid du Colombier /*Check for valid parameters.*/
573*80ee5cbfSDavid du Colombier if (ncs < 1) {
574*80ee5cbfSDavid du Colombier li_err_msg = "Invalid parameters: ncs";
575*80ee5cbfSDavid du Colombier return(-1);
576*80ee5cbfSDavid du Colombier }
577*80ee5cbfSDavid du Colombier if( tps == nil) {
578*80ee5cbfSDavid du Colombier li_err_msg = "Invalid parameters: tps";
579*80ee5cbfSDavid du Colombier return(-1);
580*80ee5cbfSDavid du Colombier }
581*80ee5cbfSDavid du Colombier if( nret == nil) {
582*80ee5cbfSDavid du Colombier li_err_msg = "Invalid parameters: nret";
583*80ee5cbfSDavid du Colombier return(-1);
584*80ee5cbfSDavid du Colombier }
585*80ee5cbfSDavid du Colombier if( ret == nil) {
586*80ee5cbfSDavid du Colombier li_err_msg = "Invalid parameters: ret";
587*80ee5cbfSDavid du Colombier return(-1);
588*80ee5cbfSDavid du Colombier }
589*80ee5cbfSDavid du Colombier
590*80ee5cbfSDavid du Colombier /*
591*80ee5cbfSDavid du Colombier * Go through the stroke array and recognize. Since this is a single
592*80ee5cbfSDavid du Colombier * stroke recognizer, each stroke is treated as a separate
593*80ee5cbfSDavid du Colombier * character or gesture. We allow only characters or gestures
594*80ee5cbfSDavid du Colombier * to be recognized at one time, since otherwise, handling
595*80ee5cbfSDavid du Colombier * the display of segmentation would be difficult.
596*80ee5cbfSDavid du Colombier */
597*80ee5cbfSDavid du Colombier clss = recognize_internal(rc,tps,&conf);
598*80ee5cbfSDavid du Colombier if (clss == nil) {
599*80ee5cbfSDavid du Colombier *nret = 1;
600*80ee5cbfSDavid du Colombier return(0);
601*80ee5cbfSDavid du Colombier }
602*80ee5cbfSDavid du Colombier
603*80ee5cbfSDavid du Colombier /*Return values.*/
604*80ee5cbfSDavid du Colombier *nret = 1;
605*80ee5cbfSDavid du Colombier return(*clss);
606*80ee5cbfSDavid du Colombier }
607*80ee5cbfSDavid du Colombier
608*80ee5cbfSDavid du Colombier
609*80ee5cbfSDavid du Colombier static rec_fn*
li_recognizer_get_extension_functions(recognizer rec)610*80ee5cbfSDavid du Colombier li_recognizer_get_extension_functions(recognizer rec)
611*80ee5cbfSDavid du Colombier {
612*80ee5cbfSDavid du Colombier rec_fn* ret;
613*80ee5cbfSDavid du Colombier
614*80ee5cbfSDavid du Colombier /*Check for LI recognizer.*/
615*80ee5cbfSDavid du Colombier
616*80ee5cbfSDavid du Colombier if( !CHECK_LI_MAGIC(rec->recognizer_specific) ) {
617*80ee5cbfSDavid du Colombier li_err_msg = "Not a LI recognizer";
618*80ee5cbfSDavid du Colombier return(nil);
619*80ee5cbfSDavid du Colombier }
620*80ee5cbfSDavid du Colombier
621*80ee5cbfSDavid du Colombier ret = make_rec_fn_array(LI_NUM_EX_FNS);
622*80ee5cbfSDavid du Colombier
623*80ee5cbfSDavid du Colombier /* ari -- clearState & getClasses are mine */
624*80ee5cbfSDavid du Colombier ret[LI_GET_CLASSES] = (rec_fn)recognizer_getClasses;
625*80ee5cbfSDavid du Colombier ret[LI_CLEAR] = (rec_fn)recognizer_clearState;
626*80ee5cbfSDavid du Colombier ret[LI_ISA_LI] = (rec_fn)isa_li;
627*80ee5cbfSDavid du Colombier ret[LI_TRAIN] = (rec_fn)recognizer_train;
628*80ee5cbfSDavid du Colombier
629*80ee5cbfSDavid du Colombier return(ret);
630*80ee5cbfSDavid du Colombier }
631*80ee5cbfSDavid du Colombier
632*80ee5cbfSDavid du Colombier static char**
li_recognizer_get_gesture_names(recognizer)633*80ee5cbfSDavid du Colombier li_recognizer_get_gesture_names(recognizer)
634*80ee5cbfSDavid du Colombier {
635*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
636*80ee5cbfSDavid du Colombier li_err_msg = "Gestures are not supported by the LI recognizer";
637*80ee5cbfSDavid du Colombier return nil;
638*80ee5cbfSDavid du Colombier }
639*80ee5cbfSDavid du Colombier
640*80ee5cbfSDavid du Colombier static xgesture
li_recognizer_set_gesture_action(recognizer,char *,xgesture,void *)641*80ee5cbfSDavid du Colombier li_recognizer_set_gesture_action(recognizer, char*, xgesture, void*)
642*80ee5cbfSDavid du Colombier {
643*80ee5cbfSDavid du Colombier /*This operation isn't supported by the LI recognizer.*/
644*80ee5cbfSDavid du Colombier li_err_msg = "Gestures are not supported by the LI recognizer";
645*80ee5cbfSDavid du Colombier return nil;
646*80ee5cbfSDavid du Colombier }
647*80ee5cbfSDavid du Colombier
648*80ee5cbfSDavid du Colombier /*
649*80ee5cbfSDavid du Colombier * Exported Functions
650*80ee5cbfSDavid du Colombier */
651*80ee5cbfSDavid du Colombier
652*80ee5cbfSDavid du Colombier /*RECOGNIZER_INITIALIZE-Initialize the recognizer.*/
653*80ee5cbfSDavid du Colombier
654*80ee5cbfSDavid du Colombier /* note from ari: this expands via pre-processor to
655*80ee5cbfSDavid du Colombier *
656*80ee5cbfSDavid du Colombier * recognizer __recognizer_internal_initialize(rec_info* ri)
657*80ee5cbfSDavid du Colombier */
658*80ee5cbfSDavid du Colombier
RECOGNIZER_INITIALIZE(ri)659*80ee5cbfSDavid du Colombier RECOGNIZER_INITIALIZE(ri)
660*80ee5cbfSDavid du Colombier {
661*80ee5cbfSDavid du Colombier recognizer r;
662*80ee5cbfSDavid du Colombier li_recognizer* rec;
663*80ee5cbfSDavid du Colombier int i;
664*80ee5cbfSDavid du Colombier
665*80ee5cbfSDavid du Colombier /*Check that locale matches.*/
666*80ee5cbfSDavid du Colombier
667*80ee5cbfSDavid du Colombier if( strcmp(ri->ri_locale,LI_SUPPORTED_LOCALE) != 0 ) {
668*80ee5cbfSDavid du Colombier li_err_msg = "Not a supported locale";
669*80ee5cbfSDavid du Colombier /* fprint(2, "Locale error.\n");*/
670*80ee5cbfSDavid du Colombier return(nil);
671*80ee5cbfSDavid du Colombier }
672*80ee5cbfSDavid du Colombier
673*80ee5cbfSDavid du Colombier /*
674*80ee5cbfSDavid du Colombier * Check that character sets match. Note that this is only approximate,
675*80ee5cbfSDavid du Colombier * since the classifier file will have more information.
676*80ee5cbfSDavid du Colombier */
677*80ee5cbfSDavid du Colombier
678*80ee5cbfSDavid du Colombier if( ri->ri_subset != nil ) {
679*80ee5cbfSDavid du Colombier for(i = 0; ri->ri_subset[i] != nil; i++ ) {
680*80ee5cbfSDavid du Colombier
681*80ee5cbfSDavid du Colombier if( strcmp(ri->ri_subset[i],UPPERCASE) != 0 &&
682*80ee5cbfSDavid du Colombier strcmp(ri->ri_subset[i],LOWERCASE) != 0 &&
683*80ee5cbfSDavid du Colombier strcmp(ri->ri_subset[i],DIGITS) != 0 &&
684*80ee5cbfSDavid du Colombier strcmp(ri->ri_subset[i],GESTURE) != 0 ) {
685*80ee5cbfSDavid du Colombier li_err_msg = "Not a supported character set";
686*80ee5cbfSDavid du Colombier /* fprint(2, "charset error.\n"); */
687*80ee5cbfSDavid du Colombier
688*80ee5cbfSDavid du Colombier return(nil);
689*80ee5cbfSDavid du Colombier }
690*80ee5cbfSDavid du Colombier }
691*80ee5cbfSDavid du Colombier }
692*80ee5cbfSDavid du Colombier
693*80ee5cbfSDavid du Colombier /* ari */
694*80ee5cbfSDavid du Colombier r = make_recognizer(ri);
695*80ee5cbfSDavid du Colombier /* fprint(2, "past make_recognizer.\n"); */
696*80ee5cbfSDavid du Colombier
697*80ee5cbfSDavid du Colombier if( r == nil ) {
698*80ee5cbfSDavid du Colombier li_err_msg = "Can't allocate storage";
699*80ee5cbfSDavid du Colombier
700*80ee5cbfSDavid du Colombier return(nil);
701*80ee5cbfSDavid du Colombier }
702*80ee5cbfSDavid du Colombier
703*80ee5cbfSDavid du Colombier /*Make a LI recognizer structure.*/
704*80ee5cbfSDavid du Colombier
705*80ee5cbfSDavid du Colombier
706*80ee5cbfSDavid du Colombier /* rec = (li_recognizer*)safe_malloc(sizeof(li_recognizer))) == nil ); */
707*80ee5cbfSDavid du Colombier
708*80ee5cbfSDavid du Colombier rec = malloc(sizeof(li_recognizer));
709*80ee5cbfSDavid du Colombier
710*80ee5cbfSDavid du Colombier r->recognizer_specific = rec;
711*80ee5cbfSDavid du Colombier
712*80ee5cbfSDavid du Colombier rec->li_rc.file_name = nil;
713*80ee5cbfSDavid du Colombier rec->li_rc.nclasses = 0;
714*80ee5cbfSDavid du Colombier
715*80ee5cbfSDavid du Colombier /*Initialize the recognizer struct.*/
716*80ee5cbfSDavid du Colombier
717*80ee5cbfSDavid du Colombier r->recognizer_load_state = li_recognizer_load;
718*80ee5cbfSDavid du Colombier r->recognizer_save_state = li_recognizer_save;
719*80ee5cbfSDavid du Colombier r->recognizer_load_dictionary = li_recognizer_load_dictionary;
720*80ee5cbfSDavid du Colombier r->recognizer_save_dictionary = li_recognizer_save_dictionary;
721*80ee5cbfSDavid du Colombier r->recognizer_free_dictionary = li_recognizer_free_dictionary;
722*80ee5cbfSDavid du Colombier r->recognizer_add_to_dictionary = li_recognizer_add_to_dictionary;
723*80ee5cbfSDavid du Colombier r->recognizer_delete_from_dictionary = li_recognizer_delete_from_dictionary;
724*80ee5cbfSDavid du Colombier r->recognizer_error = li_recognizer_error;
725*80ee5cbfSDavid du Colombier r->recognizer_translate = li_recognizer_translate;
726*80ee5cbfSDavid du Colombier r->recognizer_get_context = li_recognizer_get_context;
727*80ee5cbfSDavid du Colombier r->recognizer_set_context = li_recognizer_set_context;
728*80ee5cbfSDavid du Colombier r->recognizer_get_buffer = li_recognizer_get_buffer;
729*80ee5cbfSDavid du Colombier r->recognizer_set_buffer = li_recognizer_set_buffer;
730*80ee5cbfSDavid du Colombier r->recognizer_clear = li_recognizer_clear;
731*80ee5cbfSDavid du Colombier r->recognizer_get_extension_functions =
732*80ee5cbfSDavid du Colombier li_recognizer_get_extension_functions;
733*80ee5cbfSDavid du Colombier r->recognizer_get_gesture_names = li_recognizer_get_gesture_names;
734*80ee5cbfSDavid du Colombier r->recognizer_set_gesture_action =
735*80ee5cbfSDavid du Colombier li_recognizer_set_gesture_action;
736*80ee5cbfSDavid du Colombier
737*80ee5cbfSDavid du Colombier /*Initialize LI Magic Number.*/
738*80ee5cbfSDavid du Colombier
739*80ee5cbfSDavid du Colombier rec->li_magic = LI_MAGIC;
740*80ee5cbfSDavid du Colombier
741*80ee5cbfSDavid du Colombier /*Initialize rClassifier.*/
742*80ee5cbfSDavid du Colombier
743*80ee5cbfSDavid du Colombier rec->li_rc.file_name = nil;
744*80ee5cbfSDavid du Colombier
745*80ee5cbfSDavid du Colombier for( i = 0; i < MAXSCLASSES; i++ ) {
746*80ee5cbfSDavid du Colombier rec->li_rc.ex[i] = nil;
747*80ee5cbfSDavid du Colombier rec->li_rc.cnames[i] = nil;
748*80ee5cbfSDavid du Colombier }
749*80ee5cbfSDavid du Colombier
750*80ee5cbfSDavid du Colombier lialg_initialize(&rec->li_rc);
751*80ee5cbfSDavid du Colombier
752*80ee5cbfSDavid du Colombier /*Get rid of error message. Not needed here.*/
753*80ee5cbfSDavid du Colombier li_err_msg = nil;
754*80ee5cbfSDavid du Colombier
755*80ee5cbfSDavid du Colombier return(r);
756*80ee5cbfSDavid du Colombier }
757*80ee5cbfSDavid du Colombier
758*80ee5cbfSDavid du Colombier /*free_rClassifier-Free the rClassifier.*/
759*80ee5cbfSDavid du Colombier
760*80ee5cbfSDavid du Colombier static void
free_rClassifier(rClassifier * rc)761*80ee5cbfSDavid du Colombier free_rClassifier(rClassifier* rc)
762*80ee5cbfSDavid du Colombier {
763*80ee5cbfSDavid du Colombier int i;
764*80ee5cbfSDavid du Colombier
765*80ee5cbfSDavid du Colombier if( rc->file_name != nil) {
766*80ee5cbfSDavid du Colombier free(rc->file_name);
767*80ee5cbfSDavid du Colombier }
768*80ee5cbfSDavid du Colombier
769*80ee5cbfSDavid du Colombier for( i = 0; rc->ex[i] != nil; i++) {
770*80ee5cbfSDavid du Colombier delete_examples(rc->ex[i]);
771*80ee5cbfSDavid du Colombier free(rc->cnames[i]);
772*80ee5cbfSDavid du Colombier }
773*80ee5cbfSDavid du Colombier
774*80ee5cbfSDavid du Colombier }
775*80ee5cbfSDavid du Colombier
776*80ee5cbfSDavid du Colombier /*RECOGNIZER_FINALIZE-Deallocate the recognizer, finalize.*/
777*80ee5cbfSDavid du Colombier
RECOGNIZER_FINALIZE(r)778*80ee5cbfSDavid du Colombier RECOGNIZER_FINALIZE(r)
779*80ee5cbfSDavid du Colombier {
780*80ee5cbfSDavid du Colombier li_recognizer* rec = (li_recognizer*)r->recognizer_specific;
781*80ee5cbfSDavid du Colombier
782*80ee5cbfSDavid du Colombier /*Make sure this is a li_recognizer first*/
783*80ee5cbfSDavid du Colombier if( !CHECK_LI_MAGIC(rec) ) {
784*80ee5cbfSDavid du Colombier li_err_msg = "Not a LI recognizer";
785*80ee5cbfSDavid du Colombier return(-1);
786*80ee5cbfSDavid du Colombier }
787*80ee5cbfSDavid du Colombier
788*80ee5cbfSDavid du Colombier /*Deallocate rClassifier resources.*/
789*80ee5cbfSDavid du Colombier
790*80ee5cbfSDavid du Colombier free_rClassifier(&(rec->li_rc));
791*80ee5cbfSDavid du Colombier
792*80ee5cbfSDavid du Colombier /*Deallocate the li_recognizer struct.*/
793*80ee5cbfSDavid du Colombier free(rec);
794*80ee5cbfSDavid du Colombier
795*80ee5cbfSDavid du Colombier /*Deallocate the recognizer*/
796*80ee5cbfSDavid du Colombier delete_recognizer(r);
797*80ee5cbfSDavid du Colombier
798*80ee5cbfSDavid du Colombier return(0);
799*80ee5cbfSDavid du Colombier }
800*80ee5cbfSDavid du Colombier
801*80ee5cbfSDavid du Colombier
802*80ee5cbfSDavid du Colombier /* **************************************************
803*80ee5cbfSDavid du Colombier
804*80ee5cbfSDavid du Colombier Implementation of the Li/Yeung recognition algorithm
805*80ee5cbfSDavid du Colombier
806*80ee5cbfSDavid du Colombier ************************************************** */
807*80ee5cbfSDavid du Colombier
808*80ee5cbfSDavid du Colombier #define WORST_SCORE 0x7fffffff
809*80ee5cbfSDavid du Colombier
810*80ee5cbfSDavid du Colombier /* Dynamic programming parameters */
811*80ee5cbfSDavid du Colombier #define DP_BAND 3
812*80ee5cbfSDavid du Colombier #define MIN_SIM 0
813*80ee5cbfSDavid du Colombier #define MAX_DIST 0x7fffffff
814*80ee5cbfSDavid du Colombier #define SIM_THLD 60 /* x 100 */
815*80ee5cbfSDavid du Colombier #define DIST_THLD 3200 /* x 100 */
816*80ee5cbfSDavid du Colombier
817*80ee5cbfSDavid du Colombier /* Low-pass filter parameters -- empirically derived */
818*80ee5cbfSDavid du Colombier #define LP_FILTER_WIDTH 6
819*80ee5cbfSDavid du Colombier #define LP_FILTER_ITERS 8
820*80ee5cbfSDavid du Colombier #define LP_FILTER_THLD 250 /* x 100 */
821*80ee5cbfSDavid du Colombier #define LP_FILTER_MIN 5
822*80ee5cbfSDavid du Colombier
823*80ee5cbfSDavid du Colombier /* Pseudo-extrema parameters -- empirically derived */
824*80ee5cbfSDavid du Colombier #define PE_AL_THLD 1500 /* x 100 */
825*80ee5cbfSDavid du Colombier #define PE_ATCR_THLD 135 /* x 100 */
826*80ee5cbfSDavid du Colombier
827*80ee5cbfSDavid du Colombier /* Contour-angle derivation parameters */
828*80ee5cbfSDavid du Colombier #define T_ONE 1
829*80ee5cbfSDavid du Colombier #define T_TWO 20
830*80ee5cbfSDavid du Colombier
831*80ee5cbfSDavid du Colombier /* Pre-processing and canonicalization parameters */
832*80ee5cbfSDavid du Colombier #define CANONICAL_X 108
833*80ee5cbfSDavid du Colombier #define CANONICAL_Y 128
834*80ee5cbfSDavid du Colombier #define DIST_SQ_THRESHOLD (3*3) /* copied from fv.h */
835*80ee5cbfSDavid du Colombier #define NCANONICAL 50
836*80ee5cbfSDavid du Colombier
837*80ee5cbfSDavid du Colombier /* Tap-handling parameters */
838*80ee5cbfSDavid du Colombier #define TAP_CHAR "."
839*80ee5cbfSDavid du Colombier #define TAP_TIME_THLD 150 /* msec */
840*80ee5cbfSDavid du Colombier #define TAP_DIST_THLD 75 /* dx * dx + dy * dy */
841*80ee5cbfSDavid du Colombier #define TAP_PATHLEN 1000 /* x 100 */
842*80ee5cbfSDavid du Colombier
843*80ee5cbfSDavid du Colombier
844*80ee5cbfSDavid du Colombier /* region types */
845*80ee5cbfSDavid du Colombier #define RGN_CONVEX 0
846*80ee5cbfSDavid du Colombier #define RGN_CONCAVE 1
847*80ee5cbfSDavid du Colombier #define RGN_PLAIN 2
848*80ee5cbfSDavid du Colombier #define RGN_PSEUDO 3
849*80ee5cbfSDavid du Colombier
850*80ee5cbfSDavid du Colombier
851*80ee5cbfSDavid du Colombier typedef struct RegionList {
852*80ee5cbfSDavid du Colombier int start;
853*80ee5cbfSDavid du Colombier int end;
854*80ee5cbfSDavid du Colombier int type;
855*80ee5cbfSDavid du Colombier struct RegionList *next;
856*80ee5cbfSDavid du Colombier } region_list;
857*80ee5cbfSDavid du Colombier
858*80ee5cbfSDavid du Colombier
859*80ee5cbfSDavid du Colombier /* direction-code table; indexed by dx, dy */
860*80ee5cbfSDavid du Colombier static int lialg_dctbl[3][3] = {{1, 0, 7}, {2, 0x7FFFFFFF, 6}, {3, 4, 5}};
861*80ee5cbfSDavid du Colombier
862*80ee5cbfSDavid du Colombier /* low-pass filter weights */
863*80ee5cbfSDavid du Colombier static int lialg_lpfwts[2 * LP_FILTER_WIDTH + 1];
864*80ee5cbfSDavid du Colombier static int lialg_lpfconst = -1;
865*80ee5cbfSDavid du Colombier
866*80ee5cbfSDavid du Colombier
867*80ee5cbfSDavid du Colombier static int lialg_preprocess_stroke(point_list *);
868*80ee5cbfSDavid du Colombier static point_list *lialg_compute_dominant_points(point_list *);
869*80ee5cbfSDavid du Colombier static point_list *lialg_interpolate_points(point_list *);
870*80ee5cbfSDavid du Colombier static void lialg_bresline(pen_point *, pen_point *, point_list *, int *);
871*80ee5cbfSDavid du Colombier static void lialg_compute_chain_code(point_list *);
872*80ee5cbfSDavid du Colombier static void lialg_compute_unit_chain_code(point_list *);
873*80ee5cbfSDavid du Colombier static region_list *lialg_compute_regions(point_list *);
874*80ee5cbfSDavid du Colombier static point_list *lialg_compute_dompts(point_list *, region_list *);
875*80ee5cbfSDavid du Colombier static int *lialg_compute_contour_angle_set(point_list *, region_list *);
876*80ee5cbfSDavid du Colombier static void lialg_score_stroke(point_list *, point_list *, int *, int *);
877*80ee5cbfSDavid du Colombier static int lialg_compute_similarity(point_list *, point_list *);
878*80ee5cbfSDavid du Colombier static int lialg_compute_distance(point_list *, point_list *);
879*80ee5cbfSDavid du Colombier
880*80ee5cbfSDavid du Colombier static int lialg_read_classifier_digest(rClassifier *);
881*80ee5cbfSDavid du Colombier
882*80ee5cbfSDavid du Colombier static int lialg_canonicalize_examples(rClassifier *);
883*80ee5cbfSDavid du Colombier static int lialg_canonicalize_example_stroke(point_list *);
884*80ee5cbfSDavid du Colombier static int lialg_compute_equipoints(point_list *);
885*80ee5cbfSDavid du Colombier
886*80ee5cbfSDavid du Colombier static int lialg_compute_pathlen(point_list *);
887*80ee5cbfSDavid du Colombier static int lialg_compute_pathlen_subset(point_list *, int, int);
888*80ee5cbfSDavid du Colombier static int lialg_filter_points(point_list *);
889*80ee5cbfSDavid du Colombier static int lialg_translate_points(point_list *, int, int, int, int);
890*80ee5cbfSDavid du Colombier static void lialg_get_bounding_box(point_list *, int *, int *, int *, int *);
891*80ee5cbfSDavid du Colombier static void lialg_compute_lpf_parameters();
892*80ee5cbfSDavid du Colombier static int isqrt(int);
893*80ee5cbfSDavid du Colombier static int likeatan(int, int);
894*80ee5cbfSDavid du Colombier static int quadr(int);
895*80ee5cbfSDavid du Colombier
896*80ee5cbfSDavid du Colombier
897*80ee5cbfSDavid du Colombier /*************************************************************
898*80ee5cbfSDavid du Colombier
899*80ee5cbfSDavid du Colombier Core routines for the Li/Yeung recognition algorithm
900*80ee5cbfSDavid du Colombier
901*80ee5cbfSDavid du Colombier *************************************************************/
902*80ee5cbfSDavid du Colombier
lialg_initialize(rClassifier * rec)903*80ee5cbfSDavid du Colombier static void lialg_initialize(rClassifier *rec) {
904*80ee5cbfSDavid du Colombier int i;
905*80ee5cbfSDavid du Colombier
906*80ee5cbfSDavid du Colombier /* Initialize the dompts arrays. */
907*80ee5cbfSDavid du Colombier for (i = 0; i < MAXSCLASSES; i++) {
908*80ee5cbfSDavid du Colombier rec->dompts[i] = nil;
909*80ee5cbfSDavid du Colombier }
910*80ee5cbfSDavid du Colombier }
911*80ee5cbfSDavid du Colombier
912*80ee5cbfSDavid du Colombier
913*80ee5cbfSDavid du Colombier /*
914*80ee5cbfSDavid du Colombier * Main recognition routine -- called by HRE API.
915*80ee5cbfSDavid du Colombier */
lialg_recognize_stroke(rClassifier * rec,point_list * stroke)916*80ee5cbfSDavid du Colombier static char *lialg_recognize_stroke(rClassifier *rec, point_list *stroke) {
917*80ee5cbfSDavid du Colombier int i;
918*80ee5cbfSDavid du Colombier char *name = nil;
919*80ee5cbfSDavid du Colombier point_list *input_dompts = nil;
920*80ee5cbfSDavid du Colombier char *best_name = nil;
921*80ee5cbfSDavid du Colombier int best_score = WORST_SCORE;
922*80ee5cbfSDavid du Colombier char *curr_name;
923*80ee5cbfSDavid du Colombier point_list *curr_dompts;
924*80ee5cbfSDavid du Colombier
925*80ee5cbfSDavid du Colombier /* (void)gettimeofday(&stv, nil);*/
926*80ee5cbfSDavid du Colombier
927*80ee5cbfSDavid du Colombier if (stroke->npts < 1) goto done;
928*80ee5cbfSDavid du Colombier
929*80ee5cbfSDavid du Colombier /* Check for tap. */
930*80ee5cbfSDavid du Colombier
931*80ee5cbfSDavid du Colombier /* First thing is to filter out ``close points.'' */
932*80ee5cbfSDavid du Colombier if (lialg_filter_points(stroke) != 0) return(nil);
933*80ee5cbfSDavid du Colombier
934*80ee5cbfSDavid du Colombier /* Unfortunately, we don't have the actual time that each point */
935*80ee5cbfSDavid du Colombier /* was recorded (i.e., dt is invalid). Hence, we have to use a */
936*80ee5cbfSDavid du Colombier /* heuristic based on total distance and the number of points. */
937*80ee5cbfSDavid du Colombier if (stroke->npts == 1 || lialg_compute_pathlen(stroke) < TAP_PATHLEN)
938*80ee5cbfSDavid du Colombier return(TAP_CHAR);
939*80ee5cbfSDavid du Colombier
940*80ee5cbfSDavid du Colombier /* Pre-process input stroke. */
941*80ee5cbfSDavid du Colombier if (lialg_preprocess_stroke(stroke) != 0) goto done;
942*80ee5cbfSDavid du Colombier
943*80ee5cbfSDavid du Colombier /* Compute its dominant points. */
944*80ee5cbfSDavid du Colombier input_dompts = lialg_compute_dominant_points(stroke);
945*80ee5cbfSDavid du Colombier if (input_dompts == nil) goto done;
946*80ee5cbfSDavid du Colombier
947*80ee5cbfSDavid du Colombier /* Score input stroke against every class in classifier. */
948*80ee5cbfSDavid du Colombier for (i = 0, curr_name = rec->cnames[i], curr_dompts = rec->dompts[i];
949*80ee5cbfSDavid du Colombier i < MAXSCLASSES && curr_name != nil && curr_dompts != nil;
950*80ee5cbfSDavid du Colombier i++, curr_name = rec->cnames[i], curr_dompts = rec->dompts[i]) {
951*80ee5cbfSDavid du Colombier int sim;
952*80ee5cbfSDavid du Colombier int dist;
953*80ee5cbfSDavid du Colombier int curr_score;
954*80ee5cbfSDavid du Colombier
955*80ee5cbfSDavid du Colombier lialg_score_stroke(input_dompts, curr_dompts, &sim, &dist);
956*80ee5cbfSDavid du Colombier curr_score = dist;
957*80ee5cbfSDavid du Colombier
958*80ee5cbfSDavid du Colombier if (lidebug && curr_score < DIST_THLD)
959*80ee5cbfSDavid du Colombier fprint(2, "(%s, %d, %d) ", curr_name, sim, dist);
960*80ee5cbfSDavid du Colombier
961*80ee5cbfSDavid du Colombier /* Is it the best so far? */
962*80ee5cbfSDavid du Colombier if (curr_score < best_score && curr_score <= DIST_THLD) {
963*80ee5cbfSDavid du Colombier best_score = curr_score;
964*80ee5cbfSDavid du Colombier best_name = curr_name;
965*80ee5cbfSDavid du Colombier }
966*80ee5cbfSDavid du Colombier }
967*80ee5cbfSDavid du Colombier
968*80ee5cbfSDavid du Colombier if (lidebug)
969*80ee5cbfSDavid du Colombier fprint(2, "\n");
970*80ee5cbfSDavid du Colombier
971*80ee5cbfSDavid du Colombier /* No errors. */
972*80ee5cbfSDavid du Colombier name = best_name;
973*80ee5cbfSDavid du Colombier
974*80ee5cbfSDavid du Colombier done:
975*80ee5cbfSDavid du Colombier delete_examples(input_dompts);
976*80ee5cbfSDavid du Colombier return(name);
977*80ee5cbfSDavid du Colombier }
978*80ee5cbfSDavid du Colombier
979*80ee5cbfSDavid du Colombier
lialg_preprocess_stroke(point_list * points)980*80ee5cbfSDavid du Colombier static int lialg_preprocess_stroke(point_list *points) {
981*80ee5cbfSDavid du Colombier int minx, miny, maxx, maxy, xrange, yrange, scale, xoff, yoff;
982*80ee5cbfSDavid du Colombier
983*80ee5cbfSDavid du Colombier /* Filter out points that are too close. */
984*80ee5cbfSDavid du Colombier /* We did this earlier, when we checked for a tap. */
985*80ee5cbfSDavid du Colombier /*
986*80ee5cbfSDavid du Colombier if (lialg_filter_points(points) != 0) return(-1);
987*80ee5cbfSDavid du Colombier */
988*80ee5cbfSDavid du Colombier
989*80ee5cbfSDavid du Colombier /* assert(points->npts > 0);*/
990*80ee5cbfSDavid du Colombier
991*80ee5cbfSDavid du Colombier /* Scale up to avoid conversion errors. */
992*80ee5cbfSDavid du Colombier lialg_get_bounding_box(points, &minx, &miny, &maxx, &maxy);
993*80ee5cbfSDavid du Colombier xrange = maxx - minx;
994*80ee5cbfSDavid du Colombier yrange = maxy - miny;
995*80ee5cbfSDavid du Colombier scale = ( ((100 * xrange + CANONICAL_X / 2) / CANONICAL_X) >
996*80ee5cbfSDavid du Colombier ((100 * yrange + CANONICAL_Y / 2) / CANONICAL_Y))
997*80ee5cbfSDavid du Colombier ? (100 * CANONICAL_X + xrange / 2) / xrange
998*80ee5cbfSDavid du Colombier : (100 * CANONICAL_Y + yrange / 2) / yrange;
999*80ee5cbfSDavid du Colombier if (lialg_translate_points(points, minx, miny, scale, scale) != 0)
1000*80ee5cbfSDavid du Colombier return(-1);
1001*80ee5cbfSDavid du Colombier
1002*80ee5cbfSDavid du Colombier /* Center the stroke. */
1003*80ee5cbfSDavid du Colombier lialg_get_bounding_box(points, &minx, &miny, &maxx, &maxy);
1004*80ee5cbfSDavid du Colombier xrange = maxx - minx;
1005*80ee5cbfSDavid du Colombier yrange = maxy - miny;
1006*80ee5cbfSDavid du Colombier xoff = -((CANONICAL_X - xrange + 1) / 2);
1007*80ee5cbfSDavid du Colombier yoff = -((CANONICAL_Y - yrange + 1) / 2);
1008*80ee5cbfSDavid du Colombier if (lialg_translate_points(points, xoff, yoff, 100, 100) != 0) return(-1);
1009*80ee5cbfSDavid du Colombier
1010*80ee5cbfSDavid du Colombier /* Store the x and y ranges in the point list. */
1011*80ee5cbfSDavid du Colombier xrange = maxx - minx;
1012*80ee5cbfSDavid du Colombier yrange = maxy - miny;
1013*80ee5cbfSDavid du Colombier points->xrange = xrange;
1014*80ee5cbfSDavid du Colombier points->yrange = yrange;
1015*80ee5cbfSDavid du Colombier
1016*80ee5cbfSDavid du Colombier if (lidebug) {
1017*80ee5cbfSDavid du Colombier int i;
1018*80ee5cbfSDavid du Colombier fprint(2, "After pre-processing: %d %d %d %d\n",
1019*80ee5cbfSDavid du Colombier minx, miny, maxx, maxy);
1020*80ee5cbfSDavid du Colombier for (i = 0; i < points->npts; i++)
1021*80ee5cbfSDavid du Colombier fprint(2, " (%P)\n", points->pts[i].Point);
1022*80ee5cbfSDavid du Colombier fflush(stderr);
1023*80ee5cbfSDavid du Colombier }
1024*80ee5cbfSDavid du Colombier
1025*80ee5cbfSDavid du Colombier return(0);
1026*80ee5cbfSDavid du Colombier }
1027*80ee5cbfSDavid du Colombier
1028*80ee5cbfSDavid du Colombier
lialg_compute_dominant_points(point_list * points)1029*80ee5cbfSDavid du Colombier static point_list *lialg_compute_dominant_points(point_list *points) {
1030*80ee5cbfSDavid du Colombier point_list *ipts;
1031*80ee5cbfSDavid du Colombier region_list *regions;
1032*80ee5cbfSDavid du Colombier point_list *dpts;
1033*80ee5cbfSDavid du Colombier
1034*80ee5cbfSDavid du Colombier /* Interpolate points. */
1035*80ee5cbfSDavid du Colombier ipts = lialg_interpolate_points(points);
1036*80ee5cbfSDavid du Colombier if (ipts == nil) return(nil);
1037*80ee5cbfSDavid du Colombier if (lidebug) {
1038*80ee5cbfSDavid du Colombier int j;
1039*80ee5cbfSDavid du Colombier fprint(2, "After interpolation: %d ipts\n", ipts->npts);
1040*80ee5cbfSDavid du Colombier for (j = 0; j < ipts->npts; j++) {
1041*80ee5cbfSDavid du Colombier fprint(2, " (%P), %lud\n", ipts->pts[j].Point, ipts->pts[j].chaincode);
1042*80ee5cbfSDavid du Colombier }
1043*80ee5cbfSDavid du Colombier fflush(stderr);
1044*80ee5cbfSDavid du Colombier }
1045*80ee5cbfSDavid du Colombier
1046*80ee5cbfSDavid du Colombier /* Compute regions. */
1047*80ee5cbfSDavid du Colombier regions = lialg_compute_regions(ipts);
1048*80ee5cbfSDavid du Colombier /* assert(regions != nil);*/
1049*80ee5cbfSDavid du Colombier
1050*80ee5cbfSDavid du Colombier /* Compute dominant points. */
1051*80ee5cbfSDavid du Colombier dpts = lialg_compute_dompts(ipts, regions);
1052*80ee5cbfSDavid du Colombier if (lidebug) {
1053*80ee5cbfSDavid du Colombier int j;
1054*80ee5cbfSDavid du Colombier fprint(2, "Dominant points: ");
1055*80ee5cbfSDavid du Colombier for (j = 0; j < dpts->npts; j++) {
1056*80ee5cbfSDavid du Colombier fprint(2, "%P (%lud) ", dpts->pts[j].Point, dpts->pts[j].chaincode);
1057*80ee5cbfSDavid du Colombier }
1058*80ee5cbfSDavid du Colombier fprint(2, "\n");
1059*80ee5cbfSDavid du Colombier fflush(stderr);
1060*80ee5cbfSDavid du Colombier }
1061*80ee5cbfSDavid du Colombier
1062*80ee5cbfSDavid du Colombier /* Delete region data structure. */
1063*80ee5cbfSDavid du Colombier {
1064*80ee5cbfSDavid du Colombier region_list *curr, *next;
1065*80ee5cbfSDavid du Colombier for (curr = regions; curr != nil; ) {
1066*80ee5cbfSDavid du Colombier next = curr->next;
1067*80ee5cbfSDavid du Colombier free(curr);
1068*80ee5cbfSDavid du Colombier curr = next;
1069*80ee5cbfSDavid du Colombier }
1070*80ee5cbfSDavid du Colombier }
1071*80ee5cbfSDavid du Colombier delete_examples(ipts);
1072*80ee5cbfSDavid du Colombier return(dpts);
1073*80ee5cbfSDavid du Colombier }
1074*80ee5cbfSDavid du Colombier
1075*80ee5cbfSDavid du Colombier /* Input points are assumed to be integer-valued! */
lialg_interpolate_points(point_list * points)1076*80ee5cbfSDavid du Colombier static point_list *lialg_interpolate_points(point_list *points) {
1077*80ee5cbfSDavid du Colombier int i, j;
1078*80ee5cbfSDavid du Colombier int maxpts;
1079*80ee5cbfSDavid du Colombier point_list *newpts;
1080*80ee5cbfSDavid du Colombier
1081*80ee5cbfSDavid du Colombier /* Compute an upper-bound on the number of interpolated points. */
1082*80ee5cbfSDavid du Colombier maxpts = 0;
1083*80ee5cbfSDavid du Colombier for (i = 0; i < (points->npts - 1); i++) {
1084*80ee5cbfSDavid du Colombier pen_point *pta = &(points->pts[i]);
1085*80ee5cbfSDavid du Colombier pen_point *ptb = &(points->pts[i+1]);
1086*80ee5cbfSDavid du Colombier maxpts += abs(pta->x - ptb->x) + abs(pta->y - ptb->y);
1087*80ee5cbfSDavid du Colombier }
1088*80ee5cbfSDavid du Colombier
1089*80ee5cbfSDavid du Colombier /* Allocate an array of the requisite size. */
1090*80ee5cbfSDavid du Colombier maxpts += points->npts;
1091*80ee5cbfSDavid du Colombier /* newpts = (point_list *)safe_malloc(sizeof(point_list)); */
1092*80ee5cbfSDavid du Colombier newpts = malloc(sizeof(point_list));
1093*80ee5cbfSDavid du Colombier newpts->pts = mallocz(maxpts*sizeof(pen_point), 1);
1094*80ee5cbfSDavid du Colombier if (newpts->pts == nil) {
1095*80ee5cbfSDavid du Colombier free(newpts);
1096*80ee5cbfSDavid du Colombier return(nil);
1097*80ee5cbfSDavid du Colombier }
1098*80ee5cbfSDavid du Colombier newpts->npts = maxpts;
1099*80ee5cbfSDavid du Colombier newpts->next = nil;
1100*80ee5cbfSDavid du Colombier
1101*80ee5cbfSDavid du Colombier /* Interpolate each of the segments. */
1102*80ee5cbfSDavid du Colombier j = 0;
1103*80ee5cbfSDavid du Colombier for (i = 0; i < (points->npts - 1); i++) {
1104*80ee5cbfSDavid du Colombier pen_point *startpt = &(points->pts[i]);
1105*80ee5cbfSDavid du Colombier pen_point *endpt = &(points->pts[i+1]);
1106*80ee5cbfSDavid du Colombier
1107*80ee5cbfSDavid du Colombier lialg_bresline(startpt, endpt, newpts, &j);
1108*80ee5cbfSDavid du Colombier
1109*80ee5cbfSDavid du Colombier j--; /* end point gets recorded as start point of next segment! */
1110*80ee5cbfSDavid du Colombier }
1111*80ee5cbfSDavid du Colombier
1112*80ee5cbfSDavid du Colombier /* Add-in last point. */
1113*80ee5cbfSDavid du Colombier newpts->pts[j++] = points->pts[points->npts - 1];
1114*80ee5cbfSDavid du Colombier newpts->npts = j;
1115*80ee5cbfSDavid du Colombier
1116*80ee5cbfSDavid du Colombier /* Compute the chain code for P (the list of points). */
1117*80ee5cbfSDavid du Colombier lialg_compute_unit_chain_code(newpts);
1118*80ee5cbfSDavid du Colombier
1119*80ee5cbfSDavid du Colombier return(newpts);
1120*80ee5cbfSDavid du Colombier }
1121*80ee5cbfSDavid du Colombier
1122*80ee5cbfSDavid du Colombier
1123*80ee5cbfSDavid du Colombier /* This implementation is due to Kenny Hoff. */
lialg_bresline(pen_point * startpt,pen_point * endpt,point_list * newpts,int * j)1124*80ee5cbfSDavid du Colombier static void lialg_bresline(pen_point *startpt, pen_point *endpt,
1125*80ee5cbfSDavid du Colombier point_list *newpts, int *j) {
1126*80ee5cbfSDavid du Colombier int Ax, Ay, Bx, By, dX, dY, Xincr, Yincr;
1127*80ee5cbfSDavid du Colombier
1128*80ee5cbfSDavid du Colombier Ax = startpt->x;
1129*80ee5cbfSDavid du Colombier Ay = startpt->y;
1130*80ee5cbfSDavid du Colombier Bx = endpt->x;
1131*80ee5cbfSDavid du Colombier By = endpt->y;
1132*80ee5cbfSDavid du Colombier
1133*80ee5cbfSDavid du Colombier /* INITIALIZE THE COMPONENTS OF THE ALGORITHM THAT ARE NOT AFFECTED */
1134*80ee5cbfSDavid du Colombier /* BY THE SLOPE OR DIRECTION OF THE LINE */
1135*80ee5cbfSDavid du Colombier dX = abs(Bx-Ax); /* store the change in X and Y of the line endpoints */
1136*80ee5cbfSDavid du Colombier dY = abs(By-Ay);
1137*80ee5cbfSDavid du Colombier
1138*80ee5cbfSDavid du Colombier /* DETERMINE "DIRECTIONS" TO INCREMENT X AND Y (REGARDLESS OF DECISION) */
1139*80ee5cbfSDavid du Colombier if (Ax > Bx) { Xincr=-1; } else { Xincr=1; } /* which direction in X? */
1140*80ee5cbfSDavid du Colombier if (Ay > By) { Yincr=-1; } else { Yincr=1; } /* which direction in Y? */
1141*80ee5cbfSDavid du Colombier
1142*80ee5cbfSDavid du Colombier /* DETERMINE INDEPENDENT VARIABLE (ONE THAT ALWAYS INCREMENTS BY 1 (OR -1) ) */
1143*80ee5cbfSDavid du Colombier /* AND INITIATE APPROPRIATE LINE DRAWING ROUTINE (BASED ON FIRST OCTANT */
1144*80ee5cbfSDavid du Colombier /* ALWAYS). THE X AND Y'S MAY BE FLIPPED IF Y IS THE INDEPENDENT VARIABLE. */
1145*80ee5cbfSDavid du Colombier if (dX >= dY) { /* if X is the independent variable */
1146*80ee5cbfSDavid du Colombier int dPr = dY<<1; /* amount to increment decision if right is chosen (always) */
1147*80ee5cbfSDavid du Colombier int dPru = dPr - (dX<<1); /* amount to increment decision if up is chosen */
1148*80ee5cbfSDavid du Colombier int P = dPr - dX; /* decision variable start value */
1149*80ee5cbfSDavid du Colombier
1150*80ee5cbfSDavid du Colombier /* process each point in the line one at a time (just use dX) */
1151*80ee5cbfSDavid du Colombier for (; dX>=0; dX--) {
1152*80ee5cbfSDavid du Colombier newpts->pts[*j].x = Ax;
1153*80ee5cbfSDavid du Colombier newpts->pts[*j].y = Ay;
1154*80ee5cbfSDavid du Colombier (*j)++;
1155*80ee5cbfSDavid du Colombier
1156*80ee5cbfSDavid du Colombier if (P > 0) { /* is the pixel going right AND up? */
1157*80ee5cbfSDavid du Colombier Ax+=Xincr; /* increment independent variable */
1158*80ee5cbfSDavid du Colombier Ay+=Yincr; /* increment dependent variable */
1159*80ee5cbfSDavid du Colombier P+=dPru; /* increment decision (for up) */
1160*80ee5cbfSDavid du Colombier } else { /* is the pixel just going right? */
1161*80ee5cbfSDavid du Colombier Ax+=Xincr; /* increment independent variable */
1162*80ee5cbfSDavid du Colombier P+=dPr; /* increment decision (for right) */
1163*80ee5cbfSDavid du Colombier }
1164*80ee5cbfSDavid du Colombier }
1165*80ee5cbfSDavid du Colombier } else { /* if Y is the independent variable */
1166*80ee5cbfSDavid du Colombier int dPr = dX<<1; /* amount to increment decision if right is chosen (always) */
1167*80ee5cbfSDavid du Colombier int dPru = dPr - (dY<<1); /* amount to increment decision if up is chosen */
1168*80ee5cbfSDavid du Colombier int P = dPr - dY; /* decision variable start value */
1169*80ee5cbfSDavid du Colombier
1170*80ee5cbfSDavid du Colombier /* process each point in the line one at a time (just use dY) */
1171*80ee5cbfSDavid du Colombier for (; dY>=0; dY--) {
1172*80ee5cbfSDavid du Colombier newpts->pts[*j].x = Ax;
1173*80ee5cbfSDavid du Colombier newpts->pts[*j].y = Ay;
1174*80ee5cbfSDavid du Colombier (*j)++;
1175*80ee5cbfSDavid du Colombier
1176*80ee5cbfSDavid du Colombier if (P > 0) { /* is the pixel going up AND right? */
1177*80ee5cbfSDavid du Colombier Ax+=Xincr; /* increment dependent variable */
1178*80ee5cbfSDavid du Colombier Ay+=Yincr; /* increment independent variable */
1179*80ee5cbfSDavid du Colombier P+=dPru; /* increment decision (for up) */
1180*80ee5cbfSDavid du Colombier } else { /* is the pixel just going up? */
1181*80ee5cbfSDavid du Colombier Ay+=Yincr; /* increment independent variable */
1182*80ee5cbfSDavid du Colombier P+=dPr; /* increment decision (for right) */
1183*80ee5cbfSDavid du Colombier }
1184*80ee5cbfSDavid du Colombier }
1185*80ee5cbfSDavid du Colombier }
1186*80ee5cbfSDavid du Colombier }
1187*80ee5cbfSDavid du Colombier
lialg_compute_chain_code(point_list * pts)1188*80ee5cbfSDavid du Colombier static void lialg_compute_chain_code(point_list *pts) {
1189*80ee5cbfSDavid du Colombier int i;
1190*80ee5cbfSDavid du Colombier
1191*80ee5cbfSDavid du Colombier for (i = 0; i < (pts->npts - 1); i++) {
1192*80ee5cbfSDavid du Colombier pen_point *startpt = &(pts->pts[i]);
1193*80ee5cbfSDavid du Colombier pen_point *endpt = &(pts->pts[i+1]);
1194*80ee5cbfSDavid du Colombier int dx = endpt->x - startpt->x;
1195*80ee5cbfSDavid du Colombier int dy = endpt->y - startpt->y;
1196*80ee5cbfSDavid du Colombier int tmp = quadr(likeatan(dy, dx));
1197*80ee5cbfSDavid du Colombier int dircode = (12 - tmp) % 8;
1198*80ee5cbfSDavid du Colombier
1199*80ee5cbfSDavid du Colombier startpt->chaincode = dircode;
1200*80ee5cbfSDavid du Colombier }
1201*80ee5cbfSDavid du Colombier }
1202*80ee5cbfSDavid du Colombier
1203*80ee5cbfSDavid du Colombier
lialg_compute_unit_chain_code(point_list * pts)1204*80ee5cbfSDavid du Colombier static void lialg_compute_unit_chain_code(point_list *pts) {
1205*80ee5cbfSDavid du Colombier int i;
1206*80ee5cbfSDavid du Colombier
1207*80ee5cbfSDavid du Colombier for (i = 0; i < (pts->npts - 1); i++) {
1208*80ee5cbfSDavid du Colombier pen_point *startpt = &(pts->pts[i]);
1209*80ee5cbfSDavid du Colombier pen_point *endpt = &(pts->pts[i+1]);
1210*80ee5cbfSDavid du Colombier int dx = endpt->x - startpt->x;
1211*80ee5cbfSDavid du Colombier int dy = endpt->y - startpt->y;
1212*80ee5cbfSDavid du Colombier int dircode = lialg_dctbl[dx+1][dy+1];
1213*80ee5cbfSDavid du Colombier
1214*80ee5cbfSDavid du Colombier startpt->chaincode = dircode;
1215*80ee5cbfSDavid du Colombier }
1216*80ee5cbfSDavid du Colombier }
1217*80ee5cbfSDavid du Colombier
1218*80ee5cbfSDavid du Colombier
lialg_compute_regions(point_list * pts)1219*80ee5cbfSDavid du Colombier static region_list *lialg_compute_regions(point_list *pts) {
1220*80ee5cbfSDavid du Colombier region_list *regions;
1221*80ee5cbfSDavid du Colombier region_list *curr_reg;
1222*80ee5cbfSDavid du Colombier int *R[2 + LP_FILTER_ITERS];
1223*80ee5cbfSDavid du Colombier int *junk;
1224*80ee5cbfSDavid du Colombier int *curr, *next;
1225*80ee5cbfSDavid du Colombier int i, j;
1226*80ee5cbfSDavid du Colombier
1227*80ee5cbfSDavid du Colombier /* Initialize low-pass filter parameters if necessary. */
1228*80ee5cbfSDavid du Colombier if (lialg_lpfconst == -1)
1229*80ee5cbfSDavid du Colombier lialg_compute_lpf_parameters();
1230*80ee5cbfSDavid du Colombier
1231*80ee5cbfSDavid du Colombier /* Allocate a 2 x pts->npts array for use in computing the (filtered) Angle set, A_n. */
1232*80ee5cbfSDavid du Colombier junk = malloc((2 + LP_FILTER_ITERS) * pts->npts*sizeof(int));
1233*80ee5cbfSDavid du Colombier for (i = 0; i < (2 + LP_FILTER_ITERS); i++)
1234*80ee5cbfSDavid du Colombier R[i] = junk + (i * pts->npts);
1235*80ee5cbfSDavid du Colombier curr = R[0];
1236*80ee5cbfSDavid du Colombier
1237*80ee5cbfSDavid du Colombier /* Compute the Angle set, A, in the first element of array R. */
1238*80ee5cbfSDavid du Colombier /* Values in R are in degrees, x 100. */
1239*80ee5cbfSDavid du Colombier curr[0] = 18000; /* a_0 */
1240*80ee5cbfSDavid du Colombier for (i = 1; i < (pts->npts - 1); i++) {
1241*80ee5cbfSDavid du Colombier int d_i = pts->pts[i].chaincode;
1242*80ee5cbfSDavid du Colombier int d_iminusone = pts->pts[i-1].chaincode;
1243*80ee5cbfSDavid du Colombier int a_i;
1244*80ee5cbfSDavid du Colombier
1245*80ee5cbfSDavid du Colombier if (d_iminusone < d_i)
1246*80ee5cbfSDavid du Colombier d_iminusone += 8;
1247*80ee5cbfSDavid du Colombier
1248*80ee5cbfSDavid du Colombier a_i = (d_iminusone - d_i) % 8;
1249*80ee5cbfSDavid du Colombier
1250*80ee5cbfSDavid du Colombier /* convert to degrees, x 100 */
1251*80ee5cbfSDavid du Colombier curr[i] = ((12 - a_i) % 8) * 45 * 100;
1252*80ee5cbfSDavid du Colombier }
1253*80ee5cbfSDavid du Colombier curr[pts->npts - 1] = 18000; /* a_L-1 */
1254*80ee5cbfSDavid du Colombier
1255*80ee5cbfSDavid du Colombier /* Perform a number of filtering iterations. */
1256*80ee5cbfSDavid du Colombier next = R[1];
1257*80ee5cbfSDavid du Colombier for (j = 0; j < LP_FILTER_ITERS; j++, curr = R[j], next = R[j+1]) {
1258*80ee5cbfSDavid du Colombier for (i = 0; i < pts->npts; i++) {
1259*80ee5cbfSDavid du Colombier int k;
1260*80ee5cbfSDavid du Colombier
1261*80ee5cbfSDavid du Colombier next[i] = 0;
1262*80ee5cbfSDavid du Colombier
1263*80ee5cbfSDavid du Colombier for (k = i - LP_FILTER_WIDTH; k <= i + LP_FILTER_WIDTH; k++) {
1264*80ee5cbfSDavid du Colombier int oldval = (k < 0 || k >= pts->npts) ? 18000 : curr[k];
1265*80ee5cbfSDavid du Colombier next[i] += oldval * lialg_lpfwts[k - (i - LP_FILTER_WIDTH)]; /* overflow? */
1266*80ee5cbfSDavid du Colombier }
1267*80ee5cbfSDavid du Colombier
1268*80ee5cbfSDavid du Colombier next[i] /= lialg_lpfconst;
1269*80ee5cbfSDavid du Colombier }
1270*80ee5cbfSDavid du Colombier }
1271*80ee5cbfSDavid du Colombier
1272*80ee5cbfSDavid du Colombier /* Do final thresholding around PI. */
1273*80ee5cbfSDavid du Colombier /* curr and next are set-up correctly at end of previous loop! */
1274*80ee5cbfSDavid du Colombier for (i = 0; i < pts->npts; i++)
1275*80ee5cbfSDavid du Colombier next[i] = (abs(curr[i] - 18000) < LP_FILTER_THLD) ? 18000 : curr[i];
1276*80ee5cbfSDavid du Colombier curr = next;
1277*80ee5cbfSDavid du Colombier
1278*80ee5cbfSDavid du Colombier /* Debugging. */
1279*80ee5cbfSDavid du Colombier if (lidebug > 1) {
1280*80ee5cbfSDavid du Colombier for (i = 0; i < pts->npts; i++) {
1281*80ee5cbfSDavid du Colombier fprint(2, "%3d: (%P) %lud ",
1282*80ee5cbfSDavid du Colombier i, pts->pts[i].Point, pts->pts[i].chaincode);
1283*80ee5cbfSDavid du Colombier for (j = 0; j < 2 + LP_FILTER_ITERS; j++)
1284*80ee5cbfSDavid du Colombier fprint(2, "%d ", R[j][i]);
1285*80ee5cbfSDavid du Colombier fprint(2, "\n");
1286*80ee5cbfSDavid du Colombier }
1287*80ee5cbfSDavid du Colombier }
1288*80ee5cbfSDavid du Colombier
1289*80ee5cbfSDavid du Colombier /* Do the region segmentation. */
1290*80ee5cbfSDavid du Colombier {
1291*80ee5cbfSDavid du Colombier int start, end;
1292*80ee5cbfSDavid du Colombier int currtype;
1293*80ee5cbfSDavid du Colombier
1294*80ee5cbfSDavid du Colombier #define RGN_TYPE(val) (((val)==18000)?RGN_PLAIN:((val)<18000?RGN_CONCAVE:RGN_CONVEX))
1295*80ee5cbfSDavid du Colombier
1296*80ee5cbfSDavid du Colombier start = 0;
1297*80ee5cbfSDavid du Colombier currtype = RGN_TYPE(curr[0]);
1298*80ee5cbfSDavid du Colombier regions = malloc(sizeof(region_list));
1299*80ee5cbfSDavid du Colombier curr_reg = regions;
1300*80ee5cbfSDavid du Colombier curr_reg->start = start;
1301*80ee5cbfSDavid du Colombier curr_reg->end = 0;
1302*80ee5cbfSDavid du Colombier curr_reg->type = currtype;
1303*80ee5cbfSDavid du Colombier curr_reg->next = nil;
1304*80ee5cbfSDavid du Colombier for (i = 1; i < pts->npts; i++) {
1305*80ee5cbfSDavid du Colombier int nexttype = RGN_TYPE(curr[i]);
1306*80ee5cbfSDavid du Colombier
1307*80ee5cbfSDavid du Colombier if (nexttype != currtype) {
1308*80ee5cbfSDavid du Colombier region_list *next_reg;
1309*80ee5cbfSDavid du Colombier
1310*80ee5cbfSDavid du Colombier end = i - 1;
1311*80ee5cbfSDavid du Colombier curr_reg->end = end;
1312*80ee5cbfSDavid du Colombier if (lidebug > 1)
1313*80ee5cbfSDavid du Colombier fprint(2, " (%d, %d), %d\n", start, end, currtype);
1314*80ee5cbfSDavid du Colombier
1315*80ee5cbfSDavid du Colombier start = i;
1316*80ee5cbfSDavid du Colombier currtype = nexttype;
1317*80ee5cbfSDavid du Colombier next_reg = malloc(sizeof(region_list));
1318*80ee5cbfSDavid du Colombier next_reg->start = start;
1319*80ee5cbfSDavid du Colombier next_reg->end = 0;
1320*80ee5cbfSDavid du Colombier next_reg->type = nexttype;
1321*80ee5cbfSDavid du Colombier next_reg->next = nil;
1322*80ee5cbfSDavid du Colombier
1323*80ee5cbfSDavid du Colombier curr_reg->next = next_reg;
1324*80ee5cbfSDavid du Colombier curr_reg = next_reg;
1325*80ee5cbfSDavid du Colombier }
1326*80ee5cbfSDavid du Colombier }
1327*80ee5cbfSDavid du Colombier end = i - 1;
1328*80ee5cbfSDavid du Colombier curr_reg->end = end;
1329*80ee5cbfSDavid du Colombier if (lidebug > 1)
1330*80ee5cbfSDavid du Colombier fprint(2, " (%d, %d), %d\n", start, end, currtype);
1331*80ee5cbfSDavid du Colombier
1332*80ee5cbfSDavid du Colombier /* Filter out convex/concave regions that are too short. */
1333*80ee5cbfSDavid du Colombier for (curr_reg = regions; curr_reg; curr_reg = curr_reg->next)
1334*80ee5cbfSDavid du Colombier if (curr_reg->type == RGN_PLAIN) {
1335*80ee5cbfSDavid du Colombier region_list *next_reg;
1336*80ee5cbfSDavid du Colombier
1337*80ee5cbfSDavid du Colombier for (next_reg = curr_reg->next;
1338*80ee5cbfSDavid du Colombier next_reg != nil &&
1339*80ee5cbfSDavid du Colombier (next_reg->end - next_reg->start) < LP_FILTER_MIN;
1340*80ee5cbfSDavid du Colombier next_reg = curr_reg->next) {
1341*80ee5cbfSDavid du Colombier /* next_reg must not be plain, and it must be followed by a plain */
1342*80ee5cbfSDavid du Colombier /* assert(next_reg->type != RGN_PLAIN); */
1343*80ee5cbfSDavid du Colombier /* assert(next_reg->next != nil && (next_reg->next)->type == RGN_PLAIN); */
1344*80ee5cbfSDavid du Colombier
1345*80ee5cbfSDavid du Colombier curr_reg->next = (next_reg->next)->next;
1346*80ee5cbfSDavid du Colombier curr_reg->end = (next_reg->next)->end;
1347*80ee5cbfSDavid du Colombier
1348*80ee5cbfSDavid du Colombier free(next_reg->next);
1349*80ee5cbfSDavid du Colombier free(next_reg);
1350*80ee5cbfSDavid du Colombier }
1351*80ee5cbfSDavid du Colombier }
1352*80ee5cbfSDavid du Colombier
1353*80ee5cbfSDavid du Colombier /* Add-in pseudo-extremes. */
1354*80ee5cbfSDavid du Colombier {
1355*80ee5cbfSDavid du Colombier region_list *tmp, *prev_reg;
1356*80ee5cbfSDavid du Colombier
1357*80ee5cbfSDavid du Colombier tmp = regions;
1358*80ee5cbfSDavid du Colombier regions = nil;
1359*80ee5cbfSDavid du Colombier prev_reg = nil;
1360*80ee5cbfSDavid du Colombier for (curr_reg = tmp; curr_reg; curr_reg = curr_reg->next) {
1361*80ee5cbfSDavid du Colombier if (curr_reg->type == RGN_PLAIN) {
1362*80ee5cbfSDavid du Colombier int arclen = lialg_compute_pathlen_subset(pts,
1363*80ee5cbfSDavid du Colombier curr_reg->start,
1364*80ee5cbfSDavid du Colombier curr_reg->end);
1365*80ee5cbfSDavid du Colombier int dx = pts->pts[curr_reg->end].x -
1366*80ee5cbfSDavid du Colombier pts->pts[curr_reg->start].x;
1367*80ee5cbfSDavid du Colombier int dy = pts->pts[curr_reg->end].y -
1368*80ee5cbfSDavid du Colombier pts->pts[curr_reg->start].y;
1369*80ee5cbfSDavid du Colombier int chordlen = isqrt(10000 * (dx * dx + dy * dy));
1370*80ee5cbfSDavid du Colombier int atcr = (chordlen == 0) ? 0 : (100 * arclen + chordlen / 2) / chordlen;
1371*80ee5cbfSDavid du Colombier
1372*80ee5cbfSDavid du Colombier if (lidebug)
1373*80ee5cbfSDavid du Colombier fprint(2, "%d, %d, %d\n", arclen, chordlen, atcr);
1374*80ee5cbfSDavid du Colombier
1375*80ee5cbfSDavid du Colombier /* Split region if necessary. */
1376*80ee5cbfSDavid du Colombier if (arclen >= PE_AL_THLD && atcr >= PE_ATCR_THLD) {
1377*80ee5cbfSDavid du Colombier int mid = curr_reg->start + (curr_reg->end - curr_reg->start) / 2;
1378*80ee5cbfSDavid du Colombier int end = curr_reg->end;
1379*80ee5cbfSDavid du Colombier region_list *saved_next = curr_reg->next;
1380*80ee5cbfSDavid du Colombier
1381*80ee5cbfSDavid du Colombier curr_reg->end = mid - 1;
1382*80ee5cbfSDavid du Colombier if (prev_reg == nil)
1383*80ee5cbfSDavid du Colombier regions = curr_reg;
1384*80ee5cbfSDavid du Colombier else
1385*80ee5cbfSDavid du Colombier prev_reg->next = curr_reg;
1386*80ee5cbfSDavid du Colombier prev_reg = curr_reg;
1387*80ee5cbfSDavid du Colombier
1388*80ee5cbfSDavid du Colombier /* curr_reg = (region_list *)safe_malloc(sizeof(region_list));*/
1389*80ee5cbfSDavid du Colombier curr_reg = malloc(sizeof(region_list));
1390*80ee5cbfSDavid du Colombier curr_reg->start = mid;
1391*80ee5cbfSDavid du Colombier curr_reg->end = mid;
1392*80ee5cbfSDavid du Colombier curr_reg->type = RGN_PSEUDO;
1393*80ee5cbfSDavid du Colombier curr_reg->next = nil;
1394*80ee5cbfSDavid du Colombier prev_reg->next = curr_reg;
1395*80ee5cbfSDavid du Colombier prev_reg = curr_reg;
1396*80ee5cbfSDavid du Colombier
1397*80ee5cbfSDavid du Colombier /* curr_reg = (region_list *)malloc(sizeof(region_list)); */
1398*80ee5cbfSDavid du Colombier curr_reg = malloc(sizeof(region_list));
1399*80ee5cbfSDavid du Colombier curr_reg->start = mid + 1;
1400*80ee5cbfSDavid du Colombier curr_reg->end = end;
1401*80ee5cbfSDavid du Colombier curr_reg->type = RGN_PLAIN;
1402*80ee5cbfSDavid du Colombier curr_reg->next = nil;
1403*80ee5cbfSDavid du Colombier prev_reg->next = curr_reg;
1404*80ee5cbfSDavid du Colombier prev_reg = curr_reg;
1405*80ee5cbfSDavid du Colombier
1406*80ee5cbfSDavid du Colombier curr_reg->next = saved_next;
1407*80ee5cbfSDavid du Colombier continue;
1408*80ee5cbfSDavid du Colombier }
1409*80ee5cbfSDavid du Colombier }
1410*80ee5cbfSDavid du Colombier
1411*80ee5cbfSDavid du Colombier if (prev_reg == nil)
1412*80ee5cbfSDavid du Colombier regions = curr_reg;
1413*80ee5cbfSDavid du Colombier else
1414*80ee5cbfSDavid du Colombier prev_reg->next = curr_reg;
1415*80ee5cbfSDavid du Colombier prev_reg = curr_reg;
1416*80ee5cbfSDavid du Colombier }
1417*80ee5cbfSDavid du Colombier }
1418*80ee5cbfSDavid du Colombier }
1419*80ee5cbfSDavid du Colombier
1420*80ee5cbfSDavid du Colombier free(junk);
1421*80ee5cbfSDavid du Colombier return(regions);
1422*80ee5cbfSDavid du Colombier }
1423*80ee5cbfSDavid du Colombier
1424*80ee5cbfSDavid du Colombier
lialg_compute_dompts(point_list * pts,region_list * regions)1425*80ee5cbfSDavid du Colombier static point_list *lialg_compute_dompts(point_list *pts, region_list *regions) {
1426*80ee5cbfSDavid du Colombier point_list *dpts;
1427*80ee5cbfSDavid du Colombier int ndpts;
1428*80ee5cbfSDavid du Colombier int *cas;
1429*80ee5cbfSDavid du Colombier int nonplain;
1430*80ee5cbfSDavid du Colombier region_list *r;
1431*80ee5cbfSDavid du Colombier region_list *curr;
1432*80ee5cbfSDavid du Colombier int dp;
1433*80ee5cbfSDavid du Colombier int previx;
1434*80ee5cbfSDavid du Colombier int currix;
1435*80ee5cbfSDavid du Colombier
1436*80ee5cbfSDavid du Colombier /* Compute contour angle set. */
1437*80ee5cbfSDavid du Colombier cas = lialg_compute_contour_angle_set(pts, regions);
1438*80ee5cbfSDavid du Colombier
1439*80ee5cbfSDavid du Colombier /* Dominant points include: start_pt, end_pt, extrema_of_non_plain_regions, midpts of the preceding. */
1440*80ee5cbfSDavid du Colombier nonplain = 0;
1441*80ee5cbfSDavid du Colombier for (r = regions; r != nil; r = r->next)
1442*80ee5cbfSDavid du Colombier if (r->type != RGN_PLAIN)
1443*80ee5cbfSDavid du Colombier nonplain++;
1444*80ee5cbfSDavid du Colombier ndpts = 2 * (2 + nonplain) - 1;
1445*80ee5cbfSDavid du Colombier /* dpts = (point_list *)safe_malloc(sizeof(point_list)); */
1446*80ee5cbfSDavid du Colombier dpts = malloc(sizeof(point_list));
1447*80ee5cbfSDavid du Colombier dpts->pts = mallocz(ndpts*sizeof(pen_point), 1);
1448*80ee5cbfSDavid du Colombier if (dpts->pts == nil) {
1449*80ee5cbfSDavid du Colombier free(dpts);
1450*80ee5cbfSDavid du Colombier return(nil);
1451*80ee5cbfSDavid du Colombier }
1452*80ee5cbfSDavid du Colombier dpts->npts = ndpts;
1453*80ee5cbfSDavid du Colombier dpts->next = nil;
1454*80ee5cbfSDavid du Colombier
1455*80ee5cbfSDavid du Colombier /* Pick out dominant points. */
1456*80ee5cbfSDavid du Colombier
1457*80ee5cbfSDavid du Colombier /* Record start point. */
1458*80ee5cbfSDavid du Colombier dp = 0;
1459*80ee5cbfSDavid du Colombier previx = 0;
1460*80ee5cbfSDavid du Colombier dpts->pts[dp++] = pts->pts[previx];
1461*80ee5cbfSDavid du Colombier
1462*80ee5cbfSDavid du Colombier for (curr = regions; curr != nil; curr = curr->next)
1463*80ee5cbfSDavid du Colombier if (curr->type != RGN_PLAIN) {
1464*80ee5cbfSDavid du Colombier int max_v = 0;
1465*80ee5cbfSDavid du Colombier int min_v = 0x7fffffff; /* maxint */
1466*80ee5cbfSDavid du Colombier int max_ix = -1;
1467*80ee5cbfSDavid du Colombier int min_ix = -1;
1468*80ee5cbfSDavid du Colombier int i;
1469*80ee5cbfSDavid du Colombier
1470*80ee5cbfSDavid du Colombier for (i = curr->start; i <= curr->end; i++) {
1471*80ee5cbfSDavid du Colombier int v = cas[i];
1472*80ee5cbfSDavid du Colombier if (v > max_v) { max_v = v; max_ix = i; }
1473*80ee5cbfSDavid du Colombier if (v < min_v) { min_v = v; min_ix = i; }
1474*80ee5cbfSDavid du Colombier if (lidebug > 1)
1475*80ee5cbfSDavid du Colombier fprint(2, " %d\n", v);
1476*80ee5cbfSDavid du Colombier }
1477*80ee5cbfSDavid du Colombier
1478*80ee5cbfSDavid du Colombier currix = (curr->type == RGN_CONVEX ? max_ix : min_ix);
1479*80ee5cbfSDavid du Colombier
1480*80ee5cbfSDavid du Colombier /* Record midpoint. */
1481*80ee5cbfSDavid du Colombier dpts->pts[dp++] = pts->pts[previx + (currix - previx) / 2];
1482*80ee5cbfSDavid du Colombier
1483*80ee5cbfSDavid du Colombier /* Record extreme point. */
1484*80ee5cbfSDavid du Colombier dpts->pts[dp++] = pts->pts[currix];
1485*80ee5cbfSDavid du Colombier
1486*80ee5cbfSDavid du Colombier previx = currix;
1487*80ee5cbfSDavid du Colombier }
1488*80ee5cbfSDavid du Colombier
1489*80ee5cbfSDavid du Colombier /* Record last mid-point and end point. */
1490*80ee5cbfSDavid du Colombier currix = pts->npts - 1;
1491*80ee5cbfSDavid du Colombier dpts->pts[dp++] = pts->pts[previx + (currix - previx) / 2];
1492*80ee5cbfSDavid du Colombier dpts->pts[dp] = pts->pts[currix];
1493*80ee5cbfSDavid du Colombier
1494*80ee5cbfSDavid du Colombier /* Compute chain-code. */
1495*80ee5cbfSDavid du Colombier lialg_compute_chain_code(dpts);
1496*80ee5cbfSDavid du Colombier
1497*80ee5cbfSDavid du Colombier free(cas);
1498*80ee5cbfSDavid du Colombier return(dpts);
1499*80ee5cbfSDavid du Colombier }
1500*80ee5cbfSDavid du Colombier
1501*80ee5cbfSDavid du Colombier
lialg_compute_contour_angle_set(point_list * pts,region_list * regions)1502*80ee5cbfSDavid du Colombier static int *lialg_compute_contour_angle_set(point_list *pts,
1503*80ee5cbfSDavid du Colombier region_list *regions) {
1504*80ee5cbfSDavid du Colombier int *V;
1505*80ee5cbfSDavid du Colombier region_list *curr_reg;
1506*80ee5cbfSDavid du Colombier int i;
1507*80ee5cbfSDavid du Colombier
1508*80ee5cbfSDavid du Colombier V = malloc(pts->npts*sizeof(int));
1509*80ee5cbfSDavid du Colombier
1510*80ee5cbfSDavid du Colombier V[0] = 18000;
1511*80ee5cbfSDavid du Colombier for (curr_reg = regions; curr_reg != nil; curr_reg = curr_reg->next) {
1512*80ee5cbfSDavid du Colombier for (i = curr_reg->start; i <= curr_reg->end; i++) {
1513*80ee5cbfSDavid du Colombier if (curr_reg->type == RGN_PLAIN) {
1514*80ee5cbfSDavid du Colombier V[i] = 18000;
1515*80ee5cbfSDavid du Colombier } else {
1516*80ee5cbfSDavid du Colombier /* For now, simply choose the mid-point. */
1517*80ee5cbfSDavid du Colombier int isMidPt = i == (curr_reg->start +
1518*80ee5cbfSDavid du Colombier (curr_reg->end - curr_reg->start) / 2);
1519*80ee5cbfSDavid du Colombier V[i] = (curr_reg->type == RGN_CONVEX)
1520*80ee5cbfSDavid du Colombier ? (isMidPt ? 18000 : 0)
1521*80ee5cbfSDavid du Colombier : (isMidPt ? 0 : 18000);
1522*80ee5cbfSDavid du Colombier }
1523*80ee5cbfSDavid du Colombier }
1524*80ee5cbfSDavid du Colombier }
1525*80ee5cbfSDavid du Colombier V[pts->npts - 1] = 18000;
1526*80ee5cbfSDavid du Colombier
1527*80ee5cbfSDavid du Colombier return(V);
1528*80ee5cbfSDavid du Colombier }
1529*80ee5cbfSDavid du Colombier
1530*80ee5cbfSDavid du Colombier
1531*80ee5cbfSDavid du Colombier /*
1532*80ee5cbfSDavid du Colombier * First compute the similarity between the two strings.
1533*80ee5cbfSDavid du Colombier * If it's above a threshold, compute the distance between
1534*80ee5cbfSDavid du Colombier * the two and return it as the ``score.''
1535*80ee5cbfSDavid du Colombier * Otherwise, return the constant WORST_SCORE.
1536*80ee5cbfSDavid du Colombier *
1537*80ee5cbfSDavid du Colombier */
lialg_score_stroke(point_list * input_dompts,point_list * curr_dompts,int * sim,int * dist)1538*80ee5cbfSDavid du Colombier static void lialg_score_stroke(point_list *input_dompts, point_list *curr_dompts, int *sim, int *dist) {
1539*80ee5cbfSDavid du Colombier *sim = MIN_SIM;
1540*80ee5cbfSDavid du Colombier *dist = MAX_DIST;
1541*80ee5cbfSDavid du Colombier
1542*80ee5cbfSDavid du Colombier *sim = lialg_compute_similarity(input_dompts, curr_dompts);
1543*80ee5cbfSDavid du Colombier if (*sim < SIM_THLD) goto done;
1544*80ee5cbfSDavid du Colombier
1545*80ee5cbfSDavid du Colombier *dist = lialg_compute_distance(input_dompts, curr_dompts);
1546*80ee5cbfSDavid du Colombier
1547*80ee5cbfSDavid du Colombier done:
1548*80ee5cbfSDavid du Colombier if (lidebug)
1549*80ee5cbfSDavid du Colombier fprint(2, "%d, %d\n", *sim, *dist);
1550*80ee5cbfSDavid du Colombier }
1551*80ee5cbfSDavid du Colombier
1552*80ee5cbfSDavid du Colombier
lialg_compute_similarity(point_list * input_dompts,point_list * curr_dompts)1553*80ee5cbfSDavid du Colombier static int lialg_compute_similarity(point_list *input_dompts, point_list *curr_dompts) {
1554*80ee5cbfSDavid du Colombier int sim;
1555*80ee5cbfSDavid du Colombier point_list *A, *B;
1556*80ee5cbfSDavid du Colombier int N, M;
1557*80ee5cbfSDavid du Colombier int **G;
1558*80ee5cbfSDavid du Colombier int *junk;
1559*80ee5cbfSDavid du Colombier int i, j;
1560*80ee5cbfSDavid du Colombier
1561*80ee5cbfSDavid du Colombier /* A is the longer sequence, length N. */
1562*80ee5cbfSDavid du Colombier /* B is the shorter sequence, length M. */
1563*80ee5cbfSDavid du Colombier if (input_dompts->npts >= curr_dompts->npts) {
1564*80ee5cbfSDavid du Colombier A = input_dompts;
1565*80ee5cbfSDavid du Colombier N = input_dompts->npts;
1566*80ee5cbfSDavid du Colombier B = curr_dompts;
1567*80ee5cbfSDavid du Colombier M = curr_dompts->npts;
1568*80ee5cbfSDavid du Colombier } else {
1569*80ee5cbfSDavid du Colombier A = curr_dompts;
1570*80ee5cbfSDavid du Colombier N = curr_dompts->npts;
1571*80ee5cbfSDavid du Colombier B = input_dompts;
1572*80ee5cbfSDavid du Colombier M = input_dompts->npts;
1573*80ee5cbfSDavid du Colombier }
1574*80ee5cbfSDavid du Colombier
1575*80ee5cbfSDavid du Colombier /* Allocate and initialize the Gain matrix, G. */
1576*80ee5cbfSDavid du Colombier /* The size of G is M x (N + 1). */
1577*80ee5cbfSDavid du Colombier /* Note that row 0 is unused. */
1578*80ee5cbfSDavid du Colombier /* Similarities are x 10. */
1579*80ee5cbfSDavid du Colombier G = malloc(M*sizeof(int *));
1580*80ee5cbfSDavid du Colombier junk = malloc(M * (N + 1) * sizeof(int));
1581*80ee5cbfSDavid du Colombier for (i = 0; i < M; i++)
1582*80ee5cbfSDavid du Colombier G[i] = junk + (i * (N + 1));
1583*80ee5cbfSDavid du Colombier
1584*80ee5cbfSDavid du Colombier for (i = 1; i < M; i++) {
1585*80ee5cbfSDavid du Colombier int bval = B->pts[i-1].chaincode;
1586*80ee5cbfSDavid du Colombier
1587*80ee5cbfSDavid du Colombier /* Source column. */
1588*80ee5cbfSDavid du Colombier G[i][0] = 0;
1589*80ee5cbfSDavid du Colombier
1590*80ee5cbfSDavid du Colombier for (j = 1; j < N; j++) {
1591*80ee5cbfSDavid du Colombier int aval = A->pts[j-1].chaincode;
1592*80ee5cbfSDavid du Colombier int diff = abs(bval - aval);
1593*80ee5cbfSDavid du Colombier if (diff > 4) diff = 8 - diff;
1594*80ee5cbfSDavid du Colombier
1595*80ee5cbfSDavid du Colombier G[i][j] = (diff == 0)
1596*80ee5cbfSDavid du Colombier ? 10
1597*80ee5cbfSDavid du Colombier : (diff == 1)
1598*80ee5cbfSDavid du Colombier ? 6
1599*80ee5cbfSDavid du Colombier : 0;
1600*80ee5cbfSDavid du Colombier }
1601*80ee5cbfSDavid du Colombier
1602*80ee5cbfSDavid du Colombier /* Sink column. */
1603*80ee5cbfSDavid du Colombier G[i][N] = 0;
1604*80ee5cbfSDavid du Colombier }
1605*80ee5cbfSDavid du Colombier
1606*80ee5cbfSDavid du Colombier /* Do the DP algorithm. */
1607*80ee5cbfSDavid du Colombier /* Proceed in column order, from highest column to the lowest. */
1608*80ee5cbfSDavid du Colombier /* Within each column, proceed from the highest row to the lowest. */
1609*80ee5cbfSDavid du Colombier /* Skip the highest column. */
1610*80ee5cbfSDavid du Colombier for (j = N - 1; j >= 0; j--)
1611*80ee5cbfSDavid du Colombier for (i = M - 1; i > 0; i--) {
1612*80ee5cbfSDavid du Colombier int max = G[i][j + 1];
1613*80ee5cbfSDavid du Colombier
1614*80ee5cbfSDavid du Colombier if (i < (M - 1)) {
1615*80ee5cbfSDavid du Colombier int tmp = G[i + 1][j + 1];
1616*80ee5cbfSDavid du Colombier if (tmp > max) max = tmp;
1617*80ee5cbfSDavid du Colombier }
1618*80ee5cbfSDavid du Colombier
1619*80ee5cbfSDavid du Colombier G[i][j] += max;
1620*80ee5cbfSDavid du Colombier }
1621*80ee5cbfSDavid du Colombier
1622*80ee5cbfSDavid du Colombier sim = (10 * G[1][0] + (N - 1) / 2) / (N - 1);
1623*80ee5cbfSDavid du Colombier
1624*80ee5cbfSDavid du Colombier if (G != nil)
1625*80ee5cbfSDavid du Colombier free(G);
1626*80ee5cbfSDavid du Colombier if (junk != nil)
1627*80ee5cbfSDavid du Colombier free(junk);
1628*80ee5cbfSDavid du Colombier return(sim);
1629*80ee5cbfSDavid du Colombier }
1630*80ee5cbfSDavid du Colombier
1631*80ee5cbfSDavid du Colombier
lialg_compute_distance(point_list * input_dompts,point_list * curr_dompts)1632*80ee5cbfSDavid du Colombier static int lialg_compute_distance(point_list *input_dompts,
1633*80ee5cbfSDavid du Colombier point_list *curr_dompts) {
1634*80ee5cbfSDavid du Colombier int dist;
1635*80ee5cbfSDavid du Colombier point_list *A, *B;
1636*80ee5cbfSDavid du Colombier int N, M;
1637*80ee5cbfSDavid du Colombier int **C;
1638*80ee5cbfSDavid du Colombier int *junk;
1639*80ee5cbfSDavid du Colombier int *BE;
1640*80ee5cbfSDavid du Colombier int *TE;
1641*80ee5cbfSDavid du Colombier int i, j;
1642*80ee5cbfSDavid du Colombier
1643*80ee5cbfSDavid du Colombier /* A is the longer sequence, length N. */
1644*80ee5cbfSDavid du Colombier /* B is the shorter sequence, length M. */
1645*80ee5cbfSDavid du Colombier if (input_dompts->npts >= curr_dompts->npts) {
1646*80ee5cbfSDavid du Colombier A = input_dompts;
1647*80ee5cbfSDavid du Colombier N = input_dompts->npts;
1648*80ee5cbfSDavid du Colombier B = curr_dompts;
1649*80ee5cbfSDavid du Colombier M = curr_dompts->npts;
1650*80ee5cbfSDavid du Colombier }
1651*80ee5cbfSDavid du Colombier else {
1652*80ee5cbfSDavid du Colombier A = curr_dompts;
1653*80ee5cbfSDavid du Colombier N = curr_dompts->npts;
1654*80ee5cbfSDavid du Colombier B = input_dompts;
1655*80ee5cbfSDavid du Colombier M = input_dompts->npts;
1656*80ee5cbfSDavid du Colombier }
1657*80ee5cbfSDavid du Colombier
1658*80ee5cbfSDavid du Colombier /* Construct the helper vectors, BE and TE, which say for each column */
1659*80ee5cbfSDavid du Colombier /* what are the ``bottom'' and ``top'' rows of interest. */
1660*80ee5cbfSDavid du Colombier BE = malloc((N + 1)*sizeof(int));
1661*80ee5cbfSDavid du Colombier TE = malloc((N + 1)*sizeof(int));
1662*80ee5cbfSDavid du Colombier
1663*80ee5cbfSDavid du Colombier for (j = 1; j <= N; j++) {
1664*80ee5cbfSDavid du Colombier int bot, top;
1665*80ee5cbfSDavid du Colombier
1666*80ee5cbfSDavid du Colombier bot = j + (M - DP_BAND);
1667*80ee5cbfSDavid du Colombier if (bot > M) bot = M;
1668*80ee5cbfSDavid du Colombier BE[j] = bot;
1669*80ee5cbfSDavid du Colombier
1670*80ee5cbfSDavid du Colombier top = j - (N - DP_BAND);
1671*80ee5cbfSDavid du Colombier if (top < 1) top = 1;
1672*80ee5cbfSDavid du Colombier TE[j] = top;
1673*80ee5cbfSDavid du Colombier }
1674*80ee5cbfSDavid du Colombier
1675*80ee5cbfSDavid du Colombier /* Allocate and initialize the Cost matrix, C. */
1676*80ee5cbfSDavid du Colombier /* The size of C is (M + 1) x (N + 1). */
1677*80ee5cbfSDavid du Colombier /* Note that row and column 0 are unused. */
1678*80ee5cbfSDavid du Colombier /* Costs are x 100. */
1679*80ee5cbfSDavid du Colombier /* C = (int **)safe_malloc((M + 1) * sizeof(int *)); */
1680*80ee5cbfSDavid du Colombier C = malloc((M + 1)*sizeof( int *));
1681*80ee5cbfSDavid du Colombier junk = malloc((M + 1) * (N + 1)*sizeof(int));
1682*80ee5cbfSDavid du Colombier for (i = 0; i <= M; i++)
1683*80ee5cbfSDavid du Colombier C[i] = junk + (i * (N + 1));
1684*80ee5cbfSDavid du Colombier
1685*80ee5cbfSDavid du Colombier for (i = 1; i <= M; i++) {
1686*80ee5cbfSDavid du Colombier int bx = B->pts[i-1].x;
1687*80ee5cbfSDavid du Colombier int by = B->pts[i-1].y;
1688*80ee5cbfSDavid du Colombier
1689*80ee5cbfSDavid du Colombier for (j = 1; j <= N; j++) {
1690*80ee5cbfSDavid du Colombier int ax = A->pts[j-1].x;
1691*80ee5cbfSDavid du Colombier int ay = A->pts[j-1].y;
1692*80ee5cbfSDavid du Colombier int dx = bx - ax;
1693*80ee5cbfSDavid du Colombier int dy = by - ay;
1694*80ee5cbfSDavid du Colombier int dist = isqrt(10000 * (dx * dx + dy * dy));
1695*80ee5cbfSDavid du Colombier
1696*80ee5cbfSDavid du Colombier C[i][j] = dist;
1697*80ee5cbfSDavid du Colombier }
1698*80ee5cbfSDavid du Colombier }
1699*80ee5cbfSDavid du Colombier
1700*80ee5cbfSDavid du Colombier /* Do the DP algorithm. */
1701*80ee5cbfSDavid du Colombier /* Proceed in column order, from highest column to the lowest. */
1702*80ee5cbfSDavid du Colombier /* Within each column, proceed from the highest row to the lowest. */
1703*80ee5cbfSDavid du Colombier for (j = N; j > 0; j--)
1704*80ee5cbfSDavid du Colombier for (i = M; i > 0; i--) {
1705*80ee5cbfSDavid du Colombier int min = MAX_DIST;
1706*80ee5cbfSDavid du Colombier
1707*80ee5cbfSDavid du Colombier if (i > BE[j] || i < TE[j] || (j == N && i == M))
1708*80ee5cbfSDavid du Colombier continue;
1709*80ee5cbfSDavid du Colombier
1710*80ee5cbfSDavid du Colombier if (j < N) {
1711*80ee5cbfSDavid du Colombier if (i >= TE[j+1]) {
1712*80ee5cbfSDavid du Colombier int tmp = C[i][j+1];
1713*80ee5cbfSDavid du Colombier if (tmp < min)
1714*80ee5cbfSDavid du Colombier min = tmp;
1715*80ee5cbfSDavid du Colombier }
1716*80ee5cbfSDavid du Colombier
1717*80ee5cbfSDavid du Colombier if (i < M) {
1718*80ee5cbfSDavid du Colombier int tmp = C[i+1][j+1];
1719*80ee5cbfSDavid du Colombier if (tmp < min)
1720*80ee5cbfSDavid du Colombier min = tmp;
1721*80ee5cbfSDavid du Colombier }
1722*80ee5cbfSDavid du Colombier }
1723*80ee5cbfSDavid du Colombier
1724*80ee5cbfSDavid du Colombier if (i < BE[j]) {
1725*80ee5cbfSDavid du Colombier int tmp = C[i+1][j];
1726*80ee5cbfSDavid du Colombier if (tmp < min) min = tmp;
1727*80ee5cbfSDavid du Colombier }
1728*80ee5cbfSDavid du Colombier
1729*80ee5cbfSDavid du Colombier C[i][j] += min;
1730*80ee5cbfSDavid du Colombier }
1731*80ee5cbfSDavid du Colombier
1732*80ee5cbfSDavid du Colombier dist = (C[1][1] + N / 2) / N;
1733*80ee5cbfSDavid du Colombier
1734*80ee5cbfSDavid du Colombier if (C != nil) free(C);
1735*80ee5cbfSDavid du Colombier if (junk != nil) free(junk);
1736*80ee5cbfSDavid du Colombier if (BE != nil) free(BE);
1737*80ee5cbfSDavid du Colombier if (TE != nil) free(TE);
1738*80ee5cbfSDavid du Colombier return(dist);
1739*80ee5cbfSDavid du Colombier }
1740*80ee5cbfSDavid du Colombier
1741*80ee5cbfSDavid du Colombier
1742*80ee5cbfSDavid du Colombier /*************************************************************
1743*80ee5cbfSDavid du Colombier
1744*80ee5cbfSDavid du Colombier Digest-processing routines
1745*80ee5cbfSDavid du Colombier
1746*80ee5cbfSDavid du Colombier *************************************************************/
1747*80ee5cbfSDavid du Colombier
lialg_read_classifier_digest(rClassifier * rec)1748*80ee5cbfSDavid du Colombier static int lialg_read_classifier_digest(rClassifier *rec) {
1749*80ee5cbfSDavid du Colombier int nclasses;
1750*80ee5cbfSDavid du Colombier FILE *fp;
1751*80ee5cbfSDavid du Colombier
1752*80ee5cbfSDavid du Colombier /* Try to open the corresponding digest file. */
1753*80ee5cbfSDavid du Colombier {
1754*80ee5cbfSDavid du Colombier char *clx_path;
1755*80ee5cbfSDavid du Colombier char *dot;
1756*80ee5cbfSDavid du Colombier
1757*80ee5cbfSDavid du Colombier /* Get a copy of the filename, with some room on the end. */
1758*80ee5cbfSDavid du Colombier /* clx_path = safe_malloc(strlen(rec->file_name) + 5); */
1759*80ee5cbfSDavid du Colombier clx_path = malloc((strlen(rec->file_name) + 5) *sizeof(char));
1760*80ee5cbfSDavid du Colombier strcpy(clx_path, rec->file_name);
1761*80ee5cbfSDavid du Colombier
1762*80ee5cbfSDavid du Colombier /* Truncate the path after the last dot. */
1763*80ee5cbfSDavid du Colombier dot = strrchr(clx_path, '.');
1764*80ee5cbfSDavid du Colombier if (dot == nil) { free(clx_path); return(-1); }
1765*80ee5cbfSDavid du Colombier *(dot + 1) = 0;
1766*80ee5cbfSDavid du Colombier
1767*80ee5cbfSDavid du Colombier /* Append the classifier-digest extension. */
1768*80ee5cbfSDavid du Colombier strcat(clx_path, "clx");
1769*80ee5cbfSDavid du Colombier
1770*80ee5cbfSDavid du Colombier fp = fopen(clx_path, "r");
1771*80ee5cbfSDavid du Colombier if (fp == nil) {
1772*80ee5cbfSDavid du Colombier free(clx_path);
1773*80ee5cbfSDavid du Colombier return(-1);
1774*80ee5cbfSDavid du Colombier }
1775*80ee5cbfSDavid du Colombier
1776*80ee5cbfSDavid du Colombier free(clx_path);
1777*80ee5cbfSDavid du Colombier }
1778*80ee5cbfSDavid du Colombier
1779*80ee5cbfSDavid du Colombier /* Read-in the name and dominant points for each class. */
1780*80ee5cbfSDavid du Colombier for (nclasses = 0; !feof(fp); nclasses++) {
1781*80ee5cbfSDavid du Colombier point_list *dpts = nil;
1782*80ee5cbfSDavid du Colombier char class[BUFSIZ];
1783*80ee5cbfSDavid du Colombier int npts;
1784*80ee5cbfSDavid du Colombier int j;
1785*80ee5cbfSDavid du Colombier
1786*80ee5cbfSDavid du Colombier if (fscanf(fp, "%s %d", class, &npts) != 2) {
1787*80ee5cbfSDavid du Colombier if (feof(fp)) break;
1788*80ee5cbfSDavid du Colombier
1789*80ee5cbfSDavid du Colombier goto failed;
1790*80ee5cbfSDavid du Colombier }
1791*80ee5cbfSDavid du Colombier rec->cnames[nclasses] = strdup(class);
1792*80ee5cbfSDavid du Colombier
1793*80ee5cbfSDavid du Colombier /* Allocate a dominant-points list. */
1794*80ee5cbfSDavid du Colombier /* dpts = (point_list *)safe_malloc(sizeof(point_list)); */
1795*80ee5cbfSDavid du Colombier dpts = malloc(sizeof(point_list));
1796*80ee5cbfSDavid du Colombier dpts->pts = mallocz(npts*sizeof(pen_point), 1);
1797*80ee5cbfSDavid du Colombier if (dpts->pts == nil) goto failed;
1798*80ee5cbfSDavid du Colombier dpts->npts = npts;
1799*80ee5cbfSDavid du Colombier dpts->next = nil;
1800*80ee5cbfSDavid du Colombier
1801*80ee5cbfSDavid du Colombier /* Read in each point. */
1802*80ee5cbfSDavid du Colombier for (j = 0; j < npts; j++) {
1803*80ee5cbfSDavid du Colombier int x, y;
1804*80ee5cbfSDavid du Colombier
1805*80ee5cbfSDavid du Colombier if (fscanf(fp, "%d %d", &x, &y) != 2) goto failed;
1806*80ee5cbfSDavid du Colombier dpts->pts[j].x = x;
1807*80ee5cbfSDavid du Colombier dpts->pts[j].y = y;
1808*80ee5cbfSDavid du Colombier }
1809*80ee5cbfSDavid du Colombier
1810*80ee5cbfSDavid du Colombier /* Compute the chain-code. */
1811*80ee5cbfSDavid du Colombier lialg_compute_chain_code(dpts);
1812*80ee5cbfSDavid du Colombier
1813*80ee5cbfSDavid du Colombier /* Store the list in the rec data structure. */
1814*80ee5cbfSDavid du Colombier rec->dompts[nclasses] = dpts;
1815*80ee5cbfSDavid du Colombier
1816*80ee5cbfSDavid du Colombier continue;
1817*80ee5cbfSDavid du Colombier
1818*80ee5cbfSDavid du Colombier failed:
1819*80ee5cbfSDavid du Colombier fprint(2, "read_classifier_digest failed...\n");
1820*80ee5cbfSDavid du Colombier for (; nclasses >= 0; nclasses--) {
1821*80ee5cbfSDavid du Colombier if (rec->cnames[nclasses] != nil) {
1822*80ee5cbfSDavid du Colombier free(rec->cnames[nclasses]);
1823*80ee5cbfSDavid du Colombier rec->cnames[nclasses] = nil;
1824*80ee5cbfSDavid du Colombier }
1825*80ee5cbfSDavid du Colombier if (rec->dompts[nclasses] != nil) {
1826*80ee5cbfSDavid du Colombier delete_examples(rec->dompts[nclasses]);
1827*80ee5cbfSDavid du Colombier rec->dompts[nclasses] = nil;
1828*80ee5cbfSDavid du Colombier }
1829*80ee5cbfSDavid du Colombier }
1830*80ee5cbfSDavid du Colombier if (dpts != nil)
1831*80ee5cbfSDavid du Colombier delete_examples(dpts);
1832*80ee5cbfSDavid du Colombier fclose(fp);
1833*80ee5cbfSDavid du Colombier return(-1);
1834*80ee5cbfSDavid du Colombier }
1835*80ee5cbfSDavid du Colombier
1836*80ee5cbfSDavid du Colombier fclose(fp);
1837*80ee5cbfSDavid du Colombier return(0);
1838*80ee5cbfSDavid du Colombier }
1839*80ee5cbfSDavid du Colombier
1840*80ee5cbfSDavid du Colombier
1841*80ee5cbfSDavid du Colombier /*************************************************************
1842*80ee5cbfSDavid du Colombier
1843*80ee5cbfSDavid du Colombier Canonicalization routines
1844*80ee5cbfSDavid du Colombier
1845*80ee5cbfSDavid du Colombier *************************************************************/
1846*80ee5cbfSDavid du Colombier
lialg_canonicalize_examples(rClassifier * rec)1847*80ee5cbfSDavid du Colombier static int lialg_canonicalize_examples(rClassifier *rec) {
1848*80ee5cbfSDavid du Colombier int i;
1849*80ee5cbfSDavid du Colombier int nclasses;
1850*80ee5cbfSDavid du Colombier
1851*80ee5cbfSDavid du Colombier if (lidebug) {
1852*80ee5cbfSDavid du Colombier fprint(2, "lialg_canonicalize_examples working on %s\n",
1853*80ee5cbfSDavid du Colombier rec->file_name);
1854*80ee5cbfSDavid du Colombier }
1855*80ee5cbfSDavid du Colombier /* Initialize canonical-example arrays. */
1856*80ee5cbfSDavid du Colombier for (i = 0; i < MAXSCLASSES; i++) {
1857*80ee5cbfSDavid du Colombier rec->canonex[i] = nil;
1858*80ee5cbfSDavid du Colombier }
1859*80ee5cbfSDavid du Colombier
1860*80ee5cbfSDavid du Colombier /* Figure out number of classes. */
1861*80ee5cbfSDavid du Colombier for (nclasses = 0;
1862*80ee5cbfSDavid du Colombier nclasses < MAXSCLASSES && rec->cnames[nclasses] != nil;
1863*80ee5cbfSDavid du Colombier nclasses++)
1864*80ee5cbfSDavid du Colombier ;
1865*80ee5cbfSDavid du Colombier
1866*80ee5cbfSDavid du Colombier /* Canonicalize the examples for each class. */
1867*80ee5cbfSDavid du Colombier for (i = 0; i < nclasses; i++) {
1868*80ee5cbfSDavid du Colombier int j, k;
1869*80ee5cbfSDavid du Colombier int nex;
1870*80ee5cbfSDavid du Colombier point_list *pts, *tmp, *avg;
1871*80ee5cbfSDavid du Colombier int maxxrange, maxyrange;
1872*80ee5cbfSDavid du Colombier int minx, miny, maxx, maxy;
1873*80ee5cbfSDavid du Colombier int avgxrange, avgyrange, avgxoff, avgyoff, avgscale;
1874*80ee5cbfSDavid du Colombier
1875*80ee5cbfSDavid du Colombier
1876*80ee5cbfSDavid du Colombier if (lidebug) {
1877*80ee5cbfSDavid du Colombier fprint(2, "lialg_canonicalize_examples working on class %s\n",
1878*80ee5cbfSDavid du Colombier rec->cnames[i]);
1879*80ee5cbfSDavid du Colombier }
1880*80ee5cbfSDavid du Colombier /* Make a copy of the examples. */
1881*80ee5cbfSDavid du Colombier pts = nil;
1882*80ee5cbfSDavid du Colombier tmp = rec->ex[i];
1883*80ee5cbfSDavid du Colombier for (nex = 0; tmp != nil; nex++, tmp = tmp->next) {
1884*80ee5cbfSDavid du Colombier if ((pts = add_example(pts, tmp->npts, tmp->pts)) == nil) {
1885*80ee5cbfSDavid du Colombier delete_examples(pts);
1886*80ee5cbfSDavid du Colombier return(-1);
1887*80ee5cbfSDavid du Colombier }
1888*80ee5cbfSDavid du Colombier }
1889*80ee5cbfSDavid du Colombier
1890*80ee5cbfSDavid du Colombier /* Canonicalize each example, and derive the max x and y ranges. */
1891*80ee5cbfSDavid du Colombier maxxrange = 0;
1892*80ee5cbfSDavid du Colombier maxyrange = 0;
1893*80ee5cbfSDavid du Colombier for (j = 0, tmp = pts; j < nex; j++, tmp = tmp->next) {
1894*80ee5cbfSDavid du Colombier if (lialg_canonicalize_example_stroke(tmp) != 0) {
1895*80ee5cbfSDavid du Colombier if (lidebug) {
1896*80ee5cbfSDavid du Colombier fprint(2, "lialg_canonicalize_example_stroke returned error\n");
1897*80ee5cbfSDavid du Colombier }
1898*80ee5cbfSDavid du Colombier return(-1);
1899*80ee5cbfSDavid du Colombier }
1900*80ee5cbfSDavid du Colombier
1901*80ee5cbfSDavid du Colombier if (tmp->xrange > maxxrange) maxxrange = tmp->xrange;
1902*80ee5cbfSDavid du Colombier if (tmp->yrange > maxyrange) maxyrange = tmp->yrange;
1903*80ee5cbfSDavid du Colombier }
1904*80ee5cbfSDavid du Colombier
1905*80ee5cbfSDavid du Colombier /* Normalize max ranges. */
1906*80ee5cbfSDavid du Colombier if (((100 * maxxrange + CANONICAL_X / 2) / CANONICAL_X) >
1907*80ee5cbfSDavid du Colombier ((100 * maxyrange + CANONICAL_Y / 2) / CANONICAL_Y)) {
1908*80ee5cbfSDavid du Colombier maxyrange = (maxyrange * CANONICAL_X + maxxrange / 2) / maxxrange;
1909*80ee5cbfSDavid du Colombier maxxrange = CANONICAL_X;
1910*80ee5cbfSDavid du Colombier }
1911*80ee5cbfSDavid du Colombier else {
1912*80ee5cbfSDavid du Colombier maxxrange = (maxxrange * CANONICAL_Y + maxyrange / 2) / maxyrange;
1913*80ee5cbfSDavid du Colombier maxyrange = CANONICAL_Y;
1914*80ee5cbfSDavid du Colombier }
1915*80ee5cbfSDavid du Colombier
1916*80ee5cbfSDavid du Colombier /* Re-scale each example to max ranges. */
1917*80ee5cbfSDavid du Colombier for (j = 0, tmp = pts; j < nex; j++, tmp = tmp->next) {
1918*80ee5cbfSDavid du Colombier int scalex = (tmp->xrange == 0) ? 100 : (100 * maxxrange + tmp->xrange / 2) / tmp->xrange;
1919*80ee5cbfSDavid du Colombier int scaley = (tmp->yrange == 0) ? 100 : (100 * maxyrange + tmp->yrange / 2) / tmp->yrange;
1920*80ee5cbfSDavid du Colombier if (lialg_translate_points(tmp, 0, 0, scalex, scaley) != 0) {
1921*80ee5cbfSDavid du Colombier delete_examples(pts);
1922*80ee5cbfSDavid du Colombier return(-1);
1923*80ee5cbfSDavid du Colombier }
1924*80ee5cbfSDavid du Colombier }
1925*80ee5cbfSDavid du Colombier
1926*80ee5cbfSDavid du Colombier /* Average the examples; leave average in first example. */
1927*80ee5cbfSDavid du Colombier avg = pts; /* careful aliasing!! */
1928*80ee5cbfSDavid du Colombier for (k = 0; k < NCANONICAL; k++) {
1929*80ee5cbfSDavid du Colombier int xsum = 0;
1930*80ee5cbfSDavid du Colombier int ysum = 0;
1931*80ee5cbfSDavid du Colombier
1932*80ee5cbfSDavid du Colombier for (j = 0, tmp = pts; j < nex; j++, tmp = tmp->next) {
1933*80ee5cbfSDavid du Colombier xsum += tmp->pts[k].x;
1934*80ee5cbfSDavid du Colombier ysum += tmp->pts[k].y;
1935*80ee5cbfSDavid du Colombier }
1936*80ee5cbfSDavid du Colombier
1937*80ee5cbfSDavid du Colombier avg->pts[k].x = (xsum + j / 2) / j;
1938*80ee5cbfSDavid du Colombier avg->pts[k].y = (ysum + j / 2) / j;
1939*80ee5cbfSDavid du Colombier }
1940*80ee5cbfSDavid du Colombier
1941*80ee5cbfSDavid du Colombier /* Compute BB of averaged stroke and re-scale. */
1942*80ee5cbfSDavid du Colombier lialg_get_bounding_box(avg, &minx, &miny, &maxx, &maxy);
1943*80ee5cbfSDavid du Colombier avgxrange = maxx - minx;
1944*80ee5cbfSDavid du Colombier avgyrange = maxy - miny;
1945*80ee5cbfSDavid du Colombier avgscale = (((100 * avgxrange + CANONICAL_X / 2) / CANONICAL_X) >
1946*80ee5cbfSDavid du Colombier ((100 * avgyrange + CANONICAL_Y / 2) / CANONICAL_Y))
1947*80ee5cbfSDavid du Colombier ? (100 * CANONICAL_X + avgxrange / 2) / avgxrange
1948*80ee5cbfSDavid du Colombier : (100 * CANONICAL_Y + avgyrange / 2) / avgyrange;
1949*80ee5cbfSDavid du Colombier if (lialg_translate_points(avg, minx, miny, avgscale, avgscale) != 0) {
1950*80ee5cbfSDavid du Colombier delete_examples(pts);
1951*80ee5cbfSDavid du Colombier return(-1);
1952*80ee5cbfSDavid du Colombier }
1953*80ee5cbfSDavid du Colombier
1954*80ee5cbfSDavid du Colombier /* Re-compute the x and y ranges and center the stroke. */
1955*80ee5cbfSDavid du Colombier lialg_get_bounding_box(avg, &minx, &miny, &maxx, &maxy);
1956*80ee5cbfSDavid du Colombier avgxrange = maxx - minx;
1957*80ee5cbfSDavid du Colombier avgyrange = maxy - miny;
1958*80ee5cbfSDavid du Colombier avgxoff = -((CANONICAL_X - avgxrange + 1) / 2);
1959*80ee5cbfSDavid du Colombier avgyoff = -((CANONICAL_Y - avgyrange + 1) / 2);
1960*80ee5cbfSDavid du Colombier if (lialg_translate_points(avg, avgxoff, avgyoff, 100, 100) != 0) {
1961*80ee5cbfSDavid du Colombier delete_examples(pts);
1962*80ee5cbfSDavid du Colombier return(-1);
1963*80ee5cbfSDavid du Colombier }
1964*80ee5cbfSDavid du Colombier
1965*80ee5cbfSDavid du Colombier /* Create a point list to serve as the ``canonical representation. */
1966*80ee5cbfSDavid du Colombier if ((rec->canonex[i] = add_example(nil, avg->npts, avg->pts)) == nil) {
1967*80ee5cbfSDavid du Colombier delete_examples(pts);
1968*80ee5cbfSDavid du Colombier return(-1);
1969*80ee5cbfSDavid du Colombier }
1970*80ee5cbfSDavid du Colombier (rec->canonex[i])->xrange = maxx - minx;
1971*80ee5cbfSDavid du Colombier (rec->canonex[i])->yrange = maxy - miny;
1972*80ee5cbfSDavid du Colombier
1973*80ee5cbfSDavid du Colombier if (lidebug) {
1974*80ee5cbfSDavid du Colombier fprint(2, "%s, avgpts = %d\n", rec->cnames[i], avg->npts);
1975*80ee5cbfSDavid du Colombier for (j = 0; j < avg->npts; j++) {
1976*80ee5cbfSDavid du Colombier fprint(2, " (%P)\n", avg->pts[j].Point);
1977*80ee5cbfSDavid du Colombier }
1978*80ee5cbfSDavid du Colombier }
1979*80ee5cbfSDavid du Colombier
1980*80ee5cbfSDavid du Colombier /* Compute dominant points of canonical representation. */
1981*80ee5cbfSDavid du Colombier rec->dompts[i] = lialg_compute_dominant_points(avg);
1982*80ee5cbfSDavid du Colombier
1983*80ee5cbfSDavid du Colombier /* Clean up. */
1984*80ee5cbfSDavid du Colombier delete_examples(pts);
1985*80ee5cbfSDavid du Colombier }
1986*80ee5cbfSDavid du Colombier
1987*80ee5cbfSDavid du Colombier /* Sanity check. */
1988*80ee5cbfSDavid du Colombier for (i = 0; i < nclasses; i++) {
1989*80ee5cbfSDavid du Colombier char *best_name = lialg_recognize_stroke(rec, rec->canonex[i]);
1990*80ee5cbfSDavid du Colombier
1991*80ee5cbfSDavid du Colombier if (best_name != rec->cnames[i])
1992*80ee5cbfSDavid du Colombier fprint(2, "%s, best = %s\n", rec->cnames[i], best_name);
1993*80ee5cbfSDavid du Colombier }
1994*80ee5cbfSDavid du Colombier
1995*80ee5cbfSDavid du Colombier return(0);
1996*80ee5cbfSDavid du Colombier }
1997*80ee5cbfSDavid du Colombier
1998*80ee5cbfSDavid du Colombier
lialg_canonicalize_example_stroke(point_list * points)1999*80ee5cbfSDavid du Colombier static int lialg_canonicalize_example_stroke(point_list *points) {
2000*80ee5cbfSDavid du Colombier int minx, miny, maxx, maxy, xrange, yrange, scale;
2001*80ee5cbfSDavid du Colombier
2002*80ee5cbfSDavid du Colombier /* Filter out points that are too close. */
2003*80ee5cbfSDavid du Colombier if (lialg_filter_points(points) != 0) return(-1);
2004*80ee5cbfSDavid du Colombier
2005*80ee5cbfSDavid du Colombier /* Must be at least two points! */
2006*80ee5cbfSDavid du Colombier if (points->npts < 2) {
2007*80ee5cbfSDavid du Colombier if (lidebug) {
2008*80ee5cbfSDavid du Colombier fprint(2, "lialg_canonicalize_example_stroke: npts=%d\n",
2009*80ee5cbfSDavid du Colombier points->npts);
2010*80ee5cbfSDavid du Colombier }
2011*80ee5cbfSDavid du Colombier return(-1);
2012*80ee5cbfSDavid du Colombier }
2013*80ee5cbfSDavid du Colombier
2014*80ee5cbfSDavid du Colombier /* Scale up to avoid conversion errors. */
2015*80ee5cbfSDavid du Colombier lialg_get_bounding_box(points, &minx, &miny, &maxx, &maxy);
2016*80ee5cbfSDavid du Colombier xrange = maxx - minx;
2017*80ee5cbfSDavid du Colombier yrange = maxy - miny;
2018*80ee5cbfSDavid du Colombier scale = (((100 * xrange + CANONICAL_X / 2) / CANONICAL_X) >
2019*80ee5cbfSDavid du Colombier ((100 * yrange + CANONICAL_Y / 2) / CANONICAL_Y))
2020*80ee5cbfSDavid du Colombier ? (100 * CANONICAL_X + xrange / 2) / xrange
2021*80ee5cbfSDavid du Colombier : (100 * CANONICAL_Y + yrange / 2) / yrange;
2022*80ee5cbfSDavid du Colombier if (lialg_translate_points(points, minx, miny, scale, scale) != 0) {
2023*80ee5cbfSDavid du Colombier if (lidebug) {
2024*80ee5cbfSDavid du Colombier fprint(2, "lialg_translate_points (minx=%d,miny=%d,scale=%d) returned error\n", minx, miny, scale);
2025*80ee5cbfSDavid du Colombier }
2026*80ee5cbfSDavid du Colombier return(-1);
2027*80ee5cbfSDavid du Colombier }
2028*80ee5cbfSDavid du Colombier
2029*80ee5cbfSDavid du Colombier /* Compute an equivalent stroke with equi-distant points. */
2030*80ee5cbfSDavid du Colombier if (lialg_compute_equipoints(points) != 0) return(-1);
2031*80ee5cbfSDavid du Colombier
2032*80ee5cbfSDavid du Colombier /* Re-translate the points to the origin. */
2033*80ee5cbfSDavid du Colombier lialg_get_bounding_box(points, &minx, &miny, &maxx, &maxy);
2034*80ee5cbfSDavid du Colombier if (lialg_translate_points(points, minx, miny, 100, 100) != 0) {
2035*80ee5cbfSDavid du Colombier if (lidebug) {
2036*80ee5cbfSDavid du Colombier fprint(2, "lialg_translate_points (minx=%d,miny=%d) returned error\n", minx, miny);
2037*80ee5cbfSDavid du Colombier }
2038*80ee5cbfSDavid du Colombier return(-1);
2039*80ee5cbfSDavid du Colombier }
2040*80ee5cbfSDavid du Colombier
2041*80ee5cbfSDavid du Colombier /* Store the x and y ranges in the point list. */
2042*80ee5cbfSDavid du Colombier xrange = maxx - minx;
2043*80ee5cbfSDavid du Colombier yrange = maxy - miny;
2044*80ee5cbfSDavid du Colombier points->xrange = xrange;
2045*80ee5cbfSDavid du Colombier points->yrange = yrange;
2046*80ee5cbfSDavid du Colombier
2047*80ee5cbfSDavid du Colombier if (lidebug) {
2048*80ee5cbfSDavid du Colombier int i;
2049*80ee5cbfSDavid du Colombier fprint(2, "Canonicalized: %d, %d, %d, %d\n", minx, miny, maxx, maxy);
2050*80ee5cbfSDavid du Colombier for (i = 0; i < points->npts; i++)
2051*80ee5cbfSDavid du Colombier fprint(2, " (%P)\n", points->pts[i].Point);
2052*80ee5cbfSDavid du Colombier fflush(stderr);
2053*80ee5cbfSDavid du Colombier }
2054*80ee5cbfSDavid du Colombier
2055*80ee5cbfSDavid du Colombier return(0);
2056*80ee5cbfSDavid du Colombier }
2057*80ee5cbfSDavid du Colombier
2058*80ee5cbfSDavid du Colombier
lialg_compute_equipoints(point_list * points)2059*80ee5cbfSDavid du Colombier static int lialg_compute_equipoints(point_list *points) {
2060*80ee5cbfSDavid du Colombier pen_point *equipoints = mallocz(NCANONICAL*sizeof(pen_point), 1);
2061*80ee5cbfSDavid du Colombier int nequipoints = 0;
2062*80ee5cbfSDavid du Colombier int pathlen = lialg_compute_pathlen(points);
2063*80ee5cbfSDavid du Colombier int equidist = (pathlen + (NCANONICAL - 1) / 2) / (NCANONICAL - 1);
2064*80ee5cbfSDavid du Colombier int i;
2065*80ee5cbfSDavid du Colombier int dist_since_last_eqpt;
2066*80ee5cbfSDavid du Colombier int remaining_seglen;
2067*80ee5cbfSDavid du Colombier int dist_to_next_eqpt;
2068*80ee5cbfSDavid du Colombier
2069*80ee5cbfSDavid du Colombier if (equipoints == nil) {
2070*80ee5cbfSDavid du Colombier fprint(2, "can't allocate memory in lialg_compute_equipoints");
2071*80ee5cbfSDavid du Colombier return(-1);
2072*80ee5cbfSDavid du Colombier }
2073*80ee5cbfSDavid du Colombier
2074*80ee5cbfSDavid du Colombier if (lidebug) {
2075*80ee5cbfSDavid du Colombier fprint(2, "compute_equipoints: npts = %d, pathlen = %d, equidist = %d\n",
2076*80ee5cbfSDavid du Colombier points->npts, pathlen, equidist);
2077*80ee5cbfSDavid du Colombier fflush(stderr);
2078*80ee5cbfSDavid du Colombier }
2079*80ee5cbfSDavid du Colombier
2080*80ee5cbfSDavid du Colombier /* First original point is an equipoint. */
2081*80ee5cbfSDavid du Colombier equipoints[0] = points->pts[0];
2082*80ee5cbfSDavid du Colombier nequipoints++;
2083*80ee5cbfSDavid du Colombier dist_since_last_eqpt = 0;
2084*80ee5cbfSDavid du Colombier
2085*80ee5cbfSDavid du Colombier for (i = 1; i < points->npts; i++) {
2086*80ee5cbfSDavid du Colombier int dx1 = points->pts[i].x - points->pts[i-1].x;
2087*80ee5cbfSDavid du Colombier int dy1 = points->pts[i].y - points->pts[i-1].y;
2088*80ee5cbfSDavid du Colombier int endx = 100 * points->pts[i-1].x;
2089*80ee5cbfSDavid du Colombier int endy = 100 * points->pts[i-1].y;
2090*80ee5cbfSDavid du Colombier remaining_seglen = isqrt(10000 * (dx1 * dx1 + dy1 * dy1));
2091*80ee5cbfSDavid du Colombier dist_to_next_eqpt = equidist - dist_since_last_eqpt;
2092*80ee5cbfSDavid du Colombier
2093*80ee5cbfSDavid du Colombier while (remaining_seglen >= dist_to_next_eqpt) {
2094*80ee5cbfSDavid du Colombier if (dx1 == 0) {
2095*80ee5cbfSDavid du Colombier /* x-coordinate stays the same */
2096*80ee5cbfSDavid du Colombier if (dy1 >= 0)
2097*80ee5cbfSDavid du Colombier endy += dist_to_next_eqpt;
2098*80ee5cbfSDavid du Colombier else
2099*80ee5cbfSDavid du Colombier endy -= dist_to_next_eqpt;
2100*80ee5cbfSDavid du Colombier }
2101*80ee5cbfSDavid du Colombier else {
2102*80ee5cbfSDavid du Colombier int slope = (100 * dy1 + dx1 / 2) / dx1;
2103*80ee5cbfSDavid du Colombier int tmp = isqrt(10000 + slope * slope);
2104*80ee5cbfSDavid du Colombier int dx = (100 * dist_to_next_eqpt + tmp / 2) / tmp;
2105*80ee5cbfSDavid du Colombier int dy = (slope * dx + 50) / 100;
2106*80ee5cbfSDavid du Colombier
2107*80ee5cbfSDavid du Colombier if (dy < 0) dy = -dy;
2108*80ee5cbfSDavid du Colombier if (dx1 >= 0)
2109*80ee5cbfSDavid du Colombier endx += dx;
2110*80ee5cbfSDavid du Colombier else
2111*80ee5cbfSDavid du Colombier endx -= dx;
2112*80ee5cbfSDavid du Colombier if (dy1 >= 0)
2113*80ee5cbfSDavid du Colombier endy += dy;
2114*80ee5cbfSDavid du Colombier else
2115*80ee5cbfSDavid du Colombier endy -= dy;
2116*80ee5cbfSDavid du Colombier }
2117*80ee5cbfSDavid du Colombier
2118*80ee5cbfSDavid du Colombier equipoints[nequipoints].x = (endx + 50) / 100;
2119*80ee5cbfSDavid du Colombier equipoints[nequipoints].y = (endy + 50) / 100;
2120*80ee5cbfSDavid du Colombier nequipoints++;
2121*80ee5cbfSDavid du Colombier /* assert(nequipoints <= NCANONICAL);*/
2122*80ee5cbfSDavid du Colombier dist_since_last_eqpt = 0;
2123*80ee5cbfSDavid du Colombier remaining_seglen -= dist_to_next_eqpt;
2124*80ee5cbfSDavid du Colombier dist_to_next_eqpt = equidist;
2125*80ee5cbfSDavid du Colombier }
2126*80ee5cbfSDavid du Colombier
2127*80ee5cbfSDavid du Colombier dist_since_last_eqpt += remaining_seglen;
2128*80ee5cbfSDavid du Colombier }
2129*80ee5cbfSDavid du Colombier
2130*80ee5cbfSDavid du Colombier /* Take care of last equipoint. */
2131*80ee5cbfSDavid du Colombier if (nequipoints == NCANONICAL) {
2132*80ee5cbfSDavid du Colombier /* Good. */
2133*80ee5cbfSDavid du Colombier } else if (nequipoints == (NCANONICAL - 1)) {
2134*80ee5cbfSDavid du Colombier /* Make last original point the last equipoint. */
2135*80ee5cbfSDavid du Colombier equipoints[nequipoints] = points->pts[points->npts - 1];
2136*80ee5cbfSDavid du Colombier } else {
2137*80ee5cbfSDavid du Colombier if (lidebug) {
2138*80ee5cbfSDavid du Colombier fprint(2,"lialg_compute_equipoints: nequipoints = %d\n",
2139*80ee5cbfSDavid du Colombier nequipoints);
2140*80ee5cbfSDavid du Colombier }
2141*80ee5cbfSDavid du Colombier /* assert(false);*/
2142*80ee5cbfSDavid du Colombier return(-1);
2143*80ee5cbfSDavid du Colombier }
2144*80ee5cbfSDavid du Colombier
2145*80ee5cbfSDavid du Colombier points->npts = NCANONICAL;
2146*80ee5cbfSDavid du Colombier delete_pen_point_array(points->pts);
2147*80ee5cbfSDavid du Colombier points->pts = equipoints;
2148*80ee5cbfSDavid du Colombier return(0);
2149*80ee5cbfSDavid du Colombier }
2150*80ee5cbfSDavid du Colombier
2151*80ee5cbfSDavid du Colombier
2152*80ee5cbfSDavid du Colombier /*************************************************************
2153*80ee5cbfSDavid du Colombier
2154*80ee5cbfSDavid du Colombier Utility routines
2155*80ee5cbfSDavid du Colombier
2156*80ee5cbfSDavid du Colombier *************************************************************/
2157*80ee5cbfSDavid du Colombier
2158*80ee5cbfSDavid du Colombier /* Result is x 100. */
lialg_compute_pathlen(point_list * points)2159*80ee5cbfSDavid du Colombier static int lialg_compute_pathlen(point_list *points) {
2160*80ee5cbfSDavid du Colombier return(lialg_compute_pathlen_subset(points, 0, points->npts - 1));
2161*80ee5cbfSDavid du Colombier }
2162*80ee5cbfSDavid du Colombier
2163*80ee5cbfSDavid du Colombier
2164*80ee5cbfSDavid du Colombier /* Result is x 100. */
lialg_compute_pathlen_subset(point_list * points,int start,int end)2165*80ee5cbfSDavid du Colombier static int lialg_compute_pathlen_subset(point_list *points,
2166*80ee5cbfSDavid du Colombier int start, int end) {
2167*80ee5cbfSDavid du Colombier int pathlen;
2168*80ee5cbfSDavid du Colombier int i;
2169*80ee5cbfSDavid du Colombier
2170*80ee5cbfSDavid du Colombier pathlen = 0;
2171*80ee5cbfSDavid du Colombier for (i = start + 1; i <= end; i++) {
2172*80ee5cbfSDavid du Colombier int dx = points->pts[i].x - points->pts[i-1].x;
2173*80ee5cbfSDavid du Colombier int dy = points->pts[i].y - points->pts[i-1].y;
2174*80ee5cbfSDavid du Colombier int dist = isqrt(10000 * (dx * dx + dy * dy));
2175*80ee5cbfSDavid du Colombier pathlen += dist;
2176*80ee5cbfSDavid du Colombier }
2177*80ee5cbfSDavid du Colombier
2178*80ee5cbfSDavid du Colombier return(pathlen);
2179*80ee5cbfSDavid du Colombier }
2180*80ee5cbfSDavid du Colombier
2181*80ee5cbfSDavid du Colombier
2182*80ee5cbfSDavid du Colombier /* Note that this does NOT update points->xrange and points->yrange! */
lialg_filter_points(point_list * points)2183*80ee5cbfSDavid du Colombier static int lialg_filter_points(point_list *points) {
2184*80ee5cbfSDavid du Colombier int filtered_npts;
2185*80ee5cbfSDavid du Colombier pen_point *filtered_pts = mallocz(points->npts*sizeof(pen_point), 1);
2186*80ee5cbfSDavid du Colombier int i;
2187*80ee5cbfSDavid du Colombier
2188*80ee5cbfSDavid du Colombier if (filtered_pts == nil) {
2189*80ee5cbfSDavid du Colombier fprint(2, "can't allocate memory in lialg_filter_points");
2190*80ee5cbfSDavid du Colombier return(-1);
2191*80ee5cbfSDavid du Colombier }
2192*80ee5cbfSDavid du Colombier
2193*80ee5cbfSDavid du Colombier filtered_pts[0] = points->pts[0];
2194*80ee5cbfSDavid du Colombier filtered_npts = 1;
2195*80ee5cbfSDavid du Colombier for (i = 1; i < points->npts; i++) {
2196*80ee5cbfSDavid du Colombier int j = filtered_npts - 1;
2197*80ee5cbfSDavid du Colombier int dx = points->pts[i].x - filtered_pts[j].x;
2198*80ee5cbfSDavid du Colombier int dy = points->pts[i].y - filtered_pts[j].y;
2199*80ee5cbfSDavid du Colombier int magsq = dx * dx + dy * dy;
2200*80ee5cbfSDavid du Colombier
2201*80ee5cbfSDavid du Colombier if (magsq >= DIST_SQ_THRESHOLD) {
2202*80ee5cbfSDavid du Colombier filtered_pts[filtered_npts] = points->pts[i];
2203*80ee5cbfSDavid du Colombier filtered_npts++;
2204*80ee5cbfSDavid du Colombier }
2205*80ee5cbfSDavid du Colombier }
2206*80ee5cbfSDavid du Colombier
2207*80ee5cbfSDavid du Colombier points->npts = filtered_npts;
2208*80ee5cbfSDavid du Colombier delete_pen_point_array(points->pts);
2209*80ee5cbfSDavid du Colombier points->pts = filtered_pts;
2210*80ee5cbfSDavid du Colombier return(0);
2211*80ee5cbfSDavid du Colombier }
2212*80ee5cbfSDavid du Colombier
2213*80ee5cbfSDavid du Colombier
2214*80ee5cbfSDavid du Colombier /* scalex and scaley are x 100. */
2215*80ee5cbfSDavid du Colombier /* Note that this does NOT update points->xrange and points->yrange! */
lialg_translate_points(point_list * points,int minx,int miny,int scalex,int scaley)2216*80ee5cbfSDavid du Colombier static int lialg_translate_points(point_list *points,
2217*80ee5cbfSDavid du Colombier int minx, int miny,
2218*80ee5cbfSDavid du Colombier int scalex, int scaley) {
2219*80ee5cbfSDavid du Colombier int i;
2220*80ee5cbfSDavid du Colombier
2221*80ee5cbfSDavid du Colombier for (i = 0; i < points->npts; i++) {
2222*80ee5cbfSDavid du Colombier points->pts[i].x = ((points->pts[i].x - minx) * scalex + 50) / 100;
2223*80ee5cbfSDavid du Colombier points->pts[i].y = ((points->pts[i].y - miny) * scaley + 50) / 100;
2224*80ee5cbfSDavid du Colombier }
2225*80ee5cbfSDavid du Colombier
2226*80ee5cbfSDavid du Colombier return(0);
2227*80ee5cbfSDavid du Colombier }
2228*80ee5cbfSDavid du Colombier
2229*80ee5cbfSDavid du Colombier
lialg_get_bounding_box(point_list * points,int * pminx,int * pminy,int * pmaxx,int * pmaxy)2230*80ee5cbfSDavid du Colombier static void lialg_get_bounding_box(point_list *points,
2231*80ee5cbfSDavid du Colombier int *pminx, int *pminy,
2232*80ee5cbfSDavid du Colombier int *pmaxx, int *pmaxy) {
2233*80ee5cbfSDavid du Colombier int minx, miny, maxx, maxy;
2234*80ee5cbfSDavid du Colombier int i;
2235*80ee5cbfSDavid du Colombier
2236*80ee5cbfSDavid du Colombier minx = maxx = points->pts[0].x;
2237*80ee5cbfSDavid du Colombier miny = maxy = points->pts[0].y;
2238*80ee5cbfSDavid du Colombier for (i = 1; i < points->npts; i++) {
2239*80ee5cbfSDavid du Colombier pen_point *pt = &(points->pts[i]);
2240*80ee5cbfSDavid du Colombier if (pt->x < minx) minx = pt->x;
2241*80ee5cbfSDavid du Colombier else if (pt->x > maxx) maxx = pt->x;
2242*80ee5cbfSDavid du Colombier if (pt->y < miny) miny = pt->y;
2243*80ee5cbfSDavid du Colombier else if (pt->y > maxy) maxy = pt->y;
2244*80ee5cbfSDavid du Colombier }
2245*80ee5cbfSDavid du Colombier
2246*80ee5cbfSDavid du Colombier *pminx = minx;
2247*80ee5cbfSDavid du Colombier *pminy = miny;
2248*80ee5cbfSDavid du Colombier *pmaxx = maxx;
2249*80ee5cbfSDavid du Colombier *pmaxy = maxy;
2250*80ee5cbfSDavid du Colombier }
2251*80ee5cbfSDavid du Colombier
2252*80ee5cbfSDavid du Colombier
2253*80ee5cbfSDavid du Colombier int wtvals[] = {100, 104, 117, 143, 189, 271, 422};
2254*80ee5cbfSDavid du Colombier
lialg_compute_lpf_parameters(void)2255*80ee5cbfSDavid du Colombier static void lialg_compute_lpf_parameters(void) {
2256*80ee5cbfSDavid du Colombier int i;
2257*80ee5cbfSDavid du Colombier
2258*80ee5cbfSDavid du Colombier for (i = LP_FILTER_WIDTH; i >= 0; i--) {
2259*80ee5cbfSDavid du Colombier // double x = 0.04 * (i * i);
2260*80ee5cbfSDavid du Colombier // double tmp = 100.0 * exp(x);
2261*80ee5cbfSDavid du Colombier // int wt = floor((double)tmp);
2262*80ee5cbfSDavid du Colombier int wt = wtvals[i];
2263*80ee5cbfSDavid du Colombier lialg_lpfwts[LP_FILTER_WIDTH - i] = wt;
2264*80ee5cbfSDavid du Colombier lialg_lpfwts[LP_FILTER_WIDTH + i] = wt;
2265*80ee5cbfSDavid du Colombier }
2266*80ee5cbfSDavid du Colombier lialg_lpfconst = 0;
2267*80ee5cbfSDavid du Colombier for (i = 0; i < (2 * LP_FILTER_WIDTH + 1); i++) {
2268*80ee5cbfSDavid du Colombier lialg_lpfconst += lialg_lpfwts[i];
2269*80ee5cbfSDavid du Colombier }
2270*80ee5cbfSDavid du Colombier }
2271*80ee5cbfSDavid du Colombier
2272*80ee5cbfSDavid du Colombier
2273*80ee5cbfSDavid du Colombier /* Code from Joseph Hall (jnhall@sat.mot.com). */
isqrt(int n)2274*80ee5cbfSDavid du Colombier static int isqrt(int n) {
2275*80ee5cbfSDavid du Colombier register int i;
2276*80ee5cbfSDavid du Colombier register long k0, k1, nn;
2277*80ee5cbfSDavid du Colombier
2278*80ee5cbfSDavid du Colombier for (nn = i = n, k0 = 2; i > 0; i >>= 2, k0 <<= 1)
2279*80ee5cbfSDavid du Colombier ;
2280*80ee5cbfSDavid du Colombier nn <<= 2;
2281*80ee5cbfSDavid du Colombier for (;;) {
2282*80ee5cbfSDavid du Colombier k1 = (nn / k0 + k0) >> 1;
2283*80ee5cbfSDavid du Colombier if (((k0 ^ k1) & ~1) == 0)
2284*80ee5cbfSDavid du Colombier break;
2285*80ee5cbfSDavid du Colombier k0 = k1;
2286*80ee5cbfSDavid du Colombier }
2287*80ee5cbfSDavid du Colombier return (int) ((k1 + 1) >> 1);
2288*80ee5cbfSDavid du Colombier }
2289*80ee5cbfSDavid du Colombier
2290*80ee5cbfSDavid du Colombier
2291*80ee5cbfSDavid du Colombier /* Helper routines from Mark Hayter. */
likeatan(int tantop,int tanbot)2292*80ee5cbfSDavid du Colombier static int likeatan(int tantop, int tanbot) {
2293*80ee5cbfSDavid du Colombier int t;
2294*80ee5cbfSDavid du Colombier /* Use tan(theta)=top/bot --> order for t */
2295*80ee5cbfSDavid du Colombier /* t in range 0..0x40000 */
2296*80ee5cbfSDavid du Colombier
2297*80ee5cbfSDavid du Colombier if ((tantop == 0) && (tanbot == 0))
2298*80ee5cbfSDavid du Colombier t = 0;
2299*80ee5cbfSDavid du Colombier else
2300*80ee5cbfSDavid du Colombier {
2301*80ee5cbfSDavid du Colombier t = (tantop << 16) / (abs(tantop) + abs(tanbot));
2302*80ee5cbfSDavid du Colombier if (tanbot < 0)
2303*80ee5cbfSDavid du Colombier t = 0x20000 - t;
2304*80ee5cbfSDavid du Colombier else
2305*80ee5cbfSDavid du Colombier if (tantop < 0) t = 0x40000 + t;
2306*80ee5cbfSDavid du Colombier }
2307*80ee5cbfSDavid du Colombier return t;
2308*80ee5cbfSDavid du Colombier }
2309*80ee5cbfSDavid du Colombier
2310*80ee5cbfSDavid du Colombier
quadr(int t)2311*80ee5cbfSDavid du Colombier static int quadr(int t) {
2312*80ee5cbfSDavid du Colombier return (8 - (((t + 0x4000) >> 15) & 7)) & 7;
2313*80ee5cbfSDavid du Colombier }
2314