gocr-ruby 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +49 -0
- data/ext/gocr/Makefile +141 -0
- data/ext/gocr/Makefile.in +140 -0
- data/ext/gocr/amiga.h +31 -0
- data/ext/gocr/barcode.c +2108 -0
- data/ext/gocr/barcode.h +11 -0
- data/ext/gocr/box.c +496 -0
- data/ext/gocr/config.h +37 -0
- data/ext/gocr/config.h.in +36 -0
- data/ext/gocr/database.c +468 -0
- data/ext/gocr/detect.c +1003 -0
- data/ext/gocr/extconf.rb +6 -0
- data/ext/gocr/gocr.c +436 -0
- data/ext/gocr/gocr.h +290 -0
- data/ext/gocr/jconv.c +168 -0
- data/ext/gocr/job.c +92 -0
- data/ext/gocr/lines.c +364 -0
- data/ext/gocr/list.c +334 -0
- data/ext/gocr/list.h +91 -0
- data/ext/gocr/ocr0.c +7312 -0
- data/ext/gocr/ocr0.h +63 -0
- data/ext/gocr/ocr0n.c +1527 -0
- data/ext/gocr/ocr1.c +85 -0
- data/ext/gocr/ocr1.h +3 -0
- data/ext/gocr/otsu.c +310 -0
- data/ext/gocr/otsu.h +23 -0
- data/ext/gocr/output.c +291 -0
- data/ext/gocr/output.h +37 -0
- data/ext/gocr/pcx.c +153 -0
- data/ext/gocr/pcx.h +9 -0
- data/ext/gocr/pgm2asc.c +3259 -0
- data/ext/gocr/pgm2asc.h +105 -0
- data/ext/gocr/pixel.c +538 -0
- data/ext/gocr/pnm.c +538 -0
- data/ext/gocr/pnm.h +35 -0
- data/ext/gocr/progress.c +87 -0
- data/ext/gocr/progress.h +42 -0
- data/ext/gocr/remove.c +715 -0
- data/ext/gocr/tga.c +87 -0
- data/ext/gocr/tga.h +6 -0
- data/ext/gocr/unicode.c +1318 -0
- data/ext/gocr/unicode.h +62 -0
- data/ext/gocr/unicode_defs.h +1245 -0
- data/ext/gocr/version.h +2 -0
- data/gocr-ruby.gemspec +28 -0
- data/image.png +0 -0
- data/lib/gocr.rb +6 -0
- data/lib/gocr/image.rb +8 -0
- data/lib/gocr/version.rb +3 -0
- metadata +156 -0
data/ext/gocr/ocr1.c
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
// test routines - faster to compile
|
2
|
+
#include <stdlib.h>
|
3
|
+
#include <stdio.h>
|
4
|
+
#include "pgm2asc.h"
|
5
|
+
#include "unicode.h"
|
6
|
+
#include "amiga.h"
|
7
|
+
#include "gocr.h"
|
8
|
+
|
9
|
+
// for learn_mode/analyze_mode high, with, yoffset, num of pattern_i,
|
10
|
+
// - holes (center,radius in relative coordinates) etc. => cluster analyze
|
11
|
+
// num_hole => min-volume, tolerance border
|
12
|
+
// pattern: @@ @. @@
|
13
|
+
// .@ @. ..
|
14
|
+
// regular filter for large resolutions to make edges more smooth (on boxes)
|
15
|
+
// extra-filter (only if not recognized?)
|
16
|
+
// map + same color to (#==change)
|
17
|
+
// - anti color
|
18
|
+
// . not used
|
19
|
+
// strongest neighbour pixels (3x3) => directions
|
20
|
+
// second/third run with more and more tolerance!?
|
21
|
+
|
22
|
+
/* FIXME jb: following is unused */
|
23
|
+
#if 0
|
24
|
+
struct lobj { // line-object (for fitting to near lines)
|
25
|
+
int x0,y0; // starting point (left up)
|
26
|
+
int x1,y1; // end point (right down)
|
27
|
+
int mt; // minimum thickness
|
28
|
+
int q; // quality, overlapp
|
29
|
+
};
|
30
|
+
|
31
|
+
/* FIXME jb global */
|
32
|
+
struct lobj obj1;
|
33
|
+
#endif
|
34
|
+
|
35
|
+
// that is the first draft of feature extraction
|
36
|
+
// detect main lines and bows
|
37
|
+
// seems bad implemented, looking for better algorithms (ToDo: use autotrace)
|
38
|
+
#define MAXL 10
|
39
|
+
void ocr2(pix *b,int cs){
|
40
|
+
int x1,y1,x2,y2,l,i,j,xa[MAXL],ya[MAXL],xb[MAXL],yb[MAXL],ll[MAXL];
|
41
|
+
for(i=0;i<MAXL;i++)xa[i]=ya[i]=xb[i]=yb[i]=ll[i]=0;
|
42
|
+
for(x1=0;x1<b->x;x1++) // very slowly, but simple to program
|
43
|
+
for(y1=0;y1<b->y;y1++) // brute force
|
44
|
+
for(x2=0;x2<b->x;x2++)
|
45
|
+
for(y2=y1+1;y2<b->y;y2++)
|
46
|
+
{
|
47
|
+
if( get_line2(x1,y1,x2,y2,b,cs,100)>99 )
|
48
|
+
{ // line ???
|
49
|
+
l=(x2-x1)*(x2-x1)+(y2-y1)*(y2-y1); // len
|
50
|
+
for(i=0;i<MAXL;i++)
|
51
|
+
{ // remove similar lines (same middle point) IMPROVE IT !!!!!! ???
|
52
|
+
if(
|
53
|
+
abs(x1+x2-xa[i]-xb[i])<1+b->x/2
|
54
|
+
&& abs(y1+y2-ya[i]-yb[i])<1+b->y/2
|
55
|
+
&& abs(y1-ya[i])<1+b->y/4
|
56
|
+
&& abs(x1-xa[i])<1+b->x/4
|
57
|
+
)
|
58
|
+
{
|
59
|
+
if( l>ll[i] )
|
60
|
+
{
|
61
|
+
for(j=i;j<MAXL-1;j++)
|
62
|
+
{ // shift table
|
63
|
+
xa[j]=xa[j+1];ya[j]=ya[j+1];
|
64
|
+
xb[j]=xb[j+1];yb[j]=yb[j+1];ll[j]=ll[j+1];
|
65
|
+
}
|
66
|
+
ll[MAXL-1]=0;
|
67
|
+
}
|
68
|
+
else break; // forget it if shorter
|
69
|
+
}
|
70
|
+
if( l>ll[i] ){ // insert if larger
|
71
|
+
for(j=MAXL-1;j>i;j--){ // shift table
|
72
|
+
xa[j]=xa[j-1];ya[j]=ya[j-1];
|
73
|
+
xb[j]=xb[j-1];yb[j]=yb[j-1];ll[j]=ll[j-1];
|
74
|
+
}
|
75
|
+
xa[i]=x1;ya[i]=y1;xb[i]=x2;yb[i]=y2;ll[i]=l;
|
76
|
+
break;
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
for(i=0;i<MAXL;i++){
|
82
|
+
printf(" %2d %2d %2d %2d %3d\n",xa[i],ya[i],xb[i],yb[i],ll[i]);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
data/ext/gocr/ocr1.h
ADDED
data/ext/gocr/otsu.c
ADDED
@@ -0,0 +1,310 @@
|
|
1
|
+
/*
|
2
|
+
This is a Optical-Character-Recognition program
|
3
|
+
Copyright (C) 2000-2010 Joerg Schulenburg
|
4
|
+
|
5
|
+
This program is free software; you can redistribute it and/or
|
6
|
+
modify it under the terms of the GNU General Public License
|
7
|
+
as published by the Free Software Foundation; either version 2
|
8
|
+
of the License, or (at your option) any later version.
|
9
|
+
|
10
|
+
This program is distributed in the hope that it will be useful,
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
GNU General Public License for more details.
|
14
|
+
|
15
|
+
You should have received a copy of the GNU General Public License
|
16
|
+
along with this program; if not, write to the Free Software
|
17
|
+
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
18
|
+
|
19
|
+
see README for EMAIL-address
|
20
|
+
|
21
|
+
the following code was send by Ryan Dibble <dibbler@umich.edu>
|
22
|
+
|
23
|
+
The algorithm is very simple but works good hopefully.
|
24
|
+
|
25
|
+
Compare the grayscale histogram with a mass density diagram:
|
26
|
+
I think the algorithm is a kind of
|
27
|
+
divide a body into two parts in a way that the mass
|
28
|
+
centers have the largest distance from each other,
|
29
|
+
the function is weighted in a way that same masses have a advantage
|
30
|
+
|
31
|
+
- otsu algorithm is failing on diskrete multi color images
|
32
|
+
|
33
|
+
TODO:
|
34
|
+
RGB: do the same with all colors (CMYG?) seperately
|
35
|
+
|
36
|
+
test: hardest case = two colors
|
37
|
+
bbg: test done, using a two color gray file. Output:
|
38
|
+
# threshold: Value = 43 gmin=43 gmax=188
|
39
|
+
|
40
|
+
my changes:
|
41
|
+
- float -> double
|
42
|
+
- debug option added (vvv & 1..2)
|
43
|
+
- **image => *image, &image[i][1] => &image[i*cols+1]
|
44
|
+
- do only count pixels near contrast regions
|
45
|
+
this makes otsu much better for shadowed fonts or multi colored text
|
46
|
+
on white background
|
47
|
+
|
48
|
+
(m) Joerg Schulenburg (see README for email address)
|
49
|
+
|
50
|
+
ToDo:
|
51
|
+
- measure contrast
|
52
|
+
- detect low-contrast regions
|
53
|
+
|
54
|
+
*/
|
55
|
+
|
56
|
+
#include <stdio.h>
|
57
|
+
#include <string.h>
|
58
|
+
|
59
|
+
#define Abs(x) ((x<0)?-(x):x)
|
60
|
+
|
61
|
+
/*======================================================================*
|
62
|
+
* global thresholding routine *
|
63
|
+
* takes a 2D unsigned char array pointer, number of rows, and *
|
64
|
+
* number of cols in the array. returns the value of the threshold *
|
65
|
+
* x0,y0,x0+dx,y0+dy are the edgepoints of the interesting region *
|
66
|
+
* vvv is the verbosity for debugging purpose *
|
67
|
+
*======================================================================*/
|
68
|
+
int
|
69
|
+
otsu (unsigned char *image, int rows, int cols,
|
70
|
+
int x0, int y0, int dx, int dy, int vvv) {
|
71
|
+
|
72
|
+
unsigned char *np; // pointer to position in the image we are working with
|
73
|
+
unsigned char op1, op2, op3, op4; // predecessor of pixel *np (start value)
|
74
|
+
int maxc=0; // maximum contrast (start value)
|
75
|
+
int thresholdValue=1; // value we will threshold at
|
76
|
+
int ihist[256]; // image histogram
|
77
|
+
int chist[256]; // contrast histogram
|
78
|
+
|
79
|
+
int i, j, k; // various counters
|
80
|
+
int is, i1, i2, ns, n1, n2, gmin, gmax;
|
81
|
+
double m1, m2, sum, csum, fmax, sb;
|
82
|
+
|
83
|
+
// zero out histogram ...
|
84
|
+
memset(ihist, 0, sizeof(ihist));
|
85
|
+
memset(chist, 0, sizeof(chist));
|
86
|
+
op4=op3=op1=op2=image[y0*cols+x0];
|
87
|
+
|
88
|
+
gmin=255; gmax=0; k=dy/512+1;
|
89
|
+
// v0.43 first get max contrast, dont do it together with next step
|
90
|
+
// because it failes if we have pattern as background (on top)
|
91
|
+
for (i = 0; i < dy ; i+=k) {
|
92
|
+
np = &image[(y0+i)*cols+x0];
|
93
|
+
for (j = 0; j < dx ; j++) {
|
94
|
+
ihist[*np]++;
|
95
|
+
if (*np > gmax) gmax=*np;
|
96
|
+
if (*np < gmin) gmin=*np;
|
97
|
+
if (Abs(*np-op1)>maxc) maxc=Abs(*np-op1); /* new maximum contrast */
|
98
|
+
if (Abs(*np-op2)>maxc) maxc=Abs(*np-op2); /* new maximum contrast */
|
99
|
+
// 2010-10-07 add op3+op4 for invalid_ogv.jpg
|
100
|
+
if (Abs(*np-op3)>maxc) maxc=Abs(*np-op3); /* new maximum contrast */
|
101
|
+
if (Abs(*np-op4)>maxc) maxc=Abs(*np-op4); /* new maximum contrast */
|
102
|
+
/* we hope that maxc will be find its maximum very fast */
|
103
|
+
op4=op3;op3=op2;op2=op1; /* shift old pixel to next older */
|
104
|
+
op1=*np; /* store old pixel for contrast check */
|
105
|
+
np++; /* next pixel */
|
106
|
+
}
|
107
|
+
}
|
108
|
+
if ((vvv&1)) // Debug
|
109
|
+
fprintf(stderr,"# threshold: max_contrast= %d\n", maxc);
|
110
|
+
|
111
|
+
// generate the histogram
|
112
|
+
// Aug06 images with large white or black homogeneous
|
113
|
+
// areas give bad results, so we only add pixels on contrast edges
|
114
|
+
op4=op3=op1=op2=image[y0*cols+x0];
|
115
|
+
for (i = 0; i < dy ; i+=k) {
|
116
|
+
np = &image[(y0+i)*cols+x0];
|
117
|
+
for (j = 0; j < dx ; j++) {
|
118
|
+
if (Abs(*np-op1)>=1*maxc/4 // min_d_max_contrast=1
|
119
|
+
|| Abs(*np-op2)>=1*maxc/4 // min_d_max_contrast=2
|
120
|
+
|| Abs(*np-op3)>=1*maxc/4
|
121
|
+
|| Abs(*np-op4)>=1*maxc/4
|
122
|
+
// || Abs(*np-gmax)<=1*maxc/4
|
123
|
+
// || Abs(*np-gmin)<=1*maxc/4
|
124
|
+
)
|
125
|
+
chist[*np]++; // count only relevant pixels
|
126
|
+
op4=op3;op3=op2;op2=op1; /* shift old pixel to next older */
|
127
|
+
op1=*np; /* store old pixel for contrast check */
|
128
|
+
np++; /* next pixel */
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
// set up everything
|
133
|
+
sum = csum = 0.0;
|
134
|
+
ns = 0;
|
135
|
+
is = 0;
|
136
|
+
|
137
|
+
for (k = 0; k <= 255; k++) {
|
138
|
+
sum += (double) k * (double) chist[k]; /* x*f(x) cmass moment */
|
139
|
+
ns += chist[k]; /* f(x) cmass */
|
140
|
+
is += ihist[k]; /* f(x) imass */
|
141
|
+
// Debug: output to out_hist.dat?
|
142
|
+
// fprintf(stderr,"\chistogram %3d %6d (brightness weight)", k, ihist[k]);
|
143
|
+
}
|
144
|
+
|
145
|
+
if (!ns) {
|
146
|
+
// if n has no value we have problems...
|
147
|
+
fprintf (stderr, "NOT NORMAL, thresholdValue = 160\n");
|
148
|
+
return (160);
|
149
|
+
}
|
150
|
+
|
151
|
+
// ToDo: search max contrast in 2 near(r=8) pixels, take as mass centers + dist
|
152
|
+
// expand b/w-mass ranges by further contrast dipols
|
153
|
+
// min_max_b_b_distance + max_b_b distance
|
154
|
+
// use a range white=... black=... for between values due local thresholding
|
155
|
+
// 24x24 field (cashed) where 8x8 center is thresholded
|
156
|
+
// ToDo: only care about extremas in a 3 pixel environment
|
157
|
+
// check if there are more than 2 mass centers (more colors)
|
158
|
+
// return object colors and color radius instead of threshold value
|
159
|
+
// also the reagion, where colored objects are found
|
160
|
+
// what if more than one background color? no otsu at all?
|
161
|
+
// whats background? box with lot of other boxes in it
|
162
|
+
// threshold each box (examples/invers.png,colors.png)
|
163
|
+
// get maximum white and minimum black pixel color (possible range)
|
164
|
+
// check range between them for low..high contrast ???
|
165
|
+
// typical scenes (which must be covered):
|
166
|
+
// - white page with text of different colors (gray values)
|
167
|
+
// - binear page: background (gray=1) + black text (gray=0)
|
168
|
+
// - text mixed with big (dark) images
|
169
|
+
// ToDo: recursive clustering for maximum multipol moments?
|
170
|
+
// idea: normalize ihist to max=1024 before otsu?
|
171
|
+
|
172
|
+
// do the otsu global thresholding method
|
173
|
+
|
174
|
+
if ((vvv&1)) // Debug
|
175
|
+
fprintf(stderr,"# threshold: value ihist chist mass_dipol_moment trigg\n");
|
176
|
+
fmax = -1.0;
|
177
|
+
n1 = 0;
|
178
|
+
for (k = 0; k < 255; k++) {
|
179
|
+
n1 += chist[k]; // left mass (integration)
|
180
|
+
if (!n1) continue; // we need at least one foreground pixel
|
181
|
+
n2 = ns - n1; // right mass (num pixels - left mass)
|
182
|
+
if (n2 == 0) break; // we need at least one background pixel
|
183
|
+
csum += (double) k *chist[k]; // left mass moment
|
184
|
+
m1 = csum / n1; // left mass center (black chars)
|
185
|
+
m2 = (sum - csum) / n2; // right mass center (white background)
|
186
|
+
// max. dipol moment?
|
187
|
+
// orig: sb = (double) n1 *(double) n2 * (m1 - m2) * (m1 - m2);
|
188
|
+
sb = (double) n1 *(double) n2 * (m2 - m1); // seems to be better Aug06
|
189
|
+
/* bbg: note: can be optimized. */
|
190
|
+
if (sb > fmax) {
|
191
|
+
fmax = sb;
|
192
|
+
thresholdValue = k + 1;
|
193
|
+
// thresholdValue = (m1 + 3 * m2) / 4;
|
194
|
+
}
|
195
|
+
if ((vvv&1) && ihist[k]) // Debug
|
196
|
+
fprintf(stderr,"# threshold: %3d %6d %6d %8.3f %4d\n",
|
197
|
+
k, ihist[k], chist[k],
|
198
|
+
sb*256/(dx*dy)/(dx*dy), /* normalized dipol moment */
|
199
|
+
thresholdValue);
|
200
|
+
}
|
201
|
+
// ToDo: error = left/right point where sb is 90% of maximum?
|
202
|
+
// now we count all pixels for background detection
|
203
|
+
i1 = 0;
|
204
|
+
for (k = 0; k < thresholdValue; k++) {
|
205
|
+
i1 += ihist[k]; // left mass (integration)
|
206
|
+
}
|
207
|
+
i2 = is - i1; // right mass (num pixels - left mass)
|
208
|
+
|
209
|
+
// at this point we have our thresholding value
|
210
|
+
// black_char: value<cs, white_background: value>=cs
|
211
|
+
|
212
|
+
// can it happen? check for sureness
|
213
|
+
if (thresholdValue > gmax) {
|
214
|
+
fprintf(stderr,"# threshold: Value >gmax\n");
|
215
|
+
thresholdValue = gmax;
|
216
|
+
}
|
217
|
+
if (thresholdValue <= gmin) {
|
218
|
+
fprintf(stderr,"# threshold: Value<=gmin\n");
|
219
|
+
thresholdValue = gmin+1;
|
220
|
+
}
|
221
|
+
|
222
|
+
// debug code to display thresholding values
|
223
|
+
if ( vvv & 1 )
|
224
|
+
fprintf(stderr,"# threshold: Value = %d gmin=%d gmax=%d cmax=%d"
|
225
|
+
" b/w= %d %d\n",
|
226
|
+
thresholdValue, gmin, gmax, maxc, i1, i2);
|
227
|
+
|
228
|
+
// this is a primitive criteria for inversion and should be improved
|
229
|
+
// old: i1 >= 4*i2, but 0811qemu1.png has a bit above 1/4
|
230
|
+
if (2*i1 > 7*i2) { // more black than white, obviously black is background
|
231
|
+
if ( vvv & 1 )
|
232
|
+
fprintf(stderr,"# threshold: invert the image\n");
|
233
|
+
// we do inversion here (no data lost)
|
234
|
+
for (i = 0; i < dy ; i++) {
|
235
|
+
np = &image[(y0+i)*cols+x0];
|
236
|
+
for (j = 0; j < dx ; j++) {
|
237
|
+
*np=255-*np;
|
238
|
+
np++; /* next pixel */
|
239
|
+
}
|
240
|
+
}
|
241
|
+
thresholdValue=255-thresholdValue+1;
|
242
|
+
}
|
243
|
+
|
244
|
+
return(thresholdValue);
|
245
|
+
/* range: 0 < thresholdValue <= 255, example: 1 on b/w images */
|
246
|
+
/* 0..threshold-1 is foreground */
|
247
|
+
/* threshold..255 is background */
|
248
|
+
/* ToDo: min=blackmasscenter/2,thresh,max=(whitemasscenter+255)/2 */
|
249
|
+
}
|
250
|
+
|
251
|
+
/*======================================================================*/
|
252
|
+
/* thresholding the image (set threshold to 128+32=160=0xA0) */
|
253
|
+
/* now we have a fixed thresholdValue good to recognize on gray image */
|
254
|
+
/* - so lower bits can used for other things (bad design?) */
|
255
|
+
/* ToDo: different foreground colors, gray on black/white background */
|
256
|
+
/*======================================================================*/
|
257
|
+
int
|
258
|
+
thresholding (unsigned char *image, int rows, int cols,
|
259
|
+
int x0, int y0, int dx, int dy, int thresholdValue) {
|
260
|
+
|
261
|
+
unsigned char *np; // pointer to position in the image we are working with
|
262
|
+
|
263
|
+
int i, j; // various counters
|
264
|
+
int gmin=255,gmax=0;
|
265
|
+
int nmin=255,nmax=0;
|
266
|
+
|
267
|
+
// Aug10: i=y0+1, why 1 pixel frame? bug if v[y0] > gmax, fixed
|
268
|
+
// calculate min/max (twice?)
|
269
|
+
for (i = y0 /*+ 1*/; i < y0 + dy /*- 1*/; i++) {
|
270
|
+
np = &image[i*cols+x0+1];
|
271
|
+
for (j = x0 /*+ 1*/; j < x0 + dx /*- 1*/; j++) {
|
272
|
+
if(*np > gmax) gmax=*np;
|
273
|
+
if(*np < gmin) gmin=*np;
|
274
|
+
np++; /* next pixel */
|
275
|
+
}
|
276
|
+
}
|
277
|
+
|
278
|
+
/* allowed_threshold=gmin+1..gmax v0.43 */
|
279
|
+
if (thresholdValue<=gmin || thresholdValue>gmax){
|
280
|
+
thresholdValue=(gmin+gmax+1)/2; /* range=0..1 -> threshold=1 */
|
281
|
+
fprintf(stderr,"# thresholdValue out of range %d..%d, reset to %d\n",
|
282
|
+
gmin, gmax, thresholdValue);
|
283
|
+
}
|
284
|
+
|
285
|
+
/* b/w: min=0,tresh=1,max=1 v0.43 */
|
286
|
+
// actually performs the thresholding of the image...
|
287
|
+
// later: grayvalues should also be used, only rescaling threshold=160=0xA0
|
288
|
+
// sometimes images have no contrast (thresholdValue == gmin)
|
289
|
+
for (i = y0; i < y0+dy; i++) {
|
290
|
+
np = &image[i*cols+x0];
|
291
|
+
for (j = x0; j < x0+dx; j++) {
|
292
|
+
if ( *np > gmax || *np <gmin) // should never happen
|
293
|
+
fprintf(stderr,"ERROR th yx= %2d %2d t= %d v= %3d min/max= %d %d\n",
|
294
|
+
i,j,thresholdValue,*np,gmin,gmax);
|
295
|
+
*np = (unsigned char) (*np >= thresholdValue || thresholdValue == gmin ?
|
296
|
+
(255-(gmax - *np)* 80/(gmax - thresholdValue + 1)) :
|
297
|
+
( 0+(*np - gmin)*150/(thresholdValue - gmin )) );
|
298
|
+
if(*np > nmax) nmax=*np;
|
299
|
+
if(*np < nmin) nmin=*np;
|
300
|
+
np++;
|
301
|
+
}
|
302
|
+
}
|
303
|
+
|
304
|
+
// fprintf(stderr,"# thresholding: nmin=%d nmax=%d\n", nmin, nmax);
|
305
|
+
|
306
|
+
return(128+32); // return the new normalized threshold value
|
307
|
+
/* 0..159 is foreground */
|
308
|
+
/* 160..255 is background */
|
309
|
+
}
|
310
|
+
|
data/ext/gocr/otsu.h
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
/*
|
2
|
+
|
3
|
+
see README for EMAIL-address
|
4
|
+
|
5
|
+
*/
|
6
|
+
|
7
|
+
|
8
|
+
/*======================================================================*/
|
9
|
+
/* OTSU global thresholding routine */
|
10
|
+
/* takes a 2D unsigned char array pointer, number of rows, and */
|
11
|
+
/* number of cols in the array. returns the value of the threshold */
|
12
|
+
/*======================================================================*/
|
13
|
+
int
|
14
|
+
otsu (unsigned char *image, int rows, int cols, int x0, int y0, int dx, int dy, int vvv);
|
15
|
+
|
16
|
+
|
17
|
+
/*======================================================================*/
|
18
|
+
/* thresholding the image (set threshold to 128+32=160=0xA0) */
|
19
|
+
/* now we have a fixed thresholdValue good to recognize on gray image */
|
20
|
+
/* - so lower bits can used for other things (bad design?) */
|
21
|
+
/*======================================================================*/
|
22
|
+
int
|
23
|
+
thresholding (unsigned char *image, int rows, int cols, int x0, int y0, int dx, int dy, int thresholdValue);
|
data/ext/gocr/output.c
ADDED
@@ -0,0 +1,291 @@
|
|
1
|
+
/*
|
2
|
+
This is a Optical-Character-Recognition program
|
3
|
+
Copyright (C) 2000-2010 Joerg Schulenburg
|
4
|
+
|
5
|
+
This program is free software; you can redistribute it and/or
|
6
|
+
modify it under the terms of the GNU General Public License
|
7
|
+
as published by the Free Software Foundation; either version 2
|
8
|
+
of the License, or (at your option) any later version.
|
9
|
+
|
10
|
+
This program is distributed in the hope that it will be useful,
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
GNU General Public License for more details.
|
14
|
+
|
15
|
+
You should have received a copy of the GNU General Public License
|
16
|
+
along with this program; if not, write to the Free Software
|
17
|
+
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
18
|
+
|
19
|
+
see README for EMAIL address
|
20
|
+
*/
|
21
|
+
|
22
|
+
#include <string.h>
|
23
|
+
#include "unicode.h"
|
24
|
+
#include "output.h"
|
25
|
+
#include "pcx.h"
|
26
|
+
#include "gocr.h" /* extern job_t OCR_JOB; */
|
27
|
+
#include "unicode_defs.h" /* UNKNOWN + PICTURE */
|
28
|
+
|
29
|
+
/* function is only for debugging and for developing
|
30
|
+
it prints out a part of pixmap b at point x0,y0 to stderr
|
31
|
+
using dots .,; if no pixel, and @xoO for pixels
|
32
|
+
modify n_run and print out what would happen on 2nd, 3th loop!
|
33
|
+
new: output original and copied pixmap in the same figure
|
34
|
+
*/
|
35
|
+
void out_b(struct box *px, pix *b, int x0, int y0, int dx, int dy, int cs ){
|
36
|
+
int x,y,x2,y2,yy0,tx,ty,n1,i;
|
37
|
+
char c1, c2;
|
38
|
+
yy0=y0;
|
39
|
+
if (px) { /* overwrite rest of arguments */
|
40
|
+
if (!b) {
|
41
|
+
b=px->p;
|
42
|
+
x0=px->x0; dx=px->x1-px->x0+1;
|
43
|
+
y0=px->y0; dy=px->y1-px->y0+1; yy0=y0;
|
44
|
+
}
|
45
|
+
if(cs==0) cs=OCR_JOB->cfg.cs;
|
46
|
+
fprintf(stderr,"\n# list box x= %4d %4d d= %3d %3d r= %3d %3d"
|
47
|
+
" nrun=%d p=%p", /* ToDo: r,nrun is obsolete */
|
48
|
+
px->x0, px->y0, px->x1 - px->x0 + 1, px->y1 - px->y0 + 1,
|
49
|
+
px->x - px->x0, px->y - px->y0, OCR_JOB->tmp.n_run, (void*)px);
|
50
|
+
fprintf(stderr,"\n# dots=%d boxes=%d subboxes=%d c=%s mod=%s"
|
51
|
+
" line=%d m= %d %d %d %d",
|
52
|
+
px->dots, px->num_boxes, px->num_subboxes,
|
53
|
+
decode(px->c,ASCII), decode(px->modifier,ASCII), px->line,
|
54
|
+
px->m1 - px->y0, px->m2 - px->y0, px->m3 - px->y0, px->m4 - px->y0);
|
55
|
+
if (px->num_frames) {
|
56
|
+
int i,j,jo;
|
57
|
+
fprintf(stderr,"\n# frames= %d (sumvects=%d)",px->num_frames,
|
58
|
+
((px->num_frames)?px->num_frame_vectors[px->num_frames-1]:-1));
|
59
|
+
// output objects: volume periphery(de:Umfang) num_frame_vectors
|
60
|
+
for (jo=j=i=0; i<px->num_frames; i++, jo=j) {
|
61
|
+
fprintf(stderr,"\n# frame %d (%+4d,%3d,%2d) ",
|
62
|
+
i, px->frame_vol[i], px->frame_per[i],
|
63
|
+
px->num_frame_vectors[i]-jo);
|
64
|
+
/* print only the first vectors of each frame */
|
65
|
+
for (;j<px->num_frame_vectors[i] && j<MaxFrameVectors; j++)
|
66
|
+
fprintf(stderr," #%02d %2d %2d", j,
|
67
|
+
px->frame_vector[j][0] - px->x0,
|
68
|
+
px->frame_vector[j][1] - px->y0);
|
69
|
+
}
|
70
|
+
}
|
71
|
+
if (px->num_ac){ /* output table of chars and its probabilities */
|
72
|
+
fprintf(stderr,"\n# list box char: ");
|
73
|
+
for(i=0;i<px->num_ac && i<NumAlt;i++)
|
74
|
+
/* output the (xml-)string (picture position, barcodes, glyphs, ...) */
|
75
|
+
if (px->tas[i])
|
76
|
+
fprintf(stderr," %s(%d)", px->tas[i] ,px->wac[i]);
|
77
|
+
else
|
78
|
+
fprintf(stderr," %s(%d)",decode(px->tac[i],ASCII),px->wac[i]);
|
79
|
+
}
|
80
|
+
fprintf(stderr,"\n");
|
81
|
+
if (px->m2 && px->m1<y0 && (px->dots || y0>px->m2)) {
|
82
|
+
yy0=px->m1; dy=px->y1-yy0+1;
|
83
|
+
}
|
84
|
+
}
|
85
|
+
tx=dx/80+1;
|
86
|
+
ty=dy/40+1; /* step, usually 1, but greater on large maps */
|
87
|
+
fprintf(stderr,"# list pattern x= %4d %4d d= %3d %3d t= %d %d yy0= %d\n",
|
88
|
+
x0,y0,dx,dy,tx,ty,yy0);
|
89
|
+
if (dx>0)
|
90
|
+
for(y=yy0;y<yy0+dy;y+=ty) { /* reduce the output to max 78x40 */
|
91
|
+
/* first image is the copied and modified bitmap of the box */
|
92
|
+
if (px)
|
93
|
+
for(x=x0;x<x0+dx;x+=tx){ /* by merging sub-pixels */
|
94
|
+
n1=0; c1='.';
|
95
|
+
for(y2=y;y2<y+ty && y2<y0+dy;y2++) /* sub-pixels */
|
96
|
+
for(x2=x;x2<x+tx && x2<x0+dx;x2++)
|
97
|
+
{
|
98
|
+
if((getpixel(px->p,x2-x0+px->x0,
|
99
|
+
y2-y0+px->y0)<cs)) c1='@';
|
100
|
+
}
|
101
|
+
if (px->num_frames) { /* mark vectors */
|
102
|
+
int i;
|
103
|
+
if (c1!='$' && c1!='S') /* dont mark twice */
|
104
|
+
for (i=0;i<px->num_frame_vectors[px->num_frames-1];i++)
|
105
|
+
if ((px->frame_vector[i][0]-px->x0)/tx==(x-x0)/tx
|
106
|
+
&& (px->frame_vector[i][1]-px->y0)/ty==(y-y0)/ty)
|
107
|
+
{ c1=((c1=='@')?'$':'S'); break; }
|
108
|
+
}
|
109
|
+
fprintf(stderr,"%c", c1 );
|
110
|
+
}
|
111
|
+
|
112
|
+
/* 2nd image is the boxframe in the original bitmap */
|
113
|
+
if (dx<40) fprintf(stderr," ");
|
114
|
+
if (dx<40) /* do it only, if we have enough place */
|
115
|
+
for(x=x0;x<x0+dx;x+=tx){ /* by merging sub-pixels */
|
116
|
+
c1='.';
|
117
|
+
for(y2=y;y2<y+ty && y2<y0+dy;y2++) /* sub-pixels */
|
118
|
+
for(x2=x;x2<x+tx && x2<x0+dx;x2++)
|
119
|
+
{ if((getpixel(b,x2,y2)<cs)) c1='@'; }
|
120
|
+
fprintf(stderr,"%c", c1 );
|
121
|
+
}
|
122
|
+
|
123
|
+
c1=c2=' ';
|
124
|
+
/* mark lines with < */
|
125
|
+
if (px) if (y-y0+px->y0==px->m1 || y-y0+px->y0==px->m2
|
126
|
+
|| y-y0+px->y0==px->m3 || y-y0+px->y0==px->m4) c1='<';
|
127
|
+
if (y==y0 || y==yy0+dy-1) c2='-'; /* boxmarks */
|
128
|
+
|
129
|
+
fprintf(stderr,"%c%c\n",c1,c2);
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
/* same as out_b, but for faster use, only a box as argument
|
134
|
+
*/
|
135
|
+
void out_x(struct box *px) {
|
136
|
+
out_b(px,NULL,0, 0, 0, 0, OCR_JOB->cfg.cs);
|
137
|
+
}
|
138
|
+
|
139
|
+
|
140
|
+
/* print out two boxes side by side, for debugging comparision algos */
|
141
|
+
void out_x2(struct box *box1, struct box *box2){
|
142
|
+
int x,y,i,tx,ty,dy;
|
143
|
+
/*FIXME jb static*/static char *c1="OXXXXxx@.,,,,,,,";
|
144
|
+
pix *b=&OCR_JOB->src.p;
|
145
|
+
dy=(box1->y1-box1->y0+1);
|
146
|
+
if(dy<box2->y1-box2->y0+1)dy=box2->y1-box2->y0+1;
|
147
|
+
tx=(box1->x1-box1->x0)/40+1;
|
148
|
+
ty=(box1->y1-box1->y0)/40+1; /* step, usually 1, but greater on large maps */
|
149
|
+
if(box2)fprintf(stderr,"\n# list 2 patterns");
|
150
|
+
for(i=0;i<dy;i+=ty) { /* reduce the output to max 78x40??? */
|
151
|
+
fprintf(stderr,"\n"); y=box1->y0+i;
|
152
|
+
for(x=box1->x0;x<=box1->x1;x+=tx)
|
153
|
+
fprintf(stderr,"%c", c1[ ((getpixel(b,x,y)<OCR_JOB->cfg.cs)?0:8)+marked(b,x,y) ] );
|
154
|
+
if(!box2) continue;
|
155
|
+
fprintf(stderr," "); y=box2->y0+i;
|
156
|
+
for(x=box2->x0;x<=box2->x1;x+=tx)
|
157
|
+
fprintf(stderr,"%c", c1[ ((getpixel(b,x,y)<OCR_JOB->cfg.cs)?0:8)+marked(b,x,y) ] );
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
|
162
|
+
/* ---- list output ---- for debugging ---
|
163
|
+
* list all boxes where the results can be found within the c-option
|
164
|
+
*/
|
165
|
+
int output_list(job_t *job) {
|
166
|
+
int i = 0, j;
|
167
|
+
struct box *box2;
|
168
|
+
pix *pp = &job->src.p;
|
169
|
+
char *lc = job->cfg.lc;
|
170
|
+
|
171
|
+
fprintf(stderr,"\n# list shape for charlist %s",lc);
|
172
|
+
for_each_data(&(job->res.boxlist)) {
|
173
|
+
box2 = (struct box *) list_get_current(&(job->res.boxlist));
|
174
|
+
for (j=0; j<box2->num_ac; j++)
|
175
|
+
if (!lc || (box2->tac[j] && strchr(lc, box2->tac[j]))
|
176
|
+
|| (box2->tas[j] && strstr(lc, box2->tas[j]))) break;
|
177
|
+
if (j<box2->num_ac)
|
178
|
+
fprintf(stderr,"\n# box found in charlist");
|
179
|
+
if (!lc || (strchr(lc, box2->c) && box2->c < 256 && box2->c)
|
180
|
+
|| (strchr(lc, '_') && box2->c==UNKNOWN) /* for compability */
|
181
|
+
|| j<box2->num_ac ){ /* also list alternative chars */
|
182
|
+
if (!pp) pp=box2->p;
|
183
|
+
fprintf(stderr,
|
184
|
+
"\n# list shape %3d x=%4d %4d d= %3d %3d vf=%d ac=%d %04x %s",
|
185
|
+
i, box2->x0, box2->y0,
|
186
|
+
box2->x1 - box2->x0 + 1,
|
187
|
+
box2->y1 - box2->y0 + 1,
|
188
|
+
box2->num_frames, box2->num_ac,
|
189
|
+
(int)box2->c, /* wchar_t -> char ???? */
|
190
|
+
decode(box2->c,ASCII) );
|
191
|
+
if (job->cfg.verbose & 4) out_x(box2);
|
192
|
+
}
|
193
|
+
i++;
|
194
|
+
} end_for_each(&(job->res.boxlist));
|
195
|
+
fprintf(stderr,"\n");
|
196
|
+
return 0;
|
197
|
+
}
|
198
|
+
|
199
|
+
/* --- output of image incl. corored lines usefull for developers ---
|
200
|
+
* debugging
|
201
|
+
* bit 0+1 is used for color coding (optical marker)
|
202
|
+
* color/gray: 0x01=red, 0x02=blue, 0x04=green???
|
203
|
+
* opt: 1 - mark unknown boxes red (first pass)
|
204
|
+
* 2 - mark unknown boxes more red (final pass)
|
205
|
+
* 4 - mark lines blue
|
206
|
+
* 8 - reset coloring (remove old marker)
|
207
|
+
*/
|
208
|
+
int debug_img(char *fname, struct job_s *job, int opt) {
|
209
|
+
struct box *box2;
|
210
|
+
int x, y, ic, dx, i, j, col;
|
211
|
+
unsigned char *np;
|
212
|
+
pix *pp = &job->tmp.ppo;
|
213
|
+
|
214
|
+
if ( opt & 8 ) { /* clear debug bits in image */
|
215
|
+
for(y=0;y<pp->y;y++) {
|
216
|
+
np=&pp->p[(pp->x)*y];
|
217
|
+
for(x=0;x<pp->x;x++) {
|
218
|
+
*np = *np & 0xF1;
|
219
|
+
np++;
|
220
|
+
}
|
221
|
+
}
|
222
|
+
}
|
223
|
+
|
224
|
+
/* mark longest line which was used to estimate the rotation angle */
|
225
|
+
if ((job->cfg.verbose&32) && job->res.lines.dx)
|
226
|
+
for(i=0;i<pp->x;i++) {
|
227
|
+
y=pp->y/2;
|
228
|
+
if (job->res.lines.dx) y+=job->res.lines.dy*i/job->res.lines.dx;
|
229
|
+
x=i;
|
230
|
+
if (x<0 || x>=pp->x || y<0 || y>=pp->y) continue;
|
231
|
+
np=&pp->p[x + (pp->x)*y];
|
232
|
+
if (*np<160) continue;
|
233
|
+
if((x&7)<5 && !(x&1)) /* dotted line */
|
234
|
+
put(pp,x,y,255,8);
|
235
|
+
}
|
236
|
+
|
237
|
+
ic = ((opt & 2) ? 1 : 2); /* obsolete */
|
238
|
+
for_each_data(&(job->res.boxlist)) {
|
239
|
+
box2 = (struct box *) list_get_current(&(job->res.boxlist));
|
240
|
+
/* mark boxes in 32=0x40=blue */
|
241
|
+
if (box2->c == ' ' || box2->c == '\n') continue;
|
242
|
+
/* mark chars with green left and under line */
|
243
|
+
col = 4; /* green */
|
244
|
+
if (box2->c == UNKNOWN && (opt & 3)) col=2; /* red */
|
245
|
+
if (box2->x0>1)
|
246
|
+
for (y = box2->y0; y <= box2->y1; y++) {
|
247
|
+
np=&pp->p[box2->x0-1 + y * pp->x]; if (*np<160) continue; *np|=col; }
|
248
|
+
if (box2->y1+1<pp->y)
|
249
|
+
for (x = box2->x0; x <= box2->x1; x++) {
|
250
|
+
np=&pp->p[x + (box2->y1+1) * pp->x]; if (*np<160) continue; *np|=col; }
|
251
|
+
/* mark pictures by green cross */
|
252
|
+
if (box2->c == PICTURE)
|
253
|
+
for (x = 0; x < box2->x1-box2->x0+1; x++){
|
254
|
+
y=(box2->y1-box2->y0+1)*x/(box2->x1-box2->x0+1);
|
255
|
+
pp->p[(box2->x0+x) + (box2->y0+y) * pp->x] |= 4;
|
256
|
+
pp->p[(box2->x1-x) + (box2->y0+y) * pp->x] |= 4;
|
257
|
+
}
|
258
|
+
} end_for_each(&(job->res.boxlist));
|
259
|
+
|
260
|
+
if( opt & 4 )
|
261
|
+
{
|
262
|
+
struct tlines *lines = &job->res.lines;
|
263
|
+
int yr;
|
264
|
+
if (job->cfg.verbose)
|
265
|
+
fprintf(stderr, "# mark lines for %s.ppm\n", fname);
|
266
|
+
/* or read from outside??? */
|
267
|
+
for (i = 0; i < lines->num; i++) { /* mark lines by 0x08 = blue */
|
268
|
+
dx = lines->x1[i] - lines->x0[i] + 1;
|
269
|
+
for (j = -1; j < dx+1; j++) {
|
270
|
+
x = lines->x0[i] + j;
|
271
|
+
if (x<0 || x>=pp->x) continue;
|
272
|
+
for (y=lines->m1[i];y<=lines->m4[i];y++) {
|
273
|
+
/* box arround m2-m3 */
|
274
|
+
if (y>=lines->m2[i] && y<=lines->m3[i] && j>-1 && j<dx) continue;
|
275
|
+
yr = y; /* y.rotated */
|
276
|
+
if (lines->dx) yr += lines->dy * x / (lines->dx);
|
277
|
+
if (yr<0 || yr>=pp->y) continue;
|
278
|
+
np = &(pp->p[x + (pp->x)*yr]);
|
279
|
+
if (*np<160) continue; /* do not touch dark pixels */
|
280
|
+
if ((*np&6)!=0) continue; /* only change white pixels */
|
281
|
+
put(pp, x, yr, 255, 6); /* UPN: 255 and 6 or */
|
282
|
+
}
|
283
|
+
}
|
284
|
+
}
|
285
|
+
}
|
286
|
+
|
287
|
+
if (job->cfg.verbose&1)
|
288
|
+
fprintf(stderr,"# writing %s[.png] xy= %d %d\n", fname, pp->x, pp->y);
|
289
|
+
writeppm(fname, pp);
|
290
|
+
return 0;
|
291
|
+
}
|