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。
编程思路:
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个。
编程思路:
三、代码
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后:
可以看到采用这种方法的产生的失真非常小。
标签:info,int,header,bmp,8bit,new,24bit 来源: https://blog.csdn.net/weixin_45824717/article/details/115579107