Bmp file filter pixel adjusting / C language

Hello friends,
I am currently working on a project which is filtering a bmp file. This smoothing filter uses a 3x3 blurring kernel which is a fixed size small matrix. The convolution operation moves this kernel over the image, shifting it on pixel at a time and takes the dot product of matrix elements with the pixel values underneath. Basically this filter traverses the entire image to producing a single pixel out of 9 adjacent pixels.

In order to preserve the image size both horizontally and vertically, padding is added to the input image by adding 0 valued pixels around the edges of the image.

Related images can be seen on the links:
convolution operation image: https://i.stack.imgur.com/jIO1P.jpg
padding operation: https://i.stack.imgur.com/NAOTi.jpg

THE PROBLEM:

My code works okay except somehow it shifts an image a little bit to the right.
I could not find the reason of it. Can you help me about that.
Thanks in advance.
Output of my codes:
İnput image: https://im.ge/i/uAyBv4
output image:https://im.ge/i/uAAorP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  #include <stdio.h>
#include <stdlib.h>
#include "header.h"

typedef struct{
    uint8_t red;
    uint8_t green;
    uint8_t blue;
}rgb ;

void smoothing_filter(int height, int width, rgb image[height][width], rgb dest[height][width]);

int main() {
    FILE *inputPtr;
    if ((inputPtr = fopen("starry_night.bmp", "rb+")) == NULL) {
        printf("The input file could not be opened\n");
    } else {
        BITMAPFILEHEADER fileheader_input;
        fread(&fileheader_input, sizeof(BITMAPFILEHEADER), 1, inputPtr);
        BITMAPINFOHEADER infoheader_input;
        fread(&infoheader_input, sizeof(BITMAPINFOHEADER), 1, inputPtr);

        if (fileheader_input.bfType != 0x4d42 || fileheader_input.bfOffBits != 54 ||
            infoheader_input.biSize != 40 || infoheader_input.biBitCount != 24 || infoheader_input.biCompression != 0) {
            fclose(inputPtr);
            printf("Unsupported input file.\n");
        } else {
            int height = abs(infoheader_input.biHeight);
            int width = infoheader_input.biWidth;

            rgb (*rgb_array)[width]; // pointer to the first item in a rgb[height][width] 2D array
            rgb_array = malloc(sizeof(rgb[height][width]));
            //rgb (*rgb_array)[width] = calloc(height, width * sizeof(rgb));
            if (rgb_array == NULL){
                printf("Not enough memory rgb_array to store image.\n");
                fclose(inputPtr);
            }

            size_t result = fread(rgb_array, sizeof(rgb), height*width, inputPtr);
            if (result != height*width){
                printf("Can not read input file.\n");
                fclose(inputPtr);
            }

            infoheader_input.biWidth = width + 2;
            infoheader_input.biHeight = height + 2;
            fileheader_input.bfSize = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER) +
                                      3 * abs(infoheader_input.biWidth) * abs(infoheader_input.biHeight);
            rewind(inputPtr);

            fwrite(&(fileheader_input), sizeof(BITMAPFILEHEADER), 1, inputPtr);
            fwrite(&(infoheader_input), sizeof(BITMAPINFOHEADER), 1, inputPtr);

            rgb blanckRgb = {0, 0, 0};

            for (int i = 0; i < (width + 2); ++i) {
                fwrite(&blanckRgb, sizeof(rgb), 1, inputPtr);
            }
            for (int row = 0; row < height; ++row) {
                fwrite(&blanckRgb, sizeof(rgb), 1, inputPtr);
                for (int col = 0; col < width; ++col) {
                    fwrite(&rgb_array[row][col].red, 1, 1, inputPtr);
                    fwrite(&rgb_array[row][col].green, 1, 1, inputPtr);
                    fwrite(&rgb_array[row][col].blue, 1, 1, inputPtr);
                }
                fwrite(&blanckRgb, sizeof(rgb), 1, inputPtr);
            }
            for (int i = 0; i < (width + 2); ++i) {
                fwrite(&blanckRgb, sizeof(rgb), 1, inputPtr);
            }
            free(rgb_array);

            FILE *outputPtr;
            if ((outputPtr = fopen("starry_night_filtered.bmp", "wb")) == NULL) {
                printf("The \"starry_night_filtered.bmp\" file could not be created\n");
            }else{
                BITMAPFILEHEADER fileheader_output = fileheader_input;
                BITMAPINFOHEADER infoheader_output = infoheader_input;

                infoheader_output.biWidth = width;
                infoheader_output.biHeight = height;
                fileheader_output.bfSize = sizeof(BITMAPINFOHEADER)+sizeof(BITMAPFILEHEADER)+3*abs(width*height);

                fwrite(&(fileheader_output), sizeof(BITMAPFILEHEADER), 1, outputPtr);
                fwrite(&(infoheader_output), sizeof(BITMAPINFOHEADER), 1, outputPtr);

                rgb (*image)[infoheader_input.biWidth]; // pointer to the first item in a rgb[height][width] 2D array
                image = malloc(sizeof(rgb[infoheader_input.biHeight][infoheader_input.biWidth]));
                //rgb (*image)[infoheader_input.biWidth] = calloc(infoheader_input.biHeight, infoheader_input.biWidth * sizeof(rgb));
                if (image == NULL){
                    printf("Not enough memory image to store image.\n");
                    fclose(outputPtr);
                }

                rewind(inputPtr);

                size_t res = fread(image, sizeof(rgb), infoheader_input.biHeight*infoheader_input.biWidth, inputPtr);
                if (res != infoheader_input.biHeight*infoheader_input.biWidth){
                    printf("Can not read output file.\n");
                    fclose(outputPtr);
                }

                rgb (*dest)[width]; // pointer to the first item in a rgb[height][width] 2D array
                dest = malloc(sizeof(rgb[height][width]));
                if (dest == NULL){
                    printf("Not enough memory to store image.\n");
                    fclose(outputPtr);
                }

                smoothing_filter(infoheader_input.biHeight, infoheader_input.biWidth, image, dest);

                for (int row = 0; row < height; row++) {
                    for (int col = 0; col < width; col++) {
                        fwrite(&dest[row][col], sizeof(rgb), 1, outputPtr);
                    }
                }
                free(image);
                free(dest);
                fclose(outputPtr);
                fclose(inputPtr);
            }
        }
    }
}

