bmp文件信息头
为了存储 bmp位图的头文件(14byte) 和位图信息(40byte),开一个DATATYPE文件来存
#ifndef __DATATYPE #define __DATATYPE typedef struct _BMPFileHeader { unsigned char B; unsigned char M; unsigned long FileSize; unsigned long Reserved1; //没有Resevered2 但书上有 unsigned long HeaderLength; }BMPFileHeader; typedef struct _BMPFileInfo { unsigned long InfoSize; unsigned long ImageWidth; unsigned long ImageHeight; unsigned short Level; unsigned short ColorDepth; unsigned long Compress; unsigned long PixelBytes; unsigned long horizon; unsigned long vertical; unsigned long offset1; unsigned long offset2; }BMPFileInfo; typedef struct _Complex { double r,i; }Complex; #define Pi 3.14159265358979323846 #endif
接下来读取图像
void CBMPALGORITHMDlg::OnMenuopen() { CDC *pDC = m_image1.GetDC(); CRect myRECT; m_image1.GetClientRect(&myRECT); // TODO: Add your command handler code here char szFilter[]="BMP Files (*.bmp)|*.bmp|All Files(*.*)|*.*||"; CFileDialog dlg( TRUE,"BMP",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,szFilter ); if(dlg.DoModal() == IDOK) { CString strPathName = dlg.GetPathName(); imgbasic im(strPathName.GetBuffer(0)); SetDlgItemText(IDC_OUTPUT,im.getInfo());//输出路径 im.imshow(pDC,myRECT); // MessageBox("ok","hi~",MB_OK); } m_image1.ReleaseDC(pDC); }
CString中GetBuffer方法可以将CString转为char *
GetBuffer(0)表示从头开始
接下来显示图像和bmp文件头及其他信息
图像信息收集:
char* imgbasic::getInfo()const { char *info; info=(char*)malloc(sizeof(char)*500); info[0]='\0'; // sprintf(info,"12132\r\n"); // sprintf(info+7,"aaa\r\n"); //path sprintf(info+strlen(info),"---------path---------\r\n"); sprintf(info+strlen(info),"FilePath:%s\r\n",filepath); //BMPHeader sprintf(info+strlen(info),"-------BMPHeader-------\r\n"); sprintf(info+strlen(info),"FileType:%c %c P\r\n",MyHeader.B,MyHeader.M); sprintf(info+strlen(info),"FileSize:%ld bytes\r\n",MyHeader.FileSize); sprintf(info+strlen(info),"HeaderLength:%ld \r\n",MyHeader.HeaderLength); //BMPFileInfo sprintf(info+strlen(info),"------BMPFileInfo------\r\n"); sprintf(info+strlen(info),"InfoSize:%ld\r\n",MyInfo.InfoSize); sprintf(info+strlen(info),"ImageWidth:%ld\r\n",MyInfo.ImageWidth); sprintf(info+strlen(info),"ImageHeight:%ld\r\n",MyInfo.ImageHeight); sprintf(info+strlen(info),"Height*Width:%ld\r\n",MyInfo.ImageHeight*MyInfo.ImageWidth*3); sprintf(info+strlen(info),"Level:%ld\r\n",MyInfo.Level); sprintf(info+strlen(info),"ColorDepth:%ld\r\n",MyInfo.ColorDepth); sprintf(info+strlen(info),"PixelBytes:%ld\r\n",MyInfo.PixelBytes); sprintf(info+strlen(info),"Horizon:%ld\r\n",MyInfo.horizon); sprintf(info+strlen(info),"Vertical:%ld\r\n",MyInfo.vertical); sprintf(info+strlen(info),"ColorUsed:%ld\r\n",MyInfo.offset1);//不知为何显示为0 sprintf(info+strlen(info),"ColorImportant:%ld\r\n",MyInfo.offset2);//不知为何显示为0 return info; }
图像信息显示到文本框
imgbasic::imgbasic(char *fn) { strcpy(filepath,fn); FILE *mybmp; if(mybmp=fopen(filepath,"rb"))//以二进制形式读取 { // fread(&MyHeader,10,1,mybmp);//位图头文件是14个字节,此处用结构体读入有问题 fread(&MyHeader.B,1,1,mybmp); fread(&MyHeader.M,1,1,mybmp); fread(&MyHeader.FileSize,1,4,mybmp); fread(&MyHeader.Reserved1,1,4,mybmp); fread(&MyHeader.HeaderLength,1,4,mybmp); fread(&MyInfo,40,1,mybmp);//位图信息头40个字节 raw_img=(unsigned char*)malloc(sizeof(unsigned char)*MyInfo.PixelBytes); fread(raw_img,MyInfo.PixelBytes,1,mybmp); fclose(mybmp); //查看raw图像,在ps中设置宽度,高度,通道数量3,隔行,8位 FILE *f = fopen("t.raw","wb"); fwrite(raw_img, MyInfo.PixelBytes,1, f); fclose(f); } else{ AfxMessageBox("CAN'T OPEN FILE!", MB_OK); } }
图像的快速显示:
void imgbasic::imshow(CDC *pDC, CRect rc)const { SetDIBitsToDevice( //显示图像速度快; pDC->m_hDC, (rc.Width()-vImageWidth())/2, (rc.Height()-vImageHeight())/2, vImageWidth(), vImageHeight(), 0,0,0, vImageHeight(), raw_img, (tagBITMAPINFO *)(&MyInfo), DIB_RGB_COLORS ); }
至此的成果:
bmp慢速显示法
快速显示法是系统集成的,为了深入学习,一定要回慢速显示法
到这里我们已经能够成功的读取bmp图像的几个重要信息:
1.路径filepath
2.宽MyInfo.ImageWidth
3.高MyInfo.ImageHeight
4.文件大小MyHeader.FileSize
5.像素大小MyInfo.PixelBytes
从上面的图片可以看到,229*341的图像实际的文件大小是234662
然而229*3*341= 234267(即341行,每行存每个像素的RGB值)
这里就要考虑bmp文件在内存中的存储方式了
在内存中,文件必须存储为4的倍数(因为每个像素的类型是unsigned char,而内存中二进制必须4个4个存)
所以对于每行,必须取4的整数倍(末尾补0)
229*3=687
687/4=171.75
取整数倍后即687/4*4=688
所以得到(229*3/4*4)*341=234608 BYTE
在加上bmp头文件和位图信息的54个字节,所以FileSize=234608+54=234662
接下来去除末尾补0的部分转存raw_img为img(快速显示时不用去除,系统集成)
void imgbasic::ReadBitData() { unsigned int i,_i=0; unsigned int _img_w; _img_w=vImageWidth()*3; if((_img_w%4)!=0)_img_w=((vImageWidth()*3)/4+1)*4;//保证_img_w是4的倍数 img=(unsigned char*)malloc(sizeof(unsigned char)*data_size); for(i=0;i<vFileSize()-54;i++){ if(i%_img_w<vImageWidth()*3){ img[_i]=raw_img[i]; _i++; } } }
然后显示图像
void imgbasic::imshow(CDC *pDC, CRect rc)//慢速显示 { ReadBitData(); unsigned int i,j; for(i=0;i<vImageHeight();i++){ for(j=0;j<vImageWidth();j++){ unsigned char R = img[3*(i*vImageWidth() + j)]; unsigned char G = img[3*(i*vImageWidth() + j) + 1]; unsigned char B = img[3*(i*vImageWidth() + j) + 2]; pDC->SetPixel((rc.Width()-vImageWidth())/2+j,(rc.Height()-vImageHeight())/2+i,RGB(R,G,B)); } } }
效果如下:
发现问题:
1.图像上下颠倒了
2.颜色不对
原因:
1.bmp的像素数据从底端的一行开始向上存入文件,行内仍然保持从左至右
2.像素数据存放的顺序是BGR,而非RGB
因此接下来进行两个操作:
1.转置图像
void imgbasic::InvertImg() { unsigned char temp; unsigned int i,j,x,y; x=vImageWidth()*3; y=vImageHeight(); for(i=0;i<y/2;i++){ for(j=0;j<x;j++){ temp=img[i*x+j]; img[i*x+j]=img[(y-i-1)*x+j]; img[(y-i-1)*x+j]=temp; } } }
2.R与B互换
void imgbasic::FlipRGB() { unsigned char temp; unsigned int i,j,x,y; x=vImageWidth(); y=vImageHeight(); for(i=0;i<y;i++){ for(j=0;j<x;j++){ temp = img[3*(i*x+j)]; img[3*(i*x+j)] = img[3*(i*x+j)+2]; img[3*(i*x+j)+2] = temp; } } }
得到的结果:除了显示速度较慢外其他都一样