Edge detection in 24-bit bmp using C

Viewed 873

how are you? I have a question about an algorithm that I've been learning. It is the relatively simple algorithm, which helps to detect the edges of the image.

In summary, the algorithm works like this: it takes a 24-bit .bmp image of arbitrary dimensions and applies the Sobel operator to detect the edges in the image.

I almost managed to get satisfactory results with the code below.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>


[![#pragma pack(push, 1)
    typedef struct 
    {
      char bitmapSignatureBytes\[2\];
      uint32_t sizeOfBitmapImageBytes;
      uint16_t reserved1;
      uint16_t reserved2;
      uint32_t pixelOffset;][1]][1]
      
}bmpFileHeader;
#pragma pack(pop)
#pragma pack(push, 1)
typedef struct
{
  uint32_t  dib_header_size;  // DIB Header size in bytes (40 bytes)
  int32_t   width;         // Width of the image
  int32_t   height;        // Height of image
  uint16_t  num_planes;       // Number of color planes
  uint16_t  bits_per_pixel;   // Bits per pixel
  uint32_t  compression;      // Compression type
  uint32_t  image_size_bytes; // Image size in bytes
  int32_t   x_resolution_ppm; // Pixels per meter
  int32_t   y_resolution_ppm; // Pixels per meter
  uint32_t  num_colors;       // Number of colors  
  uint32_t  important_colors; // Important colors 
  
}bmpInfoHeader;
#pragma pack(pop)

#pragma pack(push,1)
typedef struct 
{
  uint8_t blue;
  uint8_t green;
  uint8_t red; 
  
}pixel;

#pragma pack(pop)

