C++实现bmp文件的读写

目录

BMP文件格式:

实现步骤:

涉及文件操作函数:

参考代码实现: 


<windows.h>系统头文件中包含BMP位图格式相关结构体类型定义。

BMP文件格式:

数据段名称<windows.h>中结构体定义大小(Byte)
head1.位图文件头BITMAPFILEHEADER14
2.位图信息头BITMAPINFOHEADER40
3.调色板RGBQUAD由像素所占位数决定(仅索引图像有)
Data图像数据由图像尺寸与像素位数决定

BMP格式解析参考以下博客:
常见图片文件格式简析 - 走看看 (zoukankan.com)http://t.zoukankan.com/go2bed-p-4097758.html

(7条消息) BMP格式详解_Valineliu的博客-CSDN博客_bmp是什么意思https://blog.csdn.net/u012877472/article/details/50272771?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166342149716782395386740%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=166342149716782395386740&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-50272771-null-null.142%5Ev47%5Enew_blog_pos_by_title,201%5Ev3%5Econtrol_1&utm_term=bmp%E6%A0%BC%E5%BC%8F&spm=1018.2226.3001.4187


实现步骤:

读:

1.读二进制的方式“rb"打开文件;

2.跳过位图文件头;

3.定义位图信息头结构体变量,从文件流中读取”位图信息头“至信息头结构体变量指针指向的内存块;

4.申请调色板所需空间,从文件流中读取”位图调色板“至调色板结构体指针变量指向的内存块;

5.申请图像数据所需空间,从文件流中读取”图像数据“至图像数据指针变量指向的内存块;

6.关闭文件。

写:

1.以二进制写的方式”wb"打开文件;

2.定义文件头结构体变量,填写文件头信息,写文件头进文件流;

3.定义信息头结构体变量,填写信息头信息,写信息头进文件流;

4.若为索引图象,将颜色表写入文件;

5.写位图数据进文件;

6.关闭文件.

PS:

1.读写图像位图数据时,每行数据的字节数必须为4的整数倍,缺位补齐。

2.索引图像的位图数据为颜色表的索引值。

涉及文件操作函数:

(在<cstdio>系统头文件中定义)
1. fopen()函数

FILE* fopen (const char* filename, const char* mode);
---------------------------------------------------------------
function : 以某种模式打开指定的文件.
parameter: 1. filename:指向包含要打开的文件名的字符串的指针.
           2. mode:指向指定文件打开模式的字符串的指针.

return   : 成功,fopen() 函数将返回一个指针,指向控制打开的文件流的 FILE 对象.
           失败,fopen() 函数返回一个空指针.
参考链接:C++ fopen()用法及代码示例 - 纯净天空 (vimsky.com)

2. fseek()函数

int fseek(FILE* stream, long offset, int origin);
------------------------------------------------------------------------------------------
function:重定位流(数据流/文件)上的文件内部位置指针

paragram:1.stream :要修改的文件流.
         2.offset :从原点位移的偏移量(字节数),正数表示正向偏移,负数表示负向偏移.
         3.origin :用作添加到偏移量的参考的位置。它可以具有以下值:
                    0(SEEK_SET)--文件开头位置;
                    1(SEEK_CUR)--文件当前位置;
                    2(SEEK_END)--文件尾。

return  :成功返回0,失败返回1.

参考:c++ fseek函数-天道酬勤-花开半夏 (zhangshilong.cn)

3. fread()函数

size_t fread(void * buffer, size_t size, size_t count, FILE * stream)
------------------------------------------------------------------------------------------
function : 从流中读取数据块.
parameter: 1. buffer:它以至少(size * count)个字节的大小指定指向内存块的指针以存储对象.
           2. size:它指定每个对象的大小(以字节为单位)。 size_t是无符号整数类型.
           3. count:它指定元素的数量,每个元素的大小为字节大小.
           4. stream:它指定要从中读取数据的文件流.
return   : 函数返回成功读取的对象数.

 参考:C++ fread()用法及代码示例 - 纯净天空 (vimsky.com)

4.fwrite()函数

size_t fwrite(const void * buffer, size_t size, size_t count, FILE * stream);
-----------------------------------------------------------------------------
function: 将 count 个对象写入给定的输出流,每个对象的大小为 size 个字节
parameter:1.buffer:指向其内容被写入的内存块的指针;
          2.size:每个对象的大小(以字节为单位);
          3.count:要读取的对象数;
          4.stream:要写入数据的文件流.
