/* Copyright (C) 1999-2002 Ricardo Ueda Karpischek This is free software; you can redistribute it and/or modify it under the terms of the version 2 of the GNU General Public License as published by the Free Software Foundation. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this software; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* pgmblock.c: grayscale loading and blockfinding. */ #include #include #include #include #include #include #include "common.h" #include "gui.h" #ifndef PI #define PI M_PI #endif /* Pixel buffers. */ int *ll=NULL,*rl=NULL,lsz=0; int *rx,*ry,*rz,topr,rsz=0; /* Buffer to store the largest cluster. */ int *clusterize_r; /* */ int fb(char *b) { int i,r; for (i=r=0; i<8; ++i) { r = (r<<1) + ((b[i] == '1') ? 1 : 0); } return(r); } /* This is experimental stuff to try to achieve better compression rates for PGM, remapping the currently loaded PGM file before compressing. In some cases it's possible to enhance gzip compression ratio using this service. However, we could not enhance bzip2 compression ratio. To try: pgmmap(pixmap,XRES,YRES); pgmcut("test.pgm",pb,XRES,YRES,0,XRES-1,0,YRES-1,0); And from the command line: gzip -9 test.pgm gzip -9 map Now compare size(test.pgm) + size(map) with size(file.pgm.gz). See also pgmunmap(). */ void pgmmap(unsigned char *pb,int w,int h) { int i,j,bpl; unsigned char map[256]; unsigned char *m; int C[256]; bpl = ((w/8) + ((w%8)!=0)); m = c_realloc(NULL,h*bpl,NULL); memset(m,0,h*bpl); memset(C,0,sizeof(int)*256); for (j=0; j 0) { if (pb[(j-1)*w+i] >= pb[j*w+i]) { pb[(j-1)*w+i] -= pb[j*w+i]; } else { pb[(j-1)*w+i] = (pb[j*w+i] - pb[(j-1)*w+i]); pb[(j-1)*w+i] |= 128; } } } } printf("%d rare pixels (%1.4f)\n",t,((float)t)/(w*h)); F = open("map",O_WRONLY|O_CREAT); write(F,m,h*bpl); write(F,map,256); close(F); } c_free(m); } /* Unmap PGM file. This is experimental stuff to try to achieve better compression rates for PGM. See also pgmmap(). To apply this service on the currently loaded file and save it unmapped (assuming that it was previously mapped by pgmmap): pgmunmap(pixmap,XRES,YRES); pgmcut("test.pgm",pixmap,XRES,YRES,0,XRES-1,0,YRES-1,0); */ void pgmunmap(unsigned char *pb,int w,int h) { int bpl; unsigned char map[256],invmap[256]; unsigned char *m; bpl = ((w/8) + ((w%8)!=0)); m = c_realloc(NULL,h*bpl,NULL); memset(m,0,h*bpl); { int i,j,F; F = open("map",O_RDONLY|O_CREAT); read(F,m,h*bpl); read(F,map,256); close(F); for (i=0; i<256; ++i) invmap[map[i]] = i; for (j=h-1; j>0; --j) { for (i=0; i0; --j) { p = pb + j*bpl - 1; m = 1 << (w%8); for (i=0; i 128) { m = 1; --p; } } } } /* Load PGM or PBM file into *pb. */ int loadpgm(int reset,char *f,unsigned char **pb,int *w,int *h) { /* to read the file */ static FILE *F; /* levels and line counter */ static int v,n,pio,p,bpl; /* read header */ if (reset) { int b,c,rc; F = zfopen(f,"r",&pio); /* just to avoid a compilation warning */ c = 0; /* P5 magic number */ if (((b=fgetc(F)) != 'P') || (((c=fgetc(F)) != '5') && (c!='4'))) { fprintf(stderr,"%s is not a pbm or pgm raw file\n",f); if (b=='P') { if (c=='6') fprintf(stderr,"it seems to be a PPM (unsupported) file\n"); else if (c=='6') fprintf(stderr,"it seems to be a plain PPM (unsupported) file\n"); else if (c=='2') fprintf(stderr,"it seems to be a plain PGM (unsupported) file\n"); else if (c=='1') fprintf(stderr,"it seems to be a plain PBM (unsupported) file\n"); } zfclose(F,pio); exit(1); } /* type of pixmap */ pm_t = (c == '5') ? 8 : 1; /* Header reading loop. PGM defines "whitespace" as any from space, tab, CR or LF. Comments start with '#' and end on LF. */ n = rc = 0; *w = *h = v = 0; while ((((pm_t==8) && (n<6)) || ((pm_t==1) && (n<4))) && ((c=fgetc(F)) != EOF)) { /* reading comment */ if ((c == '#') || ((rc==1) && (c!='\n'))) { rc = 1; } /* non-"whitespace" */ else if ((c!=' ') && (c!='\t') && (c!='\r') && (c!='\n')) { /* invalid char */ if ((c < '0') || ('9' < c)) { fprintf(stderr,"%s is not a pgm raw file (non-digit found)",f); fclose(F); exit(1); } /* reading width */ if (n <= 1) { n = 1; *w = *w * 10 + c - '0'; } /* reading heigth */ else if (n <= 3) { n = 3; *h = *h * 10 + c - '0'; } /* reading levels */ else if (n <= 5) { n = 5; v = v * 10 + v - '0'; } } /* "whitespace" character */ else { /* stop reading width */ if (n == 1) n = 2; /* stop reading height */ else if (n == 3) n = 4; /* stop reading levels */ else if (n == 5) n = 6; /* stop reading comment */ rc = 0; } } /* bytes per line */ if (pm_t == 8) bpl = *w; else bpl = (*w/8) + ((*w%8) != 0); /* Alloc buffer large enough to store a graymap, even for PBM files. */ *pb = c_realloc(*pb,*h * *w,NULL); if (*pb == NULL) { fprintf(stderr,"memory exhausted\n"); exit(1); } /* prepare data reading loop */ p = 0; /* reset distribution of grayshades */ memset(graydist,0,256*sizeof(int)); /* return unfinished code */ return(1); } /* Continue reading page. We read only 512k in order to refresh the display. As some grayscale files may be quite large (and, if compressed, the loading time may be 10-20 seconds), we need to inform the user about the progress. */ else { int r,d,s,t; /* pixmap size */ t = *h * bpl; /* number of bytes to read and return status */ d = (t - p); if (d > (512*1024)) { d = (512*1024); s = 1; } else s = 0; /* read d bytes */ r = fread((*pb)+p,1,d,F); if (r != d) fatal(IO,"reading %s",f); p += d; /* update distribution of grayshades */ { int i; unsigned char *q; for (q=(*pb)+p, i=0; i= esz) e = c_realloc(e,(esz=tope+de)*sizeof(int),NULL); e[tope] = j; } } } /* sentinel */ fe[N] = (tope + 1); /* array of marks */ msz = (N/32) + ((N%32) != 0); m = alloca(msz*sizeof(int)); m2 = alloca(msz*sizeof(int)); m3 = alloca(msz*sizeof(int)); if ((m==NULL) || (m2==NULL) || (m3==NULL)) fatal(ME,"at clusterize"); memset(m,0,msz*sizeof(int)); /* masks */ for (i=0; i<32; ++i) mask[i] = ((unsigned int) 1) << i; /* stack */ sp = -1; if (stsz < N) st = c_realloc(st,(stsz=N)*sizeof(int),NULL); /* Search the largest component of the graph. */ for (lcs=0, i=0; i= 0) { /* pop */ k = st[sp--]; /* search neighbours smaller than k */ for (l=0, p=0; l lcs) { lcs = n; lcf = f; memcpy(m3,m2,msz*sizeof(int)); } } /* list the largest component */ for (i=0, n=0; i 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 The distance is defined as the square of the euclidean distance. Using threshold 2, the largest cluster size must be 33. Using threshold 1, the largest cluster size must be 27. */ int test_dist_1(int i,int j) { int x[50] = { 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7, 8, 9,10,11,12,12,12,13,13,13,14,14,15,15,15,15,15,16,16,16,16, 16,17,17,17,18,21,22,22,23,23}; int y[50] = { 7, 7, 6, 6, 5, 2, 6, 4, 3, 2, 1, 4, 3, 2, 1, 4, 1, 4, 1, 1, 1, 1, 1, 6, 5, 1, 6, 5, 1, 6, 1, 7, 6, 3, 2, 1, 7, 6, 3, 2, 1, 7, 6, 3, 6, 5, 5, 4, 5, 4}; int u,v; if ((i < 0) || (50 <= i) || (j < 0) || (50 <= j)) fatal(DI,"invalid call to test_dist"); u = x[i] - x[j]; v = y[i] - y[j]; return(u*u+v*v); } /* Distance function for the tests 3 and 4 of service clusterize(). */ int test_dist_2(int i,int j) { if ((i < 0) || (500 <= i) || (j < 0) || (500 <= j)) fatal(DI,"invalid call to test_dist"); return(abs(i-j)); } /* Test the service clusterize. */ int test_clusterize(void) { /* data for test 2 */ int i; int r[27] = { 5, 7, 8, 9,10,11,12,13,14,15,16,17,18,19, 20,21,22,25,28,30,33,34,35,38,39,40,43}; /* test 1 */ if (clusterize(50,2,test_dist_1) == 33) printf("clusterize test 1 ok\n"); else printf("clusterize test 1 failed\n"); /* test 2 */ if (clusterize(50,1,test_dist_1) == 27) { for (i=0; (i<27) && (clusterize_r[i]==r[i]); ++i); if (i >= 27) printf("clusterize test 2 ok\n"); else printf("clusterize test 2 failed (cluster)\n"); } else { printf("clusterize test 2 failed (size)\n"); } /* test 3. */ if (clusterize(500,1,test_dist_2) == 500) printf("clusterize test 3 ok\n"); else printf("clusterize test 3 failed\n"); /* test 3. */ if (clusterize(500,0,test_dist_2) == 1) printf("clusterize test 4 ok\n"); else printf("clusterize test 4 failed\n"); return(1); } #endif /* Distance function for clusterization of rules. */ int dist_rules(int i,int j) { int it,ib,jt,jb,u,v; if ((i<0) || (topr ib) v = jt - ib; else if (it > jb) v = it - jb; else v = 0; /* euclidean distance (square) */ return(u*u+v*v); } /* Locate vertical lines. Returns the bounding box (l,r,t,b) of the vertical lines and the slope signal: if from top to bottom the lines go from left to right, returns +1. If the lines go from right to left, returns -1. If they're perfectly vertical, returns 0. If the service could not detect consistently the slope signal, returns -2. +-----------+ +-----------+ +-----------+ | | | | | | | \ | | / | | | | | \ | | / | | | | | \ | | / | | | | | | | | | | +-----------+ +-----------+ +-----------+ +1 -1 0 */ int vlines2(unsigned char *pb,int w,int h,int *l,int *r,int *t,int *b) { int a,i,j,k,cr,cs,x,y,sl; long long sx,sy,sx2,sy2,sxy,dx,dy; long long n; float s; /* line buffers */ if (lsz < YRES) { lsz = YRES; ll = c_realloc(ll,lsz*sizeof(int),NULL); rl = c_realloc(rl,lsz*sizeof(int),NULL); } /* rules */ topr = -1; /* search vertical rules */ for (j=0; j= 100) { if (++topr >= rsz) { rsz = (topr + 128); rx = c_realloc(rx,rsz*sizeof(int),NULL); ry = c_realloc(ry,rsz*sizeof(int),NULL); rz = c_realloc(rz,rsz*sizeof(int),NULL); } rx[topr] = i; ry[topr] = j; rz[topr] = k; /* mark pixels */ while (--k>=0) pb[(j+k)*w+i] += 102; } } } /* unmark pixels of all rules */ for (cr=0; cr<=topr; ++cr) { x = rx[cr]; y = ry[cr]; /* unmark pixels */ for (k=rz[cr], n+=k; (--k>=0); ++y) { /* unmark */ pb[y*w+x] -= 102; } } /* clusterize rules */ cs = clusterize(topr+1,200,dist_rules); /* sum of lengths of selected rules */ for (i=sl=0; i *r) *r = x; /* adjust top and bottom */ y = ry[a]; if (y < *t) *t = y; y += (rz[a]-1); if (y > *b) *b = y; /* accumulate */ sx += x; sx2 += (x*x); sy += y; sy2 += (y*y); sxy += (x*y); } } /* slope */ dx = sx2 - ((float) sx*sx) / n; dy = sy2 - ((float) sy*sy) / n; s = sxy - ((float) sx*sy) / n; if (abs(dx) > abs(dy)) { /* something strange happened.. */ return(-2); } else { int a; s /= dy; /* check the box dimension */ a = s * (*b-*t+1); if (a > 10*(*r-*l+1)) return(-2); } /* slope signal */ if (fabs(s) < eps) return(0); else if (s > 0.0) return(1); else return(-1); } /* Locate horizontal margin (left if dx=1, right if dx=-1). */ int hmargin(unsigned char *pb,int w,int h,int dx) { int i,j,k,f,d,n,cx; int p,*L,lm,r; lm = (h/100) + 1; L = alloca(sizeof(int) * lm); /* compute margin */ for (j=p=0; (j d) { f = 2; L[p] = i; } } } } if (dx == 1) { r = w; for (p=0; p r) r = L[p]; } return(r); } /* Locate vertical margin (top if dy==1, bottom if dy==-1). */ int vmargin(unsigned char *pb,int w,int h,int dy) { int i,j,n,r,f,cy; j = (dy == 1) ? 0 : h-1; /* locate top margin */ for (cy=0, r=-1, f=0; (j w) { f = 2; if (r < 0) r = j; } else if ((20*n > w) && (r < 0)) r = j; else if (20*n < w) r = -1; } } return(r); } /* Display on stdout the distribution of shades of gray on the currently loaded PGM file (assumes that one such file is loaded). */ void shades_acc(void) { int w,h; /* translate old names */ w = XRES; h = YRES; { int i,j,c[256]; for (i=0; i<256; ++i) { c[i] = 0; } for (j=0; j 30) l0 -= 30; else l0 = 0; if (r0+30 < w) r0 += 30; else r0 = w-1; /* prefer the top limit based on the vertical lines detection. */ if ((!pc) || (t0 > t)) t0 = t; if (t0 > 30) t0 -= 30; else t0 = 0; /* prefer the bottom limit based on the vertical lines detection. */ if ((!pc) || (b0 < b)) b0 = b; if (b0+30 < h) b0 += 30; else b0 = h-1; d = abs(((r-l0)-(r0-l))); /* if (d > 80) { printf("block division under suspection (%d)\n",d); } else { printf("block widths difference %d\n",d); } */ /* printf("left block: %d,%d,%d,%d\n",l0,r,t0,b0); printf("right block: %d,%d,%d,%d\n",l,r0,t0,b0); */ m = (r + l) / 2; /* create the left zone */ limits[0] = l0; limits[1] = t0; limits[2] = l0; limits[3] = b0; limits[4] = (ss==0) ? m : ((ss>0) ? r : l); limits[5] = b0; limits[6] = (ss==0) ? m : ((ss>0) ? l : r); limits[7] = t0; /* create the right zone */ limits[8] = (ss==0) ? (m+1) : ((ss>0) ? (l+1) : (r+1)); limits[9] = t0; limits[10] = (ss==0) ? (m+1) : ((ss>0) ? (r+1) : (l+1)); limits[11] = b0; limits[12] = r0; limits[13] = b0; limits[14] = r0; limits[15] = t0; zones = 2; redraw_dw = 1; redraw_zone = 1; /* finished */ show_hint(0,"finished"); return(0); } /* not finished */ return(1); }