about summary refs log tree commit diff
path: root/editor/ppmdist.c
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2006-08-19 03:12:28 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2006-08-19 03:12:28 +0000
commit1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch)
tree64c8c96cf54d8718847339a403e5e67b922e8c3f /editor/ppmdist.c
downloadnetpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz
netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz
netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'editor/ppmdist.c')
-rw-r--r--editor/ppmdist.c170
1 files changed, 170 insertions, 0 deletions
diff --git a/editor/ppmdist.c b/editor/ppmdist.c
new file mode 100644
index 00000000..90c2e3d3
--- /dev/null
+++ b/editor/ppmdist.c
@@ -0,0 +1,170 @@
+#include "ppm.h"
+#include "mallocvar.h"
+
+
+/*
+ * Yep, it's a very simple algorithm, but it was something I wanted to have
+ * available.
+ */
+
+struct colorToGrayEntry {
+    pixel           color;
+    gray            gray;
+    int             frequency;
+};
+
+/*
+ * BUG: This number was chosen pretty arbitrarily.  The program is * probably
+ * only useful for a very small numbers of colors - and that's * not only
+ * because of the O(n) search that's used.  The idea lends * itself primarily
+ * to low color (read: simple, machine generated) images.
+ */
+#define MAXCOLORS 255
+
+
+static gray
+newGrayValue(pixel *pix, struct colorToGrayEntry *colorToGrayMap, int colors) {
+
+    int color;
+    /*
+     * Allowing this to be O(n), since the program is intended for small
+     * n.  Later, perhaps sort by color (r, then g, then b) and bsearch.
+     */
+    for (color = 0; color < colors; color++) {
+        if (PPM_EQUAL(*pix, colorToGrayMap[color].color))
+            return colorToGrayMap[color].gray;
+    }
+    pm_error("This should never happen - contact the maintainer");
+    return (-1);
+}
+
+
+
+static int
+cmpColorToGrayEntryByIntensity(const void *entry1, const void *entry2) {
+
+    return ((struct colorToGrayEntry *) entry1)->gray -
+        ((struct colorToGrayEntry *) entry2)->gray;
+}
+
+
+
+static int
+cmpColorToGrayEntryByFrequency(const void * entry1, const void * entry2) {
+
+    return ((struct colorToGrayEntry *) entry1)->frequency -
+        ((struct colorToGrayEntry *) entry2)->frequency;
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    FILE           *ifp;
+    int             col, cols, row, rows, color, colors, argn;
+    int             frequency;
+    pixval          maxval;
+    pixel         **pixels;
+    pixel          *pP;
+    colorhist_vector hist;
+    gray           *grayrow;
+    gray           *gP;
+    struct colorToGrayEntry *colorToGrayMap;
+
+
+    ppm_init(&argc, argv);
+
+    argn = 1;
+    /* Default is to sort colors by intensity */
+    frequency = 0;
+
+    while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') {
+        if (pm_keymatch(argv[argn], "-frequency", 2))
+            frequency = 1;
+        else if (pm_keymatch(argv[argn], "-intensity", 2))
+            frequency = 0;
+        else
+            pm_usage( "[-frequency|-intensity] [ppmfile]" );
+        ++argn;
+    }
+
+    if (argn < argc) {
+        ifp = pm_openr(argv[argn]);
+        ++argn;
+    } else
+        ifp = stdin;
+
+    pixels = ppm_readppm(ifp, &cols, &rows, &maxval);
+    pm_close(ifp);
+    /* all done with the input file - it's entirely in memory */
+
+    /*
+     * Compute a histogram of the colors in the input.  This is good for
+     * both frequency, and indirectly the intensity, of a color.
+     */
+    hist = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
+
+    if (hist == (colorhist_vector) 0)
+        /*
+         * BUG: This perhaps should use an exponential backoff, in
+         * the number of colors, until success - cf pnmcolormap's
+         * approach.  The results are then more what's expected, but
+         * not necessarily very useful.
+         */
+        pm_error("Too many colors - Try reducing with pnmquant");
+
+    /* copy the colors into another structure for sorting */
+    MALLOCARRAY(colorToGrayMap, colors);
+    for (color = 0; color < colors; color++) {
+        colorToGrayMap[color].color = hist[color].color;
+        colorToGrayMap[color].frequency = hist[color].value;
+        /*
+         * This next is derivable, of course, but it's far faster to
+         * store it precomputed.  This can be skipped, when sorting
+         * by frequency - but again, for a small number of colors
+         * it's a small matter.
+         */
+        colorToGrayMap[color].gray = PPM_LUMIN(hist[color].color);
+    }
+
+    /*
+     * sort by intensity - sorting by frequency (in the histogram) is
+     * worth considering as a future addition.
+     */
+    if (frequency)
+        qsort(colorToGrayMap, colors, sizeof(struct colorToGrayEntry),
+              &cmpColorToGrayEntryByFrequency);
+    else
+        qsort(colorToGrayMap, colors, sizeof(struct colorToGrayEntry),
+              &cmpColorToGrayEntryByIntensity);
+
+    /*
+     * create mapping between the n colors in input, to n evenly spaced
+     * grayscale intensities.  This is done by overwriting the neatly
+     * formed gray values corresponding to the input-colors, with a new
+     * set of evenly spaced gray values.  Since maxval can be changed on
+     * a lark, we just use gray levels 0..colors-1, and adjust maxval
+     * accordingly
+     */
+    maxval = colors - 1;
+    for (color = 0; color < colors; color++)
+        colorToGrayMap[color].gray = color;
+
+    /* write pgm file, mapping colors to intensities */
+    pgm_writepgminit(stdout, cols, rows, maxval, 0);
+
+    grayrow = pgm_allocrow(cols);
+
+    for (row = 0; row < rows; row++) {
+        for (col = 0, pP = pixels[row], gP = grayrow; col < cols;
+             col++, pP++, gP++)
+            *gP = newGrayValue(pP, colorToGrayMap, colors);
+        pgm_writepgmrow(stdout, grayrow, cols, maxval, 0);
+    }
+
+    pm_close(stdout);
+
+    exit(0);
+}
+