c++数组实验进阶:类实现——动态分配数组实现bmp图片翻转图片、缩小图片、灰白处理、读取图片(C实现版本)

该实验通过C++类实现了BMP图像的翻转、缩小、灰度处理功能。在处理过程中遇到了颜色错误和索引值问题,解决方案包括以像素点为单位操作和寻找索引值的函数关系。实验要求完成图像处理代码并能正确保存结果。
摘要由CSDN通过智能技术生成

实验题目

https://wws.lanzous.com/idFBnnt0mhg

出现的问题

1.图像颜色不对
这个和bmp图像的一个像素点由三个char组成有关。这三个点实质代表RGB通道的三个参数。在翻转的时候,由于是以位为单位进行操作的,而不是以像素点为单位进行操作的,所以会发生颜色不对。
解决方法:以像素点为单位进行操作。
举例:
以下是左右翻转的部分代码,翻转时同时对每一个像素点进行操作,就能解决问题。

    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
		{
   
			for (int j = 0; j < bitmapinfoheader.biWidth * 3 / 2; j += 3)
			{
   
				unsigned char temp = t_data[i][j];
				t_data[i][j] = 
                    t_data[i][bitmapinfoheader.biWidth * 3 - 1 - j - 2];
				t_data[i][bitmapinfoheader.biWidth * 3 - 1 - j - 2] = temp;
			
			unsigned char temp1 = t_data[i][j+1];
			t_data[i][j + 1] = 
                t_data[i][bitmapinfoheader.biWidth * 3 - 1 - j - 1];
			t_data[i][bitmapinfoheader.biWidth * 3 - 1 - j - 1] = temp1;

			unsigned char temp2 = t_data[i][j + 2];
			t_data[i][j+2] = 
                t_data[i][bitmapinfoheader.biWidth * 3 - 1 - j];
			t_data[i][bitmapinfoheader.biWidth * 3 - 1 - j] = temp2;
		}
	}

2、索引值问题
图像放大时,由于索引值没控制好而出现一些小问题。
剪切、旋转图像时,也这样。
解决方法:
找索引值之间的简单函数关系。
体悟:
多元函数之间的关系一定要理清楚。

4-21补充:修改了部分函数
3、cut图像问题
由于是随便剪的,于是不能保证每行字节数是4的倍数,于是需要自己补位以及对所补的位进行初始化。具体操作在下面代码,主要讲一下在初始化的操作,利用menset函数直接初始化为0。

本次实验总体是继承上一次的实验,总体来讲比较简单。主要是处理索引值那一块容易出错,其他还好。

其次,本次实验,量确实有点大了。然后个人也只是写完,对于代码复用性的优化,懒得搞了。(搞多了也容易降低代码的健壮性,能明显分出函数的地方也分离出了函数)

结果展示

参考源码

main.cpp
#include "image.h"
#include <cstdio>
#include <cstdlib>

int main(int argc, char* argv[])
{
   
	Image img; //创建对象
	//读写BMP文件
	img.ReadBMP("Fruits.bmp");
	img.WriteBMP("FruitsCopy.bmp");
	img.WriteText("FruitsCopy.text");


	//读写text文件
	Image readtxtImage;
	readtxtImage.ReadText("FruitsCopy.text");
	readtxtImage.WriteText("writeFruits.text");


	//图像的上下翻转,并保存结果图像文件
	img.Flip(true);
	//图像的左右翻转,如img.Flip(true);并保存结果图像文件
	img.Flip(false);
	//左右翻转需要注意,每个像数占3个字节,翻转时不能简单直接翻转,
	//而需要每个字节对应地翻转


	//图像的缩放,并保存结果图像文件
	img.Resize(false);//图像缩小
	img.Resize(true);//图像放大
	//缩小时也需要注意,每个像数占3个字节,不能简单通过删除字节而缩小,
	//而需要每个字节对应地删除
	//放大也是一样的道理


	//获取图像的某点的像素值,并修改, 并保存结果图像文件
	int row = 100;
	int col = 100;
	img.At(row, col);
	img.Set(row, col, 10);
	img.Set(100);


	//使用拷贝构造函数创建新的对象
	Image new_img(img);
	new_img.WriteBMP("new_img.bmp");

	
	//截取指定区域内的图像,并保存结果图像文件(x1,y1) (x2,y2)
	new_img.Cut(120,120,360,360);
	//需要保证  x2 > x1 && y2 > y1


	//顺时针旋转图像并保存结果图像文件(简单起见,旋转角度为90度的整数倍)
	img.Rotate(90);
	img.Rotate(180);
	img.Rotate(270);


	//求图像的均值和方差,并在命令行输出
	float ave = 0;
	float vAve = 0;
	img.Mean_Variance(ave, vAve);
	printf("平均值为:%f\n方差为:%f\n", ave, vAve);

	
	//交换两个图像的数据 
	Image img1("Baboon.bmp");
	Image img2("Lena.bmp");
	Swap(img1, img2);
	//保存交换完的结果图像文件
	img1.WriteBMP("S_img1_baboon.bmp");
	img2.WriteBMP("S_img2_lena.bmp");
	return 0;
}
struct.h
#ifndef STRUCT_H
#define STRUCT_H