void smoothing_filter(int height, int width, rgb image[height][width], rgb dest[height-2][width-2])
{
    double redVal, blueVal, greenVal;

    // Iterate through rows
    for (int row = 0; row < (height-2); row++)
    {
        // Iterate through columns
        for (int col = 0; col < (width-2); col++)
        {
            // Obtain RGB values of current image pixel
            redVal = (0.0625*image[row][col].red) + (0.125*image[row][col+1].red) + (0.0625*image[row][col+2].red)
                     +(0.125*image[row+1][col].red) + (0.25*image[row+1][col+1].red) + (0.125*image[row+1][col+2].red)
                     +(0.0625*image[row+2][col].red) + (0.125*image[row+2][col+1].red) + (0.0625*image[row+2][col+2].red);
            greenVal = (0.0625*image[row][col].green) + (0.125*image[row][col+1].green) + (0.0625*image[row][col+2].green)
                       +(0.125*image[row+1][col].green) + (0.25*image[row+1][col+1].green) + (0.125*image[row+1][col+2].green)
                       +(0.0625*image[row+2][col].green) + (0.125*image[row+2][col+1].green) + (0.0625*image[row+2][col+2].green);
            blueVal = (0.0625*image[row][col].blue) + (0.125*image[row][col+1].blue) + (0.0625*image[row][col+2].blue)
                      +(0.125*image[row+1][col].blue) + (0.25*image[row+1][col+1].blue) + (0.125*image[row+1][col+2].blue)
                      +(0.0625*image[row+2][col].blue) + (0.125*image[row+2][col+1].blue) + (0.0625*image[row+2][col+2].blue);
            dest[row][col].red = (int) redVal;
            dest[row][col].green = (int) greenVal;
            dest[row][col].blue = (int) blueVal;
        }
    }
}
Last edited on
You don't seem to be taking into account that each row of pixels is padded to 4-byte alignment.
https://en.wikipedia.org/wiki/BMP_file_format#Pixel_storage

If you treat this padding as pixels, you end up with skewed images.
Actually i padded zeros to the image in order to preserve the image size in the first place as seen on the padding operation: https://i.stack.imgur.com/NAOTi.jpg
The input and output images are not the same. The color {0, 0, 0} is black not white. So it's supposed to have a small black frame.
Yes it does has a black frame in order to preserve the original image size after the convolution operation. What is the reason of the skewed image. How can i solve that
> Actually i padded zeros to the image in order to preserve the image size in the first place as seen on the padding operation:
Well one of your BMP images is guaranteed to be broken then.
Adding a pixel at each end of a row adds 6 bytes.

6 % 4 is not zero.

Imagine a 3x3 image
RGBRGBRGB
RGBRGBRGB
RGBRGBRGB


In the BMP file, this would be written like this - each row is padded to a multiple of 4 bytes.
RGBRGBRGB000
RGBRGBRGB000
RGBRGBRGB000

None of your code takes this into account.

