c99标准的bmp格式文件读入

c99标准的bmp格式文件读入

bmp简介

BMP取自位图Bitmap的缩写,也称为DIB(与设备无关的位图),是一种独立于显示器的位图数字图像文件格式。常见于微软视窗和OS/2操作系统,Windows GDI API内部使用的DIB数据结构与 BMP 文件格式几乎相同。

图像通常保存的颜色深度有2(1位)、16(4位)、256(8位)、65536(16位)和1670万(24位)种颜色(其中位是表示每点所用的数据位)。8位图像可以是索引彩色图像外,也可以是灰阶图像。表示透明的alpha通道也可以保存在一个类似于灰阶图像的独立文件中。带有集成的alpha通道的32位版本已经随着Windows XP出现,它在Windows系统的登录界面和系统主题中都有使用。

节选自:维基百科

实现效果

将一个bmp文件的数据放到Image结构中,其中包含通道数(channels),宽度(width)、高度(height)、图像数据,图像数据按照三通道b、g、r顺序排列,每个通道中第一个维度为height, 第二个维度为width。

实现

首先是头文件代码(其实应该把实现放到.c中但是懒得搞了😰)

#ifndef BMP_H
#define BMP_H

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

typedef struct {
    unsigned long    bfSize;  // 文件大小
    unsigned short    bfReserved1;  // 保留,必须设置为0(6-7字节)
    unsigned short    bfReserved2;  // 保留,必须设置为0(8-9字节)
    unsigned long    bfOffBits;  // 从文件头到像素数据的偏移
} BitMapFileHeader;  // 文件信息头结构体

typedef struct {
    unsigned long  biSize;  // 此结构体的大小(14-17字节)
    long   biWidth;  // 图像的宽(18-21字节)
    long   biHeight;  // 图像的高(22-25字节)
    unsigned short   biPlanes;  // 表示bmp图片的平面属,显然显示器只有一个平面,所以恒等于1(26-27字节)
    unsigned short   biBitCount;  // 一像素所占的位数,一般为24   (28-29字节)
    unsigned long  biCompression;  // 说明图象数据压缩的类型,0为不压缩。 (30-33字节)
    unsigned long  biSizeImage;  // 像素数据所占大小, 这个值应该等于上面文件头结构中bfSize-bfOffBits (34-37字节)
    long   biXPelsPerMeter;  // 说明水平分辨率,用象素/米表示。一般为0 (38-41字节) 
    long   biYPelsPerMeter;  // 说明垂直分辨率,用象素/米表示。一般为0 (42-45字节) 
    unsigned long   biClrUsed;  // 说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。 (46-49字节)
    unsigned long   biClrImportant;  // 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。(50-53字节)
} BitMapInfoHeader;

typedef struct {
    unsigned char rgbBlue; //该颜色的蓝色分量 
    unsigned char rgbGreen; //该颜色的绿色分量 
    unsigned char rgbRed; //该颜色的红色分量 
    unsigned char rgbReserved; //保留值,必须为0 
} RgbQuad;

typedef struct {
    int width;
    int height;
    int channels;
    unsigned char*** imageData;
    // channel height width 
}Image;


/*
初始化Image的imageData数据
*/
unsigned char*** initialImageData(int channel, int height, int width) {
    unsigned char*** imageData;
    imageData = (unsigned char***)calloc(channel, sizeof(int**));
    for (int i = 0; i < channel; i++) {
        imageData[i] = (unsigned char**)calloc(height, sizeof(int*));
    }
    for (int i = 0; i < channel; i++) {
        for (int j = 0; j < height; j++) {
            imageData[i][j] = (unsigned char*)calloc(width, sizeof(int));
        }
    }
    return imageData;
}

Image* bmpRead(const char* filePath) {
    Image* bmpImg;
    
    FILE* pFile;
    unsigned short fileType;
    BitMapFileHeader bmpFileHeader;
    BitMapInfoHeader bmpInfoHeader;
    int channels = 1;
    int width = 0;
    int height = 0;
    int step = 0;
    int offset = 0;
    unsigned char pixVal;
    RgbQuad* quad;
    int i, j, k;

    bmpImg = (Image*)malloc(sizeof(Image));
    pFile = fopen(filePath, "rb");
    if (!pFile) {
        free(bmpImg);
        return NULL;
    }

    fread(&fileType, sizeof(unsigned short), 1, pFile);

    if (fileType == 0x4D42) {

        fread(&bmpFileHeader, sizeof(BitMapFileHeader), 1, pFile);

        fread(&bmpInfoHeader, sizeof(BitMapInfoHeader), 1, pFile);

        if (bmpInfoHeader.biBitCount == 8) {
            channels = 1;
            width = bmpInfoHeader.biWidth;
            height = bmpInfoHeader.biHeight;
            offset = (channels * width) % 4;
            if (offset != 0) {
                offset = 4 - offset;
            }
            bmpImg->width = width;
            bmpImg->height = height;
            bmpImg->channels = 1;
            bmpImg->imageData = initialImageData(bmpImg->channels, bmpImg->height, bmpImg->width);

            quad = (RgbQuad*)malloc(sizeof(RgbQuad) * 256);
            fread(quad, sizeof(RgbQuad), 256, pFile);
            free(quad);

            for (i = 0; i < height; i++) {
                for (j = 0; j < width; j++) {
                    fread(&pixVal, sizeof(unsigned char), 1, pFile);
                    bmpImg->imageData[0][i][j] = pixVal;
                }
                if (offset != 0) {
                    for (j = 0; j < offset; j++) {
                        fread(&pixVal, sizeof(unsigned char), 1, pFile);
                    }
                }
            }
        }
        else if (bmpInfoHeader.biBitCount == 24) {
            channels = 3;
            width = bmpInfoHeader.biWidth;
            height = bmpInfoHeader.biHeight;

            bmpImg->width = width;
            bmpImg->height = height;
            bmpImg->channels = 3;
            bmpImg->imageData = initialImageData(bmpImg->channels, bmpImg->height, bmpImg->width);

            step = channels * width;

            offset = (channels * width) % 4;
            if (offset != 0) {
                offset = 4 - offset;
            }
            
            for (i = 0; i < height; i++) {
                for (j = 0; j < width; j++) {
                    for (k = 0; k < channels; k++){
                        fread(&pixVal, sizeof(unsigned char), 1, pFile);
                        bmpImg->imageData[k][i][j] = pixVal;
                    }
                    if (offset != 0) {
                        for (j = 0; j < offset; j++) {
                            fread(&pixVal, sizeof(unsigned char), 1, pFile);
                        }
                    }
                }
            }
            
        }
    }
    fclose(pFile);
    free(pFile);
    return bmpImg;
}

