使用 C 的 24 位 bmp 中的边缘检测

Posted

技术标签:

【中文标题】使用 C 的 24 位 bmp 中的边缘检测【英文标题】:Edge detection in 24-bit bmp using C 【发布时间】:2021-01-24 00:41:19 【问题描述】:

你好吗?我有一个关于我一直在学习的算法的问题。它是一种比较简单的算法,有助于检测图像的边缘。

总的来说,该算法的工作原理如下:它采用任意尺寸的 24 位 .bmp 图像,并应用 Sobel 算子来检测图像中的边缘。

使用下面的代码,我几乎得到了令人满意的结果。

#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;

我还发送了原始图像和修改图像的示例。 如您所见,修改后的图像被裁剪。 两张图片具有相同的尺寸,但修改后的图片会出现裁剪。

我对它可能发生的假设:

    滥用 calloc() 没有提供足够的内存来存储修改后的图像 填充问题

我遇到这个问题很长时间了,我想向社区寻求帮助以解决这个问题并提高我对 C 的熟练程度。

【问题讨论】:

它看起来像一个索引/索引边界问题。考虑到这一点,我发现当你写出修改后的图像时,你会遍历 padding 行而不是 height 行。 John Bollinger 是正确的,您用于写入数据的循环是可疑的。外部循环在width 上进行迭代,而它应该在height 上进行迭代。然后代码在像素之前输出填充。填充在行尾,但我希望你很幸运,填充为 0。所以裁剪的原因是你只输出 width 行而不是 height 行。 【参考方案1】:

裁剪输出的原因是user3386109说的:写输出BMP时,外循环

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

应该迭代到height,而不是width。 BMP 从底行开始存储,这就是图像顶部的一部分丢失的原因。

关于过滤的小评论:有一个检查似乎打算排除一个像素边距以进行边界处理,

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

请注意在右边缘和下边缘有一个差一个错误。由于lin 迭代lin &lt; height,所以底行是lin == height - 1,而不是height。同样col == width - 1 是最右边的列。

【讨论】:

以上是关于使用 C 的 24 位 bmp 中的边缘检测的主要内容,如果未能解决你的问题,请参考以下文章

matlab 图像边缘检测

Canny边缘检测算法的实现

Canny边缘检测算法的实现

OpenCV2马拉松第17圈——边缘检測(Canny边缘检測)

Atitit 边缘检测原理attilax总结

图像边缘检測小结