两帧BMP图像差分处理——C语言代码实现

    两帧差分原理基于比较连续两帧图像的像素差异,以检测图像中发生的变化。这个方法常用于运动检测、物体跟踪或其他图像变化分析。
    两帧图像都由一系列像素构成,每个像素通常有三种颜色通道(红色、绿色、蓝色,RGB)。        通常对于每一个像素,分别比较前一帧和当前帧中对应位置的像素值,使用绝对值差分计算两个像素的颜色通道差异,对红色、绿色、蓝色三个通道分别进行计算。
    当两个帧之间的像素值有明显差异时,表明图像内容在这一区域发生了变化。
    差分后的图像可以通过视觉上呈现变化的区域。没有变化的区域,像素差值为0;有变化的区域,像素差值较大。

两帧差分广泛应用于:
    运动检测:通过连续帧差分,可以检测视频中移动的物体。
    物体跟踪:分析连续帧中物体的运动路径。
    背景减除:可以通过差分帧来消除静态背景,保留运动物体。

2048*2048大小24位的BMP图像为例,实现两帧图像的差分处理:

1. 直接相减

以用时最短为目标,不进行灰度化等其他处理步骤。

// 只进行两帧差分操作 不进行灰度化处理
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#define WIDTH 2048
#define HEIGHT 2048

typedef struct {
    unsigned char b;
    unsigned char g;
    unsigned char r;
} RGBPixel;

#pragma pack(push, 1)
typedef struct {
    unsigned short bfType;
    unsigned int bfSize;
    unsigned short bfReserved1;
    unsigned short bfReserved2;
    unsigned int bfOffBits;
} BMPFileHeader;

typedef struct {
    unsigned int biSize;
    int biWidth;
    int biHeight;
    unsigned short biPlanes;
    unsigned short biBitCount;
    unsigned int biCompression;
    unsigned int biSizeImage;
    int biXPelsPerMeter;
    int biYPelsPerMeter;
    unsigned int biClrUsed;
    unsigned int biClrImportant;
} BMPInfoHeader;
#pragma pack(pop)

// 读取 BMP 图像
void load_bmp_image(const char* file_name, RGBPixel(*image)[WIDTH]) {
    FILE* file = fopen(file_name, "rb");
    if (!file) {
        printf("无法打开文件 %s\n", file_name);
        exit(1);
    }

    BMPFileHeader fileHeader;
    BMPInfoHeader infoHeader;

    fread(&fileHeader, sizeof(BMPFileHeader), 1, file);
    if (fileHeader.bfType != 0x4D42) {
        printf("文件 %s 不是有效的 BMP 文件\n", file_name);
        fclose(file);
        exit(1);
    }

    fread(&infoHeader, sizeof(BMPInfoHeader), 1, file);

    if (infoHeader.biWidth != WIDTH || infoHeader.biHeight != HEIGHT || infoHeader.biBitCount != 24) {
        printf("图像尺寸或格式不匹配\n");
        fclose(file);
        exit(1);
    }

    fseek(file, fileHeader.bfOffBits, SEEK_SET);
    fread(image, sizeof(RGBPixel), WIDTH * HEIGHT, file);

    fclose(file);
}

// 保存 BMP 图像
void save_bmp_image(const char* file_name, RGBPixel(*image)[WIDTH]) {
    FILE* file = fopen(file_name, "wb");
    if (!file) {
        printf("无法保存文件 %s\n", file_name);
        exit(1);
    }

    BMPFileHeader fileHeader;
    BMPInfoHeader infoHeader;

    fileHeader.bfType = 0x4D42;
    fileHeader.bfSize = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader) + WIDTH * HEIGHT * sizeof(RGBPixel);
    fileHeader.bfReserved1 = 0;
    fileHeader.bfReserved2 = 0;
    fileHeader.bfOffBits = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader);

    infoHeader.biSize = sizeof(BMPInfoHeader);
    infoHeader.biWidth = WIDTH;
    infoHeader.biHeight = HEIGHT;
    infoHeader.biPlanes = 1;
    infoHeader.biBitCount = 24;
    infoHeader.biCompression = 0;
    infoHeader.biSizeImage = WIDTH * HEIGHT * sizeof(RGBPixel);
    infoHeader.biXPelsPerMeter = 0;
    infoHeader.biYPelsPerMeter = 0;
    infoHeader.biClrUsed = 0;
    infoHeader.biClrImportant = 0;

    fwrite(&fileHeader, sizeof(BMPFileHeader), 1, file);
    fwrite(&infoHeader, sizeof(BMPInfoHeader), 1, file);
    fwrite(image, sizeof(RGBPixel), WIDTH * HEIGHT, file);

    fclose(file);
}

