在 C 中的 24/32 位 BMP 图像上应用高斯模糊滤镜

Apply gaussian blur filter on 24/32 bits BMP images in C

我使用以下结构在内存中加载一个 bmp 图像:

// header
typedef struct {
    uint16_t type;  // Magic identifier
    uint32_t size;  // File size in bytes
    uint16_t reserved1;  // Not used
    uint16_t reserved2; // Not used
    uint32_t offset; 
    uint32_t header_size; // Header size in bytes
    uint32_t width;  // Width of the image
    uint32_t height; // Height of image
    uint16_t planes; // Number of color planes
    uint16_t bits; // Bits per pixel
    uint32_t compression;// Compression type
    uint32_t imagesize; // image size in bytes
    uint32_t xresol; // pixels per meter
    uint32_t yresol; // pixels per meter
    uint32_t ncolours;  // nr of colours
    uint32_t importantcolours; // important colours
} BMP_Header;

// image details
typedef struct {
    BMP_Header header;
    uint64_t data_size;
    uint64_t width;
    uint64_t height;
    uint8_t *data;// allocate memory based on width and height 
} BMP_Image;

然后我想对其应用高斯滤波器。 关联的函数如下:

double gaussianModel(double x, double y, double variance) {
    return 1 / (2 * 3.14159 * pow(variance, 2)) * exp(-(x * x + y * y) / (2 * variance * variance));
}

double *generate_weights(int radius, double variance, int bits_nr) {
    double *weights = (double*)malloc(sizeof(double) * radius * radius * bits_nr);
    double sum = 0;
    for (int i = 0; i < radius; i++) {
        for (int j = 0; j < radius * bits_nr; j++) {
            weights[i * radius * bits_nr + j] = gaussianModel(i - radius / 2, j - radius / 2, variance);
            sum += weights[i * radius + j];
        }
    }

    // normalize
    for (int i = 0; i < radius * radius; i++)
        weights[i] /= sum;

    return weights;
}

double getWeightedColorValue(double *w, int len, unsigned int index, unsigned int bits_nr) {
    double sum = 0;
    for (int i = index; i < len; i+= bits_nr)
        sum += w[i];

    return sum;
}

BMP_Image* BMP_blur_collapsed(BMP_Image *img, unsigned int bits_nr, unsigned int radius, unsigned int th_number, FILE* f) {
    BMP_Image *bluredImg = malloc(sizeof(BMP_Image));
    bluredImg->header = img->header; 
    bluredImg->data_size = (img->header.size - sizeof(BMP_Header)) * bits_nr;
    bluredImg->width = img->header.width;
    bluredImg->height = img->header.height;
    bluredImg->data = malloc(sizeof(uint8_t) * bluredImg->data_size);

    for(uint64_t i = 0; i < bluredImg->data_size; ++i)
        bluredImg->data[i] = img->data[i];

    double variance = 1.94;
    double* weights = generate_weights(radius, variance, bits_nr);
    uint64_t i, j;
    double start, end;
    start = omp_get_wtime();

    #pragma omp parallel for private(i, j) collapse(2) schedule(static) num_threads(th_number)
    for (i = 0; i < img->height - radius; i++) {
        for (j = 0; j < img->width * bits_nr - radius - bits_nr * 2; j+= bits_nr ) {
            uint64_t ofs = i * img->width * bits_nr + j;

            double *distributedColorRed = (double*)malloc(sizeof(double) * radius * radius * bits_nr);
            double *distributedColorGreen = (double*)malloc(sizeof(double) * radius * radius * bits_nr);
            double *distributedColorBlue = (double*)malloc(sizeof(double) * radius * radius * bits_nr);
            
            for (int wx = 0; wx < radius; wx++) {
                for (int wy = 0; wy < radius * bits_nr; wy += bits_nr) {
                    uint64_t wofs = wx * img->width * bits_nr + wy;
                    // double currWeight = weights[wx * radius + wy];

                    distributedColorRed[wofs] = weights[wx * radius + wy] * img->data[ofs];
                    distributedColorGreen[wofs + 1] = weights[wx * radius + wy + 1] * img->data[ofs + 1];
                    distributedColorBlue[wofs + 2] = weights[wx * radius + wy + 2] * img->data[ofs + 2];
                }
            }

            bluredImg->data[ofs] = getWeightedColorValue(distributedColorRed, radius * radius * bits_nr, 0, bits_nr);
            bluredImg->data[ofs + 1] = getWeightedColorValue(distributedColorGreen, radius * radius * bits_nr, 1, bits_nr);
            bluredImg->data[ofs + 2] = getWeightedColorValue(distributedColorBlue, radius * radius * bits_nr, 2, bits_nr);

            free(distributedColorRed);
            free(distributedColorBlue);
            free(distributedColorGreen);
        }
    }

    end = omp_get_wtime();
    fprintf(f, "blur collapsed %f \n", end - start);

    free(weights);
    return bluredImg;
}