return:函数返回成功读取的对象数。如果发生错误,返回值可能小于 count

参考: C++ fwrite()实例讲解 - 码农教程 (manongjc.com)


参考代码实现: 

读:

bool readBmp(char* bmpName)
{
    //1.二进制读方式打开指定的图像文件
    FILE* fp = fopen(bmpName, "rb");
    if (fp == 0){
        cout<<"The file "<<bmpName<<" was not opened"<<endl;
        return 0;
    }

    //2.跳过位图文件头结构BITMAPFILEHEADER
    if(fseek(fp, sizeof(BITMAPFILEHEADER), 0)){
        cout<<"跳转失败"<<endl;
        return 0;
    }

    //3.定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中
    BITMAPINFOHEADER head;
    fread(&head, sizeof(BITMAPINFOHEADER), 1, fp);

    //获取图像宽、高、每像素所占位数等信息
    bmpWidth = head.biWidth;
    bmpHeight = head.biHeight;
    biBitCount = head.biBitCount;

    //定义变量,计算图像每行像素所占的字节数(必须是4的倍数)
    int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;

    //4.灰度图像有颜色表,且颜色表表项为256
    if (biBitCount == 8) {
        //申请颜色表所需要的空间,读颜色表进内存
        pColorTable = new RGBQUAD[256];             //这里可以理解为:申请了256个元素的数组空间,数组元素是RGBQUARD结构体类型
        fread(pColorTable, sizeof(RGBQUAD), 256, fp);//读取的每个对象大小为sizeof(RGBQUAD)=4byte,从fp文件流中读取了256个对象至pColorTable指向的内存块
         cout<<"读颜色表成功"<<endl;
    }

    //5.申请位图数据所需要的空间,读位图数据进内存
    pBmpBuf = new unsigned char[lineByte * bmpHeight];
    fread(pBmpBuf, 1, lineByte * bmpHeight, fp);

    //6.关闭文件
    fclose(fp);

    cout<<"图像读取成功"<<bmpName<<"的图像信息:"<<endl;
    cout<<"width="<<bmpWidth<<" ,height="<<bmpHeight<<" ,biBitCount="<< biBitCount<<endl;
    return 1;
}

写:

