本来续---数字图像处理之位图在计算机中的存储结构一文,通过参考别人的代码,进行修改和测试终于成功运行。
该实例未使用任何API和相关类,相信如果对此实例能够完全理解那么将有进一步进行数字图像处理的能力。
该实例实现三个功能:
1.读取位图文件数据。
2.将位图中像素数据保存到TXT中。
3.根据读取的位图函数中的数据保存位图文件。
具体代码如下,并附有详细注释:
头文件BmpNew.h如下:
1 typedef unsigned char BYTE; 2 typedef unsigned short WORD; 3 typedef unsigned int DWORD; 4 typedef long LONG; 5 6 //位图文件头定义; 7 //其中不包含文件类型信息(由于结构体的内存结构决定, 8 //要是加了的话将不能正确读取文件信息) 9 typedef struct tagBITMAPFILEHEADER{ 10 //WORD bfType;//文件类型,必须是0x424D,即字符“BM” 11 DWORD bfSize;//文件大小 12 WORD bfReserved1;//保留字 13 WORD bfReserved2;//保留字 14 DWORD bfOffBits;//从文件头到实际位图数据的偏移字节数 15 }BITMAPFILEHEADER; 16 17 typedef struct tagBITMAPINFOHEADER{ 18 DWORD biSize;//信息头大小 19 LONG biWidth;//图像宽度 20 LONG biHeight;//图像高度 21 WORD biPlanes;//位平面数,必须为1 22 WORD biBitCount;//每像素位数 23 DWORD biCompression; //压缩类型 24 DWORD biSizeImage; //压缩图像大小字节数 25 LONG biXPelsPerMeter; //水平分辨率 26 LONG biYPelsPerMeter; //垂直分辨率 27 DWORD biClrUsed; //位图实际用到的色彩数 28 DWORD biClrImportant; //本位图中重要的色彩数 29 }BITMAPINFOHEADER; //位图信息头定义 30 31 typedef struct tagRGBQUAD{ 32 BYTE rgbBlue; //该颜色的蓝色分量 33 BYTE rgbGreen; //该颜色的绿色分量 34 BYTE rgbRed; //该颜色的红色分量 35 BYTE rgbReserved; //保留值 36 }RGBQUAD;//调色板定义
源文件BmpNew.cpp如下:
1 #include<math.h> 2 #include <iomanip.h> 3 #include <stdlib.h> 4 #include <windows.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <fstream.h> 8 9 10 11 12 /************************************************************************/ 13 14 /*以下该模块是完成BMP图像(彩色图像是24bit RGB各8bit)的像素获取, 15 16 并存在文件名为xiang_su_zhi.txt中 17 */ 18 /************************************************************************/ 19 20 //用到的全局变量 21 22 unsigned char *pBmpBuf;//读入图像数据的指针 23 24 int bmpWidth;//图像的宽 25 26 int bmpHeight;//图像的高 27 28 RGBQUAD *pColorTable;//颜色表指针 29 30 int biBitCount;//图像类型,每像素位数 31 32 33 34 /************************************************************************/ 35 36 /* 读图像的位图数据、宽、高、颜色表及 37 38 每像素位数等数据进内存,存放在相应的全局变量中 39 */ 40 /************************************************************************/ 41 42 bool readBmp(char *bmpName) 43 { 44 45 FILE *fp=fopen(bmpName,"rb");//二进制只读方式打开指定的图像文件 46 47 if(fp==0) //判断文件是否正确打开 48 return 0; 49 50 51 //跳过位图文件头结构BITMAPFILEHEADER,使得文件指针指向信息头的开始 52 fseek(fp, sizeof(BITMAPFILEHEADER),0); 53 54 //定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中,为了获取图像的宽,高,每像素所占位数 55 BITMAPINFOHEADER head; 56 fread(&head, sizeof(BITMAPINFOHEADER), 1,fp); 57 58 //获取图像宽、高、每像素所占位数等信息 59 bmpWidth = head.biWidth; 60 bmpHeight = head.biHeight; 61 biBitCount = head.biBitCount; 62 63 //定义变量,计算图像每行像素所占的字节数(必须是4的倍数) 64 int lineByte=(bmpWidth * biBitCount/8+3)/4*4; 65 66 //灰度图像有颜色表(调色板),且颜色表表项为256 67 if(biBitCount==8) 68 { 69 //申请颜色表所需要的空间,读颜色表进内存 70 pColorTable=new RGBQUAD[256]; //申请256种颜色表大小的内存 71 fread(pColorTable,sizeof(RGBQUAD),256,fp); //读取概灰度图的颜色表到pColorTable所指向的内存中 72 } 73 74 //申请位图数据所需要的空间,读位图数据进pBmpBuf指向的内存 75 pBmpBuf=new unsigned char[lineByte * bmpHeight]; 76 fread(pBmpBuf,1,lineByte * bmpHeight,fp); 77 78 79 fclose(fp);//关闭文件 80 return 1;//读取文件成功 81 82 } 83 84 85 86 87 /************************************************************************/ 88 89 /* 给定一个图像位图数据、宽、高、颜色表指针及 90 91 每像素所占的位数等信息,将其写到指定文件中 92 */ 93 /************************************************************************/ 94 95 bool saveBmp(char *bmpName, unsigned char *imgBuf, int width, int height, int biBitCount, RGBQUAD *pColorTable) 96 { 97 //如果位图数据指针为0,则没有数据传入,函数返回 98 if(!imgBuf) 99 return 0; 100 101 //颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0 102 int colorTablesize=0; 103 104 if(biBitCount==8) 105 colorTablesize=1024;//一个RGBQUAD(颜色)结构占四个字节,对于8位的灰度图而言,一共有256*4=1024个字节大小的颜色表 106 107 //待存储图像数据每行字节数为4的倍数 108 int lineByte=(width * biBitCount/8+3)/4*4; 109 110 //以二进制写的方式打开文件 111 FILE *fp=fopen(bmpName,"wb"); 112 if(fp==0) 113 return 0; 114 115 //申请位图文件头结构变量,填写文件头信息 116 BITMAPFILEHEADER fileHead; 117 fileHead.bfType = 0x4D42;//bmp类型 118 119 //bfSize是图像文件4个组成部分之和 120 fileHead.bfSize= sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTablesize + lineByte*height; 121 fileHead.bfReserved1 = 0; 122 fileHead.bfReserved2 = 0; 123 124 125 //bfOffBits是图像文件前3个部分所需空间之和 126 fileHead.bfOffBits=54+colorTablesize; 127 128 //写文件头进文件 129 fwrite(&fileHead, sizeof(BITMAPFILEHEADER),1, fp); 130 131 132 //申请位图信息头结构变量,填写信息头信息 133 BITMAPINFOHEADER head; 134 head.biBitCount=biBitCount; 135 head.biClrImportant=0; 136 head.biClrUsed=0; 137 head.biCompression=0; 138 head.biHeight=height; 139 head.biPlanes=1; 140 head.biSize=40; 141 head.biSizeImage=lineByte*height; 142 head.biWidth=width; 143 head.biXPelsPerMeter=0; 144 head.biYPelsPerMeter=0; 145 146 //写位图信息头进内存 147 fwrite(&head, sizeof(BITMAPINFOHEADER),1, fp); 148 149 //如果灰度图像,有颜色表,写入文件 150 if(biBitCount==8) 151 fwrite(pColorTable, sizeof(RGBQUAD),256, fp); 152 153 //写位图数据进文件 154 fwrite(imgBuf, height*lineByte, 1, fp); 155 156 //关闭文件 157 fclose(fp); 158 return 1; 159 } 160 161 /************************************************************************/ 162 163 /* 以下为像素的读取函数 */ 164 165 /************************************************************************/ 166 void doIt() 167 { 168 //读入指定BMP文件进内存 169 char readPath[]="test.bmp"; 170 171 //读取图像信息 172 readBmp(readPath); 173 174 //输出图像的信息 宽度(单位像素) 高度(单位像素) 每个像素的位数 175 cout<<"width="<<bmpWidth<<" height="<<bmpHeight<<" biBitCount="<<biBitCount<<endl; 176 177 //循环变量,图像的坐标 178 //每行字节数 179 int lineByte=(bmpWidth*biBitCount/8+3)/4*4; 180 181 //循环变量,针对彩色图像,遍历每像素的三个分量 182 int m=0,n=0,count_xiang_su=0; 183 184 //将图像左下角1/4部分置成黑色 185 ofstream outfile("test2.txt",ios::in|ios::trunc); //概txt文档用于保存图像的像素数据,以8*8的矩阵形式输出 186 187 if(biBitCount==8) //对于灰度图像 188 { 189 190 /************************************************************************/ 191 192 /* 以下完成图像的分割成8*8小单元,并把像素值存储到指定文本中 193 194 由于BMP图像的像素数据是从左下角:由左往右,由下往上逐行扫描的*/ 195 196 /************************************************************************/ 197 198 int L1=0; 199 int hang=63; 200 int lie=0; 201 //int L2=0; 202 //int fen_ge=8; 203 204 //----------------------------------------------------------------- 205 //最外层for循环控制行,一次八行 206 //----------------------------------------------------------------- 207 for(int fen_ge_hang=0;fen_ge_hang<8;fen_ge_hang++)//64*64矩阵行循环 208 { 209 //----------------------------------------------- 210 //第三层for循环按列循环,循环8次8列,至此最后8列被分成了8个8*8矩阵保存在文本文档中 211 //----------------------------------------------- 212 for(int fen_ge_lie=0;fen_ge_lie<8;fen_ge_lie++)//64*64列矩阵循环 213 { 214 //---------------------------------------------------------------- 215 //最内层的两个for循环循环从最后一行左边开始读取8*8矩阵到txt文件中 216 //---------------------------------------------------------------- 217 for(L1=hang;L1>hang-8;L1--)//8*8矩阵行 218 { 219 for(int L2=lie;L2<lie+8;L2++)//8*8矩阵列 220 { 221 m=*(pBmpBuf+L1*lineByte+L2); 222 223 outfile<<m<<" "; 224 225 count_xiang_su++; 226 227 if(count_xiang_su%8==0)//每8*8矩阵读入文本文件 228 outfile<<endl; 229 } 230 } 231 //--------------------------------------------- 232 233 hang=63-fen_ge_hang*8;//64*64矩阵行变换 234 235 lie+=8;//64*64矩阵列变换 236 237 //该一行(64)由8个8*8矩阵的行组成 238 } 239 hang-=8;//64*64矩阵的列变换 240 241 lie=0;//64*64矩阵 242 } 243 } 244 245 //判断概灰度图像数据是否写入到文本文档中 246 if(!outfile) 247 { 248 cout<<"open error!"<<endl; 249 250 exit(1); 251 } 252 253 254 255 else if(biBitCount==24) 256 { 257 //彩色图像 258 //------------------------------------------------------- 259 //彩色图像的读取是每次读一行从左上角开始读,最外层控制行 260 //第二次for控制列,而彩色图每个像素由三个字节的颜色描述 261 //所以第三个for是将每个像素的R,G,B分量依次读入到文本 262 //文档中 263 //------------------------------------------------------- 264 for(int i=0;i<bmpHeight;i++) 265 { 266 for(int j=0;j<bmpWidth;j++) 267 { 268 for(int k=0;k<3;k++)//每像素RGB三个分量分别置0才变成黑色 269 { 270 m=*(pBmpBuf+i*lineByte+j*3+k); 271 272 outfile<<m<<" "; 273 274 count_xiang_su++; 275 276 if(count_xiang_su%8==0) 277 { 278 outfile<<endl; 279 } 280 } 281 n++; //计算RGB图的像素 282 } 283 284 } 285 cout<<"总的像素个素为:"<<n<<endl; 286 cout<<"----------------------------------------------------"<<endl; 287 } 288 289 290 291 //将图像数据存盘 292 293 char writePath[]="f.bmp"; 294 295 //图片处理后再存储 296 saveBmp(writePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pColorTable); 297 298 299 //清除缓冲区,pBmpBuf和pColorTable是全局变量,在文件读入时申请的空间 300 delete []pBmpBuf; 301 302 if(biBitCount==8) 303 delete []pColorTable; 304 } 305 306 307 308 /************************************************************************/ 309 310 /* 主函数 */ 311 312 /************************************************************************/ 313 314 315 void main() 316 { 317 doIt(); 318 }
测试的test.bmp图片如下:
输出保存的图片如下f.tmp:
保存的text2.txt截图如下:
至此,对于一张灰度BMP图像数据的读取,像素数据保存,以及存盘显示就已经完整的实现了,代码中或许
会有bug,请读者批评指正,共同完善。