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