// 计算两帧的差分
void compute_frame_difference(RGBPixel(*prev_frame)[WIDTH], RGBPixel(*current_frame)[WIDTH], RGBPixel(*diff_frame)[WIDTH], double* duration) {
    clock_t start_time = clock();  // 开始时间
    for (int i = 0; i < HEIGHT; i++) {
        for (int j = 0; j < WIDTH; j++) {
            // 计算每个像素的差异 (绝对值)
            diff_frame[i][j].r = abs(current_frame[i][j].r - prev_frame[i][j].r);
            diff_frame[i][j].g = abs(current_frame[i][j].g - prev_frame[i][j].g);
            diff_frame[i][j].b = abs(current_frame[i][j].b - prev_frame[i][j].b);
        }
    }
    clock_t end_time = clock();  // 结束时间
    *duration = ((double)(end_time - start_time)) / CLOCKS_PER_SEC;  // 计算时间差并存储到指针指向的变量中
}

// 主函数
int main() {
    RGBPixel(*prev_frame)[WIDTH] = malloc(HEIGHT * sizeof(*prev_frame));
    RGBPixel(*current_frame)[WIDTH] = malloc(HEIGHT * sizeof(*current_frame));
    RGBPixel(*diff_frame)[WIDTH] = malloc(HEIGHT * sizeof(*diff_frame));
    double duration;  // 用于存储计算时间

    // 读取前一帧图像
    //load_bmp_image("target_image.bmp", prev_frame);
    load_bmp_image("target_image2048.bmp", prev_frame);

    // 读取当前帧图像
    //load_bmp_image("black_image.bmp", current_frame);
    load_bmp_image("black_image2048.bmp", current_frame);

    // 计算差分图像并记录时间
    compute_frame_difference(prev_frame, current_frame, diff_frame, &duration);

    // 输出所用时间
    printf("帧差计算所用时间: %f 秒\n", duration);

    // 保存差分图像
    save_bmp_image("frame_difference.bmp", diff_frame);

    // 释放内存
    free(prev_frame);
    free(current_frame);
    free(diff_frame);

    return 0;
}
2. 差分后进行二值化操作 
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#define WIDTH 2048
#define HEIGHT 2048

typedef struct {
    unsigned char b;
    unsigned char g;
    unsigned char r;
} RGBPixel;

typedef struct {
    int x;
    int y;
} Point2D;

#pragma pack(1)
typedef struct {
    unsigned short bfType;
    unsigned int bfSize;
    unsigned short bfReserved1;
    unsigned short bfReserved2;
    unsigned int bfOffBits;
} BMPFileHeader;

typedef struct {
    unsigned int biSize;
    int biWidth;
    int biHeight;
    unsigned short biPlanes;
    unsigned short biBitCount;
    unsigned int biCompression;
    unsigned int biSizeImage;
    int biXPelsPerMeter;
    int biYPelsPerMeter;
    unsigned int biClrUsed;
    unsigned int biClrImportant;
} BMPInfoHeader;
#pragma pack()

