| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639 |
- /*Copyright (C) 2008-2009 Timothy B. Terriberry (tterribe@xiph.org)
- You can redistribute this library and/or modify it under the terms of the
- GNU Lesser General Public License as published by the Free Software
- Foundation; either version 2.1 of the License, or (at your option) any later
- version.*/
- //#include <stdlib.h>
- //#include <math.h>
- //#include <string.h>
- #include "util.h"
- #include "image.h"
- #include "binarize.h"
- #if 0
- /*Binarization based on~\cite{GPP06}.
- @ARTICLE{GPP06,
- author="Basilios Gatos and Ioannis E. Pratikakis and Stavros J. Perantonis",
- title="Adaptive Degraded Document Image Binarization",
- journal="Pattern Recognition",
- volume=39,
- number=3,
- pages="317-327",
- month=Mar,
- year=2006
- }*/
- #if 0
- /*Applies a 5x5 Wiener filter to the image, in-place, emphasizing differences
- where the local variance is small, and de-emphasizing them where it is
- large.*/
- void qr_wiener_filter(unsigned char *_img,int _width,int _height){
- unsigned *m_buf[8];
- unsigned *sn2_buf[8];
- unsigned char g;
- int x;
- int y;
- if(_width<=0||_height<=0)return;
- m_buf[0]=(unsigned *)malloc((_width+4<<3)*sizeof(*m_buf));
- sn2_buf[0]=(unsigned *)malloc((_width+4<<3)*sizeof(*sn2_buf));
- for(y=1;y<8;y++){
- m_buf[y]=m_buf[y-1]+_width+4;
- sn2_buf[y]=sn2_buf[y-1]+_width+4;
- }
- for(y=-4;y<_height;y++){
- unsigned *pm;
- unsigned *psn2;
- int i;
- int j;
- pm=m_buf[y+2&7];
- psn2=sn2_buf[y+2&7];
- for(x=-4;x<_width;x++){
- unsigned m;
- unsigned m2;
- m=m2=0;
- if(y>=0&&y<_height-4&&x>=0&&x<_width-4)for(i=0;i<5;i++)for(j=0;j<5;j++){
- g=_img[(y+i)*_width+x+j];
- m+=g;
- m2+=g*g;
- }
- else for(i=0;i<5;i++)for(j=0;j<5;j++){
- g=_img[QR_CLAMPI(0,y+i,_height-1)*_width+QR_CLAMPI(0,x+j,_width-1)];
- m+=g;
- m2+=g*g;
- }
- pm[x+4]=m;
- psn2[x+4]=(m2*25-m*m);
- }
- pm=m_buf[y&7];
- if(y>=0)for(x=0;x<_width;x++){
- int sn2;
- sn2=sn2_buf[y&7][x+2];
- if(sn2){
- int vn3;
- int m;
- /*Gatos et al. give the expression
- mu+(s2-v2)*(g-mu)/s2 ,
- which we reduce to
- mu+(s2-v2)*g/s2-(s2-v2)*mu/s2 ,
- g-(v2/s2)*g+(v2/s2)*mu ,
- g+(mu-g)*(v2/s2) .
- However, s2 is much noisier than v2, and dividing by it often gives
- extremely large adjustments, causing speckle near edges.
- Therefore we limit the ratio (v2/s2) to lie between 0 and 1.*/
- vn3=0;
- for(i=-2;i<3;i++){
- psn2=sn2_buf[y+i&7];
- for(j=0;j<5;j++)vn3+=psn2[x+j];
- }
- m=m_buf[y&7][x+2];
- vn3=vn3+1023>>10;
- sn2=25*sn2+1023>>10;
- if(vn3<sn2){
- int a;
- g=_img[y*_width+x];
- a=(m-25*g)*vn3;
- sn2*=25;
- _img[y*_width+x]=QR_CLAMP255(g+QR_DIVROUND(a,sn2));
- }
- else _img[y*_width+x]=(unsigned char)(((m<<1)+25)/50);
- }
- }
- }
- free(sn2_buf[0]);
- free(m_buf[0]);
- }
- #else
- /*Applies a 3x3 Wiener filter to the image, in-place, emphasizing differences
- where the local variance is small, and de-emphasizing them where it is
- large.*/
- void qr_wiener_filter(unsigned char *_img,int _width,int _height){
- unsigned *m_buf[4];
- unsigned *sn2_buf[4];
- unsigned char g;
- int x;
- int y;
- if(_width<=0||_height<=0)return;
- m_buf[0]=(unsigned *)malloc((_width+2<<2)*sizeof(*m_buf));
- sn2_buf[0]=(unsigned *)malloc((_width+2<<2)*sizeof(*sn2_buf));
- for(y=1;y<4;y++){
- m_buf[y]=m_buf[y-1]+_width+2;
- sn2_buf[y]=sn2_buf[y-1]+_width+2;
- }
- for(y=-2;y<_height;y++){
- unsigned *pm;
- unsigned *psn2;
- int i;
- int j;
- pm=m_buf[y+1&3];
- psn2=sn2_buf[y+1&3];
- for(x=-2;x<_width;x++){
- unsigned m;
- unsigned m2;
- m=m2=0;
- if(y>=0&&y<_height-2&&x>=0&&x<_width-2)for(i=0;i<3;i++)for(j=0;j<3;j++){
- g=_img[(y+i)*_width+x+j];
- m+=g;
- m2+=g*g;
- }
- else for(i=0;i<3;i++)for(j=0;j<3;j++){
- g=_img[QR_CLAMPI(0,y+i,_height-1)*_width+QR_CLAMPI(0,x+j,_width-1)];
- m+=g;
- m2+=g*g;
- }
- pm[x+2]=m;
- psn2[x+2]=(m2*9-m*m);
- }
- pm=m_buf[y&3];
- if(y>=0)for(x=0;x<_width;x++){
- int sn2;
- sn2=sn2_buf[y&3][x+1];
- if(sn2){
- int m;
- int vn3;
- /*Gatos et al. give the expression
- mu+(s2-v2)*(g-mu)/s2 ,
- which we reduce to
- mu+(s2-v2)*g/s2-(s2-v2)*mu/s2 ,
- g-(v2/s2)*g+(v2/s2)*mu ,
- g+(mu-g)*(v2/s2) .
- However, s2 is much noisier than v2, and dividing by it often gives
- extremely large adjustments, causing speckle near edges.
- Therefore we limit the ratio (v2/s2) to lie between 0 and 1.*/
- vn3=0;
- for(i=-1;i<2;i++){
- psn2=sn2_buf[y+i&3];
- for(j=0;j<3;j++)vn3+=psn2[x+j];
- }
- m=m_buf[y&3][x+1];
- vn3=vn3+31>>5;
- sn2=9*sn2+31>>5;
- if(vn3<sn2){
- int a;
- g=_img[y*_width+x];
- a=m-9*g;
- sn2*=9;
- _img[y*_width+x]=QR_CLAMP255(g+QR_DIVROUND(a,sn2));
- }
- else _img[y*_width+x]=(unsigned char)(((m<<1)+9)/18);
- }
- }
- }
- free(sn2_buf[0]);
- free(m_buf[0]);
- }
- #endif
- /*Computes a (conservative) foreground mask using the adaptive binarization
- threshold given in~\cite{SP00}, but knocking the threshold parameter down to
- k=0.2.
- Note on dynamic range: we assume _width*_height<=0x1000000 (24 bits).
- Returns the average background value.
- @ARTICLE{SP00,
- author="Jaakko J. Sauvola and Matti Pietik\"{a}inen",
- title="Adaptive Document Image Binarization",
- volume=33,
- number=2,
- pages="225--236",
- month=Feb,
- year=2000
- }*/
- static void qr_sauvola_mask(unsigned char *_mask,unsigned *_b,int *_nb,
- const unsigned char *_img,int _width,int _height){
- unsigned b;
- int nb;
- b=0;
- nb=0;
- if(_width>0&&_height>0){
- unsigned *col_sums;
- unsigned *col2_sums;
- int logwindw;
- int logwindh;
- int windw;
- int windh;
- int y0offs;
- int y1offs;
- unsigned g;
- unsigned g2;
- int x;
- int y;
- /*We keep the window size fairly large to ensure it doesn't fit completely
- inside the center of a finder pattern of a version 1 QR code at full
- resolution.*/
- for(logwindw=4;logwindw<8&&(1<<logwindw)<(_width+7>>3);logwindw++);
- for(logwindh=4;logwindh<8&&(1<<logwindh)<(_height+7>>3);logwindh++);
- windw=1<<logwindw;
- windh=1<<logwindh;
- col_sums=(unsigned *)malloc(_width*sizeof(*col_sums));
- col2_sums=(unsigned *)malloc(_width*sizeof(*col2_sums));
- /*Initialize sums down each column.*/
- for(x=0;x<_width;x++){
- g=_img[x];
- g2=g*g;
- col_sums[x]=(g<<logwindh-1)+g;
- col2_sums[x]=(g2<<logwindh-1)+g2;
- }
- for(y=1;y<(windh>>1);y++){
- y1offs=QR_MINI(y,_height-1)*_width;
- for(x=0;x<_width;x++){
- g=_img[y1offs+x];
- col_sums[x]+=g;
- col2_sums[x]+=g*g;
- }
- }
- for(y=0;y<_height;y++){
- unsigned m;
- unsigned m2;
- int x0;
- int x1;
- /*Initialize the sums over the window.*/
- m=(col_sums[0]<<logwindw-1)+col_sums[0];
- m2=(col2_sums[0]<<logwindw-1)+col2_sums[0];
- for(x=1;x<(windw>>1);x++){
- x1=QR_MINI(x,_width-1);
- m+=col_sums[x1];
- m2+=col2_sums[x1];
- }
- for(x=0;x<_width;x++){
- int d;
- /*Perform the test against the threshold T = (m/n)*(1+k*(s/R-1)),
- where n=windw*windh, s=sqrt((m2-(m*m)/n)/n), and R=128.
- We don't actually compute the threshold directly, as that would
- require a square root.
- Instead we perform the equivalent test:
- (m/n)*(m/n)*(m2/n-(m/n)*(m/n))/16 > (((1/k)*g-((1-k)/k)*(m/n))*32)**2
- R is split up across each side of the inequality to maximize the
- dynamic range available for the right hand side, which requires
- 31 bits in the worst case.*/
- /*(m/n)*(1+(1/5)*(sqrt((m2-m*m/n)/n)/128-1)) > g
- m*(1+(1/5)*(sqrt((m2-m*m/n)/n)/128-1)) > g*n
- m*sqrt((m2-m*m/n)/n) > 5*g*n-4*m<<7
- m*m*(m2*n-m*m) > (5*g*n-4*m<<7)**2*n*n || 5*g*n-4*m < 0 */
- g=_img[y*_width+x];
- d=(5*g<<logwindw+logwindh)-4*m;
- if(d>=0){
- unsigned mm;
- unsigned mms2;
- unsigned d2;
- mm=(m>>logwindw)*(m>>logwindh);
- mms2=(m2-mm>>logwindw+logwindh)*(mm>>logwindw+logwindh)+15>>4;
- d2=d>>logwindw+logwindh-5;
- d2*=d2;
- if(d2>=mms2){
- /*Update the background average.*/
- b+=g;
- nb++;
- _mask[y*_width+x]=0;
- }
- else _mask[y*_width+x]=0xFF;
- }
- else _mask[y*_width+x]=0xFF;
- /*Update the window sums.*/
- if(x+1<_width){
- x0=QR_MAXI(0,x-(windw>>1));
- x1=QR_MINI(x+(windw>>1),_width-1);
- m+=col_sums[x1]-col_sums[x0];
- m2+=col2_sums[x1]-col2_sums[x0];
- }
- }
- /*Update the column sums.*/
- if(y+1<_height){
- y0offs=QR_MAXI(0,y-(windh>>1))*_width;
- y1offs=QR_MINI(y+(windh>>1),_height-1)*_width;
- for(x=0;x<_width;x++){
- g=_img[y0offs+x];
- col_sums[x]-=g;
- col2_sums[x]-=g*g;
- g=_img[y1offs+x];
- col_sums[x]+=g;
- col2_sums[x]+=g*g;
- }
- }
- }
- free(col2_sums);
- free(col_sums);
- }
- *_b=b;
- *_nb=nb;
- }
- /*Interpolates a background image given the source and a conservative
- foreground mask.
- If the current window contains no foreground pixels, the average background
- value over the whole image is used.
- Note on dynamic range: we assume _width*_height<=0x8000000 (23 bits).
- Returns the average difference between the foreground and the interpolated
- background.*/
- static void qr_interpolate_background(unsigned char *_dst,
- int *_delta,int *_ndelta,const unsigned char *_img,const unsigned char *_mask,
- int _width,int _height,unsigned _b,int _nb){
- int delta;
- int ndelta;
- delta=ndelta=0;
- if(_width>0&&_height>0){
- unsigned *col_sums;
- unsigned *ncol_sums;
- int logwindw;
- int logwindh;
- int windw;
- int windh;
- int y0offs;
- int y1offs;
- unsigned b;
- unsigned g;
- int x;
- int y;
- b=_nb>0?((_b<<1)+_nb)/(_nb<<1):0xFF;
- for(logwindw=4;logwindw<8&&(1<<logwindw)<(_width+15>>4);logwindw++);
- for(logwindh=4;logwindh<8&&(1<<logwindh)<(_height+15>>4);logwindh++);
- windw=1<<logwindw;
- windh=1<<logwindh;
- col_sums=(unsigned *)malloc(_width*sizeof(*col_sums));
- ncol_sums=(unsigned *)malloc(_width*sizeof(*ncol_sums));
- /*Initialize sums down each column.*/
- for(x=0;x<_width;x++){
- if(!_mask[x]){
- g=_img[x];
- col_sums[x]=(g<<logwindh-1)+g;
- ncol_sums[x]=(1<<logwindh-1)+1;
- }
- else col_sums[x]=ncol_sums[x]=0;
- }
- for(y=1;y<(windh>>1);y++){
- y1offs=QR_MINI(y,_height-1)*_width;
- for(x=0;x<_width;x++)if(!_mask[y1offs+x]){
- col_sums[x]+=_img[y1offs+x];
- ncol_sums[x]++;
- }
- }
- for(y=0;y<_height;y++){
- unsigned n;
- unsigned m;
- int x0;
- int x1;
- /*Initialize the sums over the window.*/
- m=(col_sums[0]<<logwindw-1)+col_sums[0];
- n=(ncol_sums[0]<<logwindw-1)+ncol_sums[0];
- for(x=1;x<(windw>>1);x++){
- x1=QR_MINI(x,_width-1);
- m+=col_sums[x1];
- n+=ncol_sums[x1];
- }
- for(x=0;x<_width;x++){
- if(!_mask[y*_width+x])g=_img[y*_width+x];
- else{
- g=n>0?((m<<1)+n)/(n<<1):b;
- delta+=(int)g-_img[y*_width+x];
- ndelta++;
- }
- _dst[y*_width+x]=(unsigned char)g;
- /*Update the window sums.*/
- if(x+1<_width){
- x0=QR_MAXI(0,x-(windw>>1));
- x1=QR_MINI(x+(windw>>1),_width-1);
- m+=col_sums[x1]-col_sums[x0];
- n+=ncol_sums[x1]-ncol_sums[x0];
- }
- }
- /*Update the column sums.*/
- if(y+1<_height){
- y0offs=QR_MAXI(0,y-(windh>>1))*_width;
- y1offs=QR_MINI(y+(windh>>1),_height-1)*_width;
- for(x=0;x<_width;x++){
- if(!_mask[y0offs+x]){
- col_sums[x]-=_img[y0offs+x];
- ncol_sums[x]--;
- }
- if(!_mask[y1offs+x]){
- col_sums[x]+=_img[y1offs+x];
- ncol_sums[x]++;
- }
- }
- }
- }
- free(ncol_sums);
- free(col_sums);
- }
- *_delta=delta;
- *_ndelta=ndelta;
- }
- /*Parameters of the logistic sigmoid function that defines the threshold based
- on the background intensity.
- They should all be between 0 and 1.*/
- #define QR_GATOS_Q (0.7)
- #define QR_GATOS_P1 (0.5)
- #define QR_GATOS_P2 (0.8)
- /*Compute the final binarization mask according to Gatos et al.'s
- method~\cite{GPP06}.*/
- static void qr_gatos_mask(unsigned char *_mask,const unsigned char *_img,
- const unsigned char *_background,int _width,int _height,
- unsigned _b,int _nb,int _delta,int _ndelta){
- unsigned thresh[256];
- unsigned g;
- double delta;
- double b;
- int x;
- int y;
- /*Construct a lookup table for the thresholds.
- This bit uses floating point, but doesn't need to do much calculation, so
- emulation should be fine.*/
- b=_nb>0?(_b+0.5)/_nb:0xFF;
- delta=_ndelta>0?(_delta+0.5)/_ndelta:0xFF;
- for(g=0;g<256;g++){
- double d;
- d=QR_GATOS_Q*delta*(QR_GATOS_P2+(1-QR_GATOS_P2)/
- (1+exp(2*(1+QR_GATOS_P1)/(1-QR_GATOS_P1)-4*g/(b*(1-QR_GATOS_P1)))));
- if(d<1)d=1;
- else if(d>0xFF)d=0xFF;
- thresh[g]=(unsigned)floor(d);
- }
- /*Apply the adaptive threshold.*/
- for(y=0;y<_height;y++)for(x=0;x<_width;x++){
- g=_background[y*_width+x];
- /*_background[y*_width+x]=thresh[g];*/
- _mask[y*_width+x]=(unsigned char)(-(g-_img[y*_width+x]>thresh[g])&0xFF);
- }
- /*{
- FILE *fout;
- fout=fopen("thresh.png","wb");
- image_write_png(_background,_width,_height,fout);
- fclose(fout);
- }*/
- }
- /*Binarizes a grayscale image.*/
- void qr_binarize(unsigned char *_img,int _width,int _height){
- unsigned char *mask;
- unsigned char *background;
- unsigned b;
- int nb;
- int delta;
- int ndelta;
- /*qr_wiener_filter(_img,_width,_height);
- {
- FILE *fout;
- fout=fopen("wiener.png","wb");
- image_write_png(_img,_width,_height,fout);
- fclose(fout);
- }*/
- mask=(unsigned char *)malloc(_width*_height*sizeof(*mask));
- qr_sauvola_mask(mask,&b,&nb,_img,_width,_height);
- /*{
- FILE *fout;
- fout=fopen("foreground.png","wb");
- image_write_png(mask,_width,_height,fout);
- fclose(fout);
- }*/
- background=(unsigned char *)malloc(_width*_height*sizeof(*mask));
- qr_interpolate_background(background,&delta,&ndelta,
- _img,mask,_width,_height,b,nb);
- /*{
- FILE *fout;
- fout=fopen("background.png","wb");
- image_write_png(background,_width,_height,fout);
- fclose(fout);
- }*/
- qr_gatos_mask(_img,_img,background,_width,_height,b,nb,delta,ndelta);
- free(background);
- free(mask);
- }
- #else
- /*The above algorithms are computationally expensive, and do not work as well
- as the simple algorithm below.
- Sauvola by itself does an excellent job of classifying regions outside the
- QR code as background, which greatly reduces the chance of false alarms.
- However, it also tends to over-shrink isolated black dots inside the code,
- making them easy to miss with even slight mis-alignment.
- Since the Gatos method uses Sauvola as input to its background interpolation
- method, it cannot possibly mark any pixels as foreground which Sauvola
- classified as background, and thus suffers from the same problem.
- The following simple adaptive threshold method does not have this problem,
- though it produces essentially random noise outside the QR code region.
- QR codes are structured well enough that this does not seem to lead to any
- actual false alarms in practice, and it allows many more codes to be
- detected and decoded successfully than the Sauvola or Gatos binarization
- methods.*/
- /*A simplified adaptive thresholder.
- This compares the current pixel value to the mean value of a (large) window
- surrounding it.*/
- unsigned char *qr_binarize(const unsigned char *_img,int _width,int _height){
- unsigned char *mask = NULL;
- if(_width>0&&_height>0){
- unsigned *col_sums;
- int logwindw;
- int logwindh;
- int windw;
- int windh;
- int y0offs;
- int y1offs;
- unsigned g;
- int x;
- int y;
- mask=(unsigned char *)malloc(_width*_height*sizeof(*mask));
- /*We keep the window size fairly large to ensure it doesn't fit completely
- inside the center of a finder pattern of a version 1 QR code at full
- resolution.*/
- for(logwindw=4;logwindw<8&&(1<<logwindw)<(_width+7>>3);logwindw++);
- for(logwindh=4;logwindh<8&&(1<<logwindh)<(_height+7>>3);logwindh++);
- windw=1<<logwindw;
- windh=1<<logwindh;
- col_sums=(unsigned *)malloc(_width*sizeof(*col_sums));
- /*Initialize sums down each column.*/
- for(x=0;x<_width;x++){
- g=_img[x];
- col_sums[x]=(g<<logwindh-1)+g;
- }
- for(y=1;y<(windh>>1);y++){
- y1offs=QR_MINI(y,_height-1)*_width;
- for(x=0;x<_width;x++){
- g=_img[y1offs+x];
- col_sums[x]+=g;
- }
- }
- for(y=0;y<_height;y++){
- unsigned m;
- int x0;
- int x1;
- /*Initialize the sum over the window.*/
- m=(col_sums[0]<<logwindw-1)+col_sums[0];
- for(x=1;x<(windw>>1);x++){
- x1=QR_MINI(x,_width-1);
- m+=col_sums[x1];
- }
- for(x=0;x<_width;x++){
- /*Perform the test against the threshold T = (m/n)-D,
- where n=windw*windh and D=3.*/
- g=_img[y*_width+x];
- mask[y*_width+x]=-(g+3<<logwindw+logwindh<m)&0xFF;
- /*Update the window sum.*/
- if(x+1<_width){
- x0=QR_MAXI(0,x-(windw>>1));
- x1=QR_MINI(x+(windw>>1),_width-1);
- m+=col_sums[x1]-col_sums[x0];
- }
- }
- /*Update the column sums.*/
- if(y+1<_height){
- y0offs=QR_MAXI(0,y-(windh>>1))*_width;
- y1offs=QR_MINI(y+(windh>>1),_height-1)*_width;
- for(x=0;x<_width;x++){
- col_sums[x]-=_img[y0offs+x];
- col_sums[x]+=_img[y1offs+x];
- }
- }
- }
- free(col_sums);
- }
- #if defined(QR_DEBUG)
- {
- FILE *fout;
- fout=fopen("binary.png","wb");
- image_write_png(_img,_width,_height,fout);
- fclose(fout);
- }
- #endif
- return(mask);
- }
- #endif
- #if defined(TEST_BINARIZE)
- #include <stdio.h>
- #include "image.c"
- int main(int _argc,char **_argv){
- unsigned char *img;
- int width;
- int height;
- int x;
- int y;
- if(_argc<2){
- fprintf(stderr,"usage: %s <image>.png\n",_argv[0]);
- return EXIT_FAILURE;
- }
- /*width=1182;
- height=1181;
- img=(unsigned char *)malloc(width*height*sizeof(*img));
- for(y=0;y<height;y++)for(x=0;x<width;x++){
- img[y*width+x]=(unsigned char)(-((x&1)^(y&1))&0xFF);
- }*/
- {
- FILE *fin;
- fin=fopen(_argv[1],"rb");
- image_read_png(&img,&width,&height,fin);
- fclose(fin);
- }
- qr_binarize(img,width,height);
- /*{
- FILE *fout;
- fout=fopen("binary.png","wb");
- image_write_png(img,width,height,fout);
- fclose(fout);
- }*/
- free(img);
- return EXIT_SUCCESS;
- }
- #endif
|