c++将24位bmp转8位bmp灰度, 8位bmp灰度反色,24位转3张8位bmp灰度图

1首先我们需要定义组成bmp的四部分的结构体

#pragma once
 
using BYTE = unsigned char;//1
using WORD  = unsigned short;//2
using DWORD = unsigned int;//4
using LONG  = unsigned long;//4
 
#pragma pack(push)
#pragma pack(2) //每次读取两个字节,以确保为结构体创建的内存刚刚好是我们想要的
struct BITMAPFILEHEADER {//定义bitmap文件头结构
	WORD  bfType;//记录文件标识符,BM
	DWORD bfSize;//记录文件的大小:文件头,信息头,颜色表,颜色数据
	WORD  bfReserved1;//保留字 0
	WORD  bfReserved2;//保留字 0
	DWORD bfOffBits;//颜色数据距文件头的偏移量
};
 
//定义位图信息头结构
struct BITMAPINFOHEADER {
	DWORD biSize;//本结构体的大小
	LONG  biWidth;//位图的宽:像素
	LONG  biHeight;//位图的高:像素
	WORD  biPlanes;//为平面数(一般为1)
	WORD  biBitCount;//每个像素所需的位数:1,4,8,16,24,32
	DWORD biCompression;//位图压缩类型(一般不压缩,取0)
	DWORD biSizeImage;//位图的大小
	LONG  biXPelsPerMeter;//水平分辨率 0代表取标准值
	LONG  biYPelsPerMeter;//垂直分辨率 0代表取标准值
	DWORD biClrUsed;//颜色数 标准取0 计算方法为2的biBitCount次方
	DWORD biClrImportant;//重要颜色数 标准取0
};
 
//颜色表
struct RGBQUAD {
	BYTE rgbBlue;//蓝
	BYTE rgbGreen;//绿
	BYTE rgbRED;//红
	BYTE rgbReserved;//保留字 0
};
#pragma pack(pop)

2 构建可以读取bmp的类bitmap

#pragma once
#include "bitmapQuad.h"
#include <iostream>
#include <fstream>
 
class bitmap {
private:
	FILE* m_rfp;//读入文件指针
	FILE* m_wfp;//写入文件指针
	FILE* m_wfp1;//写入文件指针
	FILE* m_wfp2;//写入文件指针
	BITMAPFILEHEADER* m_pbfHeader;//文件头指针
	BITMAPINFOHEADER* m_pbiHeader;//位图信息头指针
	RGBQUAD* m_prgbQuad;//颜色表指针
	BYTE* m_pImgData;//位图数据指针
	DWORD m_imgSize;//位图数据大小
 
private:
	inline void readFileHeader(BITMAPFILEHEADER* bmFileHeader);//读取文件头数据
	inline void readInfoHeader(BITMAPINFOHEADER* bmInfoHeader);//读取信息头数据
	inline void readRgbQuad(RGBQUAD* rgbQuad, const int& rgbQuadSize);//读取颜色表数据
	inline void readImgData(BYTE* pImgData, const DWORD& imgSize);//读取位图数据
 
private:
	inline void writeFileHeader(BITMAPFILEHEADER* pbfHeader);//读取文件头数据
	inline void writeInfoHeader(BITMAPINFOHEADER* pbiHeader);//读取信息头数据
	inline void writeRgbQuad(RGBQUAD* prgbQuad, const int& rgbQuadSize);//读取颜色表数据
	inline void writeImgData(BYTE* pImgData, const DWORD& imgSize);//读取位图数据
 
private:
	void createGrey_8();//创建8位灰度颜色表
 
public:
	bitmap(const char* readFilePath, const char* writeFilePath);
	bitmap(const char* readFilePath, const char* writeFilePath0, const char* writeFilePath1, const char* writeFilePath2);
	~bitmap();
	void rgb24_to_grey8();//24位图转为8位灰度图
	void grey8_to_antiGrey8();//8位图反色
	void rgb24_to_3grey8();//24位图分离为3个8位灰度图
 
};

3 实现

#include "bitmap.h"
 
#pragma region bmp文件四部分的读入
/*读取文件头*/
inline
void bitmap::readFileHeader(BITMAPFILEHEADER* bmFileHeader) {
	fread(bmFileHeader, sizeof(BITMAPFILEHEADER), 1, m_rfp);
}
 
