家人萌 我因为这个作业爆炸了好多天...所以我想发一下
菜鸡一个 别骂别骂 欢迎指正
关于这个作业要先了解一下这些登西...
1)BMP 位图的结构
1、BMP文件头(14字节) ,文件的第0字节到第13字节为BMP图像的文件头。BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。
typedef struct tagBITMAPFILEHEADER
{
WORD bfType; // 位图文件的类型,必须为BM(0-1字节)
DWORD bfSize; // 整个位图文件的大小,以字节为单位(2-5字节)
WORD bfReserved1; // 位图文件保留字,必须为0(6-7字节)
WORD bfReserved2; // 位图文件保留字,必须为0(8-9字节)
DWORD bfOffBits; // 位图数据的起始位置,以相对于位图(10-13字节)
}
2、位图信息头(40字节),文件的第14个字节到第53个字节为BMP图像的信息头,位图信息头数据用于说明位图的尺寸等信息。在这部分读取位图的长和宽
typedef struct tagBITMAPINFOHEADER {
DWORD biSize; // 本结构所占用字节数(14-17字节)
LONG biWidth; // 位图的宽度,以像素为单位(18-21字节)
LONG biHeight; // 位图的高度,以像素为单位(22-25字节)
WORD biPlanes; // 目标设备的级别,必须为1(26-27字节)
WORD biBitCount;// 每个像素所需的位数,必须是1(双色), 4(16色),8(256色)或24(真彩色)之一(28-29字节)
DWORD biCompression; // 位图压缩类型,必须是 0(不压缩), 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一(30-33字节)
DWORD biSizeImage; // 位图数据部分的大小,以字节为单位(34-37字节)
LONG biXPelsPerMeter; // 位图水平分辨率,每米像素数(38-41字节)
LONG biYPelsPerMeter; // 位图垂直分辨率,每米像素数(42-45字节)
DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数(46-49字节)
DWORD biClrImportant;// 位图显示过程中重要的颜色数(50-53字节)
}
3.调色板
调色板用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
typedef struct tagRGBQUAD {
BYTE rgbBlue; // 蓝色的亮度(值范围为0-255)
BYTE rgbGreen; // 绿色的亮度(值范围为0-255)
BYTE rgbRed; // 红色的亮度(值范围为0-255)
BYTE rgbReserved;// 保留,必须为0
}
调色板中RGBQUAD结构数据的个由biBitCount来确定:
当biBitCount=1,4,8时,分别有2,16,256个表项; 2的n次方吼
当biBitCount=24时,该BMP图像就是24Bit真彩图,没有调色板项
还有!!!每个表项为4个字节
4、位图数据
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。
位图的一个像素值所占的字节数由biBitCount来确定:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节;(每个像素的颜色信息每3个字节一组,按BGR的顺序存放,其中每个字节只存一个颜色值。颜色值范围是0~255,用无符号char型存储)
!!!Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充
24位真彩图每一行占的实际字节数 =((宽度*3+3)/4)*4
灰度图每一行占的实际字节数= ((宽度+3)/4)*4
2)读取文件
利用fstream读文件
要注意BMP文件每个部分所占的字节数,因为不是每个部分的信息要需要读取,所以要利用seekg函数移动读文件的指针位置,还有就是利用read函数读取文件时,也要确定读取的字节数。
BMP的文件头没有要读取的内容,所以移动指针“跳过”文件头,然后读取信息头,可以利用sizeof()得到文件头和信息头所占的字节数。调色板部分也不需要读取,如果是24位位图,没有调色板,可以直接向下进行,其他位图都需要得到调色板的大小然后“跳过”,读取下一部分的位图数据。
这个作业写的 只能读8位和24位的 而且只是读入数据 没有修改
Image.h
#ifndef IMAGE_H
#define IMAGE_H
#include<iostream>
#include<fstream>
#include<windows.h>
#include<cmath>
#include<string>
using namespace std;
class Image {
friend Image& operator+(Image& img1, Image& img2);
public:
Image();
Image(string fliename);
~Image();
long getWidth();
long getHeight();
float Average();
float Variance();
void show();
private:
long m_width;
long m_height;
int m_bitCount;
unsigned char* m_pData;
};
Image& operator+(Image& img1, Image& img2);
#endif
Image.cpp
#include"Image.h"
Image::Image() {
this->m_width = 0;
this->m_height = 0;
this->m_pData = NULL;
}
Image::Image(string filename) {//bmp文件的地址
ifstream open(filename, ios::binary);
if (!open.is_open()) {
cout <<filename<< "文件打开成功" << endl;
}
//跳过位图文件头
open.seekg(sizeof(BITMAPFILEHEADER));
//读取位图信息头
BITMAPINFOHEADER a;
open.read((char*)&a, sizeof(BITMAPINFOHEADER));
m_height = a.biHeight;
m_width = a.biWidth;
m_bitCount= a.biBitCount;
cout << "文件"<<filename << "为" << m_bitCount << "位位图" << endl;
cout << "高度:" << m_height << endl;
cout << "宽度:" << m_width << endl;
//如果是24位位图 无调色板 直接向下进行
//其他位图有调色板 需要跳过调色板读取位图数据
if (m_bitCount != 24) {
open.seekg(4 * (pow(2.0, m_bitCount)));//调色板大小 4*色彩数
}
//读取位图数据(有0补位)
if(m_bitCount==8)
{
m_pData = new unsigned char[m_height * (((m_width + 3) / 4) * 4)];
open.read((char*)m_pData, m_height * (((m_width + 3) / 4) * 4));
}
else if (m_bitCount == 24)
{
m_pData = new unsigned char[m_height * ((m_width * 3 + 3) / 4) * 4];
open.read((char*)m_pData, m_height * ((m_width * 3 + 3) / 4) * 4);
}
open.close();
}
Image::~Image() {
delete[] m_pData;
}
long Image::getHeight(){
return m_height;
}
long Image::getWidth() {
return m_width;
}
float Image::Average() {
try {
if (m_pData == NULL)
throw exception("位图数据为空!");
if (m_bitCount != 8 && m_bitCount != 24)
throw m_bitCount;
float sum = 0;
if(m_bitCount==8)
{
for (int i = 0; i < m_height * ((m_width + 3) / 4) * 4; i++) {
sum += m_pData[i];
}
return sum * 1.0 / (m_height * m_width);
}
else if (m_bitCount == 24) {
for (int i = 0; i < m_height * ((m_width*3 + 3) / 4) * 4; i++) {
sum += m_pData[i];
}
return sum * 1.0 / (m_height * m_width*3);
}
}
catch (exception error) {
cout << error.what() << endl;
return 0;
}
catch (int) {
cout << "无法对" << m_bitCount << "位位图计算 T_T" << endl;
return 0;
}
}
float Image::Variance() {
try {
if (m_pData == NULL)
throw exception("位图数据为空!");
if (m_bitCount != 8 && m_bitCount != 24)
throw m_bitCount;
float sum = 0;
long long count = 0;
//每行补位0的个数
int n = (m_width % 4 == 0) ? 0 : 4 - m_width % 4;
if(m_bitCount==8)
{
for (int i = 0; i < m_height; i++) {
for (int j = 0; j < m_width; j++) {
sum += ((m_pData[count] - this->Average()) * (m_pData[count] - this->Average()));
count++;
}
if (n != 0)
count += (n - 1);
}
}
else if (m_bitCount == 24) {
for (int i = 0; i < m_height; i++) {
for (int j = 0; j < m_width; j++) {
float sum1 = 0, avg1 = 0;
for (int k = 0; k < 3; k++) {
sum1 += m_pData[count];
count += 1;
}
avg1 = sum1 * 1.0 / 3;
sum += ((Average() - avg1) * (Average() - avg1));
}
if (n != 0)
count += (n - 1);
}
}
return sum * 1.0 / (m_height * m_width);
}
catch (exception error) {
cout << error.what() << endl;
return 0;
}
catch (int) {
cout << "无法对" << m_bitCount << "位位图计算 T_T" << endl;
return 0;
}
}
Image& operator+(Image& img1, Image& img2) {
try {
if (img1.getHeight() != img2.getHeight() || img1.getWidth() != img2.getWidth())
throw exception("两个位图长宽不同 无法运算");
for (int i = 0; i < img1.getHeight() * ((img1.getWidth() + 3) / 4) * 4; i++) {
if (img1.m_pData[i] + img2.m_pData[i] <= 255) {
img1.m_pData[i] += img2.m_pData[i];
}
else {
img1.m_pData[i] = (img1.m_pData[i] + img2.m_pData[i]) % 255;
}
}
return img1;
}
catch (exception error) {
cout << error.what() << endl;
}
}
void Image::show() {
cout <<"位图平均值:" << Average() << endl;
cout << "位图方差:" << Variance() << endl;
}
Test.cpp
#include"Image.h"
int main() {
string filename1, filename2, filename3, filename4, filename5;//F:\\Image\\test01.bmp F:\\Image\\test02.bmp F:\\Image\\test03.bmp F:\\Image\\test04.bmp F:\\Image\\test05.bmp
cout << "输入5个位图的地址" << endl;
cin >> filename1 >> filename2 >> filename3 >> filename4 >> filename5;
//样例信息:
Image img1(filename1);//8位
Image img2(filename2);//8位
Image img3(filename3);//8位 (不同大小
Image img4(filename4);//24位
Image img5(filename5);//24位
Image img6;//空
cout << "img1:" << endl;
img1.show();
cout << "img2:" << endl;
img2.show();
cout << "img1+img2:" << endl;
(img1 + img2).show();
cout << "img1+img3:" << endl;
img1 + img3;
cout << "img4:" << endl;
img4.show();
cout << "img5:" << endl;
img5.show();
cout << "img6:" << endl;
img6.show();
}