假设img是一个BMP_Image对象,那么img->data已经存储了里面的所有像素:

img->data[i] is Red
img->data[i + 1] is Green
img->data[i + 2] is Blue

但是我使用这个函数后的输出不是预期的。

  • 你的图像与滤波系数的卷积看起来很奇怪。 一个是因为你使用变量 bits_nr 在使索引复杂化的计算中。
  • 处理颜色分量 r、g 和 b 分别.
  • 变量名variance不合适因为variance = sigma ** 2.
  • 您不需要使用 1/sqrt(2*PI)/sigma 进行乘法运算 在 gaussianModel() 中因为它在归一化中被取消了。

这是一个完全可编译的代码示例:

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

#define SIGMA 1.94
#define RADIUS 5
#define INFILE "0Jayx.bmp"
#define OUTFILE "blurred.bmp"

// header
typedef struct  __attribute__((__packed__)) {
    uint16_t type;  // Magic identifier
    uint32_t size;  // File size in bytes
    uint16_t reserved1;  // Not used
    uint16_t reserved2; // Not used
    uint32_t offset;
    uint32_t header_size; // Header size in bytes
    uint32_t width;  // Width of the image
    uint32_t height; // Height of image
    uint16_t planes; // Number of color planes
    uint16_t bits; // Bits per pixel
    uint32_t compression;// Compression type
    uint32_t imagesize; // image size in bytes
    uint32_t xresol; // pixels per meter
    uint32_t yresol; // pixels per meter
    uint32_t ncolours;  // nr of colours
    uint32_t importantcolours; // important colours
} BMP_Header;

// image details
typedef struct {
    BMP_Header header;
    uint64_t width;
    uint64_t height;
    uint8_t *b;
    uint8_t *g;
    uint8_t *r;
} BMP_Image;

double gaussianModel(double x, double y, double sigma) {
    return 1. / exp(-(x * x + y * y) / (2 * sigma * sigma));
}

double *generate_coeff(int radius, double sigma) {
    double *coeff = malloc(sizeof(double) * radius * radius);
    double sum = 0;
    for (int i = 0; i < radius; i++) {
        for (int j = 0; j < radius; j++) {
            coeff[i * radius + j] = gaussianModel(i - radius / 2, j - radius / 2, sigma);
            sum += coeff[i * radius + j];
        }
    }

    // normalize
    for (int i = 0; i < radius * radius; i++)
        coeff[i] /= sum;

    return coeff;
}