/*读取信息头*/
inline
void bitmap::readInfoHeader(BITMAPINFOHEADER* bmInfoHeader) {
	fread(bmInfoHeader, sizeof(BITMAPINFOHEADER), 1, m_rfp);
}
 
/*读取颜色表*/
inline
void bitmap::readRgbQuad(RGBQUAD* rgbQuad, const int& rgbQuadSize) {
	fread(rgbQuad, rgbQuadSize, 1, m_rfp);
}
 
/*读取位图数据*/
inline
void bitmap::readImgData(BYTE* pImgData, const DWORD& imgSize) {
	fread(pImgData, imgSize, 1, m_rfp);
}
#pragma endregion
 
 
#pragma region bmp文件四部分的写入
/*写入文件头*/
inline
void bitmap::writeFileHeader(BITMAPFILEHEADER* bmFileHeader) {
	fwrite(bmFileHeader, sizeof(BITMAPFILEHEADER), 1, m_wfp);
}
 
/*写入信息头*/
inline
void bitmap::writeInfoHeader(BITMAPINFOHEADER* bmInfoHeader) {
	fwrite(bmInfoHeader, sizeof(BITMAPINFOHEADER), 1, m_wfp);
}
 
/*写入颜色表*/
inline
void bitmap::writeRgbQuad(RGBQUAD* rgbQuad, const int& rgbQuadSize) {
	fwrite(rgbQuad, rgbQuadSize, 1, m_wfp);
}
 
/*写入位图数据*/
inline
void bitmap::writeImgData(BYTE* pImgData, const DWORD& imgSize) {
	fwrite(pImgData, imgSize, 1, m_wfp);
}
#pragma endregion
 
 
#pragma region 构造和析构函数
 
bitmap::bitmap(const char* readFilePath, const char* writeFilePath) {
	m_rfp = fopen(readFilePath, "rb");//只读二进制文件
	m_wfp = fopen(writeFilePath, "wb");//只写二进制文件
 
	m_pbfHeader = new BITMAPFILEHEADER;//为文件头分配存储空间
	readFileHeader(m_pbfHeader);//将图片的文件头读入
 
	if (m_pbfHeader->bfType == 0x4d42/*检查是否为bm图片*/) {
		m_pbiHeader = new BITMAPINFOHEADER;//为信息头分配内存空间
		readInfoHeader(m_pbiHeader);//将图片的信息头读入
 
		//24位
		if (m_pbiHeader->biBitCount == 24) {
			m_imgSize = m_pbiHeader->biSizeImage;//获取位图数据大小
			m_pImgData = new BYTE[m_imgSize];//为位图数据分配内存
			readImgData(m_pImgData, m_imgSize);//将图片数据读入
 
			fclose(m_rfp);//结束读入
		}
 
		//8位
		if (m_pbiHeader->biBitCount == 8) {
			m_prgbQuad = new RGBQUAD[256];//位颜色表分配内存空间
			readRgbQuad(m_prgbQuad, sizeof(RGBQUAD) * 256);//读取颜色表
 
			m_imgSize = m_pbiHeader->biSizeImage;//获取位图数据大小
			m_pImgData = new BYTE[m_imgSize];//为位图数据分配内存
			readImgData(m_pImgData, m_imgSize);//将图片数据读入
 
			fclose(m_rfp);//结束读入
		}
 
	}
	
}
 
bitmap::bitmap(const char* readFilePath, const char* writeFilePath0, const char* writeFilePath1, const char* writeFilePath2) {
	m_rfp = fopen(readFilePath, "rb");//只读二进制文件
	m_wfp = fopen(writeFilePath0, "wb");//只写二进制文件
	m_wfp1 = fopen(writeFilePath1, "wb");//只写二进制文件
	m_wfp2 = fopen(writeFilePath2, "wb");//只写二进制文件
 
	m_pbfHeader = new BITMAPFILEHEADER;//为文件头分配存储空间
	readFileHeader(m_pbfHeader);//将图片的文件头读入
 
	if (m_pbfHeader->bfType == 0x4d42/*检查是否为bm图片*/) {
		m_pbiHeader = new BITMAPINFOHEADER;//为信息头分配内存空间
		readInfoHeader(m_pbiHeader);//将图片的信息头读入
 
		//24位
		if (m_pbiHeader->biBitCount == 24) {
			m_imgSize = m_pbiHeader->biSizeImage;//获取位图数据大小
			m_pImgData = new BYTE[m_imgSize];//为位图数据分配内存
			readImgData(m_pImgData, m_imgSize);//将图片数据读入
 
			fclose(m_rfp);//结束读入
		}
	}
}
 
