1 /*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org)
2 You can redistribute this library and/or modify it under the terms of the
3 GNU Lesser General Public License as published by the Free Software
4 Foundation; either version 2.1 of the License, or (at your option) any later
5 version.*/
6 #include <stdlib.h>
7 #include <math.h>
8 #include <string.h>
9 #include "util.h"
10 #include "binarize.h"
11
12 #if 0
13 /*Binarization based on~\cite{GPP06}.
14 @ARTICLE{GPP06,
15 author="Basilios Gatos and Ioannis E. Pratikakis and Stavros J. Perantonis",
16 title="Adaptive Degraded Document Image Binarization",
17 journal="Pattern Recognition",
18 volume=39,
19 number=3,
20 pages="317-327",
21 month=Mar,
22 year=2006
23 }*/
24
25 #if 0
26 /*Applies a 5x5 Wiener filter to the image, in-place, emphasizing differences
27 where the local variance is small, and de-emphasizing them where it is
28 large.*/
29 void qr_wiener_filter(unsigned char *_img,int _width,int _height){
30 unsigned *m_buf[8];
31 unsigned *sn2_buf[8];
32 unsigned char g;
33 int x;
34 int y;
35 if(_width<=0||_height<=0)return;
36 m_buf[0]=(unsigned *)malloc((_width+4<<3)*sizeof(*m_buf));
37 sn2_buf[0]=(unsigned *)malloc((_width+4<<3)*sizeof(*sn2_buf));
38 for(y=1;y<8;y++){
39 m_buf[y]=m_buf[y-1]+_width+4;
40 sn2_buf[y]=sn2_buf[y-1]+_width+4;
41 }
42 for(y=-4;y<_height;y++){
43 unsigned *pm;
44 unsigned *psn2;
45 int i;
46 int j;
47 pm=m_buf[y+2&7];
48 psn2=sn2_buf[y+2&7];
49 for(x=-4;x<_width;x++){
50 unsigned m;
51 unsigned m2;
52 m=m2=0;
53 if(y>=0&&y<_height-4&&x>=0&&x<_width-4)for(i=0;i<5;i++)for(j=0;j<5;j++){
54 g=_img[(y+i)*_width+x+j];
55 m+=g;
56 m2+=g*g;
57 }
58 else for(i=0;i<5;i++)for(j=0;j<5;j++){
59 g=_img[QR_CLAMPI(0,y+i,_height-1)*_width+QR_CLAMPI(0,x+j,_width-1)];
60 m+=g;
61 m2+=g*g;
62 }
63 pm[x+4]=m;
64 psn2[x+4]=(m2*25-m*m);
65 }
66 pm=m_buf[y&7];
67 if(y>=0)for(x=0;x<_width;x++){
68 int sn2;
69 sn2=sn2_buf[y&7][x+2];
70 if(sn2){
71 int vn3;
72 int m;
73 /*Gatos et al. give the expression
74 mu+(s2-v2)*(g-mu)/s2 ,
75 which we reduce to
76 mu+(s2-v2)*g/s2-(s2-v2)*mu/s2 ,
77 g-(v2/s2)*g+(v2/s2)*mu ,
78 g+(mu-g)*(v2/s2) .
79 However, s2 is much noisier than v2, and dividing by it often gives
80 extremely large adjustments, causing speckle near edges.
81 Therefore we limit the ratio (v2/s2) to lie between 0 and 1.*/
82 vn3=0;
83 for(i=-2;i<3;i++){
84 psn2=sn2_buf[y+i&7];
85 for(j=0;j<5;j++)vn3+=psn2[x+j];
86 }
87 m=m_buf[y&7][x+2];
88 vn3=vn3+1023>>10;
89 sn2=25*sn2+1023>>10;
90 if(vn3<sn2){
91 int a;
92 g=_img[y*_width+x];
93 a=(m-25*g)*vn3;
94 sn2*=25;
95 _img[y*_width+x]=QR_CLAMP255(g+QR_DIVROUND(a,sn2));
96 }
97 else _img[y*_width+x]=(unsigned char)(((m<<1)+25)/50);
98 }
99 }
100 }
101 free(sn2_buf[0]);
102 free(m_buf[0]);
103 }
104
105 #else
106 /*Applies a 3x3 Wiener filter to the image, in-place, emphasizing differences
107 where the local variance is small, and de-emphasizing them where it is
108 large.*/
109 void qr_wiener_filter(unsigned char *_img,int _width,int _height){
110 unsigned *m_buf[4];
111 unsigned *sn2_buf[4];
112 unsigned char g;
113 int x;
114 int y;
115 if(_width<=0||_height<=0)return;
116 m_buf[0]=(unsigned *)malloc((_width+2<<2)*sizeof(*m_buf));
117 sn2_buf[0]=(unsigned *)malloc((_width+2<<2)*sizeof(*sn2_buf));
118 for(y=1;y<4;y++){
119 m_buf[y]=m_buf[y-1]+_width+2;
120 sn2_buf[y]=sn2_buf[y-1]+_width+2;
121 }
122 for(y=-2;y<_height;y++){
123 unsigned *pm;
124 unsigned *psn2;
125 int i;
126 int j;
127 pm=m_buf[y+1&3];
128 psn2=sn2_buf[y+1&3];
129 for(x=-2;x<_width;x++){
130 unsigned m;
131 unsigned m2;
132 m=m2=0;
133 if(y>=0&&y<_height-2&&x>=0&&x<_width-2)for(i=0;i<3;i++)for(j=0;j<3;j++){
134 g=_img[(y+i)*_width+x+j];
135 m+=g;
136 m2+=g*g;
137 }
138 else for(i=0;i<3;i++)for(j=0;j<3;j++){
139 g=_img[QR_CLAMPI(0,y+i,_height-1)*_width+QR_CLAMPI(0,x+j,_width-1)];
140 m+=g;
141 m2+=g*g;
142 }
143 pm[x+2]=m;
144 psn2[x+2]=(m2*9-m*m);
145 }
146 pm=m_buf[y&3];
147 if(y>=0)for(x=0;x<_width;x++){
148 int sn2;
149 sn2=sn2_buf[y&3][x+1];
150 if(sn2){
151 int m;
152 int vn3;
153 /*Gatos et al. give the expression
154 mu+(s2-v2)*(g-mu)/s2 ,
155 which we reduce to
156 mu+(s2-v2)*g/s2-(s2-v2)*mu/s2 ,
157 g-(v2/s2)*g+(v2/s2)*mu ,
158 g+(mu-g)*(v2/s2) .
159 However, s2 is much noisier than v2, and dividing by it often gives
160 extremely large adjustments, causing speckle near edges.
161 Therefore we limit the ratio (v2/s2) to lie between 0 and 1.*/
162 vn3=0;
163 for(i=-1;i<2;i++){
164 psn2=sn2_buf[y+i&3];
165 for(j=0;j<3;j++)vn3+=psn2[x+j];
166 }
167 m=m_buf[y&3][x+1];
168 vn3=vn3+31>>5;
169 sn2=9*sn2+31>>5;
170 if(vn3<sn2){
171 int a;
172 g=_img[y*_width+x];
173 a=m-9*g;
174 sn2*=9;
175 _img[y*_width+x]=QR_CLAMP255(g+QR_DIVROUND(a,sn2));
176 }
177 else _img[y*_width+x]=(unsigned char)(((m<<1)+9)/18);
178 }
179 }
180 }
181 free(sn2_buf[0]);
182 free(m_buf[0]);
183 }
184 #endif
185
186 /*Computes a (conservative) foreground mask using the adaptive binarization
187 threshold given in~\cite{SP00}, but knocking the threshold parameter down to
188 k=0.2.
189 Note on dynamic range: we assume _width*_height<=0x1000000 (24 bits).
190 Returns the average background value.
191 @ARTICLE{SP00,
192 author="Jaakko J. Sauvola and Matti Pietik\"{a}inen",
193 title="Adaptive Document Image Binarization",
194 volume=33,
195 number=2,
196 pages="225--236",
197 month=Feb,
198 year=2000
199 }*/
200 static void qr_sauvola_mask(unsigned char *_mask,unsigned *_b,int *_nb,
201 const unsigned char *_img,int _width,int _height){
202 unsigned b;
203 int nb;
204 b=0;
205 nb=0;
206 if(_width>0&&_height>0){
207 unsigned *col_sums;
208 unsigned *col2_sums;
209 int logwindw;
210 int logwindh;
211 int windw;
212 int windh;
213 int y0offs;
214 int y1offs;
215 unsigned g;
216 unsigned g2;
217 int x;
218 int y;
219 /*We keep the window size fairly large to ensure it doesn't fit completely
220 inside the center of a finder pattern of a version 1 QR code at full
221 resolution.*/
222 for(logwindw=4;logwindw<8&&(1<<logwindw)<(_width+7>>3);logwindw++);
223 for(logwindh=4;logwindh<8&&(1<<logwindh)<(_height+7>>3);logwindh++);
224 windw=1<<logwindw;
225 windh=1<<logwindh;
226 col_sums=(unsigned *)malloc(_width*sizeof(*col_sums));
227 col2_sums=(unsigned *)malloc(_width*sizeof(*col2_sums));
228 /*Initialize sums down each column.*/
229 for(x=0;x<_width;x++){
230 g=_img[x];
231 g2=g*g;
232 col_sums[x]=(g<<logwindh-1)+g;
233 col2_sums[x]=(g2<<logwindh-1)+g2;
234 }
235 for(y=1;y<(windh>>1);y++){
236 y1offs=QR_MINI(y,_height-1)*_width;
237 for(x=0;x<_width;x++){
238 g=_img[y1offs+x];
239 col_sums[x]+=g;
240 col2_sums[x]+=g*g;
241 }
242 }
243 for(y=0;y<_height;y++){
244 unsigned m;
245 unsigned m2;
246 int x0;
247 int x1;
248 /*Initialize the sums over the window.*/
249 m=(col_sums[0]<<logwindw-1)+col_sums[0];
250 m2=(col2_sums[0]<<logwindw-1)+col2_sums[0];
251 for(x=1;x<(windw>>1);x++){
252 x1=QR_MINI(x,_width-1);
253 m+=col_sums[x1];
254 m2+=col2_sums[x1];
255 }
256 for(x=0;x<_width;x++){
257 int d;
258 /*Perform the test against the threshold T = (m/n)*(1+k*(s/R-1)),
259 where n=windw*windh, s=sqrt((m2-(m*m)/n)/n), and R=128.
260 We don't actually compute the threshold directly, as that would
261 require a square root.
262 Instead we perform the equivalent test:
263 (m/n)*(m/n)*(m2/n-(m/n)*(m/n))/16 > (((1/k)*g-((1-k)/k)*(m/n))*32)**2
264 R is split up across each side of the inequality to maximize the
265 dynamic range available for the right hand side, which requires
266 31 bits in the worst case.*/
267 /*(m/n)*(1+(1/5)*(sqrt((m2-m*m/n)/n)/128-1)) > g
268 m*(1+(1/5)*(sqrt((m2-m*m/n)/n)/128-1)) > g*n
269 m*sqrt((m2-m*m/n)/n) > 5*g*n-4*m<<7
270 m*m*(m2*n-m*m) > (5*g*n-4*m<<7)**2*n*n || 5*g*n-4*m < 0 */
271 g=_img[y*_width+x];
272 d=(5*g<<logwindw+logwindh)-4*m;
273 if(d>=0){
274 unsigned mm;
275 unsigned mms2;
276 unsigned d2;
277 mm=(m>>logwindw)*(m>>logwindh);
278 mms2=(m2-mm>>logwindw+logwindh)*(mm>>logwindw+logwindh)+15>>4;
279 d2=d>>logwindw+logwindh-5;
280 d2*=d2;
281 if(d2>=mms2){
282 /*Update the background average.*/
283 b+=g;
284 nb++;
285 _mask[y*_width+x]=0;
286 }
287 else _mask[y*_width+x]=0xFF;
288 }
289 else _mask[y*_width+x]=0xFF;
290 /*Update the window sums.*/
291 if(x+1<_width){
292 x0=QR_MAXI(0,x-(windw>>1));
293 x1=QR_MINI(x+(windw>>1),_width-1);
294 m+=col_sums[x1]-col_sums[x0];
295 m2+=col2_sums[x1]-col2_sums[x0];
296 }
297 }
298 /*Update the column sums.*/
299 if(y+1<_height){
300 y0offs=QR_MAXI(0,y-(windh>>1))*_width;
301 y1offs=QR_MINI(y+(windh>>1),_height-1)*_width;
302 for(x=0;x<_width;x++){
303 g=_img[y0offs+x];
304 col_sums[x]-=g;
305 col2_sums[x]-=g*g;
306 g=_img[y1offs+x];
307 col_sums[x]+=g;
308 col2_sums[x]+=g*g;
309 }
310 }
311 }
312 free(col2_sums);
313 free(col_sums);
314 }
315 *_b=b;
316 *_nb=nb;
317 }
318
319 /*Interpolates a background image given the source and a conservative
320 foreground mask.
321 If the current window contains no foreground pixels, the average background
322 value over the whole image is used.
323 Note on dynamic range: we assume _width*_height<=0x8000000 (23 bits).
324 Returns the average difference between the foreground and the interpolated
325 background.*/
326 static void qr_interpolate_background(unsigned char *_dst,
327 int *_delta,int *_ndelta,const unsigned char *_img,const unsigned char *_mask,
328 int _width,int _height,unsigned _b,int _nb){
329 int delta;
330 int ndelta;
331 delta=ndelta=0;
332 if(_width>0&&_height>0){
333 unsigned *col_sums;
334 unsigned *ncol_sums;
335 int logwindw;
336 int logwindh;
337 int windw;
338 int windh;
339 int y0offs;
340 int y1offs;
341 unsigned b;
342 unsigned g;
343 int x;
344 int y;
345 b=_nb>0?((_b<<1)+_nb)/(_nb<<1):0xFF;
346 for(logwindw=4;logwindw<8&&(1<<logwindw)<(_width+15>>4);logwindw++);
347 for(logwindh=4;logwindh<8&&(1<<logwindh)<(_height+15>>4);logwindh++);
348 windw=1<<logwindw;
349 windh=1<<logwindh;
350 col_sums=(unsigned *)malloc(_width*sizeof(*col_sums));
351 ncol_sums=(unsigned *)malloc(_width*sizeof(*ncol_sums));
352 /*Initialize sums down each column.*/
353 for(x=0;x<_width;x++){
354 if(!_mask[x]){
355 g=_img[x];
356 col_sums[x]=(g<<logwindh-1)+g;
357 ncol_sums[x]=(1<<logwindh-1)+1;
358 }
359 else col_sums[x]=ncol_sums[x]=0;
360 }
361 for(y=1;y<(windh>>1);y++){
362 y1offs=QR_MINI(y,_height-1)*_width;
363 for(x=0;x<_width;x++)if(!_mask[y1offs+x]){
364 col_sums[x]+=_img[y1offs+x];
365 ncol_sums[x]++;
366 }
367 }
368 for(y=0;y<_height;y++){
369 unsigned n;
370 unsigned m;
371 int x0;
372 int x1;
373 /*Initialize the sums over the window.*/
374 m=(col_sums[0]<<logwindw-1)+col_sums[0];
375 n=(ncol_sums[0]<<logwindw-1)+ncol_sums[0];
376 for(x=1;x<(windw>>1);x++){
377 x1=QR_MINI(x,_width-1);
378 m+=col_sums[x1];
379 n+=ncol_sums[x1];
380 }
381 for(x=0;x<_width;x++){
382 if(!_mask[y*_width+x])g=_img[y*_width+x];
383 else{
384 g=n>0?((m<<1)+n)/(n<<1):b;
385 delta+=(int)g-_img[y*_width+x];
386 ndelta++;
387 }
388 _dst[y*_width+x]=(unsigned char)g;
389 /*Update the window sums.*/
390 if(x+1<_width){
391 x0=QR_MAXI(0,x-(windw>>1));
392 x1=QR_MINI(x+(windw>>1),_width-1);
393 m+=col_sums[x1]-col_sums[x0];
394 n+=ncol_sums[x1]-ncol_sums[x0];
395 }
396 }
397 /*Update the column sums.*/
398 if(y+1<_height){
399 y0offs=QR_MAXI(0,y-(windh>>1))*_width;
400 y1offs=QR_MINI(y+(windh>>1),_height-1)*_width;
401 for(x=0;x<_width;x++){
402 if(!_mask[y0offs+x]){
403 col_sums[x]-=_img[y0offs+x];
404 ncol_sums[x]--;
405 }
406 if(!_mask[y1offs+x]){
407 col_sums[x]+=_img[y1offs+x];
408 ncol_sums[x]++;
409 }
410 }
411 }
412 }
413 free(ncol_sums);
414 free(col_sums);
415 }
416 *_delta=delta;
417 *_ndelta=ndelta;
418 }
419
420 /*Parameters of the logistic sigmoid function that defines the threshold based
421 on the background intensity.
422 They should all be between 0 and 1.*/
423 #define QR_GATOS_Q (0.7)
424 #define QR_GATOS_P1 (0.5)
425 #define QR_GATOS_P2 (0.8)
426
427 /*Compute the final binarization mask according to Gatos et al.'s
428 method~\cite{GPP06}.*/
429 static void qr_gatos_mask(unsigned char *_mask,const unsigned char *_img,
430 const unsigned char *_background,int _width,int _height,
431 unsigned _b,int _nb,int _delta,int _ndelta){
432 unsigned thresh[256];
433 unsigned g;
434 double delta;
435 double b;
436 int x;
437 int y;
438 /*Construct a lookup table for the thresholds.
439 This bit uses floating point, but doesn't need to do much calculation, so
440 emulation should be fine.*/
441 b=_nb>0?(_b+0.5)/_nb:0xFF;
442 delta=_ndelta>0?(_delta+0.5)/_ndelta:0xFF;
443 for(g=0;g<256;g++){
444 double d;
445 d=QR_GATOS_Q*delta*(QR_GATOS_P2+(1-QR_GATOS_P2)/
446 (1+exp(2*(1+QR_GATOS_P1)/(1-QR_GATOS_P1)-4*g/(b*(1-QR_GATOS_P1)))));
447 if(d<1)d=1;
448 else if(d>0xFF)d=0xFF;
449 thresh[g]=(unsigned)floor(d);
450 }
451 /*Apply the adaptive threshold.*/
452 for(y=0;y<_height;y++)for(x=0;x<_width;x++){
453 g=_background[y*_width+x];
454 /*_background[y*_width+x]=thresh[g];*/
455 _mask[y*_width+x]=(unsigned char)(-(g-_img[y*_width+x]>thresh[g])&0xFF);
456 }
457 /*{
458 FILE *fout;
459 fout=fopen("thresh.png","wb");
460 image_write_png(_background,_width,_height,fout);
461 fclose(fout);
462 }*/
463 }
464
465 /*Binarizes a grayscale image.*/
466 void qr_binarize(unsigned char *_img,int _width,int _height){
467 unsigned char *mask;
468 unsigned char *background;
469 unsigned b;
470 int nb;
471 int delta;
472 int ndelta;
473 /*qr_wiener_filter(_img,_width,_height);
474 {
475 FILE *fout;
476 fout=fopen("wiener.png","wb");
477 image_write_png(_img,_width,_height,fout);
478 fclose(fout);
479 }*/
480 mask=(unsigned char *)malloc(_width*_height*sizeof(*mask));
481 qr_sauvola_mask(mask,&b,&nb,_img,_width,_height);
482 /*{
483 FILE *fout;
484 fout=fopen("foreground.png","wb");
485 image_write_png(mask,_width,_height,fout);
486 fclose(fout);
487 }*/
488 background=(unsigned char *)malloc(_width*_height*sizeof(*mask));
489 qr_interpolate_background(background,&delta,&ndelta,
490 _img,mask,_width,_height,b,nb);
491 /*{
492 FILE *fout;
493 fout=fopen("background.png","wb");
494 image_write_png(background,_width,_height,fout);
495 fclose(fout);
496 }*/
497 qr_gatos_mask(_img,_img,background,_width,_height,b,nb,delta,ndelta);
498 free(background);
499 free(mask);
500 }
501
502 #else
503 /*The above algorithms are computationally expensive, and do not work as well
504 as the simple algorithm below.
505 Sauvola by itself does an excellent job of classifying regions outside the
506 QR code as background, which greatly reduces the chance of false alarms.
507 However, it also tends to over-shrink isolated black dots inside the code,
508 making them easy to miss with even slight mis-alignment.
509 Since the Gatos method uses Sauvola as input to its background interpolation
510 method, it cannot possibly mark any pixels as foreground which Sauvola
511 classified as background, and thus suffers from the same problem.
512 The following simple adaptive threshold method does not have this problem,
513 though it produces essentially random noise outside the QR code region.
514 QR codes are structured well enough that this does not seem to lead to any
515 actual false alarms in practice, and it allows many more codes to be
516 detected and decoded successfully than the Sauvola or Gatos binarization
517 methods.*/
518
519 /*A simplified adaptive thresholder.
520 This compares the current pixel value to the mean value of a (large) window
521 surrounding it.*/
qr_binarize(const unsigned char * _img,int _width,int _height)522 unsigned char *qr_binarize(const unsigned char *_img,int _width,int _height){
523 unsigned char *mask = NULL;
524 if(_width>0&&_height>0){
525 unsigned *col_sums;
526 int logwindw;
527 int logwindh;
528 int windw;
529 int windh;
530 int y0offs;
531 int y1offs;
532 unsigned g;
533 int x;
534 int y;
535 mask=(unsigned char *)malloc(_width*_height*sizeof(*mask));
536 /*We keep the window size fairly large to ensure it doesn't fit completely
537 inside the center of a finder pattern of a version 1 QR code at full
538 resolution.*/
539 for(logwindw=4;logwindw<8&&(1<<logwindw)<(_width+7>>3);logwindw++);
540 for(logwindh=4;logwindh<8&&(1<<logwindh)<(_height+7>>3);logwindh++);
541 windw=1<<logwindw;
542 windh=1<<logwindh;
543 col_sums=(unsigned *)malloc(_width*sizeof(*col_sums));
544 /*Initialize sums down each column.*/
545 for(x=0;x<_width;x++){
546 g=_img[x];
547 col_sums[x]=(g<<logwindh-1)+g;
548 }
549 for(y=1;y<(windh>>1);y++){
550 y1offs=QR_MINI(y,_height-1)*_width;
551 for(x=0;x<_width;x++){
552 g=_img[y1offs+x];
553 col_sums[x]+=g;
554 }
555 }
556 for(y=0;y<_height;y++){
557 unsigned m;
558 int x0;
559 int x1;
560 /*Initialize the sum over the window.*/
561 m=(col_sums[0]<<logwindw-1)+col_sums[0];
562 for(x=1;x<(windw>>1);x++){
563 x1=QR_MINI(x,_width-1);
564 m+=col_sums[x1];
565 }
566 for(x=0;x<_width;x++){
567 /*Perform the test against the threshold T = (m/n)-D,
568 where n=windw*windh and D=3.*/
569 g=_img[y*_width+x];
570 mask[y*_width+x]=-(g+3<<logwindw+logwindh<m)&0xFF;
571 /*Update the window sum.*/
572 if(x+1<_width){
573 x0=QR_MAXI(0,x-(windw>>1));
574 x1=QR_MINI(x+(windw>>1),_width-1);
575 m+=col_sums[x1]-col_sums[x0];
576 }
577 }
578 /*Update the column sums.*/
579 if(y+1<_height){
580 y0offs=QR_MAXI(0,y-(windh>>1))*_width;
581 y1offs=QR_MINI(y+(windh>>1),_height-1)*_width;
582 for(x=0;x<_width;x++){
583 col_sums[x]-=_img[y0offs+x];
584 col_sums[x]+=_img[y1offs+x];
585 }
586 }
587 }
588 free(col_sums);
589 }
590 #if defined(QR_DEBUG)
591 {
592 FILE *fout;
593 fout=fopen("binary.png","wb");
594 image_write_png(_img,_width,_height,fout);
595 fclose(fout);
596 }
597 #endif
598 return(mask);
599 }
600 #endif
601
602 #if defined(TEST_BINARIZE)
603 #include <stdio.h>
604 #include "image.c"
605
main(int _argc,char ** _argv)606 int main(int _argc,char **_argv){
607 unsigned char *img;
608 int width;
609 int height;
610 int x;
611 int y;
612 if(_argc<2){
613 fprintf(stderr,"usage: %s <image>.png\n",_argv[0]);
614 return EXIT_FAILURE;
615 }
616 /*width=1182;
617 height=1181;
618 img=(unsigned char *)malloc(width*height*sizeof(*img));
619 for(y=0;y<height;y++)for(x=0;x<width;x++){
620 img[y*width+x]=(unsigned char)(-((x&1)^(y&1))&0xFF);
621 }*/
622 {
623 FILE *fin;
624 fin=fopen(_argv[1],"rb");
625 image_read_png(&img,&width,&height,fin);
626 fclose(fin);
627 }
628 qr_binarize(img,width,height);
629 /*{
630 FILE *fout;
631 fout=fopen("binary.png","wb");
632 image_write_png(img,width,height,fout);
633 fclose(fout);
634 }*/
635 free(img);
636 return EXIT_SUCCESS;
637 }
638 #endif
639