使用 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 < height
,所以底行是lin == height - 1
,而不是height
。同样col == width - 1
是最右边的列。
【讨论】:
以上是关于使用 C 的 24 位 bmp 中的边缘检测的主要内容,如果未能解决你的问题,请参考以下文章