static void computeGrayscaleRow(const pixel * const inputRow, gray * const outputRow, pixval const maxval, unsigned int const cols) { if (maxval <= 255) { unsigned int col; /* Use fast approximation to 0.299 r + 0.587 g + 0.114 b. */ for (col = 0; col < cols; ++col) outputRow[col] = ppm_fastlumin(inputRow[col]); } else { unsigned int col; /* Can't use fast approximation, so fall back on floats. */ for (col = 0; col < cols; ++col) outputRow[col] = PPM_LUMIN(inputRow[col]) + 0.5; } }
static acolorhist_vector mediancut( acolorhist_vector achv, int colors, int sum, unsigned char maxval, int newcolors ) { acolorhist_vector acolormap; box_vector bv; register int bi, i; int boxes; bv = (box_vector) malloc( sizeof(struct box) * newcolors ); acolormap = (acolorhist_vector) malloc( sizeof(struct acolorhist_item) * newcolors); if ( bv == (box_vector) 0 || acolormap == (acolorhist_vector) 0 ) { fprintf( stderr, " out of memory allocating box vector\n" ); fflush(stderr); exit(6); } for ( i = 0; i < newcolors; ++i ) PAM_ASSIGN( acolormap[i].acolor, 0, 0, 0, 0 ); /* ** Set up the initial box. */ bv[0].ind = 0; bv[0].colors = colors; bv[0].sum = sum; boxes = 1; /* ** Main loop: split boxes until we have enough. */ while ( boxes < newcolors ) { register int indx, clrs; int sm; register int minr, maxr, ming, mina, maxg, minb, maxb, maxa, v; int halfsum, lowersum; /* ** Find the first splittable box. */ for ( bi = 0; bi < boxes; ++bi ) if ( bv[bi].colors >= 2 ) break; if ( bi == boxes ) break; /* ran out of colors! */ indx = bv[bi].ind; clrs = bv[bi].colors; sm = bv[bi].sum; /* ** Go through the box finding the minimum and maximum of each ** component - the boundaries of the box. */ minr = maxr = PAM_GETR( achv[indx].acolor ); ming = maxg = PAM_GETG( achv[indx].acolor ); minb = maxb = PAM_GETB( achv[indx].acolor ); mina = maxa = PAM_GETA( achv[indx].acolor ); for ( i = 1; i < clrs; ++i ) { v = PAM_GETR( achv[indx + i].acolor ); if ( v < minr ) minr = v; if ( v > maxr ) maxr = v; v = PAM_GETG( achv[indx + i].acolor ); if ( v < ming ) ming = v; if ( v > maxg ) maxg = v; v = PAM_GETB( achv[indx + i].acolor ); if ( v < minb ) minb = v; if ( v > maxb ) maxb = v; v = PAM_GETA( achv[indx + i].acolor ); if ( v < mina ) mina = v; if ( v > maxa ) maxa = v; } /* ** Find the largest dimension, and sort by that component. I have ** included two methods for determining the "largest" dimension; ** first by simply comparing the range in RGB space, and second ** by transforming into luminosities before the comparison. You ** can switch which method is used by switching the commenting on ** the LARGE_ defines at the beginning of this source file. */ #ifdef LARGE_NORM if ( maxa - mina >= maxr - minr && maxa - mina >= maxg - ming && maxa - mina >= maxb - minb ) qsort( (char*) &(achv[indx]), clrs, sizeof(struct acolorhist_item), alphacompare ); else if ( maxr - minr >= maxg - ming && maxr - minr >= maxb - minb ) qsort( (char*) &(achv[indx]), clrs, sizeof(struct acolorhist_item), redcompare ); else if ( maxg - ming >= maxb - minb ) qsort( (char*) &(achv[indx]), clrs, sizeof(struct acolorhist_item), greencompare ); else qsort( (char*) &(achv[indx]), clrs, sizeof(struct acolorhist_item), bluecompare ); #endif /*LARGE_NORM*/ #ifdef LARGE_LUM { apixel p; float rl, gl, bl, al; PAM_ASSIGN(p, maxr - minr, 0, 0, 0); rl = PPM_LUMIN(p); PAM_ASSIGN(p, 0, maxg - ming, 0, 0); gl = PPM_LUMIN(p); PAM_ASSIGN(p, 0, 0, maxb - minb, 0); bl = PPM_LUMIN(p); /* GRR: treat alpha as grayscale and assign (maxa - mina) to each of R, G, B? assign (maxa - mina)/3 to each? use alpha-fractional luminosity? (normalized_alpha * lum(r,g,b)) al = dunno ... [probably should read Heckbert's paper to decide] */ if ( al >= rl && al >= gl && al >= bl ) qsort( (char*) &(achv[indx]), clrs, sizeof(struct acolorhist_item), alphacompare ); else if ( rl >= gl && rl >= bl ) qsort( (char*) &(achv[indx]), clrs, sizeof(struct acolorhist_item), redcompare ); else if ( gl >= bl ) qsort( (char*) &(achv[indx]), clrs, sizeof(struct acolorhist_item), greencompare ); else qsort( (char*) &(achv[indx]), clrs, sizeof(struct acolorhist_item), bluecompare ); } #endif /*LARGE_LUM*/ /* ** Now find the median based on the counts, so that about half the ** pixels (not colors, pixels) are in each subdivision. */ lowersum = achv[indx].value; halfsum = sm / 2; for ( i = 1; i < clrs - 1; ++i ) { if ( lowersum >= halfsum ) break; lowersum += achv[indx + i].value; } /* ** Split the box, and sort to bring the biggest boxes to the top. */ bv[bi].colors = i; bv[bi].sum = lowersum; bv[boxes].ind = indx + i; bv[boxes].colors = clrs - i; bv[boxes].sum = sm - lowersum; ++boxes; qsort( (char*) bv, boxes, sizeof(struct box), sumcompare ); } /* ** Ok, we've got enough boxes. Now choose a representative color for ** each box. There are a number of possible ways to make this choice. ** One would be to choose the center of the box; this ignores any structure ** within the boxes. Another method would be to average all the colors in ** the box - this is the method specified in Heckbert's paper. A third ** method is to average all the pixels in the box. You can switch which ** method is used by switching the commenting on the REP_ defines at ** the beginning of this source file. */ for ( bi = 0; bi < boxes; ++bi ) { #ifdef REP_CENTER_BOX register int indx = bv[bi].ind; register int clrs = bv[bi].colors; register int minr, maxr, ming, maxg, minb, maxb, mina, maxa, v; minr = maxr = PAM_GETR( achv[indx].acolor ); ming = maxg = PAM_GETG( achv[indx].acolor ); minb = maxb = PAM_GETB( achv[indx].acolor ); mina = maxa = PAM_GETA( achv[indx].acolor ); for ( i = 1; i < clrs; ++i ) { v = PAM_GETR( achv[indx + i].acolor ); minr = min( minr, v ); maxr = max( maxr, v ); v = PAM_GETG( achv[indx + i].acolor ); ming = min( ming, v ); maxg = max( maxg, v ); v = PAM_GETB( achv[indx + i].acolor ); minb = min( minb, v ); maxb = max( maxb, v ); v = PAM_GETA( achv[indx + i].acolor ); mina = min( mina, v ); maxa = max( maxa, v ); } PAM_ASSIGN( acolormap[bi].acolor, ( minr + maxr ) / 2, ( ming + maxg ) / 2, ( minb + maxb ) / 2, ( mina + maxa ) / 2 ); #endif /*REP_CENTER_BOX*/ #ifdef REP_AVERAGE_COLORS register int indx = bv[bi].ind; register int clrs = bv[bi].colors; register long r = 0, g = 0, b = 0, a = 0; for ( i = 0; i < clrs; ++i ) { r += PAM_GETR( achv[indx + i].acolor ); g += PAM_GETG( achv[indx + i].acolor ); b += PAM_GETB( achv[indx + i].acolor ); a += PAM_GETA( achv[indx + i].acolor ); } r = r / clrs; g = g / clrs; b = b / clrs; a = a / clrs; PAM_ASSIGN( acolormap[bi].acolor, r, g, b, a ); #endif /*REP_AVERAGE_COLORS*/ #ifdef REP_AVERAGE_PIXELS register int indx = bv[bi].ind; register int clrs = bv[bi].colors; register long r = 0, g = 0, b = 0, a = 0, sum = 0; for ( i = 0; i < clrs; ++i ) { r += PAM_GETR( achv[indx + i].acolor ) * achv[indx + i].value; g += PAM_GETG( achv[indx + i].acolor ) * achv[indx + i].value; b += PAM_GETB( achv[indx + i].acolor ) * achv[indx + i].value; a += PAM_GETA( achv[indx + i].acolor ) * achv[indx + i].value; sum += achv[indx + i].value; } if(sum>0) { r = r / sum; if ( r > maxval ) r = maxval; /* avoid math errors */ g = g / sum; if ( g > maxval ) g = maxval; b = b / sum; if ( b > maxval ) b = maxval; a = a / sum; if ( a > maxval ) a = maxval; } else { r = g = b = a = maxval; } /* GRR 20001228: added casts to quiet warnings; 255 DEPENDENCY */ PAM_ASSIGN( acolormap[bi].acolor, (unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a ); #endif /*REP_AVERAGE_PIXELS*/ } /* ** All done. */ free(bv); return acolormap; }
static void computeLuminosityHistogram(xel * const * const xels, unsigned int const rows, unsigned int const cols, xelval const maxval, int const format, bool const monoOnly, unsigned int ** const lumahistP, xelval * const lminP, xelval * const lmaxP, unsigned int * const pixelCountP) { /*---------------------------------------------------------------------------- Scan the image and build the luminosity histogram. If the input is a PPM, we calculate the luminosity of each pixel from its RGB components. -----------------------------------------------------------------------------*/ xelval lmin, lmax; unsigned int pixelCount; unsigned int * lumahist; MALLOCARRAY(lumahist, maxval + 1); if (lumahist == NULL) pm_error("Out of storage allocating array for %u histogram elements", maxval + 1); { unsigned int i; /* Initialize histogram to zeroes everywhere */ for (i = 0; i <= maxval; ++i) lumahist[i] = 0; } lmin = maxval; /* initial value */ lmax = 0; /* initial value */ switch (PNM_FORMAT_TYPE(format)) { case PGM_TYPE: case PBM_TYPE: { /* Compute intensity histogram */ unsigned int row; pixelCount = rows * cols; for (row = 0; row < rows; ++row) { unsigned int col; for (col = 0; col < cols; ++col) { xelval const l = PNM_GET1(xels[row][col]); lmin = MIN(lmin, l); lmax = MAX(lmax, l); ++lumahist[l]; } } } break; case PPM_TYPE: { unsigned int row; for (row = 0, pixelCount = 0; row < rows; ++row) { unsigned int col; for (col = 0; col < cols; ++col) { xel const thisXel = xels[row][col]; if (!monoOnly || PPM_ISGRAY(thisXel)) { xelval const l = PPM_LUMIN(thisXel); lmin = MIN(lmin, l); lmax = MAX(lmax, l); ++lumahist[l]; ++pixelCount; } } } } break; default: pm_error("invalid input format format"); } *lumahistP = lumahist; *pixelCountP = pixelCount; *lminP = lmin; *lmaxP = lmax; }
void thin1(bitmap_type *image, unsigned char colour) { unsigned char *ptr, *y_ptr, *y1_ptr; unsigned char bg_color; unsigned int xsize, ysize; /* Image resolution */ unsigned int x, y; /* Pixel location */ unsigned int i; /* Pass index */ unsigned int pc = 0; /* Pass count */ unsigned int count = 1; /* Deleted pixel count */ unsigned int p, q; /* Neighborhood maps of adjacent*/ /* cells */ unsigned char *qb; /* Neighborhood maps of previous*/ /* scanline */ unsigned int m; /* Deletion direction mask */ if (PPM_ISGRAY(background)) bg_color = PPM_GETR(background); else bg_color = PPM_LUMIN(background); LOG (" Thinning image.....\n "); xsize = image->width; ysize = image->height; MALLOCARRAY_NOFAIL(qb, xsize); qb[xsize-1] = 0; /* Used for lower-right pixel */ ptr = image->bitmap; while ( count ) { /* Scan image while deletions */ pc++; count = 0; for ( i = 0 ; i < 4 ; i++ ) { m = masks[i]; /* Build initial previous scan buffer. */ p = (ptr[0] == colour); for ( x = 0 ; x < xsize-1 ; x++ ) qb[x] = (unsigned char) (p = ((p<<1)&0006) | (unsigned int)(ptr[x+1] == colour)); /* Scan image for pixel deletion candidates. */ y_ptr = ptr; y1_ptr = ptr + xsize; for (y = 0; y < ysize - 1; y++, y_ptr += xsize, y1_ptr += xsize) { q = qb[0]; p = ((q<<2)&0330) | (y1_ptr[0] == colour); for ( x = 0 ; x < xsize-1 ; x++ ) { q = qb[x]; p = ((p<<1)&0666) | ((q<<3)&0110) | (unsigned int) (y1_ptr[x+1]==colour); qb[x] = (unsigned char) p; if ( ((p&m) == 0) && todelete[p] ) { count++; y_ptr[x] = bg_color; /* delete the pixel */ } } /* Process right edge pixel. */ p = (p<<1)&0666; if ( (p&m) == 0 && todelete[p] ) { count++; y_ptr[xsize-1] = bg_color; } } /* Process bottom scan line. */ q = qb[0]; p = ((q<<2)&0330); y_ptr = ptr + xsize * (ysize - 1); for ( x = 0 ; x < xsize ; x++ ) { q = qb[x]; p = ((p<<1)&0666) | ((q<<3)&0110); if ( (p&m) == 0 && todelete[p] ) { count++; y_ptr[x] = bg_color; } } } LOG2("thin1: pass %d, %d pixels deleted\n", pc, count); } free (qb); }
void thin_image(bitmap_type *image, bool bgSpec, pixel bg, at_exception_type * exp) { /* This is nasty as we need to call thin once for each * colour in the image the way I do this is to keep a second * copy of the bitmap and to use this to keep * track of which colours have not yet been processed, * trades time for pathological case memory.....*/ long m, n, num_pixels; bitmap_type bm; unsigned int const spp = image->np; unsigned int const width = image->width; unsigned int const height = image->height; if (bgSpec) background = bg; else PPM_ASSIGN(background, 255, 255, 255); /* Clone the image */ bm.height = image->height; bm.width = image->width; bm.np = image->np; MALLOCARRAY(bm.bitmap, height * width * spp); if (bm.bitmap == NULL) pm_error("Unable to get memory for thin image bitmap clone"); memcpy(bm.bitmap, image->bitmap, height * width * spp); num_pixels = height * width; switch (spp) { case 3: { Pixel *ptr = (Pixel*)bm.bitmap; Pixel bg_color; bg_color[0] = PPM_GETR(background); bg_color[1] = PPM_GETG(background); bg_color[2] = PPM_GETB(background); for (n = num_pixels - 1; n >= 0L; --n) { Pixel p; PIXEL_SET(p, ptr[n]); if (!PIXEL_EQUAL(p, bg_color)) { /* we have a new colour in the image */ LOG3("Thinning colour (%x, %x, %x)\n", p[0], p[1], p[2]); for (m = n - 1; m >= 0L; --m) { if (PIXEL_EQUAL(ptr[m], p)) PIXEL_SET(ptr[m], bg_color); } thin3(image, p); } } break; } case 1: { unsigned char * const ptr = bm.bitmap; unsigned char bg_color; if (PPM_ISGRAY(background)) bg_color = PPM_GETR(background); else bg_color = PPM_LUMIN(background); for (n = num_pixels - 1; n >= 0L; --n) { unsigned char c = ptr[n]; if (c != bg_color) { LOG1 ("Thinning colour %x\n", c); for (m = n - 1; m >= 0L; --m) if (ptr[m] == c) ptr[m] = bg_color; thin1(image, c); } } break; } default: { LOG1 ("thin_image: %u-plane images are not supported", spp); at_exception_fatal(exp, "thin_image: wrong plane images are passed"); goto cleanup; } } cleanup: free (bm.bitmap); }