The bits representing the bitmap pixels are packed in rows. The size of each row is rounded up to a multiple of 4 bytes (a 32-bit DWORD) by padding.

here is my updated code but still i do not get the output that i want.
what could be the reason

the output image:https://ibb.co/4T23JZ8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#include <stdio.h>
#include <stdlib.h>
#include "header.h"

typedef struct{
    uint8_t red;
    uint8_t green;
    uint8_t blue;
}rgb ;

void smoothing_filter(int height, int width, rgb image[height][width], rgb dest[height][width]);

int main() {
    FILE *inputPtr;
    if ((inputPtr = fopen("starry_night.bmp", "rb+")) == NULL) {
        printf("The input file could not be opened\n");
    } else {
        BITMAPFILEHEADER fileheader_input;
        fread(&fileheader_input, sizeof(BITMAPFILEHEADER), 1, inputPtr);
        BITMAPINFOHEADER infoheader_input;
        fread(&infoheader_input, sizeof(BITMAPINFOHEADER), 1, inputPtr);

        if (fileheader_input.bfType != 0x4d42 || fileheader_input.bfOffBits != 54 ||
            infoheader_input.biSize != 40 || infoheader_input.biBitCount != 24 || infoheader_input.biCompression != 0) {
            fclose(inputPtr);
            printf("Unsupported input file.\n");
        } else {
            int height = abs(infoheader_input.biHeight);
            int width = infoheader_input.biWidth;

            rgb (*rgb_array)[width]; // pointer to the first item in a rgb[height][width] 2D array
            rgb_array = malloc(sizeof(rgb[height][width]));
            //rgb (*rgb_array)[width] = calloc(height, width * sizeof(rgb));
            if (rgb_array == NULL){
                printf("Not enough memory rgb_array to store image.\n");
                fclose(inputPtr);
            }

            size_t result = fread(rgb_array, sizeof(rgb), height*width, inputPtr);
            if (result != height*width){
                printf("Can not read input file.\n");
                fclose(inputPtr);
            }

            infoheader_input.biWidth = width + 2;
            infoheader_input.biHeight = height + 2;
            fileheader_input.bfSize = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER) +
                                      3 * abs(infoheader_input.biWidth) * abs(infoheader_input.biHeight);

            // Determine padding for scanlines
            unsigned int long long padding = (4 - (infoheader_input.biWidth * sizeof(rgb)) % 4) % 4;

            rewind(inputPtr);

            fwrite(&(fileheader_input), sizeof(BITMAPFILEHEADER), 1, inputPtr);
            fwrite(&(infoheader_input), sizeof(BITMAPINFOHEADER), 1, inputPtr);

            rgb blanckRgb = {0, 0, 0};

            for (int i = 0; i < (width + 2); ++i) {
                fwrite(&blanckRgb, sizeof(rgb), 1, inputPtr);
            }
            // Write padding at end of row
            for (int row = 0; row < padding; row++)
            {
                fputc(0x00, inputPtr);
            }
            for (int row = 0; row < height; ++row) {
                fwrite(&blanckRgb, sizeof(rgb), 1, inputPtr);
                for (int col = 0; col < width; ++col) {
                    fwrite(&rgb_array[row][col].red, 1, 1, inputPtr);
                    fwrite(&rgb_array[row][col].green, 1, 1, inputPtr);
                    fwrite(&rgb_array[row][col].blue, 1, 1, inputPtr);
                }
                fwrite(&blanckRgb, sizeof(rgb), 1, inputPtr);
                // Write padding at end of row
                for (int row = 0; row < padding; row++)
                {
                    fputc(0x00, inputPtr);
                }
            }
            for (int i = 0; i < (width + 2); ++i) {
                fwrite(&blanckRgb, sizeof(rgb), 1, inputPtr);
            }
            // Write padding at end of row
            for (int row = 0; row < padding; row++)
            {
                fputc(0x00, inputPtr);
            }
            
            

            free(rgb_array);

            FILE *outputPtr;
            if ((outputPtr = fopen("starry_night_filtered.bmp", "wb")) == NULL) {
                printf("The \"starry_night_filtered.bmp\" file could not be created\n");
            }else{
                BITMAPFILEHEADER fileheader_output = fileheader_input;
                BITMAPINFOHEADER infoheader_output = infoheader_input;

                infoheader_output.biWidth = width;
                infoheader_output.biHeight = height;
                fileheader_output.bfSize = sizeof(BITMAPINFOHEADER)+sizeof(BITMAPFILEHEADER)+3*abs(width*height);

                fwrite(&(fileheader_output), sizeof(BITMAPFILEHEADER), 1, outputPtr);
                fwrite(&(infoheader_output), sizeof(BITMAPINFOHEADER), 1, outputPtr);

                rgb (*image)[infoheader_input.biWidth]; // pointer to the first item in a rgb[height][width] 2D array
                image = malloc(sizeof(rgb[infoheader_input.biHeight][infoheader_input.biWidth]));
                //rgb (*image)[infoheader_input.biWidth] = calloc(infoheader_input.biHeight, infoheader_input.biWidth * sizeof(rgb));
                if (image == NULL){
                    printf("Not enough memory image to store image.\n");
                    fclose(outputPtr);
                }

                rewind(inputPtr);

                size_t res = fread(image, sizeof(rgb), infoheader_input.biHeight*infoheader_input.biWidth, inputPtr);
                if (res != infoheader_input.biHeight*infoheader_input.biWidth){
                    printf("Can not read output file.\n");
                    fclose(outputPtr);
                }

                rgb (*dest)[width]; // pointer to the first item in a rgb[height][width] 2D array
                dest = malloc(sizeof(rgb[height][width]));
                if (dest == NULL){
                    printf("Not enough memory to store image.\n");
                    fclose(outputPtr);
                }

                smoothing_filter(infoheader_input.biHeight, infoheader_input.biWidth, image, dest);

                for (int row = 0; row < height ; row++) {
                    for (int col = 0; col < width ; col++) {
                        fwrite(&dest[row][col], sizeof(rgb), 1, outputPtr);
                    }
                }
                free(image);
                free(dest);
                fclose(outputPtr);
                fclose(inputPtr);
            }
        }
    }
}

