Пример #1
0
int msQuantizeRasterBuffer(rgbaPixel *pixelsIn,int height,int width,
      unsigned int *reqcolors, rgbaPixel *palette,
      unsigned int *palette_scaling_maxval) 
{
    register rgbaPixel *pP;
    register int col;
    unsigned char newmaxval;
    acolorhist_vector achv, acolormap=NULL;
    int row;
    int colors;
    int newcolors = 0;
    int x;
    *palette_scaling_maxval = 255;
    int row_step = width*4;

    rgbaPixel **apixels=(rgbaPixel**)malloc(height*sizeof(rgbaPixel**));
    
    for(row=0;row<height;row++) {
        apixels[row]=(rgbaPixel*)(&(pixelsIn[row*width]));
    }

    /*
     ** Step 2: attempt to make a histogram of the colors, unclustered.
     ** If at first we don't succeed, lower maxval to increase color
     ** coherence and try again.  This will eventually terminate, with
     ** maxval at worst 15, since 32^3 is approximately MAXCOLORS.
                  [GRR POSSIBLE BUG:  what about 32^4 ?]
     */
    for ( ; ; ) {
        achv = pam_computeacolorhist(
                apixels, width, height, MAXCOLORS, &colors );
        if ( achv != (acolorhist_vector) 0 )
            break;
        newmaxval = *palette_scaling_maxval / 2;
        for ( row = 0; row < height; ++row )
            for ( col = 0, pP = apixels[row]; col < width; ++col, ++pP )
                PAM_DEPTH( *pP, *pP, *palette_scaling_maxval, newmaxval );
        *palette_scaling_maxval = newmaxval;
    }
    newcolors = MS_MIN(colors, *reqcolors);
    acolormap = mediancut(achv, colors, width*height, *palette_scaling_maxval, newcolors);
    pam_freeacolorhist(achv);
    
    
    *reqcolors = newcolors;

    for (x = 0; x < newcolors; ++x) {
       palette[x].r = acolormap[x].acolor.r;
       palette[x].g = acolormap[x].acolor.g;
       palette[x].b = acolormap[x].acolor.b;
       palette[x].a = acolormap[x].acolor.a;
    }
    
   free(acolormap);
   free(apixels);
   return MS_SUCCESS;
}
Пример #2
0
static void
computeColorMapFromInput(FILE *                const ifP,
                         bool                  const allColors, 
                         int                   const reqColors, 
                         enum methodForLargest const methodForLargest,
                         enum methodForRep     const methodForRep,
                         int *                 const formatP,
                         struct pam *          const freqPamP,
                         tupletable2 *         const colormapP) {
/*----------------------------------------------------------------------------
   Produce a colormap containing the best colors to represent the
   image stream in file 'ifP'.  Figure it out using the median cut
   technique.

   The colormap will have 'reqcolors' or fewer colors in it, unless
   'allcolors' is true, in which case it will have all the colors that
   are in the input.

   The colormap has the same maxval as the input.

   Put the colormap in newly allocated storage as a tupletable2 
   and return its address as *colormapP.  Return the number of colors in
   it as *colorsP and its maxval as *colormapMaxvalP.

   Return the characteristics of the input file as
   *formatP and *freqPamP.  (This information is not really
   relevant to our colormap mission; just a fringe benefit).
-----------------------------------------------------------------------------*/
    tupletable2 colorfreqtable;

    computeHistogram(ifP, formatP, freqPamP, &colorfreqtable);
    
    if (allColors) {
        *colormapP = colorfreqtable;
    } else {
        if (colorfreqtable.size <= reqColors) {
            pm_message("Image already has few enough colors (<=%d).  "
                       "Keeping same colors.", reqColors);
            *colormapP = colorfreqtable;
        } else {
            pm_message("choosing %d colors...", reqColors);
            mediancut(colorfreqtable, freqPamP->depth,
                      reqColors, methodForLargest, methodForRep,
                      colormapP);
            pnm_freetupletable2(freqPamP, colorfreqtable);
        }
    }
}
Пример #3
0
/**
 * Compute a palette for the given RGBA rasterBuffer using a median cut quantization.
 * - rb: the rasterBuffer to quantize
 * - reqcolors: the desired number of colors the palette should contain. will be set
 *   with the actual number of entries in the computed palette
 * - forced_palette: entries that should appear in the computed palette
 * - num_forced_palette_entries: number of entries contained in "force_palette". if 0,
 *   "force_palette" can be NULL
 * - palette_scaling_maxval: the quantization process may have to reduce the image depth
 *   by iteratively dividing the pixels by 2. In case palette_scaling_maxval is set to
 *   something different than 255, the returned palette colors have to be scaled back up to
 *   255, and rb's pixels will have been scaled down to maxsize (see bug #3848)
 */
