CTF作了圖片的隱寫題,尚未造成系統的認識,先來總結一下BMP圖的組成,並經過將彩色圖轉爲二值圖的例子加深下理解。html
只寫了位圖二進制文件的格式和代碼實現,至於諸如RGB色彩和調色板是什麼的一些概念就不囉嗦了。測試
BMP位圖文件格式htm
BMP文件由文件頭、位圖信息頭、調色板和圖形數據四部分組成,真彩色圖是沒有調色板的。每部分的具體結構在代碼中具體列出並解釋。blog
結構體的對齊索引
定義文件頭部各結構體時要注意對齊的問題,至於什麼是結構體對齊,請看這篇博文,寫的很詳細http://www.cnblogs.com/motadou/archive/2009/01/17/1558438.html圖片
位圖的四字節對齊內存
這個對齊是位圖每行像素的對齊,不要和上面的結構體對齊混淆,每行像素所佔字節數必須是4字節的整數倍,不足的要填充,這時由於內存分配單位是32位的,即4字節,讀入的每行像素是連續的,不能和其餘行共佔一個內存單位get
彩色圖轉爲灰度圖it
RGB共有256種灰色份量,就是R=G=B時的色彩是灰色的,因此能夠用一個字節來表示,將彩色圖轉化爲灰度圖就是讓真彩色圖的三個顏色份量等於一個相同的數值,具體等於多少能夠與多種方法,好比求三個份量的平均值,取三個份量的最大值或者使其中兩個份量等於另外一個份量的值,還有一種經常使用的方法是取加權平均值,根據這個很著名的心理學公式Gray = R*0.299 + G*0.587 + B*0.114io
灰度圖轉化爲二值圖
二值圖只有兩個顏色,黑和白,而灰度有256種顏色,將灰度轉化爲二值是選取一個閾值,將灰度值大於這個閾值的置成白色,反之爲黑色,關於這個閾值如何選取和做用範圍也有多種方法,再也不贅述,爲了簡單我在全局範圍內選用一個固定的閾值190(針對我這張測試圖片,手敲了個190,轉化的效果還能夠)二值圖只有兩個索引,能夠用1bit表示,可是我寫的程序是用1個字節表示的,至於如何壓縮,看你嘍。。。
說了這麼多,仍是看代碼吧,這樣更容易理解,額,好像幾乎給每行都寫了註釋╮(╯▽╰)╭,不要嫌我墨跡
1 /* 2 Author:蔚藍行 3 Blog:http://www.cnblogs.com/duanv/ 4 */ 5 #include <stdio.h> 6 #include <stdlib.h> 7 8 /*位圖文件頭*/ 9 #pragma pack(1)//單字節對齊 10 typedef struct tagBITMAPFILEHEADER 11 { 12 unsigned char bfType[2];//文件格式 13 unsigned int bfSize;//文件大小 14 unsigned short bfReserved1;//保留 15 unsigned short bfReserved2;//保留 16 unsigned int bfOffBits;//數據偏移量 17 }fileHeader; 18 #pragma pack() 19 20 /*位圖信息頭*/ 21 #pragma pack(1) 22 typedef struct tagBITMAPINFOHEADER 23 { 24 unsigned int biSize;//BITMAPINFOHEADER結構所須要的字數 25 int biWidth;//圖像寬度,像素爲單位 26 int biHeight;//圖像高度,像素爲單位,爲正數,圖像是倒序的,爲負數,圖像是正序的 27 unsigned short biPlanes;//爲目標設備說明顏色平面數,總被置爲1 28 unsigned short biBitCount;//說明比特數/像素 29 unsigned int biCompression;//說明數據壓縮類型 30 unsigned int biSizeImage;//說明圖像大小,字節單位 31 int biXPixPerMeter;//水平分辨率,像素/米 32 int biYPixPerMeter;//垂直分辨率 33 unsigned int biClrUsed;//顏色索引數 34 unsigned int biClrImportant;//重要顏色索引數,爲0表示都重要 35 }fileInfo; 36 #pragma pack() 37 38 /*調色板結構*/ 39 #pragma pack(1) 40 typedef struct tagRGBQUAD 41 { 42 unsigned char rgbBlue;//藍色分亮度 43 unsigned char rgbGreen;//綠色分亮度 44 unsigned char rgbRed;//紅色分亮度 45 unsigned char rgbReserved; 46 }rgbq; 47 #pragma pack() 48 49 int main() 50 { 51 /*變量聲明*/ 52 FILE *fpBMP,*fpTwoValue;//源文件fpBMP,目標文件fpTwoValue 53 54 fileHeader *fh;//位圖文件頭 55 fileInfo *fi;//位圖信息頭 56 rgbq *rg;//調色板 57 58 int i,j,k=0; 59 unsigned char *a;//存儲源圖每行像素值 60 unsigned char b;//存儲每一個像素的灰度值或二值 61 unsigned char *c;//存儲每行像素的二值 62 63 /********************************************************************/ 64 65 /*打開源文件,建立輸出文件*/ 66 if((fpBMP=fopen("/Users/SPY/Desktop/1.bmp","rb"))==NULL){ 67 printf("file open failed"); 68 exit(0); 69 } 70 71 if((fpTwoValue=fopen("/Users/SPY/Desktop/2.bmp","wb"))==NULL){ 72 printf("file creat failed"); 73 exit(0); 74 } 75 76 /********************************************************************/ 77 78 /*建立位圖文件頭,信息頭,調色板*/ 79 fh=(fileHeader *)malloc(sizeof(fileHeader)); 80 fi=(fileInfo *)malloc(sizeof(fileInfo)); 81 rg=(rgbq *)malloc(2*sizeof(rgbq)); 82 83 /*讀入源位圖文件頭和信息頭*/ 84 fread(fh,sizeof(fileHeader),1,fpBMP); 85 fread(fi,sizeof(fileInfo),1,fpBMP); 86 87 /*修改文件頭,信息頭信息*/ 88 fi->biBitCount=8;//轉換成二值圖後,顏色深度由24位變爲8位 89 fi->biSizeImage=((fi->biWidth+3)/4)*4*fi->biHeight;//每一個像素由三字節變爲單字節,同時每行像素要四字節對齊 90 fi->biClrUsed=2;//顏色索引表數量,二值圖爲2 91 fi->biClrImportant=0;//重要顏色索引爲0,表示都重要 92 fh->bfOffBits=sizeof(fileHeader)+sizeof(fileInfo)+2*sizeof(rgbq);//數據區偏移量,等於文件頭,信息頭,索引表的大小之和 93 fh->bfSize=fh->bfOffBits+fi->biSizeImage;//文件大小,等於偏移量加上數據區大小 94 rg[0].rgbBlue=rg[0].rgbGreen=rg[0].rgbRed=rg[0].rgbReserved=0;//調色板顏色爲黑色對應的索引爲0 95 rg[1].rgbBlue=rg[1].rgbGreen=rg[1].rgbRed=255;//白色對應的索引爲1 96 rg[1].rgbReserved=0; 97 98 /********************************************************************/ 99 100 /*將位圖文件頭,信息頭和調色板寫入文件*/ 101 fwrite(fh,sizeof(fileHeader),1,fpTwoValue); 102 fwrite(fi,sizeof(fileInfo),1,fpTwoValue); 103 fwrite(rg,2*sizeof(rgbq),1,fpTwoValue); 104 105 /*將彩色圖轉爲二值圖*/ 106 a=(unsigned char *)malloc((fi->biWidth*3+3)/4*4);//給變量a申請源圖每行像素所佔大小的空間,考慮四字節對齊問題 107 c=(unsigned char *)malloc((fi->biWidth+3)/4*4);//給變量c申請目標圖每行像素所佔大小的空間,一樣四字節對齊 108 109 for(i=0;i<fi->biHeight;i++){//遍歷圖像每行的循環 110 for(j=0;j<((fi->biWidth*3+3)/4*4);j++){//遍歷每行中每一個字節的循環 111 fread(a+j,1,1,fpBMP);//將源圖每行的每個字節讀入變量a所指向的內存空間 112 //printf("%d ",a[j]); 113 } 114 for(j=0;j<fi->biWidth;j++){//循環像素寬度次,就不會計算讀入四字節填充位 115 b=(int)(0.114*(float)a[k]+0.587*(float)a[k+1]+0.299*(float)a[k+2]);//a中每三個字節分別表明BGR份量,乘上不一樣權值轉化爲灰度值 116 //printf("%d",b); 117 if(190<=(int)b) b=1;//將灰度值轉化爲二值,這裏選取的閾值爲190 118 else b=0; 119 c[j]=b;//存儲每行的二值 120 k+=3; 121 } 122 fwrite(c,(fi->biWidth+3)/4*4,1,fpTwoValue);//將二值像素四字節填充寫入文件,填充位沒有初始化,爲隨機值 123 k=0; 124 } 125 126 /********************************************************************/ 127 128 /*釋放內存空間,關閉文件*/ 129 free(fh); 130 free(fi); 131 free(rg); 132 free(a); 133 free(c); 134 fclose(fpBMP); 135 fclose(fpTwoValue); 136 printf("success!\n"); 137 return 0; 138 }