cimagemagickmagickwand

How to get color arguments to pass for sparse-color?


I have been working to get most high saturation and bright colors. For that I have below command and the C program to get same result.

convert one.jpg -alpha off -scale '50x50!' -depth 8 \
'(' -clone 0 -colorspace HSB -channel gb -separate +channel -threshold 50% -compose multiply -composite ')'\
-alpha off -compose copy_opacity -composite sparse-color:-|\
convert -size 50x50 xc: -sparse-color voronoi @- +dither -colors 6 -depth 8 -format %c histogram:info:

There are two commands executed so I divided and make it separately. Then I wrote the output of first command into text file and read that text file in second command. Please look at below two command.

/*First Part*/
convert one.jpg -alpha off -scale '50x50!' -depth 8 '(' -clone 0 -colorspace HSB -channel gb -separate +channel -threshold 50% -compose multiply -composite ')' -alpha off -compose copy_opacity -composite sparse-color:-test.txt

/*Second Part*/
convert -size 50x50 xc: -sparse-color voronoi @-test.txt +dither -colors 6 -depth 8 -format %c histogram:info:  

Using this I got the same output. Below is my C program to achieve the same. In the code the first part has been covered. But the results I got from first convert command and C code are not same. I don't understand what I missed.

#include "MagickWand/studio.h"
#include "MagickWand/MagickWand.h"
void dominantSix(){

    /* convert one.jpg -alpha off -scale '50x50!'\
     -depth 8 '(' -clone 0 -colorspace HSB -channel gb -separate +channel -threshold 50% -compose multiply -composite ')'\
      -alpha off -compose copy_opacity -composite sparse-color:-test.txt */

    MagickWand * wand, * wand0, * wand1;

    PixelIterator * iteration;
    PixelWand ** row;
    PixelInfo pixel;
    size_t x, y, row_width;

    wand = NewMagickWand();
    MagickReadImage(wand, "car.jpg");
    MagickSetImageAlphaChannel(wand, OffAlphaChannel);

    MagickScaleImage(wand, 50, 50);
    MagickSetImageDepth(wand,8);

    wand0 = CloneMagickWand(wand);
    wand1 = CloneMagickWand(wand);

    MagickTransformImageColorspace(wand0, HSBColorspace);
    MagickSetImageChannelMask(wand0, GreenChannel);
    MagickSeparateImage(wand0, GreenChannel);
    MagickThresholdImage(wand0, QuantumRange*50/100);

    MagickTransformImageColorspace(wand1, HSBColorspace);
    MagickSetImageChannelMask(wand1, BlueChannel);
    MagickSeparateImage(wand1, BlueChannel);
    MagickThresholdImage(wand1, QuantumRange*50/100);

    MagickCompositeImage(wand0, wand1, MultiplyCompositeOp, MagickFalse, 0, 0);

    wand1 = DestroyMagickWand(wand1);

    MagickSetImageAlphaChannel(wand, OffAlphaChannel);
    MagickCompositeImage(wand, wand0, CopyAlphaCompositeOp, MagickFalse, 0, 0);

    wand0 = DestroyMagickWand(wand0);


    size_t height = MagickGetImageHeight(wand);
    size_t width = MagickGetImageWidth(wand);

    iteration = NewPixelIterator(wand);
    FILE *fptr;
    fptr = fopen("program.txt", "w");
    for (y = 0; y < height; ++ y)
    {
        row = PixelGetNextIteratorRow(iteration, &row_width);
        for (x = 0; x < row_width; ++x)
        {
            PixelGetMagickColor(row[x], &pixel);
            size_t red = pixel.red*255/QuantumRange;
            size_t green = pixel.green*255/QuantumRange;
            size_t blue = pixel.blue*255/QuantumRange;
            char color[30];
            sprintf(color, "%zu%s%zu%s%s%zu%s%zu%s%zu%s%s", x, ",", y, ",", "srgb(", red, ",", green, ",", blue, ",", "1) ");
            fprintf(fptr,"%s", color);
        }
        PixelSyncIterator(iteration);
    }
    fclose(fptr);

    PixelSyncIterator(iteration);
    iteration = DestroyPixelIterator(iteration);
    wand = DestroyMagickWand(wand);
    MagickWandTerminus();
}