// 读取 BMP 图像
void load_bmp_image(const char* file_name, RGBPixel(*image)[WIDTH]) {
    FILE* file = fopen(file_name, "rb");
    if (!file) {
        printf("无法打开文件 %s\n", file_name);
        exit(1);
    }

    BMPFileHeader fileHeader;
    BMPInfoHeader infoHeader;

    fread(&fileHeader, sizeof(BMPFileHeader), 1, file);
    if (fileHeader.bfType != 0x4D42) {
        printf("文件 %s 不是有效的 BMP 文件\n", file_name);
        fclose(file);
        exit(1);
    }

    fread(&infoHeader, sizeof(BMPInfoHeader), 1, file);
    if (infoHeader.biWidth != WIDTH || infoHeader.biHeight != HEIGHT || infoHeader.biBitCount != 24) {
        printf("图像尺寸或格式不匹配\n");
        fclose(file);
        exit(1);
    }

    fseek(file, fileHeader.bfOffBits, SEEK_SET);
    fread(image, sizeof(RGBPixel), WIDTH * HEIGHT, file);

    fclose(file);
}

// 保存 BMP 图像
void save_bmp_image(const char* file_name, RGBPixel(*image)[WIDTH]) {
    FILE* file = fopen(file_name, "wb");
    if (!file) {
        printf("无法保存文件 %s\n", file_name);
        exit(1);
    }

    BMPFileHeader fileHeader;
    BMPInfoHeader infoHeader;

    fileHeader.bfType = 0x4D42;
    fileHeader.bfSize = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader) + WIDTH * HEIGHT * sizeof(RGBPixel);
    fileHeader.bfReserved1 = 0;
    fileHeader.bfReserved2 = 0;
    fileHeader.bfOffBits = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader);

    infoHeader.biSize = sizeof(BMPInfoHeader);
    infoHeader.biWidth = WIDTH;
    infoHeader.biHeight = HEIGHT;
    infoHeader.biPlanes = 1;
    infoHeader.biBitCount = 24;
    infoHeader.biCompression = 0;
    infoHeader.biSizeImage = WIDTH * HEIGHT * sizeof(RGBPixel);
    infoHeader.biXPelsPerMeter = 0;
    infoHeader.biYPelsPerMeter = 0;
    infoHeader.biClrUsed = 0;
    infoHeader.biClrImportant = 0;

    fwrite(&fileHeader, sizeof(BMPFileHeader), 1, file);
    fwrite(&infoHeader, sizeof(BMPInfoHeader), 1, file);
    fwrite(image, sizeof(RGBPixel), WIDTH * HEIGHT, file);

    fclose(file);
}

// 图像相减并进行二值化
void image_subtract(RGBPixel(*img1)[WIDTH], RGBPixel(*img2)[WIDTH], RGBPixel(*result)[WIDTH], double* duration) {
    clock_t start_time = clock();
    for (int i = 0; i < HEIGHT; i++) {
        for (int j = 0; j < WIDTH; j++) {
            int diff_r = abs(img1[i][j].r - img2[i][j].r);
            int diff_g = abs(img1[i][j].g - img2[i][j].g);
            int diff_b = abs(img1[i][j].b - img2[i][j].b);

            // 根据图像实际情况调整二值化阈值
            int avg_diff = (diff_r + diff_g + diff_b) / 3;
            if (avg_diff > THRESHOLD) {
                result[i][j].r = 255; // 白色
                result[i][j].g = 255;
                result[i][j].b = 255;
            }
            else {
                result[i][j].r = 0; // 黑色
                result[i][j].g = 0;
                result[i][j].b = 0;
            }
        }
    }
    clock_t end_time = clock();
    *duration = ((double)(end_time - start_time)) / CLOCKS_PER_SEC;
}