// 针对该结构体的字节对齐问题调整对齐策略
#pragma pack(push,1)
struct BMPFILEHEADER
{
   
	unsigned short bfType;
	unsigned int   bfSize;
	unsigned short bfReserved1;
	unsigned short bfReserved2;
	unsigned int   bfOffBits;
};
#pragma pack(pop)

struct BITMAPINFOHEADER
{
   
	unsigned long    biSize;            //本结构所占用字节数 40字节
	long             biWidth;           //位图的宽度,以像素为单位
	long             biHeight;          //位图的高度,以像素为单位
	unsigned short   biPlanes;          //目标设备的级别,必须为1
	unsigned short   biBitCount;        //每个像素所需的位数,必须是1(双色)、
										//4(16色)、8(256色)或24(真彩色)之一
	unsigned long    biCompression;     //位图压缩类型,必须是 0(BI_RGB不压缩)、
										//1(BI_RLE8压缩类型)
										//2(BI_RLE压缩类型)之一
	unsigned long    biSizeImage;       //位图的大小,以字节为单位
	long             biXPelsPerMeter;   //位图水平分辨率,每米像素数
	long             biYPelsPerMeter;   //位图垂直分辨率,每米像素数
	unsigned long    biClrUsed;         //位图实际使用的颜色表中的颜色数
	unsigned long    biClrImportant;    //位图显示过程中重要的颜色数 
};

#endif

image.cpp
#include "image.h"
#include <cstdio>
#include <cstdlib>
#include <string>
using namespace std;
//普通构造函数
Image::Image(int h, int w) :height(h), width(w)
{
   
	this->height = h;
	this->width = w;
	// 动态分配二维数组存储像素数据,注意先申请一个存放指针的数组,
	data = (unsigned char **)malloc(sizeof(unsigned char*) * this->height);
	//其大小为sizeof(unsigned char*) * bitmapinfoheader.biHeight,这点很容易错
	//申请行指针
	for (int i = 0; i < this->height; i++)
	{
   
		//注意3通道,每个像素3个字节
		data[i] = (unsigned char *)malloc(this->width * 3);
		// 读取像素数据
		for (int j = 0; j < this->width; j++)
		{
   
			data[i][j] = 0;
		}
	}
}
Image::Image(int h, int w, unsigned char val)//创建的图像像素值都为val;
{
   
	this->height = h;
	this->width = w;
	// 动态分配二维数组存储像素数据,注意先申请一个存放指针的数组,
	data = (unsigned char **)malloc(sizeof(unsigned char*) * this->height);
	//其大小为sizeof(unsigned char*) * bitmapinfoheader.biHeight,这点很容易错
	//申请行指针
	for (int i = 0; i < this->height; i++)
	{
   
		//注意3通道,每个像素3个字节
		data[i] = (unsigned char *)malloc(this->width * 3);
		// 读取像素数据
		for (int j = 0; j < this->width; j++)
		{
   
			data[i][j] = val;
		}
	}
}
Image::Image(const char* ImageName)//利用文件名从硬盘加载图像文件成为Image对象;
{
   
	this->ReadBMP(ImageName);
}
Image::Image(unsigned char *m, int rows, int cols)//从一维静态数组创建Image对象,图像的行数和列数由后面两个参数给出;
{
   
	this->height = rows;
	this->width = cols;
	// 动态分配二维数组存储像素数据,注意先申请一个存放指针的数组,
	data = (unsigned char **)malloc(sizeof(unsigned char*) * this->height);
	//其大小为sizeof(unsigned char*) * bitmapinfoheader.biHeight,这点很容易错
	//申请行指针
	for (int i = 0; i < this->height; i++)
	{
   
		//注意3通道,每个像素3个字节
		data[i] = (unsigned char *)malloc(this->width * 3);
		// 读取像素数据
		for (int j = 0; j < this->width; j++)
		{
   
			data[i][j] = *m;
			m++;
		}
	}
}
Image::Image(unsigned char m[][100], int rows)//从静态二维数组创建Image对象,图像的行数(二维数组的第一个维度)由第二个参数rows给出;
{
   
	this->height = rows;
	this->width = 100;
	// 动态分配二维数组存储像素数据,注意先申请一个存放指针的数组,
	data = (unsigned char **)malloc(sizeof(unsigned char*) * this->height);
	//其大小为sizeof(unsigned char*) * bitmapinfoheader.biHeight,这点很容易错
	//申请行指针
	for (int i = 0; i < this->height; i++)
	{
   
		//注意3通道,每个像素3个字节
		data[i] = (unsigned char *)malloc(this->width * 3);
		// 读取像素数据
		for (int j = 0; j < this->width; j++)
		{
   
			data[i][j] = m[i][j];
		}
	}
}
Image::Image(unsigned char **m, int h
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值