chiark / gitweb /
fix space handling
[ypp-sc-tools.web-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 static int resolver_done;
29
30 static void ocr_readdb(void) {
31   int ctx,nchrs;
32   OCRDatabaseNode *current, *additional;
33   char chrs[MAXGLYPHCHRS+1];
34   Pixcol cv;
35   int r,i,j;
36
37   assert(!db);
38   db= fopen("database","r");  eassert(db);
39
40   for (;;) {
41     r= fscanf(db, "%d %d", &ctx, &nchrs);
42     if (r==EOF) break;
43     eassert(r==2);
44     eassert(ctx>=0 && ctx<N_OCR_CONTEXTS);
45     eassert(nchrs>0 && nchrs<=MAXGLYPHCHRS);
46
47     for (i=0; i<nchrs; i++) {
48       int c;
49       r= fscanf(db, "%x", &c);  eassert(r==1);
50       eassert(c>0 && c<=255);
51       chrs[i]= c;
52     }
53     chrs[nchrs]= 0;
54
55     int twidth;
56     r= fscanf(db, "%d", &twidth);  eassert(r==1);
57     current= &ocr_contexts[ctx];
58     for (i=0; i<twidth; i++) {
59       r= fscanf(db, "%"PSPIXCOL(SCNx), &cv);  eassert(r==1);
60       for (j=0; j<current->nlinks; j++)
61         if (current->links[j].col == cv) {
62           current= current->links[j].then;
63           goto found_link;
64         }
65
66       additional= malloc(sizeof(*additional)); eassert(additional);
67       additional->s[0]= 0;
68       additional->nlinks= additional->alinks= 0;
69       additional->links= 0;
70       if (current->nlinks==current->alinks) {
71         current->alinks++;
72         current->alinks<<=1;
73         current->links= realloc(current->links,
74             sizeof(*current->links) * current->alinks);
75         eassert(current->links);
76       }
77       current->links[current->nlinks].col= cv;
78       current->links[current->nlinks].then= additional;
79       current->nlinks++;
80       current= additional;
81
82     found_link:;
83     }
84
85     eassert(!current->s[0]);
86     strcpy(current->s, chrs);
87   }
88   eassert(!ferror(db));
89   eassert(feof(db));
90 }      
91
92 static void callout_unknown(int w, int h, Pixcol cols[],
93                             int unk_l, int unk_r, int unk_ctx,
94                             const OcrResultGlyph *sofar, int nsofar) {
95   int jobpipe[2],donepipe[2], c, r,i, x,y;
96   const OcrResultGlyph *s;
97   const char *p;
98   char cb;
99   Pixcol pv;
100   
101   if (!resolver) {
102     r= pipe(jobpipe);  eassert(!r);
103     r= pipe(donepipe);  eassert(!r);
104     resolver_pid= fork();
105     eassert(resolver_pid!=-1);
106     if (!resolver_pid) {
107       r= dup2(jobpipe[0],0); eassert(r==0);
108       r= close(jobpipe[1]); eassert(!r);
109       r= close(donepipe[0]); eassert(!r);
110       /* we know donepipe[1] is >= 4 and we have dealt with all the others
111        * so we aren't in any danger of overwriting some other fd 4: */
112       r= dup2(donepipe[1],4); eassert(r==4);
113       execlp("./show-thing.tcl", "./show-thing.tcl",
114              "--automatic","1",(char*)0);
115       eassert(!"execlp failed");
116     }
117     r= close(jobpipe[0]); eassert(!r);
118     r= close(donepipe[1]); eassert(!r);
119     resolver= fdopen(jobpipe[1],"w"); eassert(resolver);
120     resolver_done= donepipe[0];
121   }
122   fprintf(resolver,"%d %d %d",unk_l,unk_r,unk_ctx);
123   for (i=0, s=sofar; i<nsofar; i++, s++) {
124     if (!strcmp(s->s," ")) continue;
125     fprintf(resolver," %d %d %d ",s->l,s->r,s->ctx);
126     for (p=s->s; (c= *p); p++) {
127       if (c=='\\') fprintf(resolver,"\\%c",c);
128       else if (c>=33 && c<=126) fputc(c,resolver);
129       else fprintf(resolver,"\\x%02x",(unsigned char)c);
130     }
131   }
132   fputc('\n',resolver);
133
134   fprintf(resolver,
135           "/* XPM */\n"
136           "static char *t[] = {\n"
137           "/* columns rows colors chars-per-pixel */\n"
138           "\"%d %d 2 1\",\n"
139           "\"  c black\",\n"
140           "\"o c white\",\n",
141           w,h);
142   for (y=0, pv=1; y<h; y++, pv<<=1) {
143     fputc('"',resolver);
144     for (x=0; x<w; x++)
145       fputc(cols[x] & pv ? 'o' : ' ', resolver);
146     fputs("\",\n",resolver);
147   }
148   fputs("};\n",resolver);
149   eassert(!ferror(resolver));
150   eassert(!fflush(resolver));
151
152   eassert(resolver);
153
154   for (;;) {
155     r= read(resolver_done,&cb,1);
156     if (r==-1) { eassert(errno==EINTR); continue; }
157     break;
158   }
159
160   if (r==0) {
161     pid_t pid;
162     for (;;) {
163       pid= waitpid(resolver_pid, &r, 0);
164       if (pid==-1) { eassert(errno==EINTR); continue; }
165       break;
166     }
167     eassert(pid==resolver_pid);
168     if (WIFEXITED(r)) {
169       eassert(!WEXITSTATUS(r));
170       fclose(resolver);
171       close(resolver_done);
172       resolver= 0;
173     } else if (WIFSIGNALED(r)) {
174       eassert(!"resolver child died due to signal");
175     } else {
176       eassert(!"weird wait status");
177     }
178   } else {
179     eassert(r==1);
180     eassert(cb==0);
181   }
182
183   fclose(db);
184   db= 0;
185   ocr_readdb();
186 }
187
188 static void add_result(const char *s, int l, int r, int ctx) {
189   if (nresults >= aresults) {
190     aresults++; aresults<<=1;
191     results= realloc(results,sizeof(*results)*aresults);
192     eassert(results);
193   }
194   results[nresults].s= s;
195   results[nresults].l= l;
196   results[nresults].r= r;
197   results[nresults].ctx= ctx;
198   nresults++;
199 }
200
201 OcrResultGlyph *ocr(int w, int h, Pixcol cols[]) {
202   int nspaces=-w;
203   int ctx=1,i, x;
204
205   nresults=0;
206   assert(db);
207
208   fprintf(debug,"OCR h=%d w=%d",w,h);
209   for (x=0; x<w; x++) fprintf(debug," %"PSPIXCOL(PRIx),cols[x]);
210   fprintf(debug,"\n");
211   debug_flush();
212
213  restart:
214   x=0;
215   for (;;) {
216     debug_flush();
217     /* skip spaces */
218     if (x>=w)
219       break;
220
221     if (!cols[x]) {
222       nspaces++;
223       x++;
224       if (nspaces==3) {
225         fprintf(debug,"OCR  x=%x nspaces=%d space\n",x,nspaces);
226         add_result(" ",x-nspaces,x+1,0);
227         ctx=1;
228       }
229       continue;
230     }
231     nspaces=0;
232
233     /* find character */
234     OCRDatabaseNode *current=0, *bestmatch=0;
235     int lx=x;
236     int bestmatch_rx=-1;
237     current= &ocr_contexts[ctx];
238     fprintf(debug,"OCR  lx=%d ctx=%d  ",lx,ctx);
239
240     for (;;) {
241       debug_flush();
242       fprintf(debug,"| x=%d",x);
243       if (x>w) break;
244       Pixcol cv= cols[x];
245       fprintf(debug," cv=%"PSPIXCOL(PRIx),x);
246       for (i=0; i<current->nlinks; i++)
247         if (current->links[i].col == cv)
248           goto found;
249       /* not found */
250       fprintf(debug," ?");
251       break;
252
253     found:
254       current= current->links[i].then;
255       if (current->s[0]) {
256         fprintf(debug," \"%s\"",current->s);
257         bestmatch=current; bestmatch_rx=x;
258       } else {
259         fprintf(debug," ...");
260       }
261       x++;
262     }
263
264     if (bestmatch) {
265       fprintf(debug," YES\n");
266       add_result(bestmatch->s, lx, bestmatch_rx, ctx);
267       x= bestmatch_rx+1;
268       ctx= 0;
269     } else {
270       int rx;
271       fprintf(debug," UNKNOWN");
272       for (rx=lx; rx<w && cols[rx]; rx++);
273       fprintf(debug," x=%d ctx=%d %d..%d\n",x, ctx, lx,rx);
274       debug_flush();
275       callout_unknown(w,h,cols, lx,rx-1,ctx, results,nresults);
276       goto restart;
277     }
278   }
279   add_result(0,-1,-1,0);
280   fprintf(debug,"OCR  finished %d glyphs\n",nresults);
281   debug_flush();
282   return results;
283 }
284
285 void ocr_init(void) {
286   ocr_readdb();
287 }