Originally Posted by
Mcdom34
What am I doing wrong?
Well, you asked. Immediately pokes my eye:
- Using scanf() for file names. They can contain spaces, you know, and they might be longer than the buffer you've reserved for them.
I'd just put the parameters on the command line (argv[]), to avoid the prompts altogether. - Not checking scanf() return values. It returns the number of successful conversions. If it returns less than that, or EOF, some of the parameters were not assigned to.
- sizeof (char) == sizeof (unsigned char) == 1. Not an error to use, but looks weird.
- Using a single buffer, instead of separate buffers for the source and target (filtered, result) images. That means your filter calculates the results based on a mix of source and already filtered pixels. I'd use a struct to describe each image instead, to save the amount of code I'd need to write.
Personally, I'd use an image structure and accessor functions, something like the following:
Code:
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef unsigned char pixel;
typedef struct {
int width;
int height;
size_t stride;
pixel *data;
} image;
int create_image(image *const img, const int width, const int height)
{
size_t stride = width;
pixel *data;
if (!img || width < 1 || height < 1)
return errno = EINVAL;
data = malloc(sizeof (image) + stride * (size_t)height * sizeof (pixel));
if (data == NULL)
return errno = ENOMEM;
img->width = width;
img->height = height;
img->stride = stride;
img->data = data;
return 0;
}
int getpixel(const image *const img, const int x, const int y, pixel *const p)
{
if (!img || x < 0 || y < 0 || x >= img->width || y >= img->height)
return 0;
*p = img->data[(size_t)y * img->stride + (size_t)x];
return 1;
}
int setpixel(image *const img, const int x, const int y, const pixel p)
{
if (!img || x < 0 || y < 0 || x >= img->width || y >= img->height)
return 0;
img->data[(size_t)y * img->stride + (size_t)x] = p;
return 1;
}
Both getpixel() and setpixel() return 1 if the coordinates were inside the image, 0 otherwise. Note that the final parameter to getpixel() is a pointer to a result pixel, whereas for setpixel() it is the pixel itself.
To calculate each filtered pixel, you'll need to use a temporary variable to hold the filtered pixel value, at higher range and preferably precision than the source/target (so, maybe float or double type), as well as the filter weights. For example, one part of applying a filter kernel to an image to obtain a filtered pixel could be
Code:
double pixel_sum = 0.0; /* Could also be a fixed value */
double weight_sum = 0.0; /* Could also be nonzero to "blend" to that fixed value */
pixel temp;
if (getpixel(source, x-1, y-1, &temp)) {
const double w = 0.25; /* -1,-1 has weight 0.25 */
pixel_sum += w * temp;
weight_sum += w;
}
but I'd put the weights in an array (forming the filter kernel, obviously), and have a double loop over the filter kernel using essentially the if condition above.
After all the weights in the filter kernel have been checked, you clamp the result (in case the weights might cause an overflow), and set the pixel in the target image:
Code:
if (weight_sum != 0.0) {
pixel_sum /= weight_sum;
if (pixel_sum < 0.5)
putpixel(target, x, y, (pixel)0);
else
if (pixel_sum < 255.5)
putpixel(target, x, y, (pixel)(0.5 + pixel_sum));
else
putpixel(target, x, y, (pixel)255);
} else
putpixel(target, x, y, (pixel)UNFILTERED_PIXEL);
This assumes 8-bit pixels. The UNFILTERED_PIXEL value is there in case the weights cancel each other. For example, if you had a filter that had only nonzero weights in one quarter, or both positive and negative weights, the total weight could become zero. In that case, you'll want to use a fixed pixel value.
Speed and efficiency should not be a factor here, clarity and ease of understanding is. There are ways to speed filtering up, and use not much memory, but it is an advanced topic and involves quite complicated code and techniques. Yet, even "slow" computers are fast enough to apply quite complicated filters in very large files, using the above unoptimized approach, without the user having to wait for it. (We humans have reaction time, and if something happens in a fraction of a second, and we only need it now and then, not continuously, we tend to "feel" it is instant.)