0

I've posted this program several other times, but am different troubles with it. I want to write an algorithm to blur an image, and so far have gotten every pixel value of a grayscale image into a **char array. From there, my plan is to go through each pixel, look to its immediate neighboring pixels, sum the values of them AND the pixel I am currently looking at, and get the average of all of those pixels.

After that is done, I want to set the pixel I'm currently looking at to that average value. I've written partial code for this, and I believe I'm adding it up correctly, but still do not get a resulting image. Must I provide a cast to my sum before assigning it back to the pixel?

Code:

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

int main()
{
    FILE *fin, *fout;
    char path_in[64], path_out[64];
    unsigned char **rev, px;
    int sum, width, height, read, row, i, j;

    printf("Input file name: ");
    scanf("%s", path_in);
    printf("Output file name: ");
    scanf("%s", path_out);

    printf("Width of image (in pixels): ");
    scanf("%d", &width);
    printf("Height of image (in pixels): ");
    scanf("%d", &height);

    fin = fopen(path_in, "rb");
    fout = fopen(path_out, "wb");

    rev = (unsigned char **)malloc(height * sizeof(unsigned char *));
    for(i = 0; i < height; i++)
        rev[i] = (unsigned char *)malloc(width * sizeof(unsigned char));

    for(i = 0; i < height; i++)
    {
        for(j = 0; j < width; j++)
        {
            read = fread(&px, sizeof(char), 1, fin);
            rev[i][j] = px;
        }
    }

    sum = 0;
    for(i = 0; i < height; i++)
    {
        for(j = 0; j < width; j++)
        {
            //Top row of image
            if(i == 0)
            {
                if(j == 0)
                    sum = ((unsigned)rev[i][j] + (unsigned)rev[i][j + 1] + 
                            (unsigned)rev[i + 1][j] + (unsigned)rev[i + 1][j + 1]) / 4;
                else if(j == width - 1)
                    sum = ((unsigned)rev[i][j] + (unsigned)rev[i][j - 1] +
                            (unsigned)rev[i + 1][j] + (unsigned)rev[i + 1][j - 1]) / 4;
                else
                    sum = ((unsigned)rev[i][j] + (unsigned)rev[i][j - 1] + (unsigned)rev[i][j + 1] +
                            (unsigned)rev [i + 1][j] + (unsigned)rev[i + 1][j - 1] + (unsigned)rev[i + 1][j + 1]) / 6;
            }
            //Bottom row of image
            else if(i == height - 1)
            {
                if(j == 0)
                    sum = ((unsigned)rev[i][j] + (unsigned)rev[i][j + 1] + 
                            (unsigned)rev[i - 1][j] + (unsigned)rev[i - 1][j + 1]) / 4;
                else if(j == width - 1)
                    sum = ((unsigned)rev[i][j] + (unsigned)rev[i][j - 1] +
                            (unsigned)rev[i - 1][j] + (unsigned)rev[i - 1][j - 1]) / 4;
                else
                    sum = ((unsigned)rev[i][j] + (unsigned)rev[i][j - 1] + (unsigned)rev[i][j + 1] +
                            (unsigned)rev[i - 1][j] + (unsigned)rev[i - 1][j - 1] + (unsigned)rev[i - 1][j + 1]) / 6;
            }
            //Left side of image (excluding top or bottom row)
            else if(j == 0)
                sum = ((unsigned)rev[i][j] + (unsigned)rev[i - 1][j] + (unsigned)rev[i + 1][j] +
                        (unsigned)rev[i][j + 1] + (unsigned)rev[i - 1][j + 1] + (unsigned)rev[i + 1][j + 1]) / 6;
            //Right side of image (excluding top or bottom row)
            else if(j == width - 1)
                sum = ((unsigned)rev[i][j] + (unsigned)rev[i - 1][j] + (unsigned)rev[i + 1][j] + 
                        (unsigned)rev[i][j - 1] + (unsigned)rev[i - 1][j - 1] + (unsigned)rev[i + 1][j - 1]) / 6;
            rev[i][j] = (unsigned char)sum;         
        }
    }

    for(i = 0; i < height; i++)
    {
        for(j = 0; j < width; j++)
        {
            if(j < width && i < height)
                fwrite(&rev[i][j], sizeof(char), 1, fout);
        }
    }

    fclose(fout);
    fclose(fin);

    return 0;
}

Note: this code only works with .raw grayscale images.

EDIT: I've implemented blurring to the rest of the image, but am still not getting a readable output file. I'm not sure if my arithmetic is correct, as I'm not too familiar with adding bytes together.

Delfino
  • 967
  • 4
  • 21
  • 46
  • 2
    You should not write to the same image you read from. – n0rd May 01 '15 at 00:16
  • Also you process only pixels when `i==0`. Did you try stepping through your code with debugger? Try it, it helps a lot. – n0rd May 01 '15 at 00:19
  • Why char * * and not char *?. See my reply here: http://stackoverflow.com/questions/29979411/reproducing-an-image-using-c ... – Sir Jo Black May 01 '15 at 00:40
  • I'm writing to a different file, but of the same `.raw` type. The user can specify the output file. – Delfino May 01 '15 at 01:07
  • 1
    What I mean is that you modify `rev` in place. You change pixel values while your calculations are still in progress. If you are blurring with 8 neighbors, then each pixel affects the color of 9 pixels (its own + 8 others) and you cannot modify its value until blur values are calculated for all of those pixels (which in turn have their own neighbors). Essentially this means that you have to allocate second buffer and store blur results there instead of modifying your `rev`. It's totally unrelated to files. – n0rd May 01 '15 at 21:01
  • also, instead of having ton of near-the-edge checks in your code, just extend your image by one pixel in all directions and blur only inner portion of it. It will make your code much simpler. – n0rd May 01 '15 at 21:05

1 Answers1

1

This part

rev = (char **)malloc(height * sizeof(char *));
for(i = 0; i < width; i++)
    rev[i] = (char *)malloc(width * sizeof(char));

I think you meant

for(i = 0; i < height; i++)

Other bugs:

  • you probably want to use unsigned char as the data type
  • don't use sum +=, use sum =
  • you're only doing the blur calculation for the first row of the image
  • you might want to convert each input used in the calculation to unsigned, otherwise the values will overflow

try:

for(i = 0; i < (height - 1); i++)
{
    for(j = 0; j < (width - 1); j++)
    {
        sum = ((unsigned) rev[i][j] + (unsigned) rev[i][j + 1] + 
          (unsigned) rev[i + 1][j] + (unsigned) rev[i + 1][j + 1]) / 4;
        rev[i][j] = (unsigned char) sum;
    }
}
Jack Whitham
  • 609
  • 4
  • 9
  • `unsigned char` as the data type for `sum`? – Delfino May 01 '15 at 00:27
  • Actually, `char`s are automatically promoted to `int`s, so no casts needed (it won't overflow). But you'd want `rev` to be `unsigned char` instead of simply `char`, otherwise you'll end up summing wrong numbers. – n0rd May 01 '15 at 00:38
  • So would it be better to have `sum` be defined as `int` data type, and then cast it, or should I make it `unsigned char`? – Delfino May 01 '15 at 00:52