int msQuantizeRasterBuffer(rasterBufferObj *rb,
                           unsigned int *reqcolors, rgbaPixel *palette,
                           rgbaPixel *forced_palette, int num_forced_palette_entries,
                           unsigned int *palette_scaling_maxval)
{
  rgbaPixel **apixels=NULL; /* pointer to the start rows of truecolor pixels */

  register rgbaPixel *pP;
  register int col;

  unsigned char newmaxval;
  acolorhist_vector achv, acolormap=NULL;

  int row;
  int colors;
  int newcolors = 0;

  int x;
  /*  int channels;  */

  assert(rb->type == MS_BUFFER_BYTE_RGBA);

  *palette_scaling_maxval = 255;

  apixels=(rgbaPixel**)msSmallMalloc(rb->height*sizeof(rgbaPixel*));

  for(row=0; row<rb->height; row++) {
    apixels[row]=(rgbaPixel*)(&(rb->data.rgba.pixels[row * rb->data.rgba.row_step]));
  }

  /*
   ** Step 2: attempt to make a histogram of the colors, unclustered.
   ** If at first we don't succeed, lower maxval to increase color
   ** coherence and try again.  This will eventually terminate, with
   ** maxval at worst 15, since 32^3 is approximately MAXCOLORS.
                [GRR POSSIBLE BUG:  what about 32^4 ?]
   */
  for ( ; ; ) {
    achv = pam_computeacolorhist(
             apixels, rb->width, rb->height, MAXCOLORS, &colors );
    if ( achv != (acolorhist_vector) 0 )
      break;
    newmaxval = *palette_scaling_maxval / 2;
    for ( row = 0; row < rb->height; ++row )
      for ( col = 0, pP = apixels[row]; col < rb->width; ++col, ++pP )
        PAM_DEPTH( *pP, *pP, *palette_scaling_maxval, newmaxval );
    *palette_scaling_maxval = newmaxval;
  }
  newcolors = MS_MIN(colors, *reqcolors);
  acolormap = mediancut(achv, colors, rb->width*rb->height, *palette_scaling_maxval, newcolors);
  pam_freeacolorhist(achv);


  *reqcolors = newcolors;


  for (x = 0; x < newcolors; ++x) {
    palette[x].r = acolormap[x].acolor.r;
    palette[x].g = acolormap[x].acolor.g;
    palette[x].b = acolormap[x].acolor.b;
    palette[x].a = acolormap[x].acolor.a;
  }

  free(acolormap);
  free(apixels);
  return MS_SUCCESS;
}
Пример #4
0
/**
 * Compute a palette for the given RGBA rasterBuffer using a median cut quantization.
 * - rb: the rasterBuffer to quantize
 * - reqcolors: the desired number of colors the palette should contain. will be set
 *   with the actual number of entries in the computed palette
 * - palette: preallocated array of palette entries that will be populated by the
 *   function
 * - maxval: max value of pixel intensity. In some cases, the input data has to
 *   be rescaled to compute the quantization. if the returned value of maxscale is
 *   less than 255, this means that the input pixels have been rescaled, and that
 *   the returned palette must be upscaled before being written to the png file
 * - forced_palette: entries that should appear in the computed palette
 * - num_forced_palette_entries: number of entries contained in "force_palette". if 0,
 *   "force_palette" can be NULL
 */