bool saveBmp(char* bmpName, unsigned char* imgBuf, int width, int height,
    int biBitCount, RGBQUAD* pColorTable)
{

    //如果位图数据指针为0,则没有数据传入,函数返回
    if (!imgBuf)
        return 0;


    //颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0
    int colorTablesize = 0;
    if (biBitCount == 8)
        colorTablesize = 1024;

    //待存储图像数据每行字节数为4的倍数
    int lineByte = (width * biBitCount / 8 + 3) / 4 * 4;

    //1.以二进制写的方式打开文件
    FILE* fp = fopen(bmpName, "wb");
    if (fp == 0) return 0;

    //2.申请位图文件头结构变量,填写文件头信息
    BITMAPFILEHEADER fileHead;
    fileHead.bfType = 0x4D42;//bmp类型
    //bfSize是图像文件4个组成部分之和
    fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
        + colorTablesize + lineByte * height;
    fileHead.bfReserved1 = 0;
    fileHead.bfReserved2 = 0;
    //bfOffBits是图像文件前三个部分所需空间之和
    fileHead.bfOffBits = 54 + colorTablesize;

    //写文件头进文件
    fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);

     //cout<<"写文件头成功"<<endl;

    //3.申请位图信息头结构变量,填写信息头信息
    BITMAPINFOHEADER head;
    head.biBitCount = biBitCount;
    head.biClrImportant = 0;
    head.biClrUsed = 0;
    head.biCompression = 0;
    head.biHeight = height;
    head.biPlanes = 1;
    head.biSize = 40;
    head.biSizeImage = lineByte * height;
    head.biWidth = width;
    head.biXPelsPerMeter = 0;
    head.biYPelsPerMeter = 0;
    //写位图信息头进内存
    fwrite(&head, sizeof(BITMAPINFOHEADER), 1, fp);
    //cout<<"写信息头成功"<<endl;

    //4.如果灰度图像,有颜色表,写入文件
    if (biBitCount == 8)
        fwrite(pColorTable, sizeof(RGBQUAD), 256, fp);
    //cout<<"写颜色表成功"<<endl;

    //5.写位图数据进文件
    fwrite(imgBuf, height * lineByte, 1, fp);

    //6.关闭文件
    fclose(fp);

    cout<<"图像保存成功,请查看文件"<<bmpName<<":"<<endl;
    cout<<"width="<<width<<" ,height="<<height<<" ,biBitCount="<< biBitCount<<endl;

    return 1;
}

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是利用C++实现BMP文件读写代码: ```c++ #include <iostream> #include <fstream> using namespace std; #pragma pack(push, 1) typedef struct BMPFileHeader { char bfType[2]; int bfSize; short bfReserved1; short bfReserved2; int bfOffBits; } BMPFileHeader; typedef struct BMPInfoHeader { int biSize; int biWidth; int biHeight; short biPlanes; short biBitCount; int biCompression; int biSizeImage; int biXPelsPerMeter; int biYPelsPerMeter; int biClrUsed; int biClrImportant; } BMPInfoHeader; #pragma pack(pop) void readBMP(string filename, unsigned char* &imgData, int &width, int &height, int &channels) { BMPFileHeader fileHeader; BMPInfoHeader infoHeader; int padding; FILE* fp = fopen(filename.c_str(), "rb"); if(!fp) { cout << "Failed to open file: " << filename << endl; return; } fread(&fileHeader, sizeof(BMPFileHeader), 1, fp); fread(&infoHeader, sizeof(BMPInfoHeader), 1, fp); width = infoHeader.biWidth; height = infoHeader.biHeight; channels = infoHeader.biBitCount / 8; padding = (4 - (width * channels) % 4) % 4; imgData = new unsigned char[width * height * channels]; fseek(fp, fileHeader.bfOffBits, SEEK_SET); for(int i = 0; i < height; i++) { fread(imgData + i * width * channels, channels, width, fp); fseek(fp, padding, SEEK_CUR); } fclose(fp); } void writeBMP(string filename, unsigned char* imgData, int width, int height, int channels) { BMPFileHeader fileHeader; BMPInfoHeader infoHeader; int padding; fileHeader.bfType[0] = 'B'; fileHeader.bfType[1] = 'M'; fileHeader.bfSize = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader) + (width * channels + (4 - (width * channels) % 4) % 4) * height; 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 = channels * 8; infoHeader.biCompression = 0; infoHeader.biSizeImage = (width * channels + (4 - (width * channels) % 4) % 4) * height; infoHeader.biXPelsPerMeter = 0; infoHeader.biYPelsPerMeter = 0; infoHeader.biClrUsed = 0; infoHeader.biClrImportant = 0; padding = (4 - (width * channels) % 4) % 4; FILE* fp = fopen(filename.c_str(), "wb"); if(!fp) { cout << "Failed to open file: " << filename << endl; return; } fwrite(&fileHeader, sizeof(BMPFileHeader), 1, fp); fwrite(&infoHeader, sizeof(BMPInfoHeader), 1, fp); for(int i = 0; i < height; i++) { fwrite(imgData + i * width * channels, channels, width, fp); for(int j = 0; j < padding; j++) { fputc(0, fp); } } fclose(fp); } int main() { unsigned char* imgData; int width, height, channels; readBMP("input.bmp", imgData, width, height, channels); // 对图像进行处理 writeBMP("output.bmp", imgData, width, height, channels); delete[] imgData; return 0; } ``` 其中,readBMP函数用于读取BMP文件,writeBMP函数用于写入BMP文件。在读取BMP文件时,需要注意文件头和信息头的结构体定义,并且需要考虑对齐和补齐的问题。在写入BMP文件时,需要根据读取到的图像数据的宽度、高度和通道数等信息来计算文件头和信息头中的数据,并且需要考虑对齐和补齐的问题。 以下是读取BMP文件并显示结果的示例代码: ```c++ #include <iostream> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; int main() { Mat img = imread("input.bmp"); if(img.empty()) { cout << "Failed to read image" << endl; return -1; } namedWindow("Image", WINDOW_NORMAL); imshow("Image", img); waitKey(0); return 0; } ``` 利用OpenCV库中的imread函数可以读取BMP文件,并利用namedWindow和imshow函数可以创建窗口并显示图像。需要注意的是,在使用imshow函数时需要调用waitKey函数等待按键事件,否则窗口会立即关闭。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值