int main() {
    clock_t start_time, end_time;
    double cpu_time_used, subtract_duration, target_detection_duration;

    RGBPixel(*img1)[WIDTH] = malloc(HEIGHT * sizeof(*img1));
    RGBPixel(*img2)[WIDTH] = malloc(HEIGHT * sizeof(*img2));
    RGBPixel(*img_subtracted)[WIDTH] = malloc(HEIGHT * sizeof(*img_subtracted));
    Point2D centers[MAX_POINTS];
    int center_count;

    if (!img1 || !img2 || !img_subtracted) {
        printf("内存分配失败\n");
        exit(1);
    }

    // 读取图像
    load_bmp_image("target_image2048.bmp", img1);
    load_bmp_image("black_image2048.bmp", img2);

    // 开始总时间测量
    start_time = clock();

    // 进行图像相减并测量时间
    image_subtract(img1, img2, img_subtracted, &subtract_duration);

    // 检测靶标中心并测量时间
    //detect_target_centers(img_subtracted, centers, &center_count, &target_detection_duration);

    // 结束总时间测量
    end_time = clock();
    cpu_time_used = ((double)(end_time - start_time)) / CLOCKS_PER_SEC;

    // 保存差分后的图像
    save_bmp_image("target_subtracted2048.bmp", img_subtracted);

    // 输出时间信息和检测到的靶标中心
    printf("图像相减时间: %.6f 秒\n", subtract_duration);

    // 释放内存
    free(img1);
    free(img2);
    free(img_subtracted);

    return 0;
}
3. 自适应阈值处理差分操作

第2中方法是根据既定的阈值进行二值化处理,下面这种方法是自适应阈值处理。

// 自适应阈值处理方法进行两帧图像差分
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#define WIDTH 2048
#define HEIGHT 2048
#define THRESHOLD 100  // 二值化阈值
#define ADAPTIVE_THRESHOLD_FACTOR 0.5 //自适应阈值处理

typedef struct {
    unsigned char b;
    unsigned char g;
    unsigned char r;
} RGBPixel;

typedef struct {
    int x;
    int y;
} Point2D;

#pragma pack(1)
typedef struct {
    unsigned short bfType;
    unsigned int bfSize;
    unsigned short bfReserved1;
    unsigned short bfReserved2;
    unsigned int bfOffBits;
} BMPFileHeader;

typedef struct {
    unsigned int biSize;
    int biWidth;
    int biHeight;
    unsigned short biPlanes;
    unsigned short biBitCount;
    unsigned int biCompression;
    unsigned int biSizeImage;
    int biXPelsPerMeter;
    int biYPelsPerMeter;
    unsigned int biClrUsed;
    unsigned int biClrImportant;
} BMPInfoHeader;
#pragma pack()

// 读取 BMP 图像
void load_bmp_image(const char* file_name, RGBPixel(*image)[WIDTH]) {
    FILE* file = fopen(file_name, "rb");
    if (!file) {
        printf("无法打开文件 %s\n", file_name);
        exit(1);
    }

    BMPFileHeader fileHeader;
    BMPInfoHeader infoHeader;

    fread(&fileHeader, sizeof(BMPFileHeader), 1, file);
    if (fileHeader.bfType != 0x4D42) {
        printf("文件 %s 不是有效的 BMP 文件\n", file_name);
        fclose(file);
        exit(1);
    }

    fread(&infoHeader, sizeof(BMPInfoHeader), 1, file);
    if (infoHeader.biWidth != WIDTH || infoHeader.biHeight != HEIGHT || infoHeader.biBitCount != 24) {
        printf("图像尺寸或格式不匹配\n");
        fclose(file);
        exit(1);
    }

    fseek(file, fileHeader.bfOffBits, SEEK_SET);
    fread(image, sizeof(RGBPixel), WIDTH * HEIGHT, file);

    fclose(file);
}