//析构函数
bitmap::~bitmap() {
	fclose(m_wfp);//结束写入
	if(m_pbfHeader != nullptr)
		delete m_pbfHeader;
	if (m_pbiHeader != nullptr)
		delete m_pbiHeader;
	if (m_prgbQuad != nullptr)
		delete m_prgbQuad;
	if (m_pImgData != nullptr)
		delete m_pImgData;
}
#pragma endregion
 
 
#pragma region 创建颜色表
//创建灰度8位颜色表
void bitmap::createGrey_8() {
	BYTE rgb = 0;
	BYTE rgbReserved = 0;
 
	m_prgbQuad = new RGBQUAD[256];//256色颜色分配内存
	for (int i = 0; i < 256; ++i) {
		m_prgbQuad[i].rgbBlue = rgb;
		m_prgbQuad[i].rgbGreen = rgb;
		m_prgbQuad[i].rgbRED = rgb;
		m_prgbQuad[i].rgbReserved = rgbReserved;
		++rgb;
	}
}
#pragma endregion
 
 
#pragma region 处理图片的方法
//将24位转为8位灰图
void bitmap::rgb24_to_grey8() {
	DWORD imgSize = m_pbiHeader->biHeight * (m_pbiHeader->biWidth / 4 + 1) * 4;//新图片的数据大小
	BYTE* imgData = new BYTE[imgSize]{};//为其分配内存
 
	//处理文件头
	m_pbfHeader->bfOffBits = 54 + 1024;//多加了一个颜色表256*4
	m_pbfHeader->bfSize = m_pbfHeader->bfOffBits + imgSize;//得出8位灰度时该分辨率的数据大小
	
	//处理信息头
	m_pbiHeader->biBitCount = 8;//每个像素现在8位
	m_pbiHeader->biSizeImage = imgSize;//更新数据大小
	m_pbiHeader->biClrUsed = 256;//将颜色表索引数改为256
	
	//创建颜色表
	createGrey_8();
	
	//将24位数据转换位8位数据
	int tmp;
	for (unsigned int i = 0; i < m_imgSize; i += 3) {
		tmp = (float)m_pImgData[i] * 0.114f + (float)m_pImgData[i + 1] * 0.587f + (float)m_pImgData[i + 2] * 0.299f;
		imgData[i / 3] = BYTE(tmp);
	}
 
	//写入数据
	writeFileHeader(m_pbfHeader);//写入文件头
	writeInfoHeader(m_pbiHeader);
	writeRgbQuad(m_prgbQuad, 256 * sizeof(RGBQUAD));
	writeImgData(imgData, imgSize);
}
 
//将8位灰度图反色
void bitmap::grey8_to_antiGrey8() {
	//数据区反色
	for (unsigned int i = 0; i < m_imgSize; ++i) {
		m_pImgData[i] = abs(m_pImgData[i] - 255);
	}
 
	//写入
	writeFileHeader(m_pbfHeader);
	writeInfoHeader(m_pbiHeader);
	writeRgbQuad(m_prgbQuad, sizeof(RGBQUAD) * 256);
	writeImgData(m_pImgData, m_imgSize);
}
 
