bmp格式 8bit与24bit深度相互转换

2021/4/10 18:46:44

本文主要是介绍bmp格式 8bit与24bit深度相互转换,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

bmp格式 8bit与24bit相互转换

一、实验要求

在图像处理软件中生成8bit和24bit深度的BMP文件,编写程序实现不同像素深度文件的相互转换。重点掌握函数定义、缓存区分配、倒序读写、结构体操作。

二、算法原理

1. 8bit转24bit

8bitBMP图像文件包括4部分:
位图文件头fileheader

typedef struct tagBITMAPFILEHEADER {
        WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;

位图信息头infoheader

typedef struct tagBITMAPINFOHEADER{
        DWORD      biSize;
        LONG       biWidth;
        LONG       biHeight;
        WORD       biPlanes;
        WORD       biBitCount;
        DWORD      biCompression;
        DWORD      biSizeImage;
        LONG       biXPelsPerMeter;
        LONG       biYPelsPerMeter;
        DWORD      biClrUsed;
        DWORD      biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;

调色板

typedef struct tagRGBQUAD {
        BYTE    rgbBlue;
        BYTE    rgbGreen;
        BYTE    rgbRed;
        BYTE    rgbReserved;
} RGBQUAD;

以及位图数据,结合调色板使用颜色索引值。

24bit图像不含调色板,位图数据为取值范围【0,255】的B,G,R数据,即BGR分别对应8bit。
编程思路:

Created with Raphaël 2.2.0 开始 读入8bit文件头 读入8bit信息头 读入8bit调色板 根据调色板计算24bit的BGR数据 写 24bit文件头 写 24bit信息头 写 24bit位图色彩数据 结束

2. 24bit转8bit

24bit转8bit相对困难,因为要设计图像的调色板
几种思路:(1)看看画图软件的调色板是怎么设计的

在这里插入图片描述
在这里插入图片描述
调色板中间部分很有顺序,但最开始和最后的部分没有看懂为什么这样设计,觉得可能是为了方便向其他深度如4bit转换。所以没有选择此方法。
(2)在【0,255】上等间隔取值
8bit对应256种颜色,256不是立方数,因此可以选择RGB取3+3+2=8bit。但是似乎哪个颜色分量取2bit都对整体画面会产生较大影响,所以放弃这个方法。
如果要使三个彩色分量平均取值,666=216最接近256色,但是剩下的40个值怎么取也很麻烦。

(3) 根据图像的颜色频次选择
这是我在网上找到的方法思路:统计图像中颜色出现的频次,然后根据频次最高的256种颜色设置为调色板。这种方法的好处显而易见,图像只有较少的低频次颜色区域会出现失真,图像大部分区域保留了真实的色彩。但问题是24bit对应16777216种情况,如果全部计算需要的资源太多。因此可以选择只计算高四位,即4+4+4=12bit,共4096种情况,再取频次最高的256个。
编程思路:

Created with Raphaël 2.2.0 开始 读入24bit文件头 读入24bit信息头 取24bit图像数据的BGR高四位存入大小为4096的数组 统计频次 排序找到频次最高的256色作为调色板 每个像素计算与调色板的距离 找到每个像素对应距离最小的调色板编号作为索引数据 写 8bit文件头 写 8bit信息头 写 8bit调色板 写 8bit位图色彩索引数据 结束

三、代码

1. 8转24

#define _CRT_SECURE_NO_WARNINGS

#include<iostream>
#include<Windows.h>
using namespace std;
BITMAPFILEHEADER File_header,new_header;
BITMAPINFOHEADER info_header,new_info;
tagRGBQUAD t;
static int Bpalette[256], Gpalette[256], Rpalette[256];
int main(int *argc,char *argv[])
{
	char* fpath1 = argv[1];//读取图像文件路径
	char* fpath2 = argv[2];//写入图像文件路径
	FILE* f24 = fopen(fpath1, "rb");
	FILE* f8 = fopen(fpath2, "wb");
	if (fread(&File_header, sizeof(BITMAPFILEHEADER), 1, f8) != 1)
	{
		cout << "read File_header error";
		exit(0);
	}
	if (fread(&info_header, sizeof(BITMAPINFOHEADER), 1, f8) != 1)
	{
		cout << "read info_header error";
		exit(0);
	}
	unsigned char* b1 = new unsigned char[info_header.biSizeImage];
	unsigned char* b2 = new unsigned char[3 * info_header.biSizeImage];
	unsigned char* start = b2;
	for (int i = 0; 4*i<File_header.bfOffBits-sizeof(BITMAPFILEHEADER)- sizeof(BITMAPINFOHEADER);i++)
	{
		fread(&t, sizeof(tagRGBQUAD), 1, f8);
		Bpalette[i] = t.rgbBlue;
		Gpalette[i] = t.rgbGreen;
		Rpalette[i] = t.rgbRed;
	}
	fread(b1, 1, info_header.biSizeImage, f8);
	for(int i=0;i< info_header.biSizeImage;i++)
	{ 
		*b2++ = Bpalette[b1[i]];
		*b2++ = Gpalette[b1[i]];
		*b2++ = Rpalette[b1[i]];
	}
	new_header.bfType = 0x4D42;
	new_header.bfSize = (14 + 40 + 3 * info_header.biSizeImage);
	new_header.bfReserved1 = 0;
	new_header.bfReserved2 = 0;
	new_header.bfOffBits = 14 + 40;
	new_info.biSize = 40;
	new_info.biWidth = info_header.biWidth;
	new_info.biHeight = info_header.biHeight;
	new_info.biBitCount = 24;
	new_info.biPlanes = 1;
	new_info.biCompression = info_header.biCompression;
	new_info.biSizeImage = 3 * info_header.biSizeImage;
	new_info.biXPelsPerMeter = info_header.biXPelsPerMeter;
	new_info.biYPelsPerMeter = info_header.biYPelsPerMeter;
	new_info.biClrUsed = 0;
	new_info.biClrImportant = 0;
	
	fwrite(&new_header,sizeof(new_header),1,f24);
	fwrite(&new_info, sizeof(new_info), 1, f24);
	fwrite(start, 1, new_info.biSizeImage*3, f24);
	delete[] b1;
	delete[] start;
	fclose(f8);
	fclose(f24);

	return 0;
}

2. 24转8

#define _CRT_SECURE_NO_WARNINGS

#include<iostream>
#include<Windows.h>
#include<algorithm>
using namespace std;
BITMAPFILEHEADER File_header, new_header;
BITMAPINFOHEADER info_header, new_info;
tagRGBQUAD t;
int main(int* argc, char* argv[])
{
	char* fpath1 = argv[1];//读取图像文件路径
	char* fpath2 = argv[2];//写入图像文件路径
	FILE* f24 = fopen(fpath1, "rb");
	FILE* f8 = fopen(fpath2, "wb");
	if (fread(&File_header, sizeof(BITMAPFILEHEADER), 1, f24) != 1)
	{
		cout << "read File_header error";
		exit(0);
	}
	if (fread(&info_header, sizeof(BITMAPINFOHEADER), 1, f24) != 1)
	{
		cout << "read info_header error";
		exit(0);
	}
	unsigned char* b24 = new unsigned char[info_header.biSizeImage];
	unsigned char* b8 = new unsigned char[info_header.biSizeImage / 3];

	unsigned int freq[4096] = {0};
	unsigned int *color = new unsigned int[4096];
	int pcolor = 0;
	fread(b24, sizeof(unsigned char), info_header.biSizeImage, f24);
	//找频率最高的256个颜色
	for (int i = 0; i < info_header.biSizeImage/3; i++)
	{
		int a= (b24[i*3] & 0xf0)*1000000 + (b24[i*3+1] & 0xf0)*1000 + (b24[i*3+2] & 0xf0);
		unsigned int *b=find(color, color + sizeof(color), a);
		if (b != color + sizeof(color)) {
			freq[b - color]++;
		}
		else {
			color[pcolor] = a;
			freq[pcolor]++;
			pcolor++;
		}
	}
	int temp;
	for (int i = 0; i<4096; i++)
	{
		for (int j = i; j < 4096; j++)
		{
			if (freq[j] > freq[i])
			{
				temp = color[i];
				color[i] = color[i+1];
				color[j] = temp;
			}
		}
	}
	//创建调色板
	tagRGBQUAD t[256];
	for (int i = 0; i < 256; i++)
	{
		t[i].rgbBlue = color[i] /1000000;
		t[i].rgbRed = color[i] %1000;
		t[i].rgbGreen = (color[i] - t[i].rgbBlue*1000000- t[i].rgbRed)/1000;
		t[i].rgbReserved = 0;
	}
	//找到每个像素对应距离最小的调色板编号
	for (int i = 0; i < info_header.biSizeImage / 3; i++)
	{
		long mindist = 3*pow(256,3);
		int minindex = 0;
		for (int j = 0; j < 256; j++)
		{
			long dist = (int)pow((t[j].rgbBlue - b24[i * 3]), 2) + (int)pow((t[j].rgbGreen - b24[i * 3 + 1]),2) + (int)pow((t[j].rgbRed - b24[i * 3 + 2]),2);
			if (dist < mindist) {
				mindist = dist;
				minindex = j;
			}
		}
		b8[i] = minindex;
	}
	new_header.bfType = 0x4D42;
	new_header.bfSize = (14 + 40 + 4*256 + info_header.biSizeImage/3);
	new_header.bfReserved1 = 0;
	new_header.bfReserved2 = 0;
	new_header.bfOffBits = 14 + 40+4*256;
	new_info.biSize = 40;
	new_info.biWidth = info_header.biWidth;
	new_info.biHeight = info_header.biHeight;
	new_info.biBitCount = 8;
	new_info.biPlanes = 1;
	new_info.biCompression = 0;
	new_info.biSizeImage =  info_header.biSizeImage/3;
	new_info.biXPelsPerMeter = info_header.biXPelsPerMeter;
	new_info.biYPelsPerMeter = info_header.biYPelsPerMeter;
	new_info.biClrUsed = 0;
	new_info.biClrImportant = 0;

	fwrite(&new_header, sizeof(new_header), 1, f8);
	fwrite(&new_info, sizeof(new_info), 1, f8);
	fwrite(&t, sizeof(tagRGBQUAD), 256, f8);
	fwrite(b8, 1, new_info.biSizeImage, f8);
	delete[] b24;
	delete[] b8;
	fclose(f8);
	fclose(f24);

	return 0;
}

四、结果和总结

1. 8bit转24bit的结果

用画图软件导出一个8ibt
在这里插入图片描述
在这里插入图片描述
可以看到导出后图像出现了色彩失真
转24bit后的图像:
在这里插入图片描述

2. 24bit转8bit

还是刚才那幅图像
在这里插入图片描述
转8bit后:
在这里插入图片描述

可以看到采用这种方法的产生的失真非常小。



这篇关于bmp格式 8bit与24bit深度相互转换的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程