實現(xiàn)位圖的讀取顯示保存,能完成24位位圖的轉(zhuǎn)換,是一個典型的圖像處理基礎(chǔ)程序,程序很好,很簡單清晰,容易看懂,但是所有圖像信息頭和數(shù)據(jù)的處理全在響應(yīng)函數(shù)和ondrow函數(shù)里!
將24位真彩色圖轉(zhuǎn)換為灰度圖
在圖像處理中,我們經(jīng)常需要將真彩色圖像轉(zhuǎn)換為黑白圖像。嚴(yán)格的講應(yīng)該是灰度圖,因為真正的黑白圖像是二色,即只有純黑,純白二色。開始之前,我們先簡單補(bǔ)充一下計算機(jī)中圖像的表示原理。
計算機(jī)中的圖像大致可以分成兩類:位圖(Bitmap)和矢量圖(Metafile)。 位圖可以視為一個二維的網(wǎng)格,整個圖像就是由很多個點組成的,點的個數(shù)等于位圖的寬乘以高。每個點被稱為一個像素點,每個像素點有確定的顏色,當(dāng)很多個像素合在一起時就形成了一幅完整的圖像。
我們通常使用的圖像大部分都是位圖,如數(shù)碼相機(jī)拍攝的照片,都是位圖。因為位圖可以完美的表示圖像的細(xì)節(jié),能較好的 還原圖像的原景。
但位圖也有缺點:第一是體積比較大,所以人們開發(fā)了很多壓縮圖像格式來儲存位圖圖像,目前應(yīng)用最廣的是JPEG格式,在WEB上得到了廣泛應(yīng)用,另外還有GIF,PNG等 等。第二是位圖在放大時,不可避免的會出現(xiàn)“鋸齒”現(xiàn)象,這也由位圖的本質(zhì)特點決定的。
所以在現(xiàn)實中,我們還需要使用到另一種圖像格式:矢量圖。同位圖不 同,矢量圖同位圖的原理不同,矢量圖是利用數(shù)學(xué)公式通過圓,線段等繪制出來的,所以不管如何放大都不會出現(xiàn)變形,但矢量圖不能描述非常復(fù)雜的圖像。所以矢量圖都是用來描述圖形圖案,各種CAD軟件等等都是使用矢量格式來保存文件的。
24位真色位圖轉(zhuǎn)化為8位灰度位圖
(C語言實現(xiàn))
//最近在學(xué)習(xí)圖像處理有關(guān)的基礎(chǔ)知識,并試著編程實踐,這樣有助于自己的理解和學(xué)習(xí)。首先遇到的第一個問題就是bmp位圖的基礎(chǔ)知識、bmp位圖的C語言讀入、操作、存入等。以下便是有關(guān)這期間學(xué)習(xí)的知識經(jīng)驗總結(jié):歡迎分享!歡迎建議!謝謝!
//要理解的基礎(chǔ)問題:bmp位圖文件格式(掌握了bmp位圖文件的具體格式后,才有可能更好的對其進(jìn)行編程操作實踐。
位圖文件(bitmap file)保存順序如下:
位圖頭文件(BITMAPFILEHEADER)
位圖信息頭文件(BITMAPINFOHEADER)
調(diào)色板RGBQUAD(真彩色位圖沒有調(diào)色板)
圖像數(shù)據(jù)
##釋義1##位圖頭文件(BITMAPFILEHEADER):
typedef struct tagBITMAPFILEHEADER
{ /*14 bytes BMP文件頭數(shù)據(jù)結(jié)構(gòu)含有BMP文件的類型、
文件大小和位圖起始位置等信息*/
WORD bfType; /*2,位圖文件的類型,必須為BM(0-1字節(jié))*/
DWORD bfSize; /*4,位圖文件的大小,以字節(jié)為單位(2-5字節(jié))*/
WORD bfReserved1; /*2,位圖文件保留字,必須為0(6-7字節(jié))*/
WORD bfReserved2; /*2,位圖文件保留字,必須為0(6-7字節(jié))*/
DWORD bfOffBits; /*4,位圖數(shù)據(jù)的起始位置,以相對于位圖(10-13字節(jié)) */
}BITMAPFILEHEADER,*PBITMAPFILEHEADER;
##釋義2##位圖信息頭文件(BITMAPINFOHEADER):
typedef struct tagBITMAPINFOHEADER
{ /*40 bytes BMP位圖信息頭數(shù)據(jù)用于說明位圖的尺寸等信息。*/
DWORD biSize; /*4,本結(jié)構(gòu)所占用字節(jié)數(shù)(14-17字節(jié)) */
LONG biWidth; /*4,位圖的寬度,以像素為單位(18-21字節(jié))*/
LONG biHeight; /*4,位圖的高度,以像素為單位(22-25字節(jié)) */
WORD biPlanes; /*2,目標(biāo)設(shè)備的級別,必須為1(26-27字節(jié)) */
WORD biBitCount;/*2每個像素所需的位數(shù),必須是1(雙色),(28-29字節(jié))
4(16色),8(256色)或24(真彩色)之一 */
DWORD biCompression;/*4,位圖壓縮類型,必須是 0(不壓縮),(30-33字節(jié))
1(BI_RLE8壓縮類型)或2(BI_RLE4壓縮類型)之一 */
DWORD biSizeImage; /*4,位圖的大小,以字節(jié)為單位(34-37字節(jié)) */
LONG biXPelsPerMeter;/*4,位圖水平分辨率,每米像素數(shù)(38-41字節(jié)) */
LONG biYPelsPerMeter; /*4,位圖垂直分辨率,每米像素數(shù)(42-45字節(jié)) */
DWORD biClrUsed;/*4, 位圖實際使用的顏色表中的顏色數(shù)(46-49字節(jié)) */
DWORD biClrImportant;/*4,位圖顯示過程中重要的顏色數(shù)(50-53字節(jié)) */
} BITMAPINFOHEADER,*PBITMAPINFOHEADER;
##釋義3##調(diào)色板RGBQUAD(真彩色位圖沒有調(diào)色板):
typedef struct tagRGBQUAD
{ /*顏色表用于說明位圖中的顏色,它有若干個表項
,每一個表項是一個RGBQUAD類型的結(jié)構(gòu),定義一種顏色。*/
BYTE rgbBlue; /*藍(lán)色的亮度(值范圍為0-255)*/
BYTE rgbGreen; /*綠色的亮度(值范圍為0-255) */
BYTE rgbRed;/*紅色的亮度(值范圍為0-255)*/
BYTE rgbReserved; /*保留,必須為0 */
}RGBQUAD;
##釋義4##圖像數(shù)據(jù):
對于用到調(diào)色板的位圖,圖像數(shù)據(jù)就是該像素顏色在調(diào)色板中的索引值。對于真彩色圖像,圖像數(shù)據(jù)就是實際的R、G、B值,一個像素由3個字節(jié)24位組成,第一個字節(jié)表示B,第二個字節(jié)表示G,第三個字節(jié)表示R。
注意:BMP文件是從下到上、從左都右排列的。讀文件時,最先讀到的是圖像的最下面一行的左邊的第一個像素,最后讀到的是最上面一行的最右一個像素。
//C源程序:本C程序由兩部分組成(.c文件和.h文件)。(注:VC6編譯鏈接運(yùn)行通過)
*********************************************************************************************
rgbtogray.h文件:
*****************
/* C語言讀入圖像 位圖文件結(jié)構(gòu)聲明 */
#ifndef BMP_H_INCLUDED
#define BMP_H_INCLUDED
typedef unsigned short WORD;//2*8=16
typedef unsigned long DWORD;//4*8=32
typedef long LONG;//4*8=32
typedef unsigned char BYTE;//1*8=8
#pragma pack(1)
typedef struct tagBITMAPFILEHEADER
{ /*14 bytes BMP文件頭數(shù)據(jù)結(jié)構(gòu)含有BMP文件的類型、
文件大小和位圖起始位置等信息*/
WORD bfType; /*2,位圖文件的類型,必須為BM(0-1字節(jié))*/
DWORD bfSize; /*4,位圖文件的大小,以字節(jié)為單位(2-5字節(jié))*/
WORD bfReserved1; /*2,位圖文件保留字,必須為0(6-7字節(jié))*/
WORD bfReserved2; /*2,位圖文件保留字,必須為0(6-7字節(jié))*/
DWORD bfOffBits; /*4,位圖數(shù)據(jù)的起始位置,以相對于位圖(10-13字節(jié)) */
}BITMAPFILEHEADER,*PBITMAPFILEHEADER;
#pragma pack()
#pragma pack(1)
typedef struct tagBITMAPINFOHEADER
{ /*40 bytes BMP位圖信息頭數(shù)據(jù)用于說明位圖的尺寸等信息。*/
DWORD biSize; /*4,本結(jié)構(gòu)所占用字節(jié)數(shù)(14-17字節(jié)) */
LONG biWidth; /*4,位圖的寬度,以像素為單位(18-21字節(jié))*/
LONG biHeight; /*4,位圖的高度,以像素為單位(22-25字節(jié)) */
WORD biPlanes; /*2,目標(biāo)設(shè)備的級別,必須為1(26-27字節(jié)) */
WORD biBitCount;/*2每個像素所需的位數(shù),必須是1(雙色),(28-29字節(jié))
4(16色),8(256色)或24(真彩色)之一 */
DWORD biCompression;/*4,位圖壓縮類型,必須是 0(不壓縮),(30-33字節(jié))
1(BI_RLE8壓縮類型)或2(BI_RLE4壓縮類型)之一 */
DWORD biSizeImage; /*4,位圖的大小,以字節(jié)為單位(34-37字節(jié)) */
LONG biXPelsPerMeter;/*4,位圖水平分辨率,每米像素數(shù)(38-41字節(jié)) */
LONG biYPelsPerMeter; /*4,位圖垂直分辨率,每米像素數(shù)(42-45字節(jié)) */
DWORD biClrUsed;/*4, 位圖實際使用的顏色表中的顏色數(shù)(46-49字節(jié)) */
DWORD biClrImportant;/*4,位圖顯示過程中重要的顏色數(shù)(50-53字節(jié)) */
} BITMAPINFOHEADER,*PBITMAPINFOHEADER;
#pragma pack()
#pragma pack(1)
typedef struct tagRGBQUAD
{ /*顏色表用于說明位圖中的顏色,它有若干個表項
,每一個表項是一個RGBQUAD類型的結(jié)構(gòu),定義一種顏色。*/
BYTE rgbBlue; /*藍(lán)色的亮度(值范圍為0-255)*/
BYTE rgbGreen; /*綠色的亮度(值范圍為0-255) */
BYTE rgbRed;/*紅色的亮度(值范圍為0-255)*/
BYTE rgbReserved; /*保留,必須為0 */
}RGBQUAD;/*顏色表中RGBQUAD結(jié)構(gòu)數(shù)據(jù)的個數(shù)有biBitCount來確定:
當(dāng)biBitCount=1,4,8時,分別有2,16,256個表項;
當(dāng)biBitCount=24時,沒有顏色表項。*/
#pragma pack()
#pragma pack(1)
typedef struct tagBITMAPIMAGE
{ /*位圖信息頭和顏色表組成位圖信息*/
BITMAPFILEHEADER bmiHeader; /*位圖信息頭*/
RGBQUAD bmiColors[1]; /*顏色表*/
}BITMAPIMAGE;
#pragma pack()
#endif
*********************************************************************************************
rgbtogray.h文件:
******************
/*******************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<string.h>
/********************************************************************/
//自定義的有關(guān)圖像處理的頭文件為rgbtogray.h
//系統(tǒng)定義有關(guān)圖像處理的頭文件為windows.h
//目前,引用系統(tǒng)的windows.h程序工作正常,但自己的頭文件有問題,待改正!
//最終實現(xiàn)引用自己的頭文件rgbtogray.h
//太棒了:頭文件的問題終于解決了
//#include<windows.h>
#include "rgbtogray.h"
/********************************************************************/
/*****************************************************************************/
//全局變量聲明
FILE * fpSrcBmpfile;//定義文件指針,用于讀入和存儲圖像文件
FILE * fpDestBmpfile;
/******************************************************************************/
//程序子函數(shù)聲明
void GetBmpHeader(PBITMAPFILEHEADER, PBITMAPINFOHEADER);//獲得位圖的文件頭和信息頭
void ChangeBmpHeader(PBITMAPFILEHEADER, PBITMAPINFOHEADER, WORD);//改變位圖的文件頭和信息頭
void SetBmpHeader(const PBITMAPFILEHEADER, const PBITMAPINFOHEADER);//將修改后的文件頭和信息頭存入新位圖文件
void SetRGBQUAD();//建立顏色板并存入灰度圖文件中
int RgbToGray();//24位真色圖轉(zhuǎn)化為8位灰度圖的主要函數(shù)
/******************************************************************************/
//主函數(shù)框架,菜單。ǚ奖阏{(diào)用其他程序,以便拓展)
void main()
{
int command=1;
while(command)//菜單選項,可拓展
{
printf("\n*****************************Image Processing Menu***************************\n");
printf("|--------1:RGB To GRAY transform!--------|\n");
printf("|--------0:End!---------|\n");
printf("*****************************************************************************\n");
printf("Hint:Processor Number?\n");
scanf("%d",&command);
switch(command)
{
case 1:
RgbToGray();
break;
case 0:
printf("---Bye-bye---\n");
break;
default:
printf("---Not defined number!\n");
break;
}
}
}
/******************************************************************************/
//函數(shù)體部分(副)
void GetBmpHeader(PBITMAPFILEHEADER pbfheader, PBITMAPINFOHEADER pbiheader)
{
fread(pbfheader, sizeof(BITMAPFILEHEADER), 1,fpSrcBmpfile);
fread(pbiheader, sizeof(BITMAPINFOHEADER), 1,fpSrcBmpfile);
}
void ChangeBmpHeader(PBITMAPFILEHEADER pbfheader, PBITMAPINFOHEADER pbiheader, WORD wType)
{
pbiheader->biBitCount = wType; // 24 或者 8
pbiheader->biClrUsed = (wType == 24) ? 0 : 256;
pbfheader->bfOffBits = 54 + pbiheader->biClrUsed * sizeof(RGBQUAD);
pbiheader->biSizeImage = ((((pbiheader->biWidth * pbiheader->biBitCount) + 31) & ~31) / 8) * pbiheader->biHeight;
pbfheader->bfSize = pbfheader->bfOffBits + pbiheader->biSizeImage;
}
void SetBmpHeader(const PBITMAPFILEHEADER pbfheader, const PBITMAPINFOHEADER pbiheader)
{
fwrite(pbfheader, sizeof(BITMAPFILEHEADER), 1, fpDestBmpfile);
fwrite(pbiheader, sizeof(BITMAPINFOHEADER), 1, fpDestBmpfile);
}
void SetRGBQUAD()
{
int i;
RGBQUAD rgbquad[256];
for(i=0;i<256;i++) {
rgbquad[i].rgbBlue = i;
rgbquad[i].rgbGreen = i;
rgbquad[i].rgbRed = i;
rgbquad[i].rgbReserved = 0;
}
fwrite(rgbquad, 256 * sizeof(RGBQUAD), 1, fpDestBmpfile);
}
/******************************************************************************/
//函數(shù)體部分(主)
int RgbToGray()
{
LONG w,h;
BYTE r,g,b;
BYTE gray;
BYTE count24,count8;
BYTE Bmpnul=0;
char SrcBmpfile[256];
char DestBmpfile[256];
BITMAPFILEHEADER bmfh; // bmp文件頭
BITMAPINFOHEADER bmih; // 位圖信息頭
BYTE *data;
memset(&bmfh, 0, sizeof(BITMAPFILEHEADER));//內(nèi)存初始化
memset(&bmih, 0, sizeof(BITMAPINFOHEADER));
data=(BYTE *)malloc(3*sizeof(BYTE));
if(!data)
{
printf("Error:Can not allocate memory .\n");
free(data);
return -1;
}
getchar();
printf("Input the path of SrcBmpfile:\n");
gets(SrcBmpfile);
if((fpSrcBmpfile=fopen(SrcBmpfile,"rb"))==NULL)
{
printf("Error:Open the file of SrcBmpfile failed!\n");//輸入源位圖文件
free(data);
return -1;
}
rewind(fpSrcBmpfile);
GetBmpHeader(&bmfh,&bmih);
//ceshie_start
printf("The contents in the file header of the BMP file:\n");
printf("bfType:%ld\n",bmfh.bfType);
printf("bfSize:%ld\n",bmfh.bfSize);
printf("bfReserved1:%ld\n",bmfh.bfReserved1);
printf("bfReserved2:%ld\n",bmfh.bfReserved2);
printf("bfOffBits:%ld\n",bmfh.bfOffBits);
printf("The contents in the info header:\n");
printf("biSize:%ld\n",bmih.biSize);
//ceshi_end
if(bmfh.bfType!=0x4D42)
{
printf("Error:This file is not bitmap file!\n");
free(data);
return -1;
}
if(bmih.biBitCount!=24)
{
printf("Error:This bmpfile is not 24bit bitmap!\n");
free(data);
return -1;
}
if(bmih.biCompression!=0)
{
printf("Error:This 8bit bitmap file is not BI_RGB type!\n");
free(data);
return -1;
}
printf("Input the path of the DestBmpfile:\n");//輸入目標(biāo)位圖文件
gets(DestBmpfile);
if((fpDestBmpfile=fopen(DestBmpfile,"wb"))==NULL)
{
printf("Error:Open the file of DestBmpfile failed!\n");
free(data);
return -1;
}
ChangeBmpHeader(&bmfh,&bmih,8);
SetBmpHeader(&bmfh,&bmih);
SetRGBQUAD();
count24=(4-(bmih.biWidth*3)%4)%4;
count8=(4-(bmih.biWidth)%4)%4;
for(h=bmih.biHeight-1;h>=0;h--)
{
for(w=0;w<bmih.biWidth;w++)
{
fread(data,3,1,fpSrcBmpfile);
if(feof(fpSrcBmpfile))
{
printf("Error:Read Pixel data failed!\n");
free(data);
return -1;
}
b=*data;
g=*(data+1);
r=*(data+2);
gray=(299*r+587*g+114*b)/1000;
//if(gray>120)gray=250;
fwrite(&gray,sizeof(gray),1,fpDestBmpfile);
}
fseek(fpSrcBmpfile,count24,SEEK_CUR);
fwrite(&Bmpnul,1,count8,fpDestBmpfile);
}
printf("Hint:Convert RGB To GRAY Successfully!\n");
free(data);//釋放內(nèi)存空間
fclose(fpDestBmpfile);//關(guān)閉文件指針
fclose(fpSrcBmpfile);
return 0;
}
/******************************************************************************/
*******************************************************************************************
****當(dāng)時困擾了本人很長時間的一個程序問題,最后終于解決:
#prama pack(n)
結(jié)構(gòu)體定義;
#prama pack()
##釋義##:關(guān)于struct的使用方法:
struct是一種復(fù)合數(shù)據(jù)類型,其構(gòu)成元素既可以是基本數(shù)據(jù)類型(如int、long、float等)的變量,也可以是一些復(fù)合數(shù)據(jù)類型(如array、struct、union等)的數(shù)據(jù)單元。對于結(jié)構(gòu)體,編譯器會自動進(jìn)行成員變量的對齊,以提高運(yùn)算效率。缺省情況下,編譯器為結(jié)構(gòu)體的每個成員按其自然對界(natural alignment)條件分配空間。各個成員按照它們被聲明的順序在內(nèi)存中順序存儲,第一個成員的地址和整個結(jié)構(gòu)的地址相同。
自然對界是指按結(jié)構(gòu)體的成員中size最大的成員對齊。
#pragma pack規(guī)定的對齊長度,實際使用的規(guī)則是:
結(jié)構(gòu),聯(lián)合,或者類的數(shù)據(jù)成員,第一個放在偏移為0的地方,以后每個數(shù)據(jù)成員的對齊,按照#pragma pack指定的數(shù)值和結(jié)構(gòu)體的自然對齊長度中比較小的那個進(jìn)行。
也就是說,當(dāng)#pragma pack的值等于或超過所有數(shù)據(jù)成員長度的時候,這個值的大小將不產(chǎn)生任何效果。
結(jié)構(gòu)體的對齊,按照結(jié)構(gòu)體中size最大的數(shù)據(jù)成員和#pragma pack指定值之間,較小的那個進(jìn)行。