[comp.graphics] Extended dithering, any work done?

jcocon%hazel.cs.clemson.edu@hubcap.clemson.edu (James C O'Connor III, 2846) (04/25/89)

Has any work been done on displaying 256 grey shades on a monitor with
greater than two grey shades (i.e. black and white + some number of greys)?
It seems that an extension to dither would be a good place to start.  So
far as I know, rounding is the norm in hardware (yuck!  a good dither looks
better than some 16 grey renderings I've seen!).

Jim

jbm@eos.UUCP (Jeffrey Mulligan) (04/26/89)

From article <5237@hubcap.clemson.edu>, by jcocon%hazel.cs.clemson.edu@hubcap.clemson.edu (James C O'Connor III, 2846):

> Has any work been done on displaying 256 grey shades on a monitor with
> greater than two grey shades (i.e. black and white + some number of greys)?
> It seems that an extension to dither would be a good place to start.

Some time ago I wrote a generalization of the F-S error diffuser to
use multiple quantization levels (the values of the levels could
be user-defined).  Then just a couple of weeks ago, when trying to
get a multiple level ordered dither algorithm that would run on an
array processor, I discovered that there is a very simple way to do
this which only involves 1 bit dithering:

Say the input image is already quantized, from 0 to N-1 (N levels),
and that you want to re-quantize to go between 0 and M-1 (M levels).
Let's say that the final M levels should
be uniformly spaced on the same range as the input,
so the increment between levels will be (N-1)/(M-1).
Begin by just truncating the input to M-1 levels by doing an integer
division by M-1, and multiplying the result by (N-1)/(M-1).  Then compute
the error image by subtraction.  Now dither the error image down
to 1 bit, and combine with the original quantized image.

I have left out a little detail about scaling to avoid getting M+1 bits in
the result, but that is the gist of it.

-- 

	Jeff Mulligan (jbm@aurora.arc.nasa.gov)
	NASA/Ames Research Ctr., Mail Stop 239-3, Moffet Field CA, 94035
	(415) 694-6290

dal@midgard.Midgard.MN.ORG (Dale Schumacher) (04/28/89)

In article <5237@hubcap.clemson.edu> jcocon%hazel.cs.clemson.edu@hubcap.clemson.edu writes:
|Has any work been done on displaying 256 grey shades on a monitor with
|greater than two grey shades (i.e. black and white + some number of greys)?
|It seems that an extension to dither would be a good place to start.  So
|far as I know, rounding is the norm in hardware (yuck!  a good dither looks
|better than some 16 grey renderings I've seen!).

This program is short, so I though I'd just post it.  It uses the PXM file
format libraries I posted earlier, but the algorithm is easily separated
from the file format details.  This program dithers to n levels of grey,
default 2, and creates output in the range 0..255 from similar input.

------------------------------------------------------------------------
/*
 * dither -- use a reduced set of grey values to represent an image
 *
 * Copyright (C) 1988 by Dale Schumacher.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  This software is provided "as is" without express or
 * implied warranty.
 *
 *
 * Notes:
 *
 * [1] Floyd-Steinberg dither:
 *  I should point out that the actual fractions we used were, assuming
 *  you are at X, moving left to right:
 *
 *		    X     7/16
 *	     3/16   5/16  1/16    
 *
 *  Note that the error goes to four neighbors, not three.  I think this
 *  will probably do better (at least for black and white) than the
 *  3/8-3/8-1/4 distribution, at the cost of greater processing.  I have
 *  seen the 3/8-3/8-1/4 distribution described as "our" algorithm before,
 *  but I have no idea who the credit really belongs to.

 *  Also, I should add that if you do zig-zag scanning (see my immediately
 *  previous message), it is sufficient (but not quite as good) to send
 *  half the error one pixel ahead (e.g. to the right on lines you scan
 *  left to right), and half one pixel straight down.  Again, this is for
 *  black and white;  I've not tried it with color.
 *  -- 
 *					    Lou Steinberg
 */

static char	_Program[] = "dither";
static char	_Version[] = "1.3";
static char	_Copyright[] = "Copyright 1988 by Dale Schumacher";

#include <stdio.h>
#include <ctype.h>
#include "pxm.h"

#define	DEBUG(x)	if(0)/* x */

#define	range	255		/* maximum pixel output value */

int	levels = 2;		/* number of output levels */

banner()
	{
	printf("%s v%s -- %s\n", _Program, _Version, _Copyright);
	}

usage()
	{
	fprintf(stderr,
		"usage: %s [-n levels] inpxm outpxm\n",
		_Program);
	exit(1);
	}

main(argc, argv)
	int argc;
	char *argv[];
	{
	register PX_DEF *ipx, *opx;
	FILE *f;
	register int c;
	register char *p;
	extern int optind;
	extern char *optarg;

	while((c = getopt(argc, argv, "n:V")) != EOF)
		{
		switch(c)
			{
			case 'n':
				levels = atoi(optarg);
				break;
			case 'V':
				banner();
				exit(0);
			case '?':
			default:
				usage();
			}
		}
	if((argc - optind) < 2)
		usage();
	else
		{
		ipx = px_ropen(argv[optind++]);
		opx = px_wopen(argv[optind++],
			(PXT_PIX | PXT_BIN),
			ipx->px_width, ipx->px_height, 8, NULL);
		dither(ipx, opx);
		px_close(ipx);
		px_close(opx);
		}
	exit(0);
	}

dither(ipx, opx)
	PX_DEF *ipx, *opx;
	{
	unsigned int xsize, ysize;
	unsigned int y, x;
	register int xslop, *yslop, dslop;
	register int i, j, k, t, q;
	register PX_BYTE *ibuf, *obuf;

	xsize = ipx->px_width;
	ysize = ipx->px_height;
	ibuf = px_rowalloc(xsize, ipx->px_psize);
	obuf = px_rowalloc(xsize, opx->px_psize);
	yslop = (int *) px_alloc(xsize * sizeof(int));

	t = ((range + 1) * 2) / levels;		/* threshold factor */
	q = range / (levels - 1);		/* quantization factor */
	j = (9 * range) / 32;
	for(x=0; x<xsize; ++x)
		yslop[x] = j;

	for(y=0; y<ysize; ++y)
		{
		xslop = (7 * range) / 32;
		dslop = range / 32;
		px_rrow(ipx, ibuf);
		for(x=0; x<xsize; ++x)
			{
			i = px_rgrey(ipx, ibuf, x);
			i += xslop + yslop[x];
			j = (i / t) * q;
			if(j > range)		/* quick hack to fix overflow */
				j = range;	/* which shouldn't happen :-) */
			px_wgrey(opx, obuf, x, j);
			i = i - j;
			k = (i >> 4);		/* (i / 16) */
			xslop = 7 * k;
			yslop[x] = (5 * k) + dslop;
			if(x > 0)
				yslop[x-1] += 3 * k;
			dslop = i - (15 * k);
			}
		px_wrow(opx, obuf);
		}
	}
------------------------------------------------------------------------

ksbooth@watcgl.waterloo.edu (Kelly Booth) (04/28/89)

> * Copyright (C) 1988 by Dale Schumacher.

What does something like this mean on a net posting?