前言
一、实验目的
了解为何使用抽象类,学习通过继承实现代码重用的机制和方法
掌握如何声明函数为纯虚函数
掌握如何利用纯虚函数,编写派生类的覆盖函数
二、实验内容
1.内容
定义滤波器抽象类Filter,并派生两个具体的滤波器:类MeanFilter和类MedianFilter,分别完成对图像的均值滤波和中值滤波。在抽象类Filter中定义纯虚函数Filtering,在两个派生类中实现相应的函数。
在主函数中声明基类指针,通过该指针指向不同的派生类,从而使用不同的滤波器对象对图像进行滤波。并显示滤波后的图像,注意观察两种滤波器对图像滤波效果的差异。在构造滤波器时选择不同的filterSize,观察对滤波输出图像的影响。
2.代码示例
代码如下(示例):
抽象类滤波器 Filter.h
#ifndef FILTER_H
#define FILTER_H
#include “Image.h”
class Filter
{
public:
Filter(int size); //构造函数
virtual ~Filter(); //析构函数;
virtual Matrix Filtering(const Matrix &input) = 0; //滤波函数(纯虚函数);
protected:
int filterSize;
};
#endif
派生类 均值滤波器 MeanFilter.h
#ifndef MEANFILTER_H
#define MEANFILTER_H
#include "Filter.h"
class MeanFilter : public Filter
{
public:
MeanFilter(int size);
virtual ~MeanFilter();
virtual Matrix Filtering(const Matrix &input); //均值滤波函数
};
#endif
请完成MeanFilter.cpp、MedianFilter.cpp及主函数文件
使用滤波器对图像进行滤波的参考代码
Image img("Lena_gaussian.bmp"或者"Lena_salt_and_pepper.bmp");
Filter *filter = NULL;
filter = new MeanFilter(5);
Image result_mean;
result_mean = filter->Filtering(img);
result_mean.WriteBMP("Mean.bmp");
delete filter;
filter = new MedianFilter(5);
Image result_median;
result_median = filter->Filtering(img);
medianfilter.WriteBMP("Median.bmp");
delete filter;
3.实验要求及说明
完成上述代码,并能得到正确的结果图像,对结果进行比较。
实验附带了两幅添加了不同噪声的图像(高斯白噪声Lena_gaussian.bmp和椒盐噪声Lena_salt_and_pepper.bmp)。
设计简单的控制台交互,针对不同的图像,更改滤波器大小,观察滤波效果。
说明
-
注意,Filtering函数的输入是const的Matrix对象引用,在Filtering函数内部需要获取图像高度宽度的函数也需要重载一个const类型的函数,否则编译不会通过。比如:
int Height() const {return height;} -
成员变量filterSize记录的是滤波器的大小,一般是奇数,定义在多大的图像块上进行操作,比如filterSize=3,则在输入图像的每一个像素的33区域内进行滤波操作。以33的均值滤波器为例,假设当前操作图像以像素(i,j)为中心的3*3区域为:
15 12 87 25 24 40 54 21 46
像素(i,j)的像素值为24,它的33邻域的像素值如上所示,那么均值滤波的操作是:对这个33邻域的像素求均值:(15+12+87+25+24+40+54+21+46)/9=36。那么输出图像的像素(i,j)处的像素值为36。
针对图像中的每个像素做上述操作,就完成了均值滤波。在编程实现时要特别注意,图像四条边上和四个角的像素应该如何处理;以及计算过程中不能覆盖掉原始图像的像素值,否则会影响其他像素的计算。
3.中值滤波的操作类似,只不过把求均值变成了求中值,即把filterSize*filterSize大小区域内的像素的像素值按照大小排列,结果是位于中间的那个值。比如上例中,如果用中值滤波,那么按像素值从小到大排列后的像素值是:12, 15, 21, 24, 25, 40, 46, 54, 87。中值(中间的值)是25,所以,滤波后的输出图像的像素(i,j)的像素值为25。
filterSize是可变的,表示滤波操作的空间尺度,选取不同的值所产生的效果不一样,请在实验中试着产生不同大小的滤波器,看看对图像有什么影响。
以上均为老师给的实验内容,实现如下。
三、代码实现
Main.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include"Matrix.h"
#include "Image.h"
#include"Filter.h"
#include"MeanFilter.h"
#include"MedianFilter.h"
using namespace std;
void print()
{
cout << "菜单如下:" << endl;
cout << "1.中值滤波器" << endl;
cout << "2.均值滤波器" << endl;
cout << "请输入处理方式的序号" << endl;
}
void print1()
{
cout << "3.Lena_gaussian.bmp" << endl;
cout << "4.Lena_salt_and_pepper.bmp" << endl;
cout << "请输入想要滤波的图片名序号和滤波值" << endl;
}
int main()
{
print();
int a, b,c;
cin >> a;
Image img;
Image img1("Lena_gaussian.bmp");
Image img2("Lena_salt_and_pepper.bmp");
Image result_mean;
Image result_median;
Filter* filter = NULL;
switch(a)
{
case 1:
print1();
cin >> b >> c;
filter = new MedianFilter(c);
if (b == 1)
{
result_median = filter->Filtering(img1);
}
else
{
result_median = filter->Filtering(img2);
}
result_median.WriteBMP("Median.bmp");
delete filter;
break;
case 2:
print1();
cin >> b >> c;
filter = new MeanFilter(c);
if (b == 1)
{
result_mean = filter->Filtering(img1);
}
else
{
result_mean = filter->Filtering(img2);
}
result_mean.WriteBMP("Mean.bmp");
delete filter;
break;
default:
cout << "输入错误!" << endl;
break;
}
cout << "处理完成!" << endl;
return 0;
}
Image.h
#ifndef Image_H
#define Image_H
#include "Matrix.h"
class Image : public Matrix
{
public:
Image(); //构造函数,创建行列都为零的Image对象
Image(int h, int w); //构造函数重载,创建h行,w列的Image对象
Image(int h, int w, unsigned char val); //构造函数重载,创建的图像像素值都为val;
Image(const char* ImageName); //构造函数重载,利用文件名从硬盘加载图像文件成为Image对象;
Image(unsigned char m[][100], int n); //构造函数重载,从静态数组创建Image对象;
Image(unsigned char** m, int h, int w); //构造函数重载,从动态数组创建Image对象;
Image(const Matrix& m); //构造函数重载,由Matrix类对象构造Image类对象
Image(const Image& im); //拷贝构造函数;
~Image(); //析构函数;
void ReadBMP(const char* ImageName); //从硬盘文件中读入图像数据;
void WriteBMP(const char* filename); //将图像数据保存为图像文件;
};
#endif
Image.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Image.h"
#include <Windows.h>
#include<cstdlib>
#include<cstdio>
#include <iostream>
using namespace std;
Image::Image()
{
//write your code here
height = 0;
width = 0;
data = nullptr;
}
Image::Image(int h, int w)//构造函数重载,创建h行,w列的Image对象
{
all(h, w);
}
Image::Image(const char* ImageName)//构造函数重载,利用文件名从硬盘加载图像文件成为Image对象;
{
ReadBMP(ImageName);
// cout << "被调用!" << endl;
}
Image::Image(unsigned char m[][100], int n)//构造函数重载,从静态数组创建Image对象;
{
height = n;
width = 100;
all(n, 100);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < 100; j++)
{
data[i][j] = static_cast<unsigned char>(m[i][j]);
}
}
}
Image::Image(unsigned char** m, int h, int w) //构造函数重载,从动态数组创建Image对象;
{
height = h;
width = w;
data = new double* [h];
for (int i = 0; i < h; i++)
{
data[i] = new double[w];
for (int j = 0; j < w; j++)
{
data[i][j] = static_cast<unsigned char>(m[i][j]);
}
}
}
Image::Image(const Matrix& m) :Matrix(m) //构造函数重载,由Matrix类对象构造Image类对象
{
}
Image::Image(const Image& m)
{
//write your code here
width = m.width;
height = m.height;
data = new double* [height];
for (int i = 0; i < height; i++)
{
data[i] = new double[width];
for (int j = 0; j < width; j++)
{
data[i][j] = static_cast<unsigned char> (m.data[i][j]);
}
}
//cout << "拷贝并创建新图像成功!" << endl;
}
Image:: ~Image()//析构函数;
{
/* for (int i = 0; i < height; i++)
{
delete[]data[i];
}
delete[]data;*/
}
void Image::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);
width = ih.biWidth;
height = ih.biHeight;
if (height % 4 != 0)
{
height = (height * (ih.biBitCount) / 8 + 3) / 4 * 4;
height = height / 3;
}
if (width % 4 != 0)
{
width = (width * (ih.biWidth) / 8 + 3) / 4 * 4;
width = width / 3;
}
data = new double* [height];
for (int i = 0; i < height; i++)
{
data[i] = new double[width];
}
// fseek(fp, 14+40,0);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
unsigned char r, g, b;
fread(&r, sizeof(unsigned char), 1, fp);
fread(&g, sizeof(unsigned char), 1, fp);
fread(&b, sizeof(unsigned char), 1, fp);
data[i][j] = static_cast<double>(r / 3 + g / 3 + b / 3);
}
}
fclose(fp);
// cout << "文件读取成功!" << endl;
}
void Image::WriteBMP(const char* filename) //将未被缩放的图像数据保存为图像文件;
{
FILE* fp = fopen(filename, "wb");
if (fp == NULL)
{
cout << "文件打开失败!" << endl;
exit(0);
}
BITMAPFILEHEADER fh;
BITMAPINFOHEADER ih;
// ih.biSizeImage = 737280;
fh.bfOffBits = 54;
fh.bfReserved1 = 0;
fh.bfReserved2 = 0;
fh.bfSize = height * 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 = width;
ih.biHeight = height;
ih.biSizeImage = ih.biWidth * ih.biHeight * 3;
fwrite(&fh, sizeof(BITMAPFILEHEADER), 1, fp);
fwrite(&ih, sizeof(BITMAPINFOHEADER), 1, fp);
unsigned char t = 0;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
t = static_cast<unsigned char>(data[i][j]);
for (int k = 0; k < 3; k++)
{
fwrite(&t, sizeof(unsigned char), 1, fp);
}
}
}
fclose(fp);
cout << "文件存入成功!请查看!" << endl;
}
Matrix.h
#ifndef Matrix_H
#define Matrix_H
class Matrix
{
public:
Matrix();
Matrix(int h, int w);
Matrix(int h, int w, double val);
Matrix(const Matrix& m);
~Matrix();
void all(int h, int w);
//protected:
int height;
int width;
double** data;
};
#endif
Matrix.cpp
#include"Matrix.h"
#include<cstdio>
#include<iostream>
#include<Windows.h>
#include<ctime>
#include<cmath>
using namespace std;
void Matrix::all(int h, int w)
{
height = h;
width = w;
for (int i = 0; i < height; i++)
{
data = new double* [width];
}
}
Matrix::Matrix()
{
height = 0;
width = 0;
data = nullptr;
}
Matrix::Matrix(int h, int w)
{
all(h, w);
}
Matrix::Matrix(int h, int w, double val)
{
all(h, w);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
data[i][j] = val;
}
}
}
Matrix::Matrix(const Matrix& m)
{
width = m.width;
height = m.height;
data = new double* [height];
for (int i = 0; i < height; i++)
{
data[i] = new double[width];
for (int j = 0; j < width; j++)
{
data[i][j] = m.data[i][j];
}
}
// cout << "拷贝构造成功!" << endl;
}
Matrix::~Matrix()
{
/*for (int i = 0; i < height; i++)
{
delete[]data[i];
}
delete[]data;*/
}
Filter.h
#define _CRT_SECURE_NO_WARNINGS
#ifndef FILTER_H
#define FILTER_H
#include <iostream>
#include "Image.h"
using namespace std;
class Filter
{
public:
Filter()
{
filtersize = 0;
}
Filter(int size)//构造函数
{
filtersize = size;
}
virtual ~Filter()//析构函数;
{
}
virtual Matrix Filtering(const Matrix& input) = 0; //滤波函数(纯虚函数);
protected:
int filtersize;
};
#endif
MeanFilter.h
#define _CRT_SECURE_NO_WARNINGS
#ifndef MEANFILTER_H
#define MEANFILTER_H
#include <iostream>
#include "Filter.h"
using namespace std;
class MeanFilter : public Filter
{
public:
MeanFilter(int size)
{
filtersize = size;
}
virtual ~MeanFilter()
{
}
virtual Matrix Filtering(const Matrix& input); //均值滤波函数
};
Matrix MeanFilter::Filtering(const Matrix& input)
{
int h = input.height + filtersize;
int w = input.width + filtersize;
double** m = new double* [h];//扩容
for (int i = 0; i < h; i++)
{
m[i] = new double[w];
for (int j = 0; j < w; j++)
{
m[i][j] = 0;
}
}
for (int i = filtersize/2; i < h-3; i++)//填充扩容后的m数组,先填充和data同区域的数据
{
for (int j =0; j < w; j++)
{
if (j >= filtersize / 2&& j <w-filtersize)
{
m[i][j] = input.data[i - filtersize / 2][j - filtersize / 2];
}
else if (j < filtersize / 2)//取右值
{
m[i][j] = input.data[i-filtersize/2][j];
}
else if (j >=w-filtersize)//取左值
{
m[i][j]=m[i][j - filtersize / 2];
}
}
}
for (int i = 0; i < h; i++)//填充扩容后的m数组
{
for (int j = 0; j < w; j++)
{
if (i < filtersize / 2)
{
if (j <= w / 2)
{
m[i][j] = m[i + filtersize - 1][j + filtersize - 1];//取其右下角的值
}
else
{
m[i][j] = m[i + filtersize - 1][j - filtersize + 1];//取其左下角的值
}
}
else if (i > input.height + filtersize / 2)
{
if (j <= w / 2)
{
m[i][j] = m[i - filtersize + 1][j + filtersize - 1];//取其右上角的值
}
else
{
m[i][j] = m[i - filtersize + 1][j - filtersize + 1];//取其左上角的值
}
}
}
}
for (int i = filtersize / 2; i < input.height + filtersize / 2; i++)
{
for (int j = filtersize / 2; j < input.width + filtersize / 2; j++)
{
double sum = 0;
for (int x = i - filtersize / 2; x < i + filtersize / 2; x++)
{
for (int y = j - filtersize / 2; y < j + filtersize / 2; y++)
{
sum += m[x][y];
}
}
input.data[i - filtersize/2][j - filtersize/2] = sum/(double)(filtersize*filtersize);
}
}
for (int i = 0; i < h; i++)
{
delete[]m[i];
}
delete[]m;
return input;
}
#endif
MedianFilter.h
#ifndef MEDIANFILTER_H
#define MEDIANFILTER_H
#include <iostream>
#include "Filter.h"
using namespace std;
class MedianFilter : public Filter
{
public:
MedianFilter(int size)
{
filtersize = size;
}
virtual ~MedianFilter()
{
}
virtual Matrix Filtering(const Matrix& input); // 中值滤波器函数
};
double sort(double 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;
}
}
double t = s[min];
s[min] = s[i];
s[i] = t;
}
return s[len / 2];
}
Matrix MedianFilter::Filtering(const Matrix& input)
{
int h = input.height + filtersize;
int w = input.width + filtersize;
double** m = new double* [h];//扩容
for (int i = 0; i < h; i++)
{
m[i] = new double[w];
for (int j = 0; j < w; j++)
{
m[i][j] = 0;
}
}
for (int i = filtersize / 2; i < h - 3; i++)//填充扩容后的m数组,先填充和data同区域的数据
{
for (int j = 0; j < w; j++)
{
if (j >= filtersize / 2 && j < w - filtersize)
{
m[i][j] = input.data[i - filtersize / 2][j - filtersize / 2];
}
else if (j < filtersize / 2)//取右值
{
m[i][j] = input.data[i - filtersize / 2][j];
}
else if (j >= w - filtersize)//取左值
{
m[i][j] = m[i][j - filtersize / 2];
}
}
}
for (int i = 0; i < h; i++)//填充扩容后的m数组
{
for (int j = 0; j < w; j++)
{
if (i < filtersize / 2)
{
if (j <= w / 2)
{
m[i][j] = m[i + filtersize - 1][j + filtersize - 1];//取其右下角的值
}
else
{
m[i][j] = m[i + filtersize - 1][j - filtersize + 1];//取其左下角的值
}
}
else if (i > input.height + filtersize / 2)
{
if (j <= w / 2)
{
m[i][j] = m[i - filtersize + 1][j + filtersize - 1];//取其右上角的值
}
else
{
m[i][j] = m[i - filtersize + 1][j - filtersize + 1];//取其左上角的值
}
}
}
}
for (int i = filtersize / 2; i < input.height + filtersize / 2; i++)
{
for (int j = filtersize / 2; j < input.width + filtersize / 2; j++)
{
int k = 0;
int sum = filtersize * filtersize;
double* s =new double[sum];//将二维数组暂赋给一维数组,调用选择排序求中值
for (int x = i - filtersize / 2; x <= i + filtersize / 2; x++)
{
for (int y = j - filtersize / 2; y <= j + filtersize / 2; y++)
{
s[k] = m[x][y];
k++;
}
}
input.data[i-filtersize/2][j-filtersize/2] = sort(s, sum);
}
}
for (int i = 0; i < h; i++)
{
delete[]m[i];
}
delete[]m;
return input;
};
#endif
四、总结
做完这篇实验,对虚函数和代码重复利用的相关应用加强了很多。但认为我的不足之处还是在于如何调用我写的这些虚构函数,这篇实验是使用了题目已经提供好的方式进行调用,但我想如果让我自己写主函数还是有一定困难的。这次实验由于滤波器的实现函数较短,就把实现内容和类的声明放在一起写了。对于滤波器函数实现这一块,我的主要思路是对原来的data函数进行扩容,这样就能保证在扩容后的条件下,四个边和四个角能够在一个正矩阵里从而进行数据处理了。有问题欢迎大家提出一起学习鸭!