BMP_Image *BMP_blur_collapsed(BMP_Image *img, int radius, double sigma) {
    BMP_Image *bimg = malloc(sizeof(BMP_Image));
    bimg->header = img->header;
    bimg->width = img->header.width;
    bimg->height = img->header.height;
    bimg->b = malloc(bimg->width * bimg->height);
    bimg->g = malloc(bimg->width * bimg->height);
    bimg->r = malloc(bimg->width * bimg->height);

    int b, g, r;

    double *coeff = generate_coeff(radius, sigma);
    int i, j, m, n;

    for (i = 0; i < img->height - radius; i++) {
        for (j = 0; j < img->width - radius; j++) {
            b = g = r = 0;
            for (m = 0; m < radius; m++) {
                for (n = 0; n < radius; n++) {
                     b += coeff[m * radius + n] * img->b[(i + m) * img->width + (j + n)];
                     g += coeff[m * radius + n] * img->g[(i + m) * img->width + (j + n)];
                     r += coeff[m * radius + n] * img->r[(i + m) * img->width + (j + n)];
                }
            }
            bimg->b[i * bimg->width + j] = b;
            bimg->g[i * bimg->width + j] = g;
            bimg->r[i * bimg->width + j] = r;
        }
    }
    free(coeff);

    return bimg;
}

void free_img(BMP_Image *img)
{
    free(img->b);
    free(img->g);
    free(img->r);
    free(img);
}

BMP_Image *read_bmp_file(char *filename)
{
    FILE *fp;
    int i, j, bytesperline;
    BMP_Image *img;

    img = malloc(sizeof(BMP_Image));

    if (NULL == (fp = fopen(filename, "r"))) {
        perror(filename);
        exit(1);
    }
    fread(&img->header, sizeof(char), sizeof(BMP_Header), fp);
    img->width = img->header.width;
    img->height = img->header.height;

    bytesperline = ((img->width * 3 + 3) / 4) * 4;      // word alignment

    img->b = malloc(img->width * img->height);
    img->g = malloc(img->width * img->height);
    img->r = malloc(img->width * img->height);

    for (i = 0; i < img->height; i++) {
        for (j = 0; j < img->width; j++) {
            img->b[i * img->width + j] = getc(fp);
            img->g[i * img->width + j] = getc(fp);
            img->r[i * img->width + j] = getc(fp);
        }
        for (j = img->width * 3; j < bytesperline; j++) {
            getc(fp);
        }
    }
    return img;
}

void write_bmp_file(BMP_Image *img, char *filename)
{
    FILE *fp;
    int i, j, bytesperline;

    if (NULL == (fp = fopen(filename, "w"))) {
        perror(filename);
        exit(1);
    }
    bytesperline = ((img->width * 3 + 3) / 4) * 4;      // word alignment
    fwrite(&img->header, sizeof(char), sizeof(BMP_Header), fp);

    for (i = 0; i < img->height; i++) {
        for (j = 0; j < img->width; j++) {
            putc(img->b[i * img->width + j], fp);
            putc(img->g[i * img->width + j], fp);
            putc(img->r[i * img->width + j], fp);
        }
        for (j = img->width * 3; j < bytesperline; j++) {
            putc(0, fp);
        }
    }
}

int main()
{
    BMP_Image *img = read_bmp_file(INFILE);
    BMP_Image *bimg = BMP_blur_collapsed(img, RADIUS, SIGMA);
    write_bmp_file(bimg, OUTFILE);

    free_img(img);
    free_img(bimg);

    return 0;
}

生成的图像半径=5,sigma=1.94:

[编辑]
修改代码支持32位bmp文件:

  • 删除 bytesperline 相关代码。
  • uint8_t *a; 附加到结构 BMP_Image。
  • 找到b, g, r像素处理代码,追加a处理 此后(只需更改变量名称)。

如果你熟悉patch命令,下面是补丁文件 可以应用于:

patch -u < gauss.diff

将我以前的源代码转换为 32 位版本。 (您需要修改第一行和第二行的文件名 根据您使用的源文件名。)

gauss.diff:

-- gauss.c.O   2021-12-09 08:57:36.504685533 +0900
+++ gauss.c     2021-12-09 09:49:57.342895501 +0900
@@ -36,6 +36,7 @@
     uint8_t *b;
     uint8_t *g;
     uint8_t *r;
