实验目的:
利用c/c++ 作为编程环境进行图像处理的编程实践,掌握Windows下的BMP图像的存储格式和编程方法。先用C语言熟悉编程环境进行图像的简单处理。
实验要求:
掌握Windows 下的BMP图像的存储格式和基本的处理方法;
实验指导
三、显示图像的数据
对于读入的图像文件,显示位图数据(选择一部分区域显示即可)。用256色或256级灰度图像进行验证。有两种显示方式:从上到下,从下到上。自己验证。
四、计算图像的直方图(为0的不显示;每行最大显示50,直方图先归一化,再乘以50)
对于256级灰度级的图像进行计算。存入一个256元素的数组中,并显示其直方图数据。示意图显示方式:用行表示灰度级,在列上用“*”的多少表示直方图数据的大小。(数据为0的不显示,如果值太大,按比例缩小一下)
五、图像的增亮或减暗
输入一个数字,如果是正数对图像进行增亮,如果是负数对图像进行减暗。
用其他软件进行查看,源图像和被增亮或减暗的图像进行对比,是不是达到了预想的效果。
六、main() 函数的要求
main()函数将指定BMP文件读入内存,将图像信息打印输出,最后又原样存入指定文件中。并进行计算需要的数据。
界面:
必做:
0----------结束
1----------读图像(8/24位)
2-----------写图像(8/24位)
3-----------显示图像数据(1行到10行,1列到10列,10*10的大小)(8/24位)
4-----------计算直方图(100-200之间的灰度,1-10行,1-10列,10*10的大小)(8位)
5-----------对图像进行增亮或减暗(如加上50或减去50等)(8位)
6-----------图像反色(8/24位)
选做:
7-----------彩色图变成灰度图(8/24位)
8-----------显示调色板(8位)
9-----------24位彩色图像转换为8位灰度图像
10-----------24位彩色图像转换为8位彩色图像
用户界面
显示图像数据
计算直方图(8位图像)
仅仅使用一个通道:rgbBlue
蓝 灰度192
红 灰度64
调整图像亮度
图像反色
代码:
#include<iostream>
#include<windows.h>
#include <fstream>
using namespace std;
unsigned char *pBmpBuf;//读入图像数据的指针
int bmpWidth;//图像的宽
int bmpHeight;//图像的高
RGBQUAD* pColorTable;//颜色表指针
int biBitCount;//图像类型,每像素位数
char readPath[] = "1.BMP";
char writePath[] = "2.BMP";
double pixelCount;
bool readBmp(char *bmpName)
{
//二进制读方式打开指定的图像文件
FILE* fp = fopen(bmpName, "rb");
if (fp == 0) return 0;
//跳过位图文件头结构BITMAPFILEHEADER
fseek(fp, sizeof(BITMAPFILEHEADER), 0);
//定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中
BITMAPINFOHEADER head;
fread(&head, sizeof(BITMAPINFOHEADER), 1, fp);
//获取图像宽、高、每像素所占位数等信息
bmpWidth = head.biWidth;
bmpHeight = head.biHeight;
biBitCount = head.biBitCount;
//定义变量,计算图像每行像素所占的字节数(必须是4的倍数)
int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;
//灰度图像有颜色表,且颜色表表项为256
if (biBitCount == 8) {
//申请颜色表所需要的空间,读颜色表进内存
pColorTable = new RGBQUAD[256];
fread(pColorTable, sizeof(RGBQUAD), 256, fp);
}
//申请位图数据所需要的空间,读位图数据进内存
pBmpBuf = new unsigned char[lineByte * bmpHeight];
fread(pBmpBuf, 1, lineByte * bmpHeight, fp);
cout<<"像素:"<<pixelCount<<endl;
//关闭文件
fclose(fp);
return 1;
}
bool saveBmp(char *bmpName,unsigned char* imgBuf, int width, int height,int biBitCount, RGBQUAD* pColorTable)
{
//如果位图数据指针为0,则没有数据传入,函数返回
if (!imgBuf)
return 0;
//颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0
int colorTablesize = 0;
if (biBitCount == 8)
colorTablesize = 1024;
//待存储图像数据每行字节数为4的倍数
int lineByte = (width * biBitCount / 8 + 3) / 4 * 4;
//以二进制写的方式打开文件
FILE* fp = fopen(bmpName, "wb");
if (fp == 0) return 0;
//申请位图文件头结构变量,填写文件头信息
BITMAPFILEHEADER fileHead;
fileHead.bfType = 0x4D42;//bmp类型
//bfSize是图像文件4个组成部分之和
fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
+ colorTablesize + lineByte * height;
fileHead.bfReserved1 = 0;
fileHead.bfReserved2 = 0;
//bfOffBits是图像文件前3个部分所需空间之和
fileHead.bfOffBits = 54 + colorTablesize;
//写文件头进文件
fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);
//申请位图信息头结构变量,填写信息头信息
BITMAPINFOHEADER head;
head.biBitCount = biBitCount;
head.biClrImportant = 0;
head.biClrUsed = 0;
head.biCompression = 0;
head.biHeight = height;
head.biPlanes = 1;
head.biSize = 40;
head.biSizeImage = lineByte * height;
head.biWidth = width;
head.biXPelsPerMeter = 0;
head.biYPelsPerMeter = 0;
//写位图信息头进内存
fwrite(&head, sizeof(BITMAPINFOHEADER), 1, fp);
//如果灰度图像,有颜色表,写入文件
if (biBitCount == 8)
fwrite(pColorTable, sizeof(RGBQUAD), 256, fp);
//写位图数据进文件
fwrite(imgBuf, height * lineByte, 1, fp);
//关闭文件
fclose(fp);
cout<<"图像已保存"<<endl;
return 1;
}
int outBMP(char *bmpName)
{
//二进制读方式打开指定的图像文件
FILE* fp = fopen(bmpName, "rb");
if (fp == 0) return 0;
//跳过位图文件头结构BITMAPFILEHEADER
fseek(fp, sizeof(BITMAPFILEHEADER), 0);
//定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中
BITMAPINFOHEADER head;
fread(&head, sizeof(BITMAPINFOHEADER), 1, fp);
//获取图像宽、高、每像素所占位数等信息
bmpWidth = head.biWidth;
bmpHeight = head.biHeight;
biBitCount = head.biBitCount;
pixelCount=bmpWidth*bmpHeight;
//cout<<"像素:"<<pixelCount<<endl;
//定义变量,计算图像每行像素所占的字节数(必须是4的倍数)
int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;
//灰度图像有颜色表,且颜色表表项为256
if (biBitCount == 8) {
//申请颜色表所需要的空间,读颜色表进内存
pColorTable = new RGBQUAD[256];
fread(pColorTable, sizeof(RGBQUAD), 256, fp);
}
//申请位图数据所需要的空间,读位图数据进内存
pBmpBuf = new unsigned char[lineByte * bmpHeight];
fread(pBmpBuf, 1, lineByte * bmpHeight, fp);
double tmp[100][100],max=0;
for (int i = 0; i < 100; i++)
for (int t = 0; t < 100; t++)
tmp[i][t] = pBmpBuf[i * bmpHeight + t * lineByte];
for (int i = 0; i < 100; i++)
{
for (int t = 0; t < 100; t++)
cout << tmp[i][t] << " ";
cout << endl;
//关闭文件
fclose(fp);
return 1;
}
}
void printChart(int i,double grayFrequency)
{
double grayCount=grayFrequency*50;
cout<<i<<" "<<int(grayCount)<<" ";
for(int j=1;j<=int(grayCount);j++)
{
cout<<"*";
if(j==50)break;
}
cout<<endl;
}
int Histogram(char *bmpName,int biBitCount)
{
if(biBitCount!=8)
{
cout<<"图片不是8位!"<<endl;
cout<<biBitCount;
system("pause");return 0;
}
BITMAPFILEHEADER bmpFileHeader;
BITMAPINFOHEADER bmpInfoHeader;
RGBQUAD bmpColorTable[256];
BYTE bmpValue[500 * 500];
FILE *fp;
//打开lena.bmp图像文件
fp = fopen(bmpName, "rb");
//读取图像信息
fread(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, fp);
fread(&bmpInfoHeader, sizeof(BITMAPINFOHEADER), 1, fp);
fread(bmpColorTable, sizeof(RGBQUAD), 256, fp);
bmpWidth = bmpInfoHeader.biWidth;
bmpHeight = bmpInfoHeader.biHeight;
pixelCount=bmpWidth*bmpHeight;
fread(bmpValue, 1,pixelCount, fp);
cout<<"像素:"<<pixelCount<<endl;
//将图像灰度值存入一位数组中
int grayValue[500 * 500] = { 0 };
for (int i = 0; i < pixelCount; i++)
{
grayValue[i] = bmpColorTable[bmpValue[i]].rgbBlue;
}
//统计直方图
int grayCount[256] = { 0 };
double grayFrequency[256] = { 0.0 };
for (int i = 0; i < pixelCount; i++)
{
grayCount[grayValue[i]]++;
}
for (int i = 0; i < 256; i++)
{
if (grayCount[i])
{
grayFrequency[i] = grayCount[i] / pixelCount;
printChart(i,grayFrequency[i]);
//printf("灰度值%3d 频数为%6d 频率为%f\n", i, grayCount[i], grayFrequency[i]);
}
}
//关闭图像文件
fclose(fp);
}
int SetColor(int i,RGBQUAD* &pColorTable,int delta)
{
if(pColorTable[i].rgbBlue+delta<0)pColorTable[i].rgbBlue=0;
else pColorTable[i].rgbBlue+=delta;
if(pColorTable[i].rgbGreen+delta<0)pColorTable[i].rgbGreen=0;
else pColorTable[i].rgbGreen+=delta;
if(pColorTable[i].rgbRed+delta<0)pColorTable[i].rgbRed=0;
else pColorTable[i].rgbRed+=delta;
if(pColorTable[i].rgbBlue+delta>255)pColorTable[i].rgbBlue=255;
else pColorTable[i].rgbBlue+=delta;
if(pColorTable[i].rgbGreen+delta>255)pColorTable[i].rgbGreen=255;
else pColorTable[i].rgbGreen+=delta;
if(pColorTable[i].rgbRed+delta>255)pColorTable[i].rgbRed=255;
else pColorTable[i].rgbRed+=delta;
}
int Lightness(int biBitCount,RGBQUAD* &pColorTable)
{
if(biBitCount!=8)
{
cout<<"图片不是8位!"<<endl;
system("pause");return 0;
}
int delta=0;
cout<<"请输入修改亮度的参数:";
cin>>delta;
for(int i=0;i<=255;i++)
{
SetColor(i,pColorTable,delta);
}
saveBmp(writePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pColorTable);
}
int SetColor2(int i,RGBQUAD* &pColorTable)
{
pColorTable[i].rgbBlue=255-pColorTable[i].rgbBlue;
pColorTable[i].rgbGreen=255-pColorTable[i].rgbGreen;
pColorTable[i].rgbRed=255-pColorTable[i].rgbRed;
}
int invertColor(RGBQUAD* &pColorTable)
{
for(int i=0;i<=255;i++)
{
SetColor2(i,pColorTable);
}
saveBmp(writePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pColorTable);
}
void printMenu()
{
cout<<"------------------------------------------------------------------"<<endl;
cout<<"Menu"<<endl;
cout<<"1)读图像(8/24位)"<<endl;
cout<<"2)写图像(8/24位)"<<endl;
cout<<"3)显示图像数据(1行到10行,1列到10列,10*10的大小)(8/24位)"<<endl;
cout<<"4)计算直方图(100-200之间的灰度,1-10行,1-10列,10*10的大小)(8位)"<<endl;
cout<<"5)对图像进行增亮或减暗(如加上50或减去50等)(8位)"<<endl;
cout<<"6)图像反色(8/24位)"<<endl;
cout<<"7)彩色图变成灰度图(8/24位)"<<endl;
cout<<"8)显示调色板(8位)"<<endl;
cout<<"9)24位彩色图像转换为8位灰度图像"<<endl;
cout<<"0)24位彩色图像转换为8位彩色图像"<<endl;
cout<<"a)退出"<<endl;
cout<<"------------------------------------------------------------------"<<endl;
char ch;
cin>>ch;
switch(ch)
{
case '1':
//用一个函数,把指定BMP文件读入内存
readBmp(readPath);system("cls");
cout<<"------------------------------------------------------------------"<<endl;
cout<<"状态:";
cout<<"已读入图像"<<endl;
break;
case '2':
saveBmp(writePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pColorTable);
//清除缓冲区,p和pColorTable是全局变量,在文件读入时申请的空间
delete[]pBmpBuf;
if (biBitCount == 8)
delete[]pColorTable;
system("cls");
cout<<"------------------------------------------------------------------"<<endl;
cout<<"状态:";
cout<<"已写入图像"<<endl;
break;
case '3':
system("cls");
cout<<"------------------------------------------------------------------"<<endl;
cout<<"状态:";
cout<<"显示图像数据"<<endl;
outBMP(readPath);
//pixelShow();
break;
case '4': system("cls");
cout<<"------------------------------------------------------------------"<<endl;
cout<<"状态:";
cout<<"显示直方图"<<endl;
Histogram(readPath,biBitCount);
break;
case '5': system("cls");
cout<<"------------------------------------------------------------------"<<endl;
cout<<"状态:";
cout<<"调整亮度"<<endl;
Lightness(biBitCount,pColorTable);
break;
case '6': system("cls");
cout<<"------------------------------------------------------------------"<<endl;
cout<<"状态:";
cout<<"反色"<<endl;
invertColor(pColorTable);
break;
case '7':break;
case '8':break;
case '9':break;
case '0': system("cls");
cout<<"状态:";
printf("width=%d,height=%d, biBitCount=%d/n", bmpWidth, bmpHeight, biBitCount);
cout<<endl;
break;
case 'a':break;
}
}
int main()
{
while(1)
{
printMenu();
}
return 0;
}