chiark / gitweb /
43636f2d1c18861d77d890e8f49c2373a1bff627
[ypp-sc-tools.db-live.git] / pctb / ocr.c
1 /*
2   */
3
4 #include "ocr.h"
5
6 typedef struct {
7   Pixcol col;
8   struct OCRDatabaseNode *then;
9 } OCRDatabaseLink;
10
11 #define MAXGLYPHCHRS 3
12
13 typedef struct OCRDatabaseNode {
14   char s[MAXGLYPHCHRS+1]; /* null-terminated; "" means no match here */
15   int nlinks, alinks;
16   OCRDatabaseLink *links;
17 } OCRDatabaseNode;
18
19 #define N_OCR_CONTEXTS 2
20
21 static OCRDatabaseNode ocr_contexts[N_OCR_CONTEXTS];
22 static FILE *db;
23 static OcrResultGlyph *results;
24 static int aresults, nresults;
25
26 static FILE *resolver;
27 static pid_t resolver_pid;
28
29 static void ocr_readdb(void) {
30   int ctx,nchrs;
31   OCRDatabaseNode *current, *additional;
32   char chrs[MAXGLYPHCHRS+1];
33   Pixcol cv;
34   int r,i,j;
35
36   assert(!db);
37   db= fopen("database","r");  eassert(db);
38
39   for (;;) {
40     r= fscanf(db, "%d %d", &ctx, &nchrs);
41     if (r==EOF) break;
42     eassert(r==2);
43     eassert(ctx>=0 && ctx<N_OCR_CONTEXTS);
44     eassert(nchrs>0 && nchrs<=MAXGLYPHCHRS);
45
46     for (i=0; i<nchrs; i++) {
47       int c;
48       r= fscanf(db, "%x", &c);  eassert(r==1);
49       eassert(c>0 && c<=255);
50       chrs[i]= c;
51     }
52     chrs[nchrs]= 0;
53
54     int twidth;
55     r= fscanf(db, "%d", &twidth);  eassert(r==1);
56     current= &ocr_contexts[ctx];
57     for (i=0; i<twidth; i++) {
58       r= fscanf(db, "%"PSPIXCOL(SCNx), &cv);  eassert(r==1);
59       for (j=0; j<current->nlinks; j++)
60         if (current->links[j].col == cv) {
61           current= current->links[j].then;
62           goto found_link;
63         }
64
65       additional= malloc(sizeof(*additional)); eassert(additional);
66       additional->s[0]= 0;
67       additional->nlinks= additional->alinks= 0;
68       additional->links= 0;
69       if (current->nlinks==current->alinks) {
70         current->alinks++;
71         current->alinks<<=1;
72         current->links= realloc(current->links,
73             sizeof(*current->links) * current->alinks);
74         eassert(current->links);
75       }
76       current->links[current->nlinks].col= cv;
77       current->links[current->nlinks].then= additional;
78       current->nlinks++;
79       current= additional;
80
81     found_link:;
82     }
83
84     eassert(!current->s[0]);
85     strcpy(current->s, chrs);
86   }
87   eassert(!ferror(db));
88   eassert(feof(db));
89 }      
90
91 static void callout_unknown(int w, int h, Pixcol cols[],
92                             int unk_l, int unk_r, int unk_ctx,
93                             const OcrResultGlyph *sofar, int nsofar) {
94   int pfd[2], c, r,i, x,y;
95   const OcrResultGlyph *s;
96   const char *p;
97   Pixcol pv;
98   
99   if (!resolver) {
100     r= pipe(pfd);  eassert(!r);
101     resolver_pid= fork();
102     eassert(resolver_pid!=-1);
103     if (!resolver_pid) {
104       r= dup2(pfd[0],0); eassert(!r);
105       r= close(pfd[1]); eassert(!r);
106       execlp("./show-thing.tcl", "./show-thing.tcl",(char*)0);
107       eassert(!"execlp failed");
108     }
109     r= close(pfd[0]); eassert(!r);
110     resolver= fdopen(pfd[1],"w"); eassert(resolver);
111   }
112   fprintf(resolver,"%d %d %d",unk_l,unk_r,unk_ctx);
113   for (i=0, s=sofar; i<nsofar; i++, s++) {
114     fprintf(resolver," %d %d %d ",s->l,s->r,s->ctx);
115     for (p=s->s; (c= *p); p++) {
116       if (c=='\\') fprintf(resolver,"\\%c",c);
117       else if (c>=33 && c<=126) fputc(c,resolver);
118       else fprintf(resolver,"\\x%02x",(unsigned char)c);
119     }
120   }
121   fputc('\n',resolver);
122
123   fprintf(resolver,
124           "/* XPM */\n"
125           "static char *t[] = {\n"
126           "/* columns rows colors chars-per-pixel */\n"
127           "\"%d %d 2 1\",\n"
128           "\"  c black\",\n"
129           "\"o c white\",\n",
130           w,h);
131   for (y=0, pv=1; y<h; y++, pv<<=1) {
132     fputc('"',resolver);
133     for (x=0; x<w; x++)
134       fputc(cols[x] & pv ? 'o' : ' ', resolver);
135     fputs("\",\n",resolver);
136   }
137   fputs("};\n",resolver);
138   eassert(!ferror(resolver));
139   eassert(!fflush(resolver));
140
141   for (;;) {
142     eassert(resolver);
143     pid_t pid= waitpid(resolver_pid, &r, WUNTRACED);
144     if (pid==-1) { eassert(errno==EINTR); continue; }
145     eassert(pid==resolver_pid);
146     if (WIFEXITED(r)) {
147       eassert(!WEXITSTATUS(r));
148       fclose(resolver);
149       resolver= 0;
150     } else if (WIFSTOPPED(r)) {
151       r= kill(resolver_pid,SIGCONT);
152       eassert(!r);
153     } else if (WIFSIGNALED(r)) {
154       eassert(!"resolver child died due to signal");
155     } else {
156       eassert(!"weird wait status");
157     }
158     struct stat stab, fstab;
159     r= stat("database",&stab);  eassert(!r);
160     r= fstat(fileno(db),&fstab);  eassert(!r);
161     if (stab.st_ino != fstab.st_ino ||
162         stab.st_dev != fstab.st_dev)
163       break;
164   }
165   fclose(db);
166   db= 0;
167   ocr_readdb();
168 }
169
170 static void add_result(const char *s, int l, int r, int ctx) {
171   if (nresults >= aresults) {
172     aresults++; aresults<<=1;
173     results= realloc(results,sizeof(*results)*aresults);
174     eassert(results);
175   }
176   results[nresults].s= s;
177   results[nresults].l= l;
178   results[nresults].r= r;
179   results[nresults].ctx= ctx;
180   nresults++;
181 }
182
183 OcrResultGlyph *ocr(int w, int h, Pixcol cols[]) {
184   int nspaces=0;
185   int ctx=1,i, x;
186
187   nresults=0;
188   assert(db);
189
190   fprintf(debug,"OCR h=%d w=%d",w,h);
191   for (x=0; x<w; x++) fprintf(debug," %"PSPIXCOL(PRIx),cols[x]);
192   fprintf(debug,"\n");
193   debug_flush();
194
195  restart:
196   x=0;
197   for (;;) {
198     debug_flush();
199     /* skip spaces */
200     if (x>=w)
201       break;
202
203     if (!cols[x]) {
204       nspaces++;
205       x++;
206       if (nspaces>3) ctx=1;
207       continue;
208     }
209
210     /* find character */
211     OCRDatabaseNode *current=0, *bestmatch=0;
212     int lx=x;
213     int bestmatch_rx=-1;
214     current= &ocr_contexts[ctx];
215     fprintf(debug,"OCR  lx=%d ctx=%d  ",lx,ctx);
216
217     for (;;) {
218       debug_flush();
219       fprintf(debug,"| x=%d",x);
220       if (x>w) break;
221       Pixcol cv= cols[x];
222       fprintf(debug," cv=%"PSPIXCOL(PRIx),x);
223       for (i=0; i<current->nlinks; i++)
224         if (current->links[i].col == cv)
225           goto found;
226       /* not found */
227       fprintf(debug," ?");
228       break;
229
230     found:
231       current= current->links[i].then;
232       if (current->s[0]) {
233         fprintf(debug," \"%s\"",current->s);
234         bestmatch=current; bestmatch_rx=x;
235       } else {
236         fprintf(debug," ...");
237       }
238       x++;
239     }
240
241     if (bestmatch) {
242       fprintf(debug," YES\n");
243       add_result(bestmatch->s, lx, bestmatch_rx, ctx);
244       x= bestmatch_rx+1;
245       ctx= 0;
246     } else {
247       int rx;
248       fprintf(debug," UNKNOWN");
249       for (rx=lx; rx<w && cols[rx]; rx++);
250       fprintf(debug," x=%d ctx=%d %d..%d\n",x, ctx, lx,rx);
251       debug_flush();
252       callout_unknown(w,h,cols, lx,rx-1,ctx, results,nresults);
253       goto restart;
254     }
255   }
256   add_result(0,-1,-1,0);
257   fprintf(debug,"OCR  finished %d glyphs\n",nresults);
258   debug_flush();
259   return results;
260 }
261
262 void ocr_init(void) {
263   ocr_readdb();
264 }