int _mapcache_imageio_quantize_image(mapcache_image *rb,
      unsigned int *reqcolors, rgbaPixel *palette,
      unsigned int *maxval,
      rgbaPixel *forced_palette, int num_forced_palette_entries) {

   rgbaPixel **apixels=NULL; /* pointer to the start rows of truecolor pixels */
   register rgbaPixel *pP;
   register int col;

   unsigned char newmaxval;
   acolorhist_vector achv, acolormap=NULL;

   int row;
   int colors;
   int newcolors = 0;

   int x;
   /*  int channels;  */


   *maxval = 255;

   apixels=(rgbaPixel**)malloc(rb->h*sizeof(rgbaPixel**));
   if(!apixels) return MAPCACHE_FAILURE;

   for(row=0;row<rb->h;row++) {
      apixels[row]=(rgbaPixel*)(&(rb->data[row * rb->stride]));
   }

   /*
    ** Step 2: attempt to make a histogram of the colors, unclustered.
    ** If at first we don't succeed, lower maxval to increase color
    ** coherence and try again.  This will eventually terminate, with
    ** maxval at worst 15, since 32^3 is approximately MAXCOLORS.
                  [GRR POSSIBLE BUG:  what about 32^4 ?]
    */
   for ( ; ; ) {
      achv = pam_computeacolorhist(
            apixels, rb->w, rb->h, MAXCOLORS, &colors );
      if ( achv != (acolorhist_vector) 0 )
         break;
      newmaxval = *maxval / 2;
      for ( row = 0; row < rb->h; ++row )
         for ( col = 0, pP = apixels[row]; col < rb->w; ++col, ++pP )
            PAM_DEPTH( *pP, *pP, *maxval, newmaxval );
      *maxval = newmaxval;
   }
   newcolors = MAPCACHE_MIN(colors, *reqcolors);
   acolormap = mediancut(achv, colors, rb->w*rb->h, *maxval, newcolors);
   pam_freeacolorhist(achv);


   *reqcolors = newcolors;


   for (x = 0; x < newcolors; ++x) {
      palette[x].r = acolormap[x].acolor.r;
      palette[x].g = acolormap[x].acolor.g;
      palette[x].b = acolormap[x].acolor.b;
      palette[x].a = acolormap[x].acolor.a;
   }

   free(acolormap);
   free(apixels);
   return MAPCACHE_SUCCESS;
}
Пример #5
0
/*
 * Voronoi iteration: new palette color is computed from weighted average of colors that map to that palette entry.
 */
static void viter_init(const colormap *map,
                     f_pixel *average_color, float *average_color_count,
                     f_pixel *base_color, float *base_color_count)
{
    colormap_item *newmap = map->palette;
    int newcolors = map->colors;
    for (int i=0; i < newcolors; i++) {
        average_color_count[i] = 0;
        average_color[i] = (f_pixel){0,0,0,0};
    }

    // Rather than only using separate mapping and averaging steps
    // new palette colors are computed at the same time as mapping is done
    // but to avoid first few matches moving the entry too much
    // some base color and weight is added
    if (base_color) {
        for (int i=0; i < newcolors; i++) {
            float value = 1.0+newmap[i].popularity/2.0;
            base_color_count[i] = value;
            base_color[i] = (f_pixel){
                .a = newmap[i].acolor.a * value,
                .r = newmap[i].acolor.r * value,
                .g = newmap[i].acolor.g * value,
                .b = newmap[i].acolor.b * value,
            };
        }
    }
}

static void viter_update_color(f_pixel acolor, float value, colormap *map, int match,
                             f_pixel *average_color, float *average_color_count,
                             const f_pixel *base_color, const float *base_color_count)
{
    average_color[match].a += acolor.a * value;
    average_color[match].r += acolor.r * value;
    average_color[match].g += acolor.g * value;
    average_color[match].b += acolor.b * value;
    average_color_count[match] += value;

    if (base_color) {
        map->palette[match].acolor = (f_pixel){
            .a = (average_color[match].a + base_color[match].a) / (average_color_count[match] + base_color_count[match]),
            .r = (average_color[match].r + base_color[match].r) / (average_color_count[match] + base_color_count[match]),
            .g = (average_color[match].g + base_color[match].g) / (average_color_count[match] + base_color_count[match]),
            .b = (average_color[match].b + base_color[match].b) / (average_color_count[match] + base_color_count[match]),
        };
    }
}

static void viter_finalize(colormap *map, f_pixel *average_color, float *average_color_count)
{
    for (int i=0; i < map->colors; i++) {
        if (average_color_count[i]) {
            map->palette[i].acolor = (f_pixel){
                .a = (average_color[i].a) / average_color_count[i],
                .r = (average_color[i].r) / average_color_count[i],
                .g = (average_color[i].g) / average_color_count[i],
                .b = (average_color[i].b) / average_color_count[i],
            };
        }
        map->palette[i].popularity = average_color_count[i];
    }
}

void viter_do_interation(const hist *hist, colormap *map, float min_opaque_val)
{
    f_pixel average_color[map->colors];
    float average_color_count[map->colors];

    hist_item *achv = hist->achv;
    viter_init(map, average_color,average_color_count, NULL,NULL);

    for(int j=0; j < hist->size; j++) {

        int match = best_color_index(achv[j].acolor, map, min_opaque_val, NULL);
        viter_update_color(achv[j].acolor, achv[j].perceptual_weight, map, match, average_color,average_color_count, NULL,NULL);
    }

    viter_finalize(map, average_color,average_color_count);
}