bool bmpWrite(const char* filePath, Image* image) {
    FILE* pFile;
    unsigned short fileType;
    BitMapFileHeader bmpFileHeader;
    BitMapInfoHeader bmpInfoHeader;
    int step;
    int offset;
    unsigned char pixVal = '\0';
    int i, j;
    RgbQuad* quad;

    pFile = fopen(filePath, "wb");
    if (!pFile) {
        return false;
    }

    fileType = 0x4D42;
    fwrite(&fileType, sizeof(unsigned short), 1, pFile);


    if (image->channels == 3) { // 24位,通道,彩图  
        step = image->channels * image->width;
        offset = step % 4;
        if (offset != 4) {
            step += 4 - offset;
        }

        bmpFileHeader.bfSize = image->height * step + 54;
        bmpFileHeader.bfReserved1 = 0;
        bmpFileHeader.bfReserved2 = 0;
        bmpFileHeader.bfOffBits = 54;
        fwrite(&bmpFileHeader, sizeof(BitMapFileHeader), 1, pFile);

        bmpInfoHeader.biSize = 40;
        bmpInfoHeader.biWidth = image->width;
        bmpInfoHeader.biHeight = image->height;
        bmpInfoHeader.biPlanes = 1;
        bmpInfoHeader.biBitCount = 24;
        bmpInfoHeader.biCompression = 0;
        bmpInfoHeader.biSizeImage = image->height * step;
        bmpInfoHeader.biXPelsPerMeter = 0;
        bmpInfoHeader.biYPelsPerMeter = 0;
        bmpInfoHeader.biClrUsed = 0;
        bmpInfoHeader.biClrImportant = 0;
        fwrite(&bmpInfoHeader, sizeof(BitMapInfoHeader), 1, pFile);

        
        for (i = 0; i < image->height; i++) {
            for (j = 0; j < image->width; j++) {
                    for (int k = 0; k < image->channels; k++) {
                    pixVal = image->imageData[k][i][j];
                    fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
                }
                if (offset != 0) {
                    for (j = 0; j < 4 - offset; j++) {
                        pixVal = 0;
                        fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
                    }
                }
            }
        }
    }
    else if (image->channels == 1) {  // 8位,单通道,灰度图   
        step = image->width;
        offset = step % 4;
        if (offset != 4) {
            step += 4 - offset;
        }

        bmpFileHeader.bfSize = 54 + 256 * 4 + image->width;
        bmpFileHeader.bfReserved1 = 0;
        bmpFileHeader.bfReserved2 = 0;
        bmpFileHeader.bfOffBits = 54 + 256 * 4;
        fwrite(&bmpFileHeader, sizeof(BitMapFileHeader), 1, pFile);

        bmpInfoHeader.biSize = 40;
        bmpInfoHeader.biWidth = image->width;
        bmpInfoHeader.biHeight = image->height;
        bmpInfoHeader.biPlanes = 1;
        bmpInfoHeader.biBitCount = 8;
        bmpInfoHeader.biCompression = 0;
        bmpInfoHeader.biSizeImage = image->height * step;
        bmpInfoHeader.biXPelsPerMeter = 0;
        bmpInfoHeader.biYPelsPerMeter = 0;
        bmpInfoHeader.biClrUsed = 256;
        bmpInfoHeader.biClrImportant = 256;
        fwrite(&bmpInfoHeader, sizeof(BitMapInfoHeader), 1, pFile);

        quad = (RgbQuad*)malloc(sizeof(RgbQuad) * 256);
        for (i = 0; i < 256; i++) {
            quad[i].rgbBlue = i;
            quad[i].rgbGreen = i;
            quad[i].rgbRed = i;
            quad[i].rgbReserved = 0;
        }
        fwrite(quad, sizeof(RgbQuad), 256, pFile);
        free(quad);

        for (i = 0; i < image->height; i++) {
            for (j = 0; j < image->width; j++) {
                pixVal = image->imageData[0][i][j];
                fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
            }
            if (offset != 0) {
                for (j = 0; j < 4 - offset; j++) {
                    pixVal = 0;
                    fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
                }
            }
        }
    }
    fclose(pFile);
    free(pFile);
    return true;
}

#endif

代码使用

#include "bmp.h"
#include <stdio.h>

int main() {
    const char* sourceImagePath = "in.bmp";
    const char* targetImagePath = "out.bmp";
    
    Image* img = bmpRead(sourceImagePath);
	
	for (int k = 0; k < img->channels; k++) {  // 三个通道分别为b、g、r
		for (int j = 0; j < img->height; j++) {
			for (int i = 0; i < img->width; i++) {
				printf("%d\t", img->imageData[k][j][i]);
			}
			printf("\n");
		}
		printf("\n");
	}

    bmpWrite(targetPath, img);
    

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值