//将24位分离成3个8位图
void bitmap::rgb24_to_3grey8() {
	DWORD imgSize = m_pbiHeader->biHeight * (m_pbiHeader->biWidth / 4 + 1) * 4;//新图片数据大小
	BYTE* imgData0 = new BYTE[imgSize]{};//为其1分配内存
	BYTE* imgData1 = new BYTE[imgSize]{};//为其2分配内存
	BYTE* imgData2 = new BYTE[imgSize]{};//为其3分配内存
 
	#pragma region 生成第一张图片
	//生成第一张图片
		//处理文件头
	m_pbfHeader->bfOffBits = 54 + 1024;//多加了一个颜色表256*4
	m_pbfHeader->bfSize = m_pbfHeader->bfOffBits + imgSize;//得出8位灰度时该分辨率的数据大小
 
		//处理信息头
	m_pbiHeader->biBitCount = 8;//每个像素现在8位
	m_pbiHeader->biSizeImage = imgSize;//更新数据大小
	m_pbiHeader->biClrUsed = 256;//将颜色表索引数改为256
 
		//处理颜色表
	createGrey_8();
 
	//分离数据
	for (unsigned int i = 0; i < m_imgSize; i += 3) {
		imgData0[i / 3] = m_pImgData[i];
	}
 
	//写入数据
	writeFileHeader(m_pbfHeader);//写入文件头
	writeInfoHeader(m_pbiHeader);
	writeRgbQuad(m_prgbQuad, 256 * sizeof(RGBQUAD));
	writeImgData(imgData0, imgSize);
 
	//关闭
	fclose(m_wfp);
	m_wfp = m_wfp1;
	#pragma endregion
 
	#pragma region 生成第二张图片
	//生成第二张图片
		//分离数据
	for (unsigned int i = 0; i < m_imgSize; i += 3) {
		imgData0[i / 3] = m_pImgData[i + 1];
	}
		//写入数据
	writeFileHeader(m_pbfHeader);
	writeInfoHeader(m_pbiHeader);
	writeRgbQuad(m_prgbQuad, 256 * sizeof(RGBQUAD));
	writeImgData(imgData0, imgSize);
 
		//关闭
	fclose(m_wfp);
	m_wfp = m_wfp2;
	#pragma endregion
 
	#pragma region 生成第三张图片
	//生成第三张图片
		//分离数据
	for (unsigned int i = 0; i < m_imgSize; i += 3) {
		imgData0[i / 3] = m_pImgData[i + 2];
	}
	//写入数据
	writeFileHeader(m_pbfHeader);
	writeInfoHeader(m_pbiHeader);
	writeRgbQuad(m_prgbQuad, 256 * sizeof(RGBQUAD));
	writeImgData(imgData0, imgSize);
	#pragma endregion
 
}
#pragma endregion
 

一个测试读写BMP文件的例子 自己定义了BMP文件的结构体

#pragma pack(push)  
#pragma pack(1)  //改变内存对齐模式

typedef struct  tag_bitmap_file_header {
	unsigned short 		file_type;
	unsigned long 		file_size;
	unsigned short 		reserved1;
	unsigned short 		reserved2;
	unsigned int 		offset_bits;
}bitmap_file_header;

typedef struct tag_bitmap_info_header {
	unsigned int 		bitmap_info_size;
	int 				bitmap_width;
	int 				bitmap_height;
	unsigned short 		planes;
	unsigned short 		image_depth;
	unsigned int 		compression;
	unsigned int 		image_size;
	int 				x_pels_permeter;
	int 				y_pels_permeter;
	unsigned int 		color_used;
	unsigned int 		color_important;
}bitmap_info_header;

typedef struct tag_bitmap_palette {
	unsigned char 		blue;
	unsigned char 		green;
	unsigned char 		red;
	unsigned char 		reserved;
}bitmap_palette;

#pragma pack(pop)

实现代码

#pragma region 创建颜色表
//创建灰度8位颜色表
RGBQUAD* m_prgbQuad;
void createGrey_8() {
	BYTE rgb = 0;
	BYTE rgbReserved = 0;

	m_prgbQuad = new RGBQUAD[256];//256色颜色分配内存
	for (int i = 0; i < 256; ++i) {
		m_prgbQuad[i].rgbBlue = rgb;
		m_prgbQuad[i].rgbGreen = rgb;
		m_prgbQuad[i].rgbRed = rgb;
		m_prgbQuad[i].rgbReserved = rgbReserved;
		++rgb;
	}
}
#pragma endregion