// 保存 BMP 图像
void save_bmp_image(const char* file_name, RGBPixel(*image)[WIDTH]) {
    FILE* file = fopen(file_name, "wb");
    if (!file) {
        printf("无法保存文件 %s\n", file_name);
        exit(1);
    }

    BMPFileHeader fileHeader;
    BMPInfoHeader infoHeader;

    fileHeader.bfType = 0x4D42;
    fileHeader.bfSize = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader) + WIDTH * HEIGHT * sizeof(RGBPixel);
    fileHeader.bfReserved1 = 0;
    fileHeader.bfReserved2 = 0;
    fileHeader.bfOffBits = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader);

    infoHeader.biSize = sizeof(BMPInfoHeader);
    infoHeader.biWidth = WIDTH;
    infoHeader.biHeight = HEIGHT;
    infoHeader.biPlanes = 1;
    infoHeader.biBitCount = 24;
    infoHeader.biCompression = 0;
    infoHeader.biSizeImage = WIDTH * HEIGHT * sizeof(RGBPixel);
    infoHeader.biXPelsPerMeter = 0;
    infoHeader.biYPelsPerMeter = 0;
    infoHeader.biClrUsed = 0;
    infoHeader.biClrImportant = 0;

    fwrite(&fileHeader, sizeof(BMPFileHeader), 1, file);
    fwrite(&infoHeader, sizeof(BMPInfoHeader), 1, file);
    fwrite(image, sizeof(RGBPixel), WIDTH * HEIGHT, file);

    fclose(file);
}

// 自适应阈值处理
void image_subtract(RGBPixel(*img1)[WIDTH], RGBPixel(*img2)[WIDTH], RGBPixel(*result)[WIDTH], double* duration) {
    clock_t start_time = clock();

    for (int i = 0; i < HEIGHT; i++) {
        for (int j = 0; j < WIDTH; j++) {
            // 获取两个像素的亮度值,使用加权平均
            int brightness1 = (img1[i][j].r + img1[i][j].g + img1[i][j].b) / 3;
            int brightness2 = (img2[i][j].r + img2[i][j].g + img2[i][j].b) / 3;

            // 根据亮度的差异动态调整阈值
            int local_threshold = (int)(ADAPTIVE_THRESHOLD_FACTOR * (brightness1 + brightness2) / 2);

            int diff_r = abs(img1[i][j].r - img2[i][j].r);
            int diff_g = abs(img1[i][j].g - img2[i][j].g);
            int diff_b = abs(img1[i][j].b - img2[i][j].b);

            // 使用局部阈值进行二值化
            if (diff_r > local_threshold || diff_g > local_threshold || diff_b > local_threshold) {
                result[i][j].r = 255; // 填充为白色
                result[i][j].g = 255;
                result[i][j].b = 255;
            }
            else {
                result[i][j].r = 0; // 填充为黑色
                result[i][j].g = 0;
                result[i][j].b = 0;
            }
        }
    }

    clock_t end_time = clock();
    *duration = ((double)(end_time - start_time)) / CLOCKS_PER_SEC;
}

int main() {
    clock_t start_time, end_time;
    double cpu_time_used, subtract_duration, target_detection_duration;

    /*RGBPixel(*img1)[WIDTH] = malloc(HEIGHT * sizeof(*img1));
    RGBPixel(*img2)[WIDTH] = malloc(HEIGHT * sizeof(*img2));
    RGBPixel(*img_subtracted)[WIDTH] = malloc(HEIGHT * sizeof(*img_subtracted));*/
    RGBPixel(*img1)[WIDTH] = malloc(HEIGHT * WIDTH * sizeof(RGBPixel));
    RGBPixel(*img2)[WIDTH] = malloc(HEIGHT * WIDTH * sizeof(RGBPixel));
    RGBPixel(*img_subtracted)[WIDTH] = malloc(HEIGHT * WIDTH * sizeof(RGBPixel));

    Point2D centers[MAX_POINTS];
    int center_count;

    if (!img1 || !img2 || !img_subtracted) {
        printf("内存分配失败\n");
        exit(1);
    }

    // 读取图像
    load_bmp_image("img1.bmp", img1);
    load_bmp_image("img2.bmp", img2);

    // 开始总时间测量
    start_time = clock();

    // 进行图像相减并测量时间
    image_subtract(img1, img2, img_subtracted, &subtract_duration);

    // 结束总时间测量
    end_time = clock();
    cpu_time_used = ((double)(end_time - start_time)) / CLOCKS_PER_SEC;

    // 保存差分后的图像
    save_bmp_image("subtracted_img.bmp", img_subtracted);

    printf("图像相减时间: %.6f 秒\n", subtract_duration);

    // 释放内存
    free(img1);
    free(img2);
    free(img_subtracted);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值