void smoothing_filter(int height, int width, rgb image[height][width], rgb dest[height-2][width-2])
{
    double redVal, blueVal, greenVal;

    // Iterate through rows
    for (int row = 0; row < (height-2); row++)
    {
        // Iterate through columns
        for (int col = 0; col < (width-2); col++)
        {
            // Obtain RGB values of current image pixel
            redVal = (0.0625*image[row][col].red) + (0.125*image[row][col+1].red) + (0.0625*image[row][col+2].red)
                     +(0.125*image[row+1][col].red) + (0.25*image[row+1][col+1].red) + (0.125*image[row+1][col+2].red)
                     +(0.0625*image[row+2][col].red) + (0.125*image[row+2][col+1].red) + (0.0625*image[row+2][col+2].red);
            greenVal = (0.0625*image[row][col].green) + (0.125*image[row][col+1].green) + (0.0625*image[row][col+2].green)
                       +(0.125*image[row+1][col].green) + (0.25*image[row+1][col+1].green) + (0.125*image[row+1][col+2].green)
                       +(0.0625*image[row+2][col].green) + (0.125*image[row+2][col+1].green) + (0.0625*image[row+2][col+2].green);
            blueVal = (0.0625*image[row][col].blue) + (0.125*image[row][col+1].blue) + (0.0625*image[row][col+2].blue)
                      +(0.125*image[row+1][col].blue) + (0.25*image[row+1][col+1].blue) + (0.125*image[row+1][col+2].blue)
                      +(0.0625*image[row+2][col].blue) + (0.125*image[row+2][col+1].blue) + (0.0625*image[row+2][col+2].blue);
            dest[row][col].red = (int) redVal;
            dest[row][col].green = (int) greenVal;
            dest[row][col].blue = (int) blueVal;
        }
    }
}
Your main is 100++ lines of copy/pasted code, and you only fixed up the padding in some of the places you needed to.

Perhaps
1
2
3
4
5
typedef struct {
    BITMAPFILEHEADER fileheader;
    BITMAPINFOHEADER infoheader;
    rgb **pixels;
} mybm;


Then write a few more functions like
- readHeader(filename,mybm*)
- readBitmap(filename,mybm*)
- writeHeader(filename,mybm*)
- writeBitmap(filename,mybm*)
- readFile(filename,mybm*)
- writeFile(filename,mybm*)

Your first test is to see if you can just do this
1
2
3
4
5
int main ( ) {
    mybm bm;
    readFile("input.bmp",&bm);
    readFile("output.bmp",&bm);
}

Without doing anything else, establish that you can reliably just read in an image and write it back out again without adding any image distortions.

Build your solid foundation first.
Is it possible that the bitmap does not have a BITMAPINFOHEADER but instead a BITMAPCOREINFO?

The BITMAPCOREINFO uses RGBTRIPLE. So it would be better to use RGBTRIPLE instead of rgb.

See:

https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapcoreinfo

Note this:
but each scan line must be padded with zeros to end on a LONG boundary


I agree with salem c. You should do this step by step:
First apply the black frame to a simple small one colored image. After this is done successfully you apply the filter.
Last edited on
Topic archived. No new replies allowed.