bmpTest.h :介紹BMP檔案的格式及結構定義
bmpTest.cpp : 24bitBMP顔色資料到256色位圖顔色資料的轉換函數實作,具體算法可參考以 前的一個文章
bmpTransfer.cpp : 讀入一個24bitBMP檔案,轉換成一個256色BMP檔案的程式
編譯完成後得到的程式,如bmpTransfer.exe
執行 bmpTransfer file1 file2
file1是24bit的BMP位圖源檔案名,file2是新生成的256色位圖檔案名
可以用windows畫闆程式檢視結果,似乎比直接用畫闆程式将24bitBMP存成256色BMP檔案的轉換效果要好哦。
bmpTest.h
/*************
bmpTest.h
**************/
#ifndef __BMPTEST_H_
#define __BMPTEST_H_
#include <stdio.h>
typedef unsigned char BYTE;
typedef unsigned short WORD;
// BMP圖像各部分說明如下
/***********
第一部分 位圖檔案頭
該結構的長度是固定的,為14個位元組,各個域的依次如下:
2byte :檔案類型,必須是0x4d42,即字元串"BM"。
4byte :整個檔案大小
4byte :保留字,為0
4byte :從檔案頭到實際的位圖圖像資料的偏移位元組數。
*************/
typedef struct
{
long imageSize;
long blank;
long startPosition;
void show(void)
{
printf("BMP Head:\n");
printf("Image Size:%d\n",imageSize);
printf("Image Data Start Position : %d\n",startPosition);
}
}BmpHead;
/*********************
第二部分 位圖資訊頭
該結構的長度也是固定的,為40個位元組,各個域的依次說明如下:
4byte :本結構的長度,值為40
4byte :圖像的寬度是多少象素。
4byte :圖像的高度是多少象素。
2Byte :必須是1。
2Byte :表示顔色時用到的位數,常用的值為1(黑白二色圖)、4(16色圖)、8(256色圖)、24(真彩色圖)。
4byte :指定位圖是否壓縮,有效值為BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS。Windows位圖可采用RLE4和RLE8的壓縮格式,BI_RGB表示不壓縮。
4byte :指定實際的位圖圖像資料占用的位元組數,可用以下的公式計算出來:
圖像資料 = Width' * Height * 表示每個象素顔色占用的byte數(即顔色位數/8,24bit圖為3,256色為1)
要注意的是:上述公式中的biWidth'必須是4的整數倍(不是biWidth,而是大于或等于biWidth的最小4的整數倍)。
如果biCompression為BI_RGB,則該項可能為0。
4byte :目标裝置的水準分辨率。
4byte :目标裝置的垂直分辨率。
4byte :本圖像實際用到的顔色數,如果該值為0,則用到的顔色數為2的(顔色位數)次幂,如顔色位數為8,2^8=256,即256色的位圖
4byte :指定本圖像中重要的顔色數,如果該值為0,則認為所有的顔色都是重要的。
***********************************/
typedef struct
{
long Length;
long width;
long height;
WORD colorPlane;
WORD bitColor;
long zipFormat;
long realSize;
long xPels;
long yPels;
long colorUse;
long colorImportant;
void show(void)
{
printf("infoHead Length:%d\n",Length);
printf("width&height:%d*%d\n",width,height);
printf("colorPlane:%d\n",colorPlane);
printf("bitColor:%d\n",bitColor);
printf("Compression Format:%d\n",zipFormat);
printf("Image Real Size:%d\n",realSize);
printf("Pels(X,Y):(%d,%d)\n",xPels,yPels);
printf("colorUse:%d\n",colorUse);
printf("Important Color:%d\n",colorImportant);
}
}InfoHead;
/***************************
第三部分 調色盤結構
對于256色BMP位圖,顔色位數為8,需要2^8 = 256個調色盤;
對于24bitBMP位圖,各象素RGB值直接儲存在圖像資料區,不需要調色盤,不存在調色盤區
rgbBlue: 該顔色的藍色分量。
rgbGreen: 該顔色的綠色分量。
rgbRed: 該顔色的紅色分量。
rgbReserved:保留值。
************************/
typedef struct
{
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
void show(void)
{
printf("Mix Plate B,G,R:%d %d %d\n",rgbBlue,rgbGreen,rgbRed);
}
}RGBMixPlate;
/****************************
第四部分 圖像資料區
對于用到調色闆的位圖,圖像資料就是該象素顔色在調色闆中的索引值;
對于真彩色圖,圖像資料就是實際的R、G、B值。
2色圖,用1位就可以表示該象素的顔色,是以1個位元組可以表示8個象素。
16色圖,用4位可以表示一個象素的顔色,是以1個位元組可以表示2個象素。
256色圖,1個位元組剛好可以表示1個象素。
真彩色圖,3個位元組才能表示1個象素。
****************************/
//将24bit的象素顔色資料轉換為256色圖的圖像資料(即索引值)
int Transfer(WORD *color24bit, int len, BYTE *Index, RGBMixPlate *mainColor);
#endif
bmpTest.cpp
/***************
bmpTest.cpp
****************/
#include "bmpTest.h"
#include <string.h>
#include <assert.h>
//計算平方差的函數
int PFC(int color1, int color2)
{
int x,y,z;
x = (color1 & 0xf) - (color2 & 0xf);
y = ((color1>>4) & 0xf) - ((color2>>4) & 0xf);
z = ((color1>>8) & 0xf) - ((color2>>8) & 0xf);
return (x*x + y*y + z*z);
};
//直接插入排序
int Sort1(int *src, int *attach, int n)
{
int cur, cur1;
int i,j,k=0;
for (i = 1; i < n; i++)
{
cur = src[i];
cur1 = attach[i];
for (j = i - 1; j >= 0; j--)
{
if (cur > src[j])
{
src[j+1] = src[j];
attach[j+1] = attach[j];
}
else
break;
}
src[j+1] = cur;
attach[j+1] = cur1;
}
return 0;
}
//快速排序
int Sort2(int *src, int *attach, int n)
{
if (n <= 12)
return Sort1(src, attach, n);
int low = 1, high = n - 1;
int tmp;
while (low <= high)
{
while (src[low] >= src[0])
{
if (++low > n - 1)
break;
}
while (src[high] < src[0])
{
if (--high < 1)
break;
}
if (low > high)
break;
{
tmp = src[low];
src[low] = src[high];
src[high] = tmp;
tmp = attach[low];
attach[low] = attach[high];
attach[high] = tmp;
}
low++;
high--;
}
{
tmp = src[low - 1];
src[low - 1] = src[0];
src[0] = tmp;
tmp = attach[low - 1];
attach[low - 1] = attach[0];
attach[0] = tmp;
}
if (low > 1)
Sort2(src, attach, low - 1);
if (low < n)
Sort2(&src[low], &attach[low], n - low);
return 0;
}
//将24bit的象素顔色資料轉換為256色圖的圖像資料(即索引值)
int Transfer(WORD *color24bit, int len, BYTE *Index, RGBMixPlate *mainColor)
{
int usedTimes[4096] = {0};
int miniColor[4096];
for (int i = 0; i < 4096; i++)
miniColor[i] = i;
i = 0;
for (i = 0; i < len; i++)
{
assert(color24bit[i] < 4096);
usedTimes[color24bit[i]]++;
}
int numberOfColors = 0;
for (i = 0; i < 4096; i++)
{
if (usedTimes[i] > 0)
numberOfColors++;
}
//對usedTimes進行排序,排序過程中minColor數組(儲存了顔色值)也作與useTimes
//數組相似的交換
Sort2(usedTimes, miniColor, 4096);
//usedTimes數組中是各顔色使用頻率,從高到低排列,顯然第numberOfColor個之後的都為0
//miniColor數組中是相應的顔色資料
//将前256個顔色資料儲存到256色位圖的調色盤中
for (i = 0; i < 256; i++)
{
mainColor[i].rgbBlue = (BYTE)((miniColor[i]>>8)<<4);
mainColor[i].rgbGreen = (BYTE)(((miniColor[i]>>4) & 0xf)<<4);
mainColor[i].rgbRed = (BYTE)((miniColor[i] & 0xf)<<4);
mainColor[i].rgbReserved = 0;
}
int *colorIndex = usedTimes;//用原來的useTimes數組來儲存索引值
memset(colorIndex, 0, sizeof(int) * 4096);
if (numberOfColors <= 256)
{
for (i = 0; i < numberOfColors; i++)
colorIndex[miniColor[i]] = i;
}
else//為第256之後的顔色在前256種顔色中找一個最接近的
{
for (i = 0; i < 256; i++)
colorIndex[miniColor[i]] = i;
int index, tmp, tmp1;
for (i = 256; i < numberOfColors; i++)
{
tmp = PFC(miniColor[0], miniColor[i]);
index = 0;
for (int j = 1; j < 256; j++)
{
tmp1 = PFC(miniColor[j], miniColor[i]);
if (tmp > tmp1)
{
tmp = tmp1;
index = j;
}
}
colorIndex[miniColor[i]] = index;
}
}
//記錄各點顔色資料的索引值,即256色位圖的顔色資料
for (i = 0; i < len; i++)
{
assert(colorIndex[color24bit[i]] < 256);
Index[i] = colorIndex[color24bit[i]];
}
return 1;
}
bmpTransfer.cpp
/********************
bmpTransfer.cpp
********************/
#include "bmpTest.h"
#include <string.h>
int __cdecl main(int argc,char* argv[])
{
if (argc < 3)
{
printf("Usage:\n");
printf(" %s filename1 filename2\n", argv[0]);
printf(" filename1 : source 24bit BMP filename like: xxx.bmp\n");
printf(" filename2 : new 256 color BMP filename\n");
return -1;
}
BmpHead headBMP;
InfoHead infoHead;
FILE* p;
char* filename = argv[1];
p = fopen(filename,"rb");
if (p == NULL)
{
printf("!!!file %s open failed.\n", filename);
}
printf("file %s open success.\n",filename);
/********** read BMP head ********************/
fseek(p,2,SEEK_CUR);
fread(&headBMP,1,12,p);
headBMP.show();
fread(&infoHead,1,40,p);
infoHead.show();
if (infoHead.bitColor != 24)
{
fclose(p);
printf("This is not a 24bit BMP file.\n");
return -1;
}
/*********** read Image Date **************/
long nData = infoHead.realSize;
BYTE* pColorData = new BYTE[nData];
fread(pColorData,1,nData,p);
printf("last 4 byte of color Data:%x %x %x %x\n",\
pColorData[nData-4],pColorData[nData-3],\
pColorData[nData-2],pColorData[nData-1]);
/*********** read file over ***************/
int leftData = 0;
char ch = 0;
while (!feof(p))
{
fread(&ch,1,1,p);
leftData++;
}
if (leftData)
printf("%d bytes not read in file.\n", leftData);
printf("read file over.\n");
if(!fclose(p))
{
printf("file close.\n");
}
// 24位BMP檔案資訊都讀出來了,可以檢視列印資訊
/************ 24bit到256色的顔色資料轉換 *****************/
BYTE* Index = new BYTE[nData/3];
RGBMixPlate mainColor[256];
memset(mainColor, 0, sizeof(mainColor));
WORD* shortColor = new WORD[nData/3];
int iRed, iGreen, iBlue;
for (int i = 0; i < nData/3; i++)
{//取RGB顔色的高4位
iRed = pColorData[i*3]>>4;
iGreen = pColorData[i*3+1]>>4;
iBlue = pColorData[i*3+2]>>4;
shortColor[i] = (iRed<<8) + (iGreen<<4) + iBlue;
}
delete []pColorData;
//調用轉換函數
Transfer(shortColor, nData/3, Index, mainColor);
delete []shortColor;
//轉換完成,256色位圖的調色盤資料(儲存在mainColor)和圖像資料區的資料(儲存在Index中)
/************ 寫一個新的256色BMP檔案 *******************/
//修改一下前面讀出的BmpHead和InfoHead的結構資訊
headBMP.imageSize = 14 + 40 + 4*256 + nData/3;
// 4*256是調色盤的長度,nData/3是圖像資料區長度
headBMP.startPosition += 4*256; //新檔案加上了調色盤部分
infoHead.bitColor = 8; //顔色位數改為8
infoHead.realSize = nData/3; //圖像資料區長度
//寫新檔案
char* newFile = argv[2];
FILE *p1 = fopen(newFile,"wb");
if (NULL == p1)
{
printf("open new file failed.\n");
return -1;
}
char hh[2] = {0x42, 0x4D};
fwrite(hh,1,2,p1); //BMP檔案開頭兩位元組, 0x4d42 = "BM"
fwrite(&headBMP, 1, 12, p1); //BMP檔案頭
fwrite(&infoHead, 1, 40, p1); //BMP檔案頭資訊
fwrite(mainColor, 1, sizeof(mainColor), p1);//寫調色盤資訊
fwrite(Index, 1, nData/3, p1); //顔色資料
fclose(p1);
//釋放配置設定的記憶體
delete []Index;
return 0;
}