int main(int argc, char const *argv[]) {
  dominantSix();
  return 0;
}

Below is the source Image and the links of two text files one is from CLI and other from C.

Text file output from C

Text file output from CLI

one.jpg


Solution

  • Edit for eliminating program.txt step

    If you're already writing , you may be able to skip the sparse-color extraction, and apply the colors directly to MagickSparseColorImage

    // ... Omitting first half of function ...
    MagickSetImageAlphaChannel(wand, OffAlphaChannel);
    MagickCompositeImage(wand, wand0, CopyAlphaCompositeOp, MagickFalse, 0, 0);
    
    wand0 = DestroyMagickWand(wand0);
    
    size_t height = MagickGetImageHeight(wand);
    size_t width = MagickGetImageWidth(wand);
    size_t spares_index = 0;
    // Allocated enough memory to hold Sparse Color data
    double * sparse_args = malloc(sizeof(double) * height * width * 5);
    // Iterate over pixel data
    iteration = NewPixelIterator(wand);
    for (y = 0; y < height; ++ y)
    {
        row = PixelGetNextIteratorRow(iteration, &row_width);
        for (x = 0; x < row_width; ++x)
        {
            PixelGetMagickColor(row[x], &pixel);
            // Append coord + color data if pixel is visible.
            if (pixel.alpha == QuantumRange) {
                sparse_args[spares_index++] = (double)x;
                sparse_args[spares_index++] = (double)y;
                sparse_args[spares_index++] = pixel.red / QuantumRange;
                sparse_args[spares_index++] = pixel.green / QuantumRange;
                sparse_args[spares_index++] = pixel.blue / QuantumRange;
            }
        }
    }
    iteration = DestroyPixelIterator(iteration);
    wand = DestroyMagickWand(wand);
    
    // Generate new image to apply spares-color data
    MagickWand * final;
    final = NewMagickWand();
    MagickSetSize(final, 50, 50);
    MagickReadImage(final, "xc:");
    MagickSparseColorImage(final, VoronoiColorInterpolate, spares_index, sparse_args);
    MagickQuantizeImage(final, 6, RGBColorspace, 0, NoDitherMethod, MagickFalse);
    
    // Generate Histogram
    size_t histogram_size = 0;
    PixelWand ** colors = MagickGetImageHistogram(final, &histogram_size);
    for (int index = 0; index < histogram_size; ++index) {
        PixelWand * color = colors[index];
        size_t color_count = PixelGetColorCount(color);
        if (color_count) {
            char * color_name = PixelGetColorAsString(color);
            printf("%s => %lu\n", color_name, color_count);
            free(color_name);
        }
    }
    // Clean-up
    free(sparse_args);
    colors = DestroyPixelWands(colors, histogram_size);
    final = DestroyMagickWand(final);
    

    Original Answer

    Hard to tell where/what you need help with. But to generate the program.txt data, you should be able to leverage the SPARES-COLOR: protocol.

    // ...
    MagickSetImageAlphaChannel(wand, OffAlphaChannel);
    MagickCompositeImage(wand, wand0, CopyAlphaCompositeOp, MagickFalse, 0, 0);
    
    wand0 = DestroyMagickWand(wand0);
    
    // Set image format to sparse protocol
    MagickSetImageFormat(wand, "SPARSE-COLOR");
    // Extract format data to buffer.
    size_t blob_length = 0;
    unsigned char * blob = MagickGetImageBlob(wand, &blob_length);
    // Write buffer to file
    FILE * fptr;
    fptr = fopen("program.txt", "w");
    fwrite(blob, sizeof(unsigned char), blob_length, fptr);
    fclose(fptr);
    free(blob);
    
    wand = DestroyMagickWand(wand);
    MagickWandTerminus();
    

    Or even simpler

    // ...
    MagickSetImageAlphaChannel(wand, OffAlphaChannel);
    MagickCompositeImage(wand, wand0, CopyAlphaCompositeOp, MagickFalse, 0, 0);
    
    wand0 = DestroyMagickWand(wand0);
    
    // Write sparse program
    MagickWriteImage(wand, "SPARSE-COLOR:program.txt");
    
    wand = DestroyMagickWand(wand);
    MagickWandTerminus();