前言
一、实验目的
掌握类模板的定义和使用.
二、实验内容
1.内容
在之前的实验中,我们定义了一个矩阵类Matrix和其子类图像类Image。在数据的存储和处理过程中,我们用过unsigned char和double类型的data指针获取矩阵元素或者图像像素的值。但是现实中的矩阵或者图像数据并不仅仅使用浮点类型和8bits的无符号整数类型,比如有的单反相机可以拍摄32bits无符号整数像素值的图像(unsigned int);在图像处理中经常将图像灰度值缩放到区间[0,1]内的浮点数(double);再比如对图像进行傅里叶变换后,得到是复数。因此矩阵的数据类型可以有很多变化。为了实现代码的重用,现在我们使用类模板重构之前的类。
要求:
- 定义类模板Mat描述矩阵,矩阵元素的数据类型可以是任意类型,即二级指针data所指向的数据类型采用模板技术进行泛化。
- 参考之前的实验,实现Mat类模板的构造函数、析构函数、拷贝构造函数、运算符重载等各种函数。
- 使用“引用计数”机制,实现Mat对象间的“浅拷贝”和“浅赋值”。
- 实现该类模板的ReadBMP,WriteBMP等函数。在存储图像文件时,像素值一般选取 unsigned char类型(这也是我们前面实验使用该类型的原因)。但是我们的Mat模板类的数据范围不局限于此,因此需要在这两个函数体内完成数据类型转换操作。即把矩阵的元素转换成[0,255]区间内的整数,然后才能保存成BMP文件。通常的做法是计算所有矩阵元素的最大值和最小值,在WriteBMP函数内将对应的数据分别赋成255和0,其它中间的数据转换成0和255之间的无符号8bit整数。这么做仅仅是为了存储时迁就图片文件的格式要求,丢失了原来数据的精度。
- ReadText和WriteText函数把数据写入文本文件,这两个函数在实现时需要特别注意,思考如何才能把不同类型的数据合理在文本文件中写入和读取。先自行设计解决方案,更好的办法在实验10中实现。
- 定义成员函数Normalize,该函数返回以double特化的模板类对象,该对象将调用者的数据缩放到[0,1]区间。
- 将实验8的滤波器类进行模板化重构,能够适用于Mat类模板。
主函数中实现:
- 读入图像文件,并将数据缩放到[0,1]区间
- 将图像取反色,并输出显示。
- 获取矩阵中某个元素的值。
- 对矩阵进行旋转、缩放、裁剪、reshape等操作。
- 对矩阵进行加减等操作。
- 对矩阵进行滤波,显示并保存成图像文件。
- 计算原矩阵和滤波后矩阵的差,并把结果保存。
2.代码示例
代码如下(示例):
Mat.hpp
template< typename T>
class Mat
{
public:
Mat(); //无参数的构造函数,创建行列都为零的Mat对象
Mat(int h, int w); //构造函数重载,创建h行,w列的Mat对象
Mat(int h, int w, T val); //构造函数重载,矩阵元素的值都为val;
Mat(const char* ImageName); //构造函数重载,利用文件名从硬盘加载图像文件成为Mat对象;
Mat(T **m, int h, int w); //构造函数重载,从动态数组创建Mat对象;
Mat(const Mat &m); //拷贝构造函数;
virtual ~Mat(); //析构函数;
void ReadBMP(const char *ImageName); //从硬盘文件中读入图像数据
void WriteBMP(const char *filename); //将数据保存为图像文件
// 下面2个读写文本文件的函数,需要考虑不同数据类型的存储
void ReadText(const char *ImageName); //从文本文件中读入数据
void WriteText(const char *filename); //将数据保存为文本文件
int Height();//得到矩阵高度
int Height() const;//得到矩阵高度
int Width();//得到矩阵宽度
int Width() const;//得到矩阵宽度
T Min(); //得到矩阵元素的最小值
T Min() const; //得到矩阵元素的最小值
T Max(); //得到矩阵元素的最大值
T Max() const; //得到矩阵元素的最大值
T& At(int row, int col); //获取某点的值
const T& At(int row, int col) const; //获取某点的值,const重载
void Set(int row, int col, T value); //设置元素(row,col)为某值;
void Set(T value); //设置所有元素为同一值;
void Flip(int code); //翻转; 根据code的值:0:左右翻转,1:上下翻转;
void Resize(int h, int w); //缩放
void Crop(int x1, int y1, int x2, int y2);//裁剪点(x1,y1)到点(x2,y2)
void Rotate(int degree);//旋转,90度的整数倍
void Transpose(); // 转置
void Reshape(int h, int w); //在元素总数不变的情况下,将矩阵的行列变为参数给定的大小
bool IsEmpty();// 判断是否为空矩阵
bool IsSquare();// 判断矩阵是否为方阵
Mat<T> MajorDiagonal();// 求主对角线上的元素,输出一个N行1列的矩阵,N为主对角线上元素的个数
Mat<T> MinorDiagonal();// 求副对角线上的元素,输出一个N行1列的矩阵,N为副对角线上元素的个数
Mat<T> Row(int n);// 返回矩阵的第n行上的元素,组出一个1行N列的矩阵输出,N为第n行上元素的个数
Mat<T> Column(int n);// 返回矩阵的第n列上的元素,组出一个N行1列的矩阵输出,N为第n列上元素的个数
void Cat(Mat<T> &m, int code); // 将m与当前对象进行拼接,code代表拼接的方式
void CopyTo(Mat<T> &m); // 将矩阵复制给m,完成深拷贝
Mat<T> Clone(); // 从当前对象拷贝创建一个新的矩阵,完成深拷贝
Mat<double> void Normalize();//将矩阵元素的值变换到0-1范围内,以double类型的Mat对象输出。注意:在这个函数里,无法访问Mat<double>类型的对象的私有成员data,需要调用其At函数获得某个元素。
Mat<T>& operator=(const Mat<T> &m); //重载赋值运算符,完成对象间的拷贝;
bool operator==(const Mat<T> &m); //判断两个Mat对象是否相等
friend Mat<T> operator+(const Mat<T> &lhs, const Mat<T> &rhs); //对应元素的数值相加;
friend Mat<T> operator-(const Mat<T> &lhs, const Mat<T> &rhs); //对应元素的数值相减;
Mat<T>& operator++(); //前置自加;
Mat<T>& operator--(); //前置自减;
Mat<T> operator ++(int); //后置自加;
Mat<T> operator --(int); //后置自减;
Mat<double> operator-(); // 取反;注意要把矩阵的数据规整到[0,1]区间后,再用1减
friend Mat<T> operator+(Mat<T> &m, T num); //所有元素加上同一数值;
friend Mat<T> operator-(Mat<T> &m, T num); //所有元素减去同一数值;
friend Mat<T> operator*(Mat<T> &m, T num); //所有元素乘上同一数值;
friend Mat<T> operator/(Mat<T> &m, T num); //所有元素除以同一数值;
//另外,用友元函数再写出一个T类型的数和一个Mat对象的加,减,乘,除
Mat<T> gray2bw(T t); //以给定阈值t进行二值化,返回结果对象
friend void Swap(Mat<T> &a, Mat<T> &b);//使用友元函数交换两个Mat对象
private:
// 自己实现一个结构体,来存储矩阵的行数、列数、引用计数和数据指针
// 这里需要声明指向该结构体的指针作为数据成员
};
Filter.hpp
//Filter类
template <class T>
class Filter
{
public:
Filter(int size); //构造函数
virtual ~Filter(); //析构函数;
virtual Mat<T> Filtering(const Mat<T> &input) = 0; //滤波函数(纯虚函数);
protected:
int filterSize;
};
//meanFilter类
template <class T>
class MeanFilter : public Filter<T>
{
public:
MeanFilter(int size);
virtual ~MeanFilter();
virtual Mat<T> Filtering(const Mat<T> &input); //均值滤波函数
};
//median类
template <class T>
class MedianFilter : public Filter<T>
{
public:
MedianFilter(int size);
virtual ~MedianFilter();
virtual Mat<T> Filtering(const Mat<T> &input); // 中值滤波器函数
};
3.实验要求及说明
完成上述代码以及main函数,验证并实现以上方法。
注意:类模板的头文件,我们以“hpp”作为文件扩展名。Mat类模板和Filter类模板(包括其子类模板)的所有实现代码都放在它们的hpp文件里,即声明和实现放在一起,不将实现代码单独放在cpp文件。
注意:
在Filter和它的两个派生类模板的代码实现过程中,会出现在派生类模板的成员函数里无法使用父类模板里定义的成员变量,比如在MeanFilter的Filtering函数里使用filterSize,编译器会报错,找不到这个变量。这是因为我们写的继承关系发生在类模板之间,而不是实例化后的真正的类之间,编译器无法去推断父类模板里的成员,需要你显式指出该成员来自哪里。有两种解决办法,在派生类模板中这样使用:
Filter::filterSize 或者 this->filterSize
具体可以查找网上相关的资料,比如:
https://blog.youkuaiyun.com/sb985/article/details/79670881
以上均为老师给的实验内容,实现如下。
三、代码实现
Main.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<Windows.h>
#include"Mat.hpp"
#include"Filter.hpp"
using namespace std;
int main()
{
Mat <double,unsigned char>img1("Fruits_480x511.bmp");
img1.WriteBMP("img1.bmp");//测试写
Mat <double,unsigned char>img2("scene2_fg.bmp");
img2.Normalize().WriteBMP("img2Normalize.bmp");//测试Normalize
img2.operator-();
img2.WriteBMP("img2operator-.bmp");//取反
cout << "获取元素值为:" << img2.At(5, 5) << endl;//获取元素值
img1.Rotate(180);//旋转
img1.WriteBMP("img1Rotate.bmp");
img1.Resize(400, 400);//缩放
img1.WriteBMP("img1Resize.bmp");
img1.Reshape(480,512);//转置
img1.WriteBMP("img1Reshape.bmp");
img1.Crop(1, 1, 400, 400);//裁剪
img1.WriteBMP("img1Crop.bmp");
Mat<double,unsigned char>img3("Airplane.bmp");
Mat<double,unsigned char>img4("Baboon.bmp");
img3.Cat(img4, 1);//矩阵拼接
img3.WriteBMP("img3Cat.bmp");
img1 + 10;//矩阵加值
img1.WriteBMP("img1+10.bmp");
img1 - 10;//矩阵减值
img1.WriteBMP("img1-10.bmp");
Mat<int,unsigned char>img5("Lena_gaussian.bmp");//滤波器
Mat<int, unsigned char>img6("Lena_salt_and_pepper.bmp");
Filter<int,unsigned char>* filter = NULL;
filter = new MedianFilter<int,unsigned char>(5);
img6 = filter->Filtering(img5);
img6.WriteBMP("img5MedianFilter.bmp");
img6 = filter->Filtering(img5);
img6.WriteBMP("img5MeanFilter.bmp");
return 0;
}
Mat.hpp
#ifndef MAT_HPP
#define MAT_HPP
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<Windows.h>
#include<cstdlib>
#include<cassert>
using namespace std;
template< class T,class T1>
class Mat
{
public://实现和声明在一块写
void all(int h, int w)
{
ptr->height = h;
ptr->width = w;
ptr->data = new T * [h];
for (int i = 0; i < h; i++)
{
ptr->data[i] = new T[ptr->width];
}
}
Mat() //无参数的构造函数,创建行列都为零的Mat对象
{
ptr = (struct Databack*)malloc(sizeof(struct Databack));
ptr->refcnt = 1;
}
Mat(int h, int w) //构造函数重载,创建h行,w列的Mat对象
{
ptr = (struct Databack*)malloc(sizeof(struct Databack));
ptr->height = h;
ptr->width = w;
ptr->data = new T* [ptr->height];
for (int i = 0; i < ptr->height; i++)
{
ptr->data[i] = new T[ptr->width];
}
ptr->refcnt=1;
}
Mat(int h, int w, T val) //构造函数重载,矩阵元素的值都为val;
{
ptr->height = h;
ptr->width = w;
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
ptr->data[i][j] = val;
}
}
ptr->refcnt=1;
}
Mat(const char* ImageName) //构造函数重载,利用文件名从硬盘加载图像文件成为Mat对象;
{
ptr = (struct Databack*)malloc(sizeof(struct Databack));
ReadBMP(ImageName);
ptr->refcnt = 1;
}
Mat(T** m, int h, int w) //构造函数重载,从动态数组创建Mat对象;
{
ptr->height = h;
ptr->width = w;
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
ptr->data[i][j] = m[i][j];
}
}
}
Mat(const Mat& m) //拷贝构造函数;
{
ptr = (struct Databack*)malloc(sizeof(struct Databack));
ptr->height = m.ptr->height;
ptr->width = m.ptr->width;
ptr->data = new T*[ptr->height];
for (int i = 0; i < ptr->height; i++)
{
ptr->data[i] = new T[ptr->width];
for (int j = 0; j < ptr->width; j++)
{
this->ptr->data[i][j] = m.ptr->data[i][j];
}
}
ptr->refcnt = m.ptr->refcnt;
ptr->refcnt++;
}
virtual ~Mat() //析构函数;
{
ptr->refcnt--;
if (ptr->refcnt == 0)
{
for (int i = 0; i < ptr->height; i++)
{
delete[]ptr->data[i];
}
delete[]ptr->data;
}
}
void ReadBMP(const char* ImageName) //从硬盘文件中读入图像数据
{
FILE* fp = fopen(ImageName, "rb");
if (fp == NULL)
{
cout << "文件打开失败!" << endl;
exit(0);
}
BITMAPFILEHEADER fh;
BITMAPINFOHEADER ih;
fread(&fh, sizeof(BITMAPFILEHEADER), 1, fp);
fread(&ih, sizeof(BITMAPINFOHEADER), 1, fp);
ptr->width = ih.biWidth;
ptr->height = ih.biHeight;
if (ptr->height % 4 != 0)
{
ptr->height = (ptr->height * (ih.biBitCount) / 8 + 3) / 4 * 4;
ptr->height = ptr->height / 3;
}
if (ptr->width % 4 != 0)
{
ptr->width = (ptr->width * (ih.biBitCount) / 8 + 3) / 4 * 4;
ptr->width = ptr->width / 3;
}
ptr->data = new T * [ptr->height];
for (int i = 0; i < ptr->height; i++)
{
ptr->data[i] = new T[ptr->width];
for (int j = 0; j < ptr->width; j++)
{
T1 r, g, b;
fread(&r, sizeof(T1), 1, fp);
fread(&g, sizeof(T1), 1, fp);
fread(&b, sizeof(T1), 1, fp);
ptr->data[i][j] = static_cast<T>(r / 3 + g / 3 + b / 3);
}
}
fclose(fp);
}
void WriteBMP(const char* filename) //将数据保存为图像文件
{
FILE* fp = fopen(filename, "wb");
if (fp == NULL)
{
cout << "文件写入失败!" << endl;
exit(0);
}
BITMAPFILEHEADER fh;
BITMAPINFOHEADER ih;
fh.bfOffBits = 54;
fh.bfReserved1 = 0;
fh.bfReserved2 = 0;
fh.bfSize = ptr->height * ptr->width * 3 + 54;
fh.bfType = 19778;
ih.biBitCount = 24;
ih.biCompression = 0;
ih.biXPelsPerMeter = 0;
ih.biYPelsPerMeter = 0;
ih.biClrImportant = 0;
ih.biSize = 40;
ih.biPlanes = 1;
ih.biClrUsed = 0;
ih.biWidth = ptr->width;
ih.biHeight = ptr->height;
ih.biSizeImage = ih.biWidth * ih.biHeight * 3;
fwrite(&fh, sizeof(BITMAPFILEHEADER), 1, fp);
fwrite(&ih, sizeof(BITMAPINFOHEADER), 1, fp);
T1 t = 0;
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
t = static_cast<T1>(ptr->data[i][j]);
for (int k = 0; k < 3; k++)
{
fwrite(&t, sizeof(T1), 1, fp);
}
}
}
fclose(fp);
}
// 下面2个读写文本文件的函数,需要考虑不同数据类型的存储
void ReadText(const char* ImageName) //从文本文件中读入数据
{
FILE* fp;
if (fp = fopen(ImageName, "rb") == 0)
{
cout << "文件打开失败!" << endl;
exit(0);
}
BITMAPFILEHEADER fh;
BITMAPINFOHEADER ih;
fread(&fh, sizeof(BITMAPFILEHEADER), 1, fp);
fread(&ih, sizeof(BITMAPINFOHEADER), 1, fp);
ptr->heigh = ih.biHeight;
ptr->width = ih.biWidth;
if (ptr->height % 4 != 0)
{
ptr->height = (ptr->height * (ih.biBitCount) / 8 + 3) / 4 * 4;
ptr->height = ptr->height / 3;
}
if (ptr->width % 4 != 0)
{
ptr->width = (ptr->width * (ih.biWidth) / 8 + 3) / 4 * 4;
ptr->width = ptr->width / 3;
}
ptr->data= new T*[ptr->height];
for (int i = 0; i < ptr->height; i++)
{
ptr->data[i] = new T [ptr->width];
for (int j = 0; j < ptr->width; j++)
{
T1 r, g, b;
fread(&r, sizeof(T1), 1, fp);
fread(&g, sizeof(T1), 1, fp);
fread(&b, sizeof(T1), 1, fp);
ptr->data[i][j] = static_cast<T>(r + g + b) / 3;
}
}
fclose(fp);
}
void WriteText(const char* filename) //将数据保存为文本文件
{
FILE* fp = fopen(filename, "wb");
if (fp == NULL)
{
cout << "文件写入失败!" << endl;
exit(0);
}
BITMAPFILEHEADER fh;
BITMAPINFOHEADER ih;
fh.bfOffBits = 54;
fh.bfReserved1 = 0;
fh.bfReserved2 = 0;
fh.bfSize = ptr->height * ptr->width * 3 + 54;
fh.bfType = 19778;
ih.biBitCount = 24;
ih.biCompression = 0;
ih.biXPelsPerMeter = 0;
ih.biYPelsPerMeter = 0;
ih.biClrImportant = 0;
ih.biSize = 40;
ih.biPlanes = 1;
ih.biClrUsed = 0;
ih.biWidth = ptr->width;
ih.biHeight = ptr->height;
ih.biSizeImage = ih.biWidth * ih.biHeight * 3;
fwrite(&fh, sizeof(BITMAPFILEHEADER), 1, fp);
fwrite(&ih, sizeof(BITMAPINFOHEADER), 1, fp);
T1 t = 0;
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
t = static_cast<T1>(ptr->data[i][j]);
for (int k = 0; k < 3; k++)
{
fwrite(&t, sizeof(T1), 1, fp);
}
}
}
fclose(fp);
}
int Height()//得到矩阵高度
{
return ptr->height;
}
int Height() const//得到矩阵高度
{
return ptr->height;
}
int Width()//得到矩阵宽度
{
return ptr->width;
}
int Width() const//得到矩阵宽度
{
return ptr->width;
}
T Min() //得到矩阵元素的最小值
{
T min = ptr->data[0][0];
for (int i =0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
min < ptr->data[i][j] ? min : ptr->data[i][j];
}
}
}
T Min() const //得到矩阵元素的最小值
{
T min = ptr->data[0][0];
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
min < ptr->data[i][j] ? min : ptr->data[i][j];
}
}
}
T Max() //得到矩阵元素的最大值
{
T max = ptr->data[0][0];
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
max > ptr->data[i][j] ? max: ptr->data[i][j];
}
}
}
T Max() const//得到矩阵元素的最大值
{
T max = ptr->data[0][0];
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
max > ptr->data[i][j] ? max : ptr->data[i][j];
}
}
}
T& At(int row, int col) //获取某点的值
{
return ptr->data[row][col];
}
const T& At(int row, int col) const //获取某点的值,const重载
{
return ptr->data[row - 1][col - 1];
}
void Set(int row, int col, T value) //设置元素(row,col)为某值;
{
ptr->data[row - 1][col - 1] = value;
}
void Set(T value) //设置所有元素为同一值;
{
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
ptr->data[i][j] = value;
}
}
}
void Flip(int code) //翻转; 根据code的值:0:左右翻转,1:上下翻转;
{
T1** t;
t = new T1* [ptr->height];
for (int j = 0; j < ptr->height; j++)
{
t[j] = new T1[ptr->width];
}
if (code == 0)
{
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
t[i][j] = ptr->data[i][ptr->width - 1 - j];
}
}
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
ptr->data[i][j] = t[i][j];
}
}
cout << "左右翻转成功!请查看!" << endl;
}
else if (code == 1)
{
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
t[i][j] = ptr->data[ptr->height - 1 - i][j];
}
}
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
ptr->data[i][j] = t[i][j];
}
}
cout << "上下翻转成功!请查看!" << endl;
}
for (int i = 0; i < ptr->height; i++)
{
delete[]t[i];
}
delete[]t;
}
void Resize(int h, int w) //缩放
{
int oldh = h;
int oldw = w;
int oldwidth =ptr-> width;
int oldheight = ptr->height;
//新值与原值的差值
int dw = static_cast<int>(fabs(oldwidth - w));
int dh = static_cast<int>(fabs(oldheight - h));
if (dw < 1)//防止dw == 0
{
dw = 1;
}
if (dh < 1)
{
dh = 1;
}
int maxw = w > oldwidth ? w : oldwidth;
int maxh = h > oldheight ? h : oldheight;
int difw = maxw / dw;
int difh = maxh / dh;
T** mdata = new T* [h];
for (int i = 0, prii = 0; i < h; i++)
{
mdata[i] = new T[w];
if ((i + 1) % difh == 0)
{
if (h < oldheight)
{
prii++;
}
else
{
continue;
}
}
for (int j = 0, prij = 0; j < w; j++)
{
if ((j + 1) % difw == 0)
{
if (w < oldwidth)
{
prij++;
}
else
{
continue;
}
}
mdata[i][j] = ptr->data[prii][prij];
if (prij < oldwidth - 1)
prij++;
}
if (prii < oldheight - 1)
prii++;
}
//填充跳过的指定增行/列
if (w > oldwidth)//填充列
{
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w; j++)
{
if ((j + 1) % difw == 0)
{
mdata[i][j] = mdata[i][j - 1];//指定 增 列填充
}
}
}
}
if (h > oldheight)//填充行
{
for (int i = 0; i < h; i++)
{
if ((i + 1) % difh == 0)//指定 增 行填充
{
for (int j = 0; j < w; j++)
{
mdata[i][j] = mdata[i - 1][j];
}
}
}
}
ptr->data = mdata;
ptr->height = h;
ptr->width = w;
cout << "文件缩放成功!" << endl;
}
void Crop(int x1, int y1, int x2, int y2)//裁剪点(x1,y1)到点(x2,y2)
{
ptr->height = static_cast<T>(fabs(y2 - y1) + 1);
ptr->width = static_cast<T>(fabs(x2 - x1) + 1);
T** t;
t = new T* [ptr->height];
for (int i = 0; i < ptr->height; i++)
{
t[i] = new T[ptr->width];
}
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
t[i][j] = ptr->data[y1 + i-1][x1 + j-1];
}
}
ptr->data = new T* [ptr->height];
for (int i = 0; i < ptr->height; i++)
{
ptr->data[i] = new T[ptr->width];
for (int j = 0; j < ptr->width; j++)
{
ptr->data[i][j] = t[i][j];
}
}
for (int i = 0; i < ptr->height; i++)
{
delete[]t[i];
}
delete[]t;
cout << "文件剪裁成功!请查看" << endl;
}
void Rotate(int degree)//旋转,90度的整数倍
{
int m;
T** t = nullptr;
degree = fabs(degree / 90);
if (degree == 1)
{
t = new T * [ptr->width];
for (int i = 0; i < ptr->width; i++)
{
t[i] = new T [ptr->height];
}
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
t[j][ptr->height - 1 - i] = ptr->data[i][j];
}
}
ptr->data = new T* [ptr->width];
for (int i = 0; i < ptr->width; i++)
{
ptr->data[i] = new T[ptr->height];
}
for (int i = 0; i < ptr->width; i++)
{
for (int j = 0; j < ptr->height; j++)
{
ptr->data[i][j] = t[i][j];
}
}
m = ptr->height;
ptr->height = ptr->width;
ptr->width = m;
for (int i = 0; i < ptr->width; i++)
{
delete[]t[i];
}
delete[]t;
}
else if (degree == 2)
{
t = new T* [ptr->height];
for (int i = 0; i <ptr->height; i++)
{
t[i] = new T[ptr->width];
}
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
t[ptr->height - 1 - i][j] = ptr->data[i][j];
}
}
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
ptr->data[i][j] = t[i][j];
}
}
for (int i = 0; i < ptr->height; i++)
{
delete[]t[i];
}
delete[]t;
}
else if (degree == 3)
{
t = new T* [ptr->width];
for (int i = 0; i < ptr->width; i++)
{
t[i] = new T[ptr->height];
}
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
t[j][i] = ptr->data[i][j];
}
}
ptr->data= new T* [ptr->width];
for (int i = 0; i <ptr-> width; i++)
{
ptr->data[i] = new T[ptr->height];
}
m = ptr->height;
ptr->height = ptr->width;
ptr->width = m;
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
ptr->data[i][j] = t[i][j];
}
}
for (int i = 0; i < ptr->width; i++)
{
delete[]t[i];
}
delete[]t;
}
cout << "文件旋转成功!请查看!" << endl;
}
void Transpose() // 转置
{
T** t;
t = new T* [ptr->width];
for (int i = 0; i < ptr->width; i++)
{
t[i] = new T[ptr->height];
}
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
t[j][i] = ptr->data[i][j];
}
}
for (int i = 0; i < ptr->height; i++)
{
delete[]ptr->data[i];
}
delete[]ptr->data;
ptr->data = t;
int h = ptr->height;
ptr->height = ptr->width;
ptr->width = h;
}
void Reshape(int h, int w) //在元素总数不变的情况下,将矩阵的行列变为参数给定的大小
{
if (h * w == ptr->height * ptr->width)
{
long long k = 0;
T* m = new T[h * w + 1];
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w; j++)
{
if (k < h * w)
{
m[k] = ptr->data[i][j];
k++;
}
}
}
k = 0;
all(h, w);
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w; j++)
{
if (k < h * w)
{
ptr->data[i][j] = m[k];
k++;
}
}
}
}
else cout << "不符合改变条件!" << endl;
}
bool IsEmpty()// 判断是否为空矩阵
{
T sum = 0;
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
sum = sum + ptr->data[i][j];
}
}
if (sum == 0)
{
return true;
}
else
{
return false;
}
}
bool IsSquare()// 判断矩阵是否为方阵
{
if (ptr->height == ptr->width)
{
return true;
}
else return false;
}
Mat<T,T1> MajorDiagonal()// 求主对角线上的元素,输出一个N行1列的矩阵,N为主对角线上元素的个数
{
int n;
n = ptr->width < ptr->height ? ptr->width : ptr->height;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (i == j)
{
cout << ptr->data[i][j] << "/t";
}
}
cout << endl;
}
}
Mat<T,T1> MinorDiagonal()// 求副对角线上的元素,输出一个N行1列的矩阵,N为副对角线上元素的个数
{
int n;
n = ptr->width < ptr->height ? ptr->width : ptr->height;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (i == n - j - 1)
{
cout << ptr->data[i][j] << "/t";
}
}
cout << endl;
}
}
Mat<T,T1> Row(int n)// 返回矩阵的第n行上的元素,组出一个1行N列的矩阵输出,N为第n行上元素的个数
{
for (int j = 0; j < ptr->width; j++)
{
cout << ptr->data[n - 1][j] << "/t";
}
return ptr->data[n - 1][0];
}
Mat<T,T1> Column(int n)// 返回矩阵的第n列上的元素,组出一个N行1列的矩阵输出,N为第n列上元素的个数
{
for (int i = 0; i < ptr->height; i++)
{
cout << ptr->data[i][n - 1] << endl;
}
return ptr->data[0][n - 1];
}
void Cat(Mat<T,T1>& m, int code) // 将m与当前对象进行拼接,code代表拼接的方式
{
int h, w;
int k = 0;
T** t;
t = new T* [ptr->height];
for (int i = 0; i < ptr->height; i++)
{
t[i] = new T[ptr->width];
for (int j = 0; j < ptr->width; j++)
{
t[i][j] = ptr->data[i][j];
}
}
if (code == 1)// 左右拼接
{
for (int i = 0; i < ptr->height; i++)
{
delete[]ptr->data[i];
}
delete[]ptr->data;
w = ptr->width;
ptr->width += m.ptr->width;
all(ptr->height, ptr->width);
//ptr->data = new T* [ptr->height];
for (int i = 0; i < ptr->height; i++)
{
//ptr->data[i] = new T[ptr->width];
for (int j = 0; j < ptr->width; j++)
{
if (j < w)
{
ptr->data[i][j] = t[i][j];
}
else
{
ptr->data[i][j] = m.ptr->data[i][j - w];
}
}
}
}
else//上下拼接
{
h = ptr->height;
ptr->height = ptr->height + m.ptr->height;
ptr->data = new T* [ptr->height];
for (int i = 0; i < ptr->height; i++)
{
ptr->data[i] = new T[ptr->width];
for (int j = 0; j < ptr->width; j++)
{
if (i < h)
{
ptr->data[i][j] = t[i][j];
}
else
{
ptr->data[i][j] = m.ptr->data[i - h][j];
}
}
}
}
cout << "文件拼接成功!" << endl;
}
void CopyTo(Mat<T,T1>& m)// 将矩阵复制给m,完成深拷贝
{
m.ptr->height=ptr->height;
m.ptr->width = ptr->width;
m.ptr->data = new T * [ptr->height];
for (int i = 0; i < ptr->height; i++)
{
ptr->data[i] = new T[ptr->width];
for (int j = 0; j < ptr->width; j++)
{
m.ptr->data[i][j] =ptr->data[i][j];
}
}
}
Mat<T,T1> Clone() // 从当前对象拷贝创建一个新的矩阵,完成深拷贝
{
Mat<T,T1> m(ptr->height,ptr->width);
m.ptr->height = ptr->height;
m.ptr->width = ptr->width;
m.ptr->data = new T * [ptr->height];
for (int i = 0; i < ptr->height; i++)
{
ptr->data[i] = new T[ptr->width];
for (int j = 0; j < ptr->width; j++)
{
m.ptr->data[i][j] = ptr->data[i][j];
}
}
return m;
}
Mat<double,T1> Normalize()//将矩阵元素的值变换到0-1范围内,以double类型的Mat对象输出。注意:在这个函数里,无法访问Mat<double>类型的对象的私有成员data,需要调用其At函数获得某个元素。
{
Mat<double,T1> m(ptr->height,ptr->width);
double max = static_cast<double>(this->At(1, 1)), min =static_cast<double> (this->At(1, 1));
for (int i = 0; i < this->Height(); i++)
{
for (int j = 0; j < this->Width(); j++)
{
max = max > static_cast<double>(this->At(i, j)) ? max : static_cast<double>(this->At(i, j));
min = min < static_cast<double>(this->At(i, j))? min : static_cast<double>(this->At(i, j));
}
}
for (int i = 0; i < static_cast<double>(this->Height()); i++)
{
for (int j = 0; j < static_cast<double>(this->Width()); j++)
{
if (static_cast<double>(this->At(i, j)) == max)
{
m.ptr->data[i][j]= 1;
}
else if (static_cast<double>(this->At(i, j)) == min)
{
m.ptr->data[i][j]=0;
}
else
{
m.ptr->data[i][j] = (static_cast<double>(this->At(i, j)) - min) / max;
}
}
}
return m;
}
Mat<T,T1>& operator=(const Mat<T,T1>& m) //重载赋值运算符,完成对象间的拷贝;
{
ptr->height = m.ptr->height;
ptr->width = m.ptr->width;
ptr->data = new T* [ptr->height];
for (int i = 0; i < ptr->height; i++)
{
ptr->data[i] = new T[ptr->width];
for (int j = 0; j < ptr->width; j++)
{
ptr->data[i][j] = m.ptr->data[i][j];
}
}
return(*this);
}
bool operator==(const Mat<T,T1>& m) //判断两个Mat对象是否相等
{
int flag = 0;
if (ptr->height != m.ptr->height)
{
flag++; cout << "不相等!" << endl;
return false;
}
else if (ptr->width != m.ptr->width)
{
flag++; cout << "不相等!" << endl;
return false;
}
else
{
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
if (ptr->data[i][j] != m.ptr->data[i][j])
{
cout << "不相等!" << endl;
return false;
}
}
}
}
if (flag == 0)
{
cout << "相等!" << endl;
return true;
}
}
friend Mat<T,T1> operator+(const Mat<T,T1>& m1, const Mat<T,T1>& m2) //对应元素的数值相加;
{
Mat m(m1);
for (int i = 0; i < m1.ptr->height; i++)
{
for (int j = 0; j < m1.ptr->width; j++)
{
m.ptr->data[i][j] = m1.ptr->data[i][j] + m2.ptr->data[i][j];
if (m.ptr->data[i][j] >= 255)
{
m.ptr->data[i][j] = 255;
}
}
}
cout << "文件相加成功!" << endl;
return m;
}
friend Mat<T,T1> operator-(const Mat<T,T1>& m1, const Mat<T,T1>& m2) //对应元素的数值相减;
{
Mat m(m1);
for (int i = 0; i < m1.ptr->height; i++)
{
for (int j = 0; j < m1.ptr->width; j++)
{
if (m1.ptr->data[i][j] - m2.ptr->data[i][j] <= 0)
{
m.ptr->data[i][j] = 0;
}
else
{
m.ptr->data[i][j] = fabs(m1.ptr->data[i][j] - m2.ptr->data[i][j]);
}
}
}
cout << "文件相减成功!" << endl;
return m;
}
Mat<T,T1>& operator++() //前置自加;
{
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
ptr->data[i][j]++;
}
}
return *this;
}
Mat<T,T1>& operator--() //前置自减;
{
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
ptr->data[i][j]--;
}
}
return *this;
}
Mat<T,T1> operator ++(int) //后置自加;
{
Mat old = *this;
++(*this);
return old;
}
Mat<T,T1> operator --(int) //后置自减;
{
Mat old = *this;
--(*this);
return old;
}
Mat<double,T1> operator-() // 取反;注意要把矩阵的数据规整到[0,1]区间后,再用1减
{
Normalize();
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
ptr->data[i][j] = 1.0 - ptr->data[i][j];
}
}
return (*this);
}
friend Mat<T,T1> operator+(Mat<T,T1>& m, T num) //所有元素加上同一数值;
{
for (int i = 0; i < m.ptr->height; i++)
{
for (int j = 0; j < m.ptr->width; j++)
{
m.ptr->data[i][j] += num;
}
}
return m;
}
friend Mat<T,T1> operator-(Mat<T,T1>& m, T num) //所有元素减去同一数值;
{
for (int i = 0; i < m.ptr->height; i++)
{
for (int j = 0; j < m.ptr->width; j++)
{
m.ptr->data[i][j] -= num;
}
}
return m;
}
friend Mat<T,T1> operator*(Mat<T,T1>& m, T num) //所有元素乘上同一数值;
{
for (int i = 0; i < m.ptr->height; i++)
{
for (int j = 0; j < m.ptr->width; j++)
{
m.ptr->data[i][j] *= num;
}
}
return m;
}
friend Mat<T,T1> operator/(Mat<T,T1>&m1, T num)//所有元素除以同一数值;
{
Mat m;
for (int i = 0; i < m1.ptr->height; i++)
{
for (int j = 0; j < m1.ptr->width; j++)
{
m.ptr->data[i][j] = m1.ptr->data[i][j] / num;
}
}
return m;
}
//另外,用友元函数再写出一个T类型的数和一个Mat对象的加,减,乘,除
Mat<T,T1> gray2bw(T t)//以给定阈值t进行二值化,返回结果对象
{
Normalize();
for (int i = 0; i < ptr->height; i++)
{
for (int j = 0; j < ptr->width; j++)
{
if (ptr->data[i][j] < t)
{
ptr->data[i][j] = 0;
}
else
{
ptr->data[i][j] = 1;
}
}
}
cout << "文件二值化成功!" << endl;
return (*this);
}
friend void Swap(Mat<T,T1>&a, Mat<T,T1>&b)//使用友元函数交换两个Mat对象
{
int h;
h = a.ptr->height;
a.ptr->height = b.ptr->height;
b.ptr->height = h;
h = a.ptr->width;
a.ptr->width = b.ptr->width;
b.ptr->width = h;
double** t = a.ptr->data;
a.ptr->data = b.ptr->data;
b.ptr->data = t;
}
//protected:
struct Databack// 自己实现一个结构体,来存储矩阵的行数、列数、引用计数和数据指针
{
T** data;
int height;
int width;
int refcnt;
};
Databack* ptr; // 这里需要声明指向该结构体的指针作为数据成员
};
#endif
Filter.hpp
#ifndef FILTER_FPP
#define FILTER_HPP
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include"Mat.hpp"
template <class T,class T1>
class Filter
{
public:
Filter() {}
Filter(T size) //构造函数
{
filtersize = size;
}
virtual ~Filter() {} //析构函数;
virtual Mat<T,T1> Filtering(const Mat<T,T1>& input) = 0; //滤波函数(纯虚函数);
protected:
T filtersize;
};
//meanFilter类
template <class T,class T1>
class MeanFilter : public Filter<T,T1>
{
public:
MeanFilter(T size)
{
this->filtersize = size;
}
virtual ~MeanFilter(){}
virtual Mat<T,T1> Filtering(const Mat<T,T1>& input) //均值滤波函数
{
int h = input.ptr->height + this->filtersize;
int w = input.ptr->width + this->filtersize;
T** m = new T* [h];//扩容
for (int i = 0; i < h; i++)
{
m[i] = new T[w];
for (int j = 0; j < w; j++)
{
m[i][j] = 0;
}
}
for (int i = this->filtersize / 2; i < h - 3; i++)//填充扩容后的m数组,先填充和ptr->data同区域的数据
{
for (int j = 0; j < w; j++)
{
if (j >= this->filtersize / 2 && j < w - this->filtersize)
{
m[i][j] = input.ptr->data[i - this->filtersize / 2][j - this->filtersize / 2];
}
else if (j < this->filtersize / 2)//取右值
{
m[i][j] = input.ptr->data[i - this->filtersize / 2][j];
}
else if (j >= w - this->filtersize)//取左值
{
m[i][j] = m[i][j - this->filtersize / 2];
}
}
}
for (int i = 0; i < h; i++)//填充扩容后的m数组
{
for (int j = 0; j < w; j++)
{
if (i < this->filtersize / 2)
{
if (j <= w / 2)
{
m[i][j] = m[i + this->filtersize - 1][j + this->filtersize - 1];//取其右下角的值
}
else
{
m[i][j] = m[i + this->filtersize - 1][j - this->filtersize + 1];//取其左下角的值
}
}
else if (i > input.ptr->height + this->filtersize / 2)
{
if (j <= w / 2)
{
m[i][j] = m[i - this->filtersize + 1][j + this->filtersize - 1];//取其右上角的值
}
else
{
m[i][j] = m[i - this->filtersize + 1][j - this->filtersize + 1];//取其左上角的值
}
}
}
}
for (int i = this->filtersize / 2; i < input.ptr->height + this->filtersize / 2; i++)
{
for (int j = this->filtersize / 2; j < input.ptr->width + this->filtersize / 2; j++)
{
T sum = 0;
for (int x = i - this->filtersize / 2; x < i + this->filtersize / 2; x++)
{
for (int y = j - this->filtersize / 2; y < j + this->filtersize / 2; y++)
{
sum += m[x][y];
}
}
input.ptr->data[i - this->filtersize / 2][j - this->filtersize / 2] = sum / (T)(this->filtersize * this->filtersize);
}
}
for (int i = 0; i < h; i++)
{
delete[]m[i];
}
delete[]m;
return input;
}
};
template <class T>
T sort(T s[], int len)//选择排序法后返回中间值
{
int i = 0, j = 0;
for (int i = 0; i < len - 1; i++)
{
int min = i;
for (int j = i + 1; j < len; j++)
{
if (s[j] < s[min])
{
min = j;
}
}
T t = s[min];
s[min] = s[i];
s[i] = t;
}
return s[len / 2];
}
//median类
template <class T,class T1>
class MedianFilter : public Filter<T,T1>
{
public:
MedianFilter(T size)
{
this->filtersize = size;
}
virtual ~MedianFilter(){}
virtual Mat<T,T1> Filtering(const Mat<T,T1>& input) // 中值滤波器函数
{
int h = input.ptr->height + this->filtersize;
int w = input.ptr->width + this->filtersize;
T** m = new T* [h];//扩容
for (int i = 0; i < h; i++)
{
m[i] = new T[w];
for (int j = 0; j < w; j++)
{
m[i][j] = 0;
}
}
for (int i = this->filtersize / 2; i < h - 3; i++)//填充扩容后的m数组,先填充和ptr->data同区域的数据
{
for (int j = 0; j < w; j++)
{
if (j >= this->filtersize / 2 && j < w - this->filtersize)
{
m[i][j] = input.ptr->data[i - this->filtersize / 2][j - this->filtersize / 2];
}
else if (j < this->filtersize / 2)//取右值
{
m[i][j] = input.ptr->data[i - this->filtersize / 2][j];
}
else if (j >= w - this->filtersize)//取左值
{
m[i][j] = m[i][j - this->filtersize / 2];
}
}
}
for (int i = 0; i < h; i++)//填充扩容后的m数组
{
for (int j = 0; j < w; j++)
{
if (i < this->filtersize / 2)
{
if (j <= w / 2)
{
m[i][j] = m[i + this->filtersize - 1][j + this->filtersize - 1];//取其右下角的值
}
else
{
m[i][j] = m[i + this->filtersize - 1][j - this->filtersize + 1];//取其左下角的值
}
}
else if (i > input.ptr->height + this->filtersize / 2)
{
if (j <= w / 2)
{
m[i][j] = m[i - this->filtersize + 1][j + this->filtersize - 1];//取其右上角的值
}
else
{
m[i][j] = m[i - this->filtersize + 1][j - this->filtersize + 1];//取其左上角的值
}
}
}
}
for (int i = this->filtersize / 2; i < input.ptr->height + this->filtersize / 2; i++)
{
for (int j = this->filtersize / 2; j < input.ptr->width + this->filtersize / 2; j++)
{
int k = 0;
int sum = this->filtersize * this->filtersize;
T* s = new T[sum];//将二维数组暂赋给一维数组,调用选择排序求中值
for (int x = i - this->filtersize / 2; x <= i + this->filtersize / 2; x++)
{
for (int y = j - this->filtersize / 2; y <= j + this->filtersize / 2; y++)
{
s[k] = m[x][y];
k++;
}
}
input.ptr->data[i - this->filtersize / 2][j - this->filtersize / 2] = sort(s, sum);
}
}
for (int i = 0; i < h; i++)
{
delete[]m[i];
}
delete[]m;
return input;
}
};
#endif
```cpp
四、总结
总结来说,这篇文章是基于之前的实验通过类模板来实现,我认为难点在于结构体中refcnt的使用,它是用于记录指向这块数据的类的个数,防止析构时出现悬垂指针(数据块被析构了,但仍有指针傻傻地继续指向它)等问题。其他就是类里模板函数的书写了,还有就是注意如果模板里定义了两个类型的话,比如T1、T2,在实例化的时候两者都要写清楚。有问题欢迎大家提出一起学习鸭!