Below is the file 'aca319/lab8q1.c' from this revision. You can also download the file.


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#define POINTER(img,x,y)		(img+(3 * (((x)*width)+(y))) )

unsigned char *
load_ppm(const unsigned char *filename, int *width, int *height)
{
	FILE *f = fopen(filename, "r");
	int v, mv;
	unsigned char *rv;

	if (!f) {
		fprintf(stderr, "Unable to load input file: %s\n", filename);
		return NULL;
	}
	if ((fscanf(f, "P%d\n", &v) == EOF) || (v != 6)) {
		fprintf(stderr, "Wrong file version.\n");
		return NULL;
	}
	if ((fscanf(f, "%d %d\n", width, height) == EOF)) {
		fprintf(stderr, "Unable to read height and width.\n");
		return NULL;
	}
	if ((fscanf(f, "%d\n", &mv) == EOF)) {
		fprintf(stderr, "Unable to max value.\n");
		return NULL;
	}
	rv = malloc(sizeof(unsigned char) * *height * *width * 3);
	if (!rv) {
		fprintf(stderr, "Unable to allocate image buffer\n");
		return NULL;
	}
	fread(rv, sizeof(unsigned char), *height * *width * 3, f);
	return rv;
}

void
write_ppm(const unsigned char *filename, int width, int height, const unsigned char *data)
{
	FILE *out;

	out = fopen(filename, "w");
	if (!out) {
		fprintf(stderr, "Unable to open output file.\n");
		return;
	}
	fprintf(out, "P6\n%d %d\n255\n", width, height);
	fwrite(data, sizeof(unsigned char), width*height*3, out);
	fclose(out);
}

void
minimum(const unsigned char **pixels, int count, char *result)
{
	int i, p;
	for (p=0;p<3;p++) {
		result[p] = *(pixels[0] + p);
		for (i=0;i<count;i++) {
			if (*(pixels[i] + p) < result[p])
				result[p] = *(pixels[i] + p);
		}
	}
}

void
maximum(const unsigned char **pixels, int count, char *result)
{
	int i, p;
	for (p=0;p<3;p++) {
		result[p] = *(pixels[0] + p);
		for (i=0;i<count;i++) {
			if (*(pixels[i] + p) > result[p])
				result[p] = *(pixels[i] + p);
		}
	}
}

void
average(const unsigned char **pixels, int count, char *result)
{
	int i, p;

	for (p=0;p<3;p++) {
		int total = 0;
		for (i=0;i<count;i++) {
			total += *(pixels[i] + p);
		}
		result[p] = total / count;
	}
}

int
int_compare(const void *a, const void *b)
{
	int c = *(int *)a;
	int d = *(int *)b;
	return d - c;
}

void
median(const unsigned char **pixels, int count, char *result)
{
	int i, p;
	int median;

	unsigned char *s = calloc(count, sizeof(unsigned char));
	if (!s) {
		fprintf(stderr, "Out of memory in median()\n");
	}

	for (p=0;p<3;p++) {
		for (i=0;i<count;i++) {
			s[i] = *(pixels[i] + p);
		}
		qsort(s, count, sizeof(unsigned char), int_compare);
		if (count % 2) {
			/* average the two middle values to get median */
			median = (s[count/2] + s[(count/2)+1]) / 2;
		} else {
			median = s[count/2];
		}
		result[p] = median;
	}
	free(s);
}

unsigned char *
run_filter(const unsigned char *clean_image, const unsigned char *dirty_image,
		int width, int height,
		void (*filter_function)(const unsigned char **, int, char *),
		double *error)
{
	unsigned char *rv;
	int i, j;
	const unsigned char *cells[9];

	assert(clean_image != NULL);
	assert(dirty_image != NULL);
	assert(error != NULL);

	rv = calloc(sizeof(unsigned char), height * width * 3);
	if (!rv) {
		fprintf(stderr, "Out of memory in run_filter\n");
		return NULL;
	}

	fprintf(stderr, "Applying filter to image of size %dx%d\n", width, height);

	for (i=0;i<height;i++) {
		for (j=0;j<width;j++) {
			const unsigned char *clean, *unfiltered;
			int count;

			clean = POINTER(clean_image, i, j);
			unfiltered = POINTER(dirty_image, i, j);

			count = 0;
			/* this cell; always should be added */
			cells[count] = unfiltered;
			count++;
			/* the row below */
			if ((i > 0) && (j > 0)) {
				cells[count] = POINTER(dirty_image, i-1, j-1);
				count++;
			}
			if (i > 0) {
				cells[count] = POINTER(dirty_image, i-1, j);
				count++;
			}
			if ((i > 0) && (j < (width - 1))) {
				cells[count] = POINTER(dirty_image, i-1, j+1);
				count++;
			}
			/* this row */
			if (j>0) {
				cells[count] = POINTER(dirty_image, i, j-1);
				count++;
			}
			if (j<width-1) {
				cells[count] = POINTER(dirty_image, i, j+1);
				count++;
			}
			/* the row above */
			if ((i < height - 1) && (j > 0)) {
				cells[count] = POINTER(dirty_image, i+1, j-1);
				count++;
			}
			if (i < height - 1) {
				cells[count] = POINTER(dirty_image, i+1, j);
				count++;
			}
			if ((i < height - 1) && (j < (width - 1))) {
				cells[count] = POINTER(dirty_image, i+1, j+1);
				count++;
			}

			filter_function(cells, count, rv + (3 * ((i*width) + j)));
		}
	}

	return rv;
}

int
main(int argc, char *argv[])
{
	int height, width;
	double error;
	unsigned char *clean, *noisy;
	unsigned char *result;

	clean = load_ppm("ViGIR.ppm", &width, &height);
	noisy = load_ppm("ViGIRnoisy.ppm", &width, &height);

//	write_ppm("bypass.ppm", width, height, noisy);
	result = run_filter(clean, noisy, width, height, minimum, &error);
	write_ppm("minimum.ppm", width, height, result);
	free(result);

	result = run_filter(clean, noisy, width, height, maximum, &error);
	write_ppm("maximum.ppm", width, height, result);
	free(result);

	result = run_filter(clean, noisy, width, height, average, &error);
	write_ppm("average.ppm", width, height, result);
	free(result);

	result = run_filter(clean, noisy, width, height, median, &error);
	write_ppm("median.ppm", width, height, result);
	free(result);

	free(clean);
	free(noisy);

	return 0;
}