【实验八 抽象类】

本文档介绍了通过C++实现抽象类Filter及其派生类MeanFilter和MedianFilter来处理图像滤波的过程。实验目标是理解抽象类的作用,掌握纯虚函数的使用,并对比不同滤波器对图像的影响。实验内容包括定义滤波器类,实现均值滤波和中值滤波,以及使用控制台交互选择滤波器和图像进行处理。最后,给出了完整的代码实现和实验总结。
摘要由CSDN通过智能技术生成


前言

一、实验目的

了解为何使用抽象类,学习通过继承实现代码重用的机制和方法
掌握如何声明函数为纯虚函数
掌握如何利用纯虚函数,编写派生类的覆盖函数

二、实验内容

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)。
设计简单的控制台交互,针对不同的图像,更改滤波器大小,观察滤波效果。
说明

  1. 注意,Filtering函数的输入是const的Matrix对象引用,在Filtering函数内部需要获取图像高度宽度的函数也需要重载一个const类型的函数,否则编译不会通过。比如:
    int Height() const {return height;}

  2. 成员变量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函数进行扩容,这样就能保证在扩容后的条件下,四个边和四个角能够在一个正矩阵里从而进行数据处理了。有问题欢迎大家提出一起学习鸭!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值