Browse code

Add jpgscancropmargin.pike

Dario Rodriguez authored on 28/12/2020 19:10:54
Showing 1 changed files
1 1
new file mode 100755
... ...
@@ -0,0 +1,177 @@
1
+#!/usr/bin/pike
2
+/*
3
+ * cropzoomer.pike
4
+ *
5
+ *  Utility to zoom a little on scanned pages (req. RGB page)
6
+ *
7
+ * Author: Dario Rodriguez dario@softhome.net
8
+ * The program is licensed under the terms of the MIT/X license.
9
+ */
10
+
11
+int bgthreshold=64; // from 0 to 255
12
+int amplitude=15; // from 1 to 255
13
+
14
+int darkenfactor=2; // from 1 to 255
15
+
16
+int
17
+main(int argc, array(string) argv)
18
+{
19
+        Image.Image in,out,inhsv,indistance;
20
+        string infile,outfile;
21
+        if(argc!=3 || argv[argc-1]=="--help") {
22
+                write("Syntax: "+argv[0]+" infile.jpg outfile.png\n");
23
+                return(1);
24
+        }
25
+        write(argv[1]+" -> "+argv[2]+"\n");
26
+        write("* Generating intermediate representations...");
27
+        infile=argv[1];
28
+        outfile=argv[2];
29
+        in=Image.load(infile);
30
+        inhsv=in->copy()->rgb_to_hsv();
31
+        indistance=in->copy()->distancesq(255,255,255);
32
+        out=Image.Image(in.xsize(),in.ysize());
33
+        // Guess the most used bg color
34
+        write("done.\n* Guessing most used bg hue...");
35
+        array(int) hues=allocate(256);
36
+        int x,y;
37
+        int h,n,selh,seln,j;
38
+        int c;
39
+        for(y=0;y<in.ysize();y++) {
40
+                for(x=0;x<in.xsize();x++) {
41
+                        c=indistance.getpixel(x,y)[0];
42
+                        if(c<bgthreshold) {
43
+                                // possible bg
44
+                                c=inhsv.getpixel(x,y)[0];
45
+                                hues[c]++;                              
46
+                        }
47
+                }
48
+                if(!(y%1000))
49
+                        write(".");
50
+        }
51
+        for(selh=0,seln=0,h=0;h<256;h++) {
52
+                for(n=0,j=-amplitude;j<amplitude;j++) {
53
+                        n+=hues[(h+256+j)%256];
54
+                }
55
+                if(n>seln) {
56
+                        seln=n;
57
+                        selh=h;
58
+                }
59
+        }
60
+        write(""+selh);
61
+        // Getting image statistics
62
+        write(".\n* Getting image statistics...");
63
+        int d;
64
+        array(int) ycoverage=allocate(in.ysize());
65
+        for(y=0;y<in.ysize();y++)
66
+                ycoverage[y]=0;
67
+        array(int) xcoverage=allocate(in.xsize());
68
+        for(x=0;x<in.xsize();x++)
69
+                xcoverage[x]=0;
70
+        for(y=0;y<in.ysize();y++) {
71
+                for(x=0;x<in.xsize();x++) {
72
+                        c=indistance.getpixel(x,y)[0];
73
+                        h=inhsv.getpixel(x,y)[0];
74
+                        // calc. hue distance to bg hue wrapping on the color wheel
75
+                        if(h<selh) {
76
+                                if((selh-h)<(h+256-selh))
77
+                                        d=selh-h;
78
+                                else
79
+                                        d=h+256-selh;
80
+                        } else {
81
+                                if((h-selh)<(selh+256-h))
82
+                                        d=h-selh;
83
+                                else
84
+                                        d=selh+256-h;
85
+                        }
86
+                        // heuristics
87
+                        if(!(c<bgthreshold && d<bgthreshold)) {
88
+                                ycoverage[y]++;
89
+                                xcoverage[x]++;
90
+                        }
91
+                }
92
+                if(!(y%1000))
93
+                        write(".");
94
+        }
95
+        // Looking for largest white zone
96
+        write(".\n* Looking for white zone from the center...");
97
+        // average numbers to a 40th of the length
98
+        int pos;
99
+        int i;
100
+        int yblock=(int) in.ysize()/40;
101
+        int xblock=(int) in.xsize()/40;
102
+        if(yblock>=2) {
103
+                array(int) last=allocate(yblock);
104
+                for(pos=0;pos<yblock;pos++)
105
+                        last[pos]=ycoverage[pos];
106
+                for(y=0,pos=0;y<in.ysize();y++,pos++,pos%=yblock) {
107
+                        last[pos]=ycoverage[y];
108
+                        for(c=0,i=0;i<yblock;i++)
109
+                                c+=last[i];
110
+                        ycoverage[y]=(int) (c/yblock);
111
+                }
112
+        }
113
+        if(xblock>=2) {
114
+                array(int) last=allocate(xblock);
115
+                for(pos=0;pos<xblock;pos++)
116
+                        last[pos]=xcoverage[pos];
117
+                for(x=0,pos=0;x<in.xsize();x++,pos++,pos%=xblock) {
118
+                        last[pos]=xcoverage[x];
119
+                        for(c=0,i=0;i<xblock;i++)
120
+                                c+=last[i];
121
+                        xcoverage[x]=(int) (c/xblock);
122
+                }
123
+        }
124
+        // get ymin/ymax and xmin/xmax
125
+        int ymax=0,ymin=in.xsize();
126
+        for(y=0;y<sizeof(ycoverage);y++) {
127
+                if(ycoverage[y]<ymin)
128
+                        ymin=ycoverage[y];
129
+                if(ycoverage[y]>ymax)
130
+                        ymax=ycoverage[y];
131
+        }
132
+        int xmax=0,xmin=in.xsize();
133
+        for(x=0;x<sizeof(xcoverage);x++) {
134
+                if(xcoverage[x]<xmin)
135
+                        xmin=xcoverage[x];
136
+                if(xcoverage[x]>xmax)
137
+                        xmax=xcoverage[x];
138
+        }
139
+        int xdiff=(xmax-xmin);
140
+        int ydiff=(ymax-ymin);
141
+        int xminthreshold=xmin+xdiff/50;
142
+        int yminthreshold=ymin+ydiff/50;
143
+        // look for first dip under yminthreshold
144
+        int y0,y1,x0,x1;
145
+        for(y=in.ysize()/4;y>0;y--) {
146
+                if(ycoverage[y]<yminthreshold)
147
+                        break;
148
+        }
149
+        y0=y-2*yblock;
150
+        y0=(y0<0)?0:y0;
151
+        for(y=in.ysize()*3/4;y<(in.ysize()-1);y++) {
152
+                if(ycoverage[y]<yminthreshold)
153
+                        break;
154
+        }
155
+        y1=y+2*yblock;
156
+        y1=(y1>=in.ysize())?(in.ysize()-1):y1;
157
+        // look for first dip under xminthreshold
158
+        for(x=in.xsize()/4;x>0;x--) {
159
+                if(xcoverage[x]<xminthreshold)
160
+                        break;
161
+        }
162
+        x0=x-2*xblock;
163
+        x0=(x0<0)?0:x0;
164
+        for(x=in.xsize()*3/4;x<(in.xsize()-1);x++) {
165
+                if(xcoverage[x]<xminthreshold)
166
+                        break;
167
+        }
168
+        x1=x+2*xblock;
169
+        x1=(x1>=in.xsize())?(in.xsize()-1):x1;
170
+        // Crop
171
+        write(".\n* Cropping from "+out.xsize()+"x"+out.ysize()+" rectangle ("+x0+","+y0+")-("+x1+","+y1+")...");
172
+        out=in->copy(x0,y0,x1,y1,255,255,255);
173
+        // Write result
174
+        write("done.\n* Writing result to disk...");
175
+        Stdio.write_file(outfile,Image.PNG.encode(out));
176
+        write("done.\n* Process finished.\n");
177
+}