C++ C語言 讀取32位BMP圖片轉爲8位灰度圖

BMP格式詳細介紹鏈接:

BMP格式詳解


BMP格式總結:

① 前14字節描述文件類型,大小,數據偏移

② 接着的40字節描述圖片的大小,寬度,高度,位深度,分辨率等等

③ 位深度常見的是1,4,8,24,32。其中1表示只有黑白兩種顏色,4是16色,8是256色,24是RGB形式表示的,32是在24的基礎上加上透明度的RGBA表示。

④ 目前常見的彩色圖片位24位或32位,8位一般爲灰度圖

⑤ 如果是8位圖(256色),40字節的圖片描述後面會有顏色表,描述圖片上256種顏色的RGBA值。灰度圖的第 i 種顏色的RGB值均爲 i ,A值爲0。

⑥由於windows默認的掃描最小單位是4字節,所以BMP格式爲了能夠更快地按行讀取,如果每行的字節數不是4的倍數,會在後面補0直到長度爲4的倍數。可以簡單地得知,32位圖不需要對齊。(對齊規則)


PS:如果用fread整體讀入BMP文件信息和圖片信息,可能由於 內存對齊 的原因,讀入的信息可能存放不到內存裏預期的地方,下面的代碼實現種用#pragma pack(1)解決由於內存對齊產生的問題


簡單的實現代碼:

#include <cstdio>
#include <cstring>
#include <windows.h>
#include <iostream>
using namespace std; 

//設置內存對齊 
#pragma pack(1)

typedef struct tag_color_32{
	BYTE Red;
	BYTE Green;
	BYTE Blue;
	BYTE Alpha;
} color_32;

int main(int argc,char** argv){
    FILE *fp,*out;                     // 定義一個文件指針  
    BITMAPFILEHEADER bmpFileHeader;  // 定義一個 BMP 文件頭的結構體  
    BITMAPINFOHEADER bmpInfo;               // 定義一個 BMP 文件信息結構體   
    string s(argv[1]);
    s = s + "tmp.bmp";
    FILE *out = fopen(s.c_str(),"wb");
    
    if((fp = fopen(argv[1], "rb")) == NULL)
    {  
        printf("Cann't open the file!\n");  
        return 0;
    }
    
    fseek(fp, 0, SEEK_SET);
  
    
    fread(&bmpFileHeader,14,1,fp);
    fread(&bmpInfo, sizeof(BITMAPINFOHEADER), 1, fp);
  
  
    // 輸出BMP文件的位圖文件頭的所有信息  
    printf("位圖文件頭主要是對位圖文件的一些描述:BMPFileHeader\n\n");  
    printf("文件標識符 = 0X%X\n", bmpFileHeader.bfType);  
    printf("BMP 文件大小 = %d 字節\n", bmpFileHeader.bfSize);  
    printf("保留值1 = %d \n", bmpFileHeader.bfReserved1);  
    printf("保留值2 = %d \n", bmpFileHeader.bfReserved2);  
    printf("文件頭到圖像數據位開始的偏移量 = %d 字節\n", bmpFileHeader.bfOffBits);  
  
    // 輸出BMP文件的位圖信息頭的所有信息  
    printf("\n\n位圖信息頭主要是對位圖圖像方面信息的描述:BMPInfo\n\n");  
    printf("信息頭的大小 = %d 字節\n", bmpInfo.biSize);  
    printf("位圖的高度 = %d \n", bmpInfo.biHeight);  
    printf("位圖的寬度 = %d \n", bmpInfo.biWidth);  
    printf("圖像的位面數(位面數是調色板的數量,默認爲1個調色板) = %d \n", bmpInfo.biPlanes);   
    printf("每個像素的位數 = %d 位\n", bmpInfo.biBitCount);  
    printf("壓縮類型 = %d \n", bmpInfo.biCompression);  
    printf("圖像的大小 = %d 字節\n", bmpInfo.biSizeImage);  
    printf("水平分辨率 = %d \n", bmpInfo.biXPelsPerMeter);  
    printf("垂直分辨率 = %d \n", bmpInfo.biYPelsPerMeter);  
    printf("使用的色彩數 = %d \n", bmpInfo.biClrUsed);  
    printf("重要的色彩數 = %d \n", bmpInfo.biClrImportant);  
    
    printf("\n\n\n壓縮說明:有0(不壓縮),1(RLE 8,8位RLE壓縮),2(RLE 4,4位RLE壓縮,3(Bitfields,位域存放)");
    
    //32位色圖 
    if(bmpInfo.biBitCount == 32){
    	int Height = bmpInfo.biHeight;
    	int Width = bmpInfo.biWidth;
    	bool reverse = false; 
		if(Height < 0){
			reverse = true;
			Height *= -1;
		}
		
		color_32 *pixels;
		pixels = new color_32[Height*Width];
		//fseek(fp,bmpFileHeader.bfOffBits,SEEK_SET);
		for(int i = 0;i < Height * Width;i++)
			fread(pixels+i,4,1,fp);
		
		//設置文件頭 
		bmpInfo.biBitCount = 8;
		bmpInfo.biClrUsed = 256;
		//灰度圖顏色表 
		RGBQUAD pRGB[256];
		for(int i = 0;i < 256;i++){
			pRGB[i].rgbRed = i;
			pRGB[i].rgbGreen = i;
			pRGB[i].rgbBlue = i;
			pRGB[i].rgbReserved = 0;
		}
		bmpFileHeader.bfOffBits = 54 + 4 * 256;
		//考慮對齊規則 計算每一行的字節數 
		int LineByte = ((bmpInfo.biBitCount / 8 * Width + 3) / 4) * 4;
		bmpFileHeader.bfSize = 54 + 4 * 256 + Height * LineByte;
		
		
		//寫入新的文件 
		fseek(out,0,SEEK_SET);
		fwrite(&bmpFileHeader,14,1,out);
    	fwrite(&bmpInfo,40,1,out);
    	fwrite(&pRGB,4*256,1,out);
    	
    	for(int i = 0;i < Height;i++){
    		for(int j = 0;j < Width;j++){
    			float pr = pixels[i*Width + j].Red;
    			float pg = pixels[i*Width + j].Green;
    			float pb = pixels[i*Width + j].Blue;
    			//提取灰度低的黑色部分
				BYTE data = pr * 0.299 + pg * 0.587 + pb * 0.114;
    			if((pr - pg) > 5 || (pr - pb) > 5) data = 255;
    			data = data < 25 ? 0 : 255;
    			fwrite(&data,1,1,out);
			}
			for(int j = Width;j < LineByte;j++){
				BYTE data = 0;
				fwrite(&data,1,1,out);
			}
		}
	}
	fclose(out);
    fclose(fp);  
  
    while(1);  
  
    return 0;  
  
} 




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章