pngquant_error pngquant(read_info *input_image, write_info *output_image, int floyd, int reqcolors, int ie_bug, int speed_tradeoff)
{
    float min_opaque_val;

    verbose_printf("  reading file corrected for gamma %2.1f\n", 1.0/input_image->gamma);

    min_opaque_val = modify_alpha(input_image,ie_bug);
    assert(min_opaque_val>0);

    hist *hist = histogram(input_image, reqcolors, speed_tradeoff);
    hist_item *achv = hist->achv;

    colormap *acolormap = NULL;
    float least_error = -1;
    int feedback_loop_trials = 56-9*speed_tradeoff;
    const double percent = (double)(feedback_loop_trials>0?feedback_loop_trials:1)/100.0;

    do
    {
        verbose_printf("  selecting colors");

        colormap *newmap = mediancut(hist, min_opaque_val, reqcolors);

        verbose_printf("...");

        float total_error=0;
        f_pixel average_color[newmap->colors], base_color[newmap->colors];
        float average_color_count[newmap->colors], base_color_count[newmap->colors];

        if (feedback_loop_trials) {

            viter_init(newmap, average_color,average_color_count,base_color,base_color_count);

            for(int i=0; i < hist->size; i++) {
                float diff;
                int match = best_color_index(achv[i].acolor, newmap, min_opaque_val, &diff);
                assert(diff >= 0);
                assert(achv[i].perceptual_weight > 0);
                total_error += diff * achv[i].perceptual_weight;

                viter_update_color(achv[i].acolor, achv[i].perceptual_weight, newmap, match,
                                   average_color,average_color_count,base_color,base_color_count);

                achv[i].adjusted_weight = (achv[i].perceptual_weight+achv[i].adjusted_weight) * (1.0+sqrtf(diff));
            }
        }

        if (total_error < least_error || !acolormap) {
            if (acolormap) pam_freecolormap(acolormap);

            acolormap = newmap;

            viter_finalize(acolormap, average_color,average_color_count);

            least_error = total_error;
            feedback_loop_trials -= 1; // asymptotic improvement could make it go on forever
        } else {
            feedback_loop_trials -= 6;
            if (total_error > least_error*4) feedback_loop_trials -= 3;
            pam_freecolormap(newmap);
        }

        verbose_printf("%d%%\n",100-MAX(0,(int)(feedback_loop_trials/percent)));
    }
    while(feedback_loop_trials > 0);

    verbose_printf("  moving colormap towards local minimum\n");

    int iterations = MAX(5-speed_tradeoff,0); iterations *= iterations;
    for(int i=0; i < iterations; i++) {
        viter_do_interation(hist, acolormap, min_opaque_val);
    }

    pam_freeacolorhist(hist);

    output_image->width = input_image->width;
    output_image->height = input_image->height;
    output_image->gamma = 0.45455;

    /*
    ** Step 3.7 [GRR]: allocate memory for the entire indexed image
    */

    output_image->indexed_data = malloc(output_image->height * output_image->width);
    output_image->row_pointers = malloc(output_image->height * sizeof(output_image->row_pointers[0]));

    if (!output_image->indexed_data || !output_image->row_pointers) {
        fprintf(stderr, "  insufficient memory for indexed data and/or row pointers\n");
        return OUT_OF_MEMORY_ERROR;
    }

    for (int row = 0;  row < output_image->height;  ++row) {
        output_image->row_pointers[row] = output_image->indexed_data + row*output_image->width;
    }

    // tRNS, etc.
    sort_palette(output_image, acolormap);

    /*
     ** Step 4: map the colors in the image to their closest match in the
     ** new colormap, and write 'em out.
     */
    verbose_printf("  mapping image to new colors...");

    float remapping_error;

    if (floyd) {
        // if dithering, save rounding error and stick to that palette
        // otherwise palette can be improved after remapping
        set_palette(output_image, acolormap);
        remapping_error = remap_to_palette_floyd(input_image, output_image, acolormap, min_opaque_val, ie_bug);
    } else {
        remapping_error = remap_to_palette(input_image, output_image, acolormap, min_opaque_val, ie_bug);
        set_palette(output_image, acolormap);
    }

    verbose_printf("MSE=%.3f\n", remapping_error*256.0f);

    pam_freecolormap(acolormap);

    return SUCCESS;
}