void saveToBMP()
{
	//读test.bmp 再写回到mybmp.bmp
	Mat src = imread("d:\\test.bmp");
	//颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0
	
	bitmap_file_header src_file_head;  //BITMAPFILEHEADER
	bitmap_info_header src_info_head;  //BITMAPINFOHEADER
	bitmap_palette src_palette;        //RGBQUAD
	//BITMAPFILEHEADER src_file_head;
	//BITMAPINFOHEADER src_info_head;
	//RGBQUAD			 src_palette;
	int s1 = sizeof(BITMAPFILEHEADER);
	int s2 = sizeof(bitmap_file_header);
	int s3 = sizeof(bitmap_palette);
	FILE* fsrc = fopen("d:\\test.bmp", "rb");
	fread(&src_file_head, 1, sizeof(BITMAPFILEHEADER), fsrc);
	fread(&src_info_head, 1, sizeof(BITMAPINFOHEADER), fsrc);
	fread(&src_palette, 1, sizeof(RGBQUAD), fsrc);
	int src_image_size = src_info_head.image_size; //biSizeImage;
	unsigned char* src_image_buff = new unsigned char[src_image_size];
	fread(src_image_buff, 1, src_image_size, fsrc);
	fclose(fsrc);

	FILE* fw = fopen("d:\\mybmp.bmp", "wb");
	if (fw == nullptr)
		return;
	fwrite(&src_file_head, 1, sizeof(bitmap_file_header), fw);
	fwrite(&src_info_head, 1, sizeof(bitmap_info_header), fw);
	fwrite(&src_palette, 1, sizeof(bitmap_palette), fw);
	fwrite(src_image_buff, 1, src_image_size /*line_bytes * src.rows*/, fw);
	fclose(fw);
	//delete[] src_image_buff;
	
	/// <summary>
	/// 写8位 BMP文件
	/// </summary>
	auto rows = src.rows;
	int colorTablesize = 0;
	auto biBitCount = src.depth();
	if (src.depth() == 8)
		colorTablesize = 1024;
	auto depth = src.depth();
	auto channels = src.channels();
	unsigned char* pData = src.data;
	auto elem_size = src.elemSize();
	//int line_bytes = (src.cols * elem_size +4) / 4 *4 ;  //24 bit
	int line_bytes = (src.cols * elem_size  / 4 +1) *4 ;  //8 bit
	int image_size = line_bytes * src.rows;
	bitmap_file_header fileHead;
	fileHead.file_type = 0x4D42;	
	fileHead.reserved1 = 0;
	fileHead.reserved2 = 0;
	//fileHead.offset_bits = 54 + 0;  //24 bit
	fileHead.offset_bits = 54 + 1024;  //8 bit
	fileHead.file_size =fileHead.offset_bits //sizeof(bitmap_file_header)	+ sizeof(bitmap_info_header) + sizeof(bitmap_palette)
		+ image_size;
	

	//write file head
	FILE* fp = fopen("d:\\mybmp2.bmp", "wb");
	if (fp == nullptr)
		return;
	fwrite(&fileHead,1, sizeof(bitmap_file_header), fp);

	bitmap_info_header head;
	head.bitmap_info_size = 40;
	head.image_depth = 8; //elem_size * 8; //32 bit 
	head.color_important = 0;
	//head.color_used = 0;  //24 bit
	head.color_used = 256; //8 bit
	head.compression = 0;
	head.bitmap_height = src.rows;
	head.bitmap_width = src.cols*3;
	head.planes = 1;
	head.image_size = image_size;// line_bytes* src.rows;
	head.x_pels_permeter = 0;
	head.y_pels_permeter = 0;
	fwrite(&head, 1,sizeof(bitmap_info_header), fp);

	//如果灰度图像,有颜色表,写入文件 
	if (head.image_depth == 8)
	{
		createGrey_8();
		fwrite(m_prgbQuad, sizeof(RGBQUAD), 256, fp);
	}
	else {
		bitmap_palette palette = src_palette;

		palette.blue = 255; palette.green = 255; palette.red = 255; palette.reserved = 255;
		fwrite(&palette, 1, sizeof(bitmap_palette), fp);
	}

	//fwrite(src_image_buff, 1, src_image_size /*line_bytes * src.rows*/, fp);
	fwrite(src_image_buff, 1, src_image_size, fp);
	fclose(fp);
	delete[] src_image_buff;
	delete[] m_prgbQuad;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值