+    uint8_t *a;
 } BMP_Image;

 double gaussianModel(double x, double y, double sigma) {
@@ -67,25 +68,28 @@
     bimg->b = malloc(bimg->width * bimg->height);
     bimg->g = malloc(bimg->width * bimg->height);
     bimg->r = malloc(bimg->width * bimg->height);
+    bimg->a = malloc(bimg->width * bimg->height);

-    int b, g, r;
+    int b, g, r, a;

     double *coeff = generate_coeff(radius, sigma);
     int i, j, m, n;

     for (i = 0; i < img->height - radius; i++) {
         for (j = 0; j < img->width - radius; j++) {
-            b = g = r = 0;
+            b = g = r = a = 0;
             for (m = 0; m < radius; m++) {
                 for (n = 0; n < radius; n++) {
                      b += coeff[m * radius + n] * img->b[(i + m) * img->width + (j + n)];
                      g += coeff[m * radius + n] * img->g[(i + m) * img->width + (j + n)];
                      r += coeff[m * radius + n] * img->r[(i + m) * img->width + (j + n)];
+                     a += coeff[m * radius + n] * img->r[(i + m) * img->width + (j + n)];
                 }
             }
             bimg->b[i * bimg->width + j] = b;
             bimg->g[i * bimg->width + j] = g;
             bimg->r[i * bimg->width + j] = r;
+            bimg->a[i * bimg->width + j] = a;
         }
     }
     free(coeff);
@@ -98,13 +102,15 @@
     free(img->b);
     free(img->g);
     free(img->r);
+    free(img->a);
     free(img);
 }

 BMP_Image *read_bmp_file(char *filename)
 {
     FILE *fp;
-    int i, j, bytesperline;
+//  int i, j, bytesperline;
+    int i, j;
     BMP_Image *img;

     img = malloc(sizeof(BMP_Image));
@@ -117,21 +123,25 @@
     img->width = img->header.width;
     img->height = img->header.height;

-    bytesperline = ((img->width * 3 + 3) / 4) * 4;      // word alignment
+//  bytesperline = ((img->width * 3 + 3) / 4) * 4;      // word alignment

     img->b = malloc(img->width * img->height);
     img->g = malloc(img->width * img->height);
     img->r = malloc(img->width * img->height);
+    img->a = malloc(img->width * img->height);

     for (i = 0; i < img->height; i++) {
         for (j = 0; j < img->width; j++) {
             img->b[i * img->width + j] = getc(fp);
             img->g[i * img->width + j] = getc(fp);
             img->r[i * img->width + j] = getc(fp);
+            img->a[i * img->width + j] = getc(fp);
         }
+/*
         for (j = img->width * 3; j < bytesperline; j++) {
             getc(fp);
         }
+*/
     }
     return img;
 }
@@ -139,13 +149,14 @@
 void write_bmp_file(BMP_Image *img, char *filename)
 {
     FILE *fp;
-    int i, j, bytesperline;
+//  int i, j, bytesperline;
+    int i, j;

     if (NULL == (fp = fopen(filename, "w"))) {
         perror(filename);
         exit(1);
     }
-    bytesperline = ((img->width * 3 + 3) / 4) * 4;      // word alignment
+//  bytesperline = ((img->width * 3 + 3) / 4) * 4;      // word alignment
     fwrite(&img->header, sizeof(char), sizeof(BMP_Header), fp);

     for (i = 0; i < img->height; i++) {
@@ -153,10 +164,13 @@
             putc(img->b[i * img->width + j], fp);
             putc(img->g[i * img->width + j], fp);
             putc(img->r[i * img->width + j], fp);
+            putc(img->a[i * img->width + j], fp);
         }
+/*
         for (j = img->width * 3; j < bytesperline; j++) {
             putc(0, fp);
         }
+*/
     }
}