int randNum(void);
int main(void){
  
  bmpFileHeader myBmpFileHeader;
  bmpInfoHeader myBmpInfoHeader;


  FILE *bmpImage = fopen("work.bmp", "rb");
  FILE *newBmpImage = fopen("border_work.bmp", "wb");

  if (bmpImage == NULL)
  {
    printf("Error occured when opening file\n");
  }
    


    fread(&myBmpFileHeader, sizeof(myBmpFileHeader), 1, bmpImage);
    fread(&myBmpInfoHeader, sizeof(myBmpInfoHeader), 1, bmpImage);

    if (myBmpFileHeader.bitmapSignatureBytes[0]==0x42 && myBmpFileHeader.bitmapSignatureBytes[1]==0x4D && myBmpInfoHeader.dib_header_size == 40 && myBmpInfoHeader.bits_per_pixel == 24 && myBmpInfoHeader.compression ==0 )
    {
      printf(" File is probably BMP\n");
    }else{
      printf("Error\n");
    }
    int width = myBmpInfoHeader.width;
    //printf("Width %i\n", width );
    int height = abs(myBmpInfoHeader.height);
    //printf("Height: %i\n", height );


    pixel(*image)[width] = calloc(height, width * sizeof(pixel));
    pixel(*image_blur)[width] = calloc(height, width * sizeof(pixel));

    int padding = (4 - (width * sizeof(pixel)) % 4) % 4;

    for (int i = 0; i < height; ++i)
    {
      fread(image[i], sizeof(pixel), width, bmpImage);
      fseek(bmpImage, padding, SEEK_CUR);

    }

    int gx[3][3];
    int gy[3][3];

    gx[0][0] = -1;
    gx[0][1] = 0;
    gx[0][2] = 1;

    gx[1][0] = -2;
    gx[1][1] = 0;
    gx[1][2] = 2;

    gx[2][0] = -1;
    gx[2][1] = 0;
    gx[2][2] = 1;


    gy[0][0] = -1;
    gy[0][1] = -2;
    gy[0][2] = -1;

    gy[1][0] = 0;
    gy[1][1] = 0;
    gy[1][2] = 0;

    gy[2][0] = 1;
    gy[2][1] = 2;
    gy[2][2] = 1;

    int gxValBlue;
    int gyValBlue;

    int gxValGreen;
    int gyValGreen;

    int gxValRed;
    int gyValRed;

    int squaredBlue;
    int squaredGreen;
    int squaredRed;



    

       for (int lin = 0; lin < height; ++lin)
    {

      for (int col = 0; col < width; ++col)
      {



        if (lin !=0 && lin != height && col != 0 && col != width)// tem todos os vizinhos
        {




          gxValBlue = (image[lin-1][col-1].blue * gx[0][0] + image[lin-1][col].blue * gx[0][1] + image[lin-1][col+1].blue * gx[0][2] + image[lin][col-1].blue * gx[1][0] + image[lin][col].blue * gx[1][1] + image[lin][col+1].blue * gx[1][2] + image[lin-1][col-1].blue * gx[2][0] + image[lin+1][col].blue * gx[2][1] + image[lin+1][col+1].blue * gx[2][2]);
          gyValBlue = (image[lin-1][col-1].blue * gy[0][0] + image[lin-1][col].blue * gy[0][1] + image[lin-1][col+1].blue * gy[0][2] + image[lin][col-1].blue * gy[1][0] + image[lin][col].blue * gy[1][1] + image[lin][col+1].blue * gy[1][2] + image[lin-1][col-1].blue * gy[2][0] + image[lin+1][col].blue * gy[2][1] + image[lin+1][col+1].blue * gy[2][2]);
          
          squaredBlue = (int)sqrt(gxValBlue*gxValBlue + gyValBlue*gyValBlue);

          gxValGreen = (image[lin-1][col-1].green * gx[0][0] + image[lin-1][col].green * gx[0][1] + image[lin-1][col+1].green * gx[0][2] + image[lin][col-1].green * gx[1][0] + image[lin][col].green * gx[1][1] + image[lin][col+1].green * gx[1][2] + image[lin-1][col-1].green * gx[2][0] + image[lin+1][col].green * gx[2][1] + image[lin+1][col+1].green * gx[2][2]);
          gyValGreen = (image[lin-1][col-1].green * gy[0][0] + image[lin-1][col].green * gy[0][1] + image[lin-1][col+1].green * gy[0][2] + image[lin][col-1].green * gy[1][0] + image[lin][col].green * gy[1][1] + image[lin][col+1].green * gy[1][2] + image[lin-1][col-1].green * gy[2][0] + image[lin+1][col].green * gy[2][1] + image[lin+1][col+1].green * gy[2][2]);
          
          squaredGreen = (int)sqrt(gxValGreen*gxValGreen + gyValGreen*gyValGreen);

          gxValRed = (image[lin-1][col-1].red * gx[0][0] + image[lin-1][col].red * gx[0][1] + image[lin-1][col+1].red * gx[0][2] + image[lin][col-1].red * gx[1][0] + image[lin][col].red * gx[1][1] + image[lin][col+1].red * gx[1][2] + image[lin-1][col-1].red * gx[2][0] + image[lin+1][col].red * gx[2][1] + image[lin+1][col+1].red * gx[2][2]);
          gyValRed = (image[lin-1][col-1].red * gy[0][0] + image[lin-1][col].red * gy[0][1] + image[lin-1][col+1].red * gy[0][2] + image[lin][col-1].red * gy[1][0] + image[lin][col].red * gy[1][1] + image[lin][col+1].red * gy[1][2] + image[lin-1][col-1].red * gy[2][0] + image[lin+1][col].red * gy[2][1] + image[lin+1][col+1].red * gy[2][2]);
          
          squaredRed = (int)sqrt(gxValRed*gxValRed + gyValRed*gyValRed);

         

          if (squaredBlue > 255)
          {
            image_blur[lin][col].blue = 255;
          }else{
            image_blur[lin][col].blue = squaredBlue;
          }

         

          if (squaredGreen > 255)
          {
            image_blur[lin][col].green = 255;
          }else{
            image_blur[lin][col].green = squaredGreen;
          }


          if (squaredRed > 255)
          {
            image_blur[lin][col].red = 255;
          }else{
            image_blur[lin][col].red = squaredRed;
          }
          
          
          
          

        }else { // bottom

          
          image_blur[lin][col].blue = 0;
          image_blur[lin][col].green = 0;
          image_blur[lin][col].red = 0;


        }




      
    }
 
      }
fwrite(&myBmpFileHeader, sizeof(myBmpFileHeader),1, newBmpImage);
fwrite(&myBmpInfoHeader, sizeof(myBmpInfoHeader), 1, newBmpImage);


for (int i = 0; i < width; ++i)
{
  
  for (int k = 0; k < padding; ++k)
  {
    fputc(0x00, newBmpImage);
  }
  fwrite(image_blur[i], sizeof(pixel), width, newBmpImage);
}


fclose(newBmpImage);
fclose(bmpImage);
free(image);
free(image_blur);
return 0;
}

I am also sending an example of the original image and modified image. As you can see, the modified image is cropped. The two images have the same dimensions, but the modified image appears cropped.

My assumptions that it may be happening:

  1. misuse of calloc () that is not providing enough memory to store the modified image
  2. problem with padding

I have had this problem for a long time and I would like to ask the community for help to resolve this issue and raise my level of proficiency in C. Here is an example of output image

1 Answers

The reason for the cropped output is what user3386109 said: when writing the output BMP, the outer loop

for (int i = 0; i < width; ++i)

should iterate up to height, not width. BMPs are stored starting from the bottom row, this is why a portion of the top of the image went missing.

A minor comment about the filtering: there is a check that appears to intend to exclude a one-pixel margin for boundary handling,

if (lin !=0 && lin != height && col != 0 && col != width)

Beware there is an off-by-one error on the right and bottom edges. Since lin iterates for lin < height, the bottom row is lin == height - 1, not height. Similarly col == width - 1 is the rightmost column.

Related