图像实验4 - 图像去噪

一、实验目的

1、掌握算术均值滤波器、几何均值滤波器、谐波和逆谐波均值滤波器进行图像去噪的算法
2、掌握利用中值滤波器进行图像去噪的算法
3、掌握自适应中值滤波算法
4、掌握自适应局部降低噪声滤波器去噪算法
5、掌握彩色图像去噪步骤

二、实验内容

1、均值滤波
具体内容:利用 OpenCV 对灰度图像像素进行操作,分别利用算术均值滤波器、几何均值滤波器、谐波和逆谐波均值滤波器进行图像去。模板大小为5*5。(注:请分别为图像添加高斯噪声、胡椒噪声、盐噪声和椒盐噪声,并观察滤波效果)
2、中值滤波
具体内容:利用 OpenCV 对灰度图像像素进行操作,分别利用 5*5 和 9*9尺寸的模板对图像进行中值滤波。(注:请分别为图像添加胡椒噪声、盐噪声和椒盐噪声,并观察滤波效果)
3、自适应均值滤波。
具体内容:利用 OpenCV 对灰度图像像素进行操作,设计自适应局部降低噪声滤波器去噪算法。模板大小 7*7(对比该算法的效果和均值滤波器的效果)
4、自适应中值滤波
具体内容:利用 OpenCV 对灰度图像像素进行操作,设计自适应中值滤波算法对椒盐图像进行去噪。模板大小 7*7(对比中值滤波器的效果)
5、彩色图像均值滤波
具体内容:利用 OpenCV 对彩色图像 RGB 三个通道的像素进行操作,利用算术均值滤波器和几何均值滤波器进行彩色图像去噪。模板大小为 5*5。

三、实验完成情况

添加噪声

核心代码

//添加指定数值噪声(椒:0,盐:255)
Mat saltAndPepperNoise(const Mat& input, int n, int value) {
    int x, y;
    int row = input.rows, col = input.cols;
    bool color = input.channels() > 1;
    Mat target = input.clone();
    //随机取位置添加指定噪声
    for (int i = 0; i < n; i++) {
        x = rand() % row;
        y = rand() % col;
        if (color) {
            target.at<Vec3b>(x, y)[0] = value;
            target.at<Vec3b>(x, y)[1] = value;
            target.at<Vec3b>(x, y)[2] = value;
        }
        else {
            target.at<uchar>(x, y) = value;
        }
    }
    return target;
}

//获取随机高斯噪声图像
Mat gaussianNoise(const Mat& input, int avg, int sd) {
    Mat target = Mat::zeros(input.size(), input.type());
    //Opencv随机数类
    RNG rng(rand());
    //高斯分布的平均数或均匀分布的最小值
    int a = avg;
    //高斯分布的标准差或均匀分布的最大值
    int b = sd;
    //类型,NORMAL为高斯分布,UNIFORM为均匀分布
    int distType = RNG::NORMAL;
    //产生一个符合高斯分布(或均匀分布)的图像
    rng.fill(target, distType, a, b);
    return target;
}

实现截图




1、均值滤波

核心代码

//对图像进行滤波处理
Mat filterProcess(const Mat& img, uchar kernel(const Mat&, int, int, int, int), int size, int q = 1) {
    Mat target = img.clone();
    vector<Mat> channels;
    int m = size / 2;
    split(target, channels);
    for (Mat ch : channels) {
        Mat src = ch.clone();
        for (int i = src.rows - size; i >= 0; i--)
            for (int j = src.cols - size; j >= 0; j--) {
                ch.at<uchar>(i + m, j + m) = kernel(src, size, i, j, q);
            }
    }
    merge(channels, target);
    return target;
}
//算术均值计算
static uchar arithmeticMeanKernel(const Mat& img, int size, int i, int j, int q) {
    double res = 0;
    for (int r = i + size - 1; r >= i; r--) {
        for (int c = j + size - 1; c >= j; c--) {
            res += img.at<uchar>(r, c);
        }
    }
    return (uchar)(res / ((double)size * size));
}
//几何均值计算
static uchar geometricMeanKernel(const Mat& img, int size, int i, int j, int q) {
    double res = 1;
    for (int r = i + size - 1; r >= i; r--) {
        for (int c = j + size - 1; c >= j; c--) {
            res *= img.at<uchar>(r, c);
        }
    }
    return (uchar)(pow(res, 1.0 / ((double)size * size)));
}
//谐波平均值计算
static uchar harmonicMeanKernel(const Mat& img, int size, int i, int j, int q) {
    double res = 0;
    for (int r = i + size - 1; r >= i; r--) {
        for (int c = j + size - 1; c >= j; c--) {
            res += 1.0 / ((double)img.at<uchar>(r, c) + 1);
        }
    }
    return (uchar)(((double)size * size) / res - 1);
}
//反谐波平均值计算
static uchar antiHarmonicMeanKernel(const Mat& img, int size, int i, int j, int q) {
    double res1 = 0, res2 = 0;
    for (int r = i + size - 1; r >= i; r--) {
        for (int c = j + size - 1; c >= j; c--) {
            res1 += pow(img.at<uchar>(r, c), q + 1);
            res2 += pow(img.at<uchar>(r, c), q);
        }
    }
    return (uchar)(res1 / res2);
}

实现截图

高斯噪声




胡椒噪声




盐噪声




椒盐噪声




2、中值滤波

核心代码

//快速选择中值
static int quickSelect(vector<uchar>& vec, int left, int right, int target) {
    int t = vec[left], l = left, r = right;
    bool isLeft = true;
    while (l < r) {
        if (isLeft) {
            while (l < r && vec[r] >= t)
                r--;
            vec[l] = vec[r];
        }
        else {
            while (l < r && vec[l] < t)
                l++;
            vec[r] = vec[l];
        }
        isLeft = !isLeft;
    }
    vec[l] = t;
    if (l < target)
        return quickSelect(vec, l + 1, right, target);
    else if (l > target)
        return quickSelect(vec, left, l - 1, target);
    return vec[l];
}

//中值计算
static uchar medianKernel(const Mat& img, int size, int i, int j, int q) {
    vector<uchar> values;
    for (int r = i + size - 1; r >= i; r--) {
        for (int c = j + size - 1; c >= j; c--) {
            values.push_back(img.at<uchar>(r, c));
        }
    }
    int cnt = size * size;
    return quickSelect(values, 0, cnt - 1, cnt / 2);
}

实现截图








3、自适应均值滤波

核心代码

static uchar adaptiveMeanKernel(const Mat& img, int size, int i, int j, int q) {
    double avg = 0, ds = 0;
    uchar u = img.at<uchar>(i + size / 2, j + size / 2);
    for (int r = i + size - 1; r >= i; r--) {
        for (int c = j + size - 1; c >= j; c--) {
            avg += img.at<uchar>(r, c);
        }
    }
    avg /= size*size;
    for (int r = i + size - 1; r >= i; r--) {
        for (int c = j + size - 1; c >= j; c--) {
            ds += pow(avg - img.at<uchar>(r, c), 2);
        }
    }
    double rate = q / ds;
    if (rate > 1.0)
        rate = 1.0;
    return (int)(u - rate * (u - avg));
}

实现截图(由于自适应均值主要针对加性噪声,因此只测试高斯噪声的去噪效果)

4、自适应中值滤波

核心代码

static uchar adaptiveMedianKernel(const Mat& img, int size, int i, int j, int max_size) {
    vector<uchar> values;
    for (int r = i + size - 1; r >= i; r--) {
        for (int c = j + size - 1; c >= j; c--) {
            values.push_back(img.at<uchar>(r, c));
        }
    }
    int cnt = size * size;
    int mid = quickSelect(values, 0, cnt - 1, cnt / 2);
    int min = quickSelect(values, 0, cnt - 1, 0);
    int max = quickSelect(values, 0, cnt - 1, cnt - 1);
    if (mid < max && min < mid) {
        uchar u = img.at<uchar>(i + size / 2, j + size / 2);
        return (min < u&& u < max) ? u : mid;
    }
    if (size == max_size || i == 0 || j == 0 || i + size + 1 >= img.rows || j + size + 1 >= img.cols)
        return mid;
    return adaptiveMedianKernel(img, size + 2, i - 1, j - 1, max_size);
}

实现截图




5、彩色图像均值滤波

核心代码

核心滤波处理函数filterProcess支持对彩色图像分解处理,因此与灰度图像使用相同的均值计算Kernel函数

实现截图

添加噪声




高斯噪声




胡椒噪声




盐噪声




椒盐噪声




四、实验中的问题

  1. 对于滤波器的理解不够深,耗费了很长时间加深理解关于滤波器处理噪声方面的知识。
  2. 由于滤波器均为手动滤波,在代码编写方面比之前的实验更难一些,也更容易碰到很多问题。
  3. 关于合适的自适应均值滤波的估计值,高斯噪声的均值和标准差等参数的选取方面也耗费了很多时间。

五、实验结果

源码

lab4.cpp

#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>

using namespace std;
using namespace cv;

const static std::string path = "F:\\Documents\\高级图像处理\\Image\\";

//图片路径组合
string getFullPath(string name) {
    return path + name;
}

//打开图片显示窗口
void openWindows(string win_name, Mat img, int x = 500, int y = 200) {
    //窗口命名,指定大小,生成位置
    namedWindow(win_name, WINDOW_AUTOSIZE);
    moveWindow(win_name, x, y);
    //生成窗口显示图片
    imshow(win_name, img);
    //等待键入
    waitKey();
    //关闭窗口
    destroyWindow(win_name);
}

//统一数字输入函数
template<typename T>T inputNumber(string desc) {
    system("cls");
    T input;
    cout << desc;
    cin >> input;
    cout << endl;
    return input;
}

//统一图片打开函数,用于简化路径和处理打开图片错误
Mat openImage(string name, int type = 1) {
    //图片读取函数,返回图像存储类(包含存储方式、存储矩阵、矩阵大小等)
    Mat img = imread(getFullPath(name), type);
    if (img.empty()) {
        cout << "无效图片,读取失败" << endl;
        exit(-1);
    }
    return img;
}

//图像基类
class Image {
protected:
    Mat img;
public:
    Mat getImage() {
        return img.clone();
    }
};

//彩色图像处理类
class ColorImage :public Image {
public:
    ColorImage(Mat img,string name) {
        this->img = img.clone();
        openWindows(name, img);
    }
    //读取彩色图像并展示
    ColorImage(string path) {
        img = openImage(path, IMREAD_COLOR);
        openWindows("彩色图像", img);
    }
};

//灰度图像处理类
class GrayImage :public Image {
public:
    GrayImage(Mat img,string name) {
        this->img = img.clone();
        openWindows(name, img);
    }
    //仅读取灰度方式读取图像并展示(IMREAD_GRAYSCALE)
    GrayImage(string path) {
        img = openImage(path, IMREAD_GRAYSCALE);
        openWindows("灰度图像", img);
    }
};

//添加噪声处理类
class Noise {
    Mat src;
private:
    //防止图像溢出的图像加减处理,使用saturate_cast进行限制
    Mat handleImageAddition(Mat img, Mat add, bool ifadd = true) {
        Mat target = img.clone();
        vector<Mat> target_channels;
        vector<Mat> add_channels;
        split(target, target_channels);
        split(add, add_channels);
        for (int c = 0; c < target_channels.size(); c++) {
            Mat curr = target_channels[c];
            Mat cadd = add_channels[c];
            for (int i = 0; i < curr.rows; i++)
                for (int j = 0; j < curr.cols; j++)
                    curr.at<uchar>(i, j) = saturate_cast<uchar>(ifadd ? curr.at<uchar>(i, j) + cvRound(cadd.at<uchar>(i, j)) : curr.at<uchar>(i, j) - cvRound(cadd.at<uchar>(i, j)));
        }
        merge(target_channels, target);
        return target;
    }

    //添加指定数值噪声(椒:0,盐:255)
    Mat saltAndPepperNoise(const Mat& input, int n, int value) {
        int x, y;
        int row = input.rows, col = input.cols;
        bool color = input.channels() > 1;
        Mat target = input.clone();
        //随机取位置添加指定噪声
        for (int i = 0; i < n; i++) {
            x = rand() % row;
            y = rand() % col;
            if (color) {
                target.at<Vec3b>(x, y)[0] = value;
                target.at<Vec3b>(x, y)[1] = value;
                target.at<Vec3b>(x, y)[2] = value;
            }
            else {
                target.at<uchar>(x, y) = value;
            }
        }
        return target;
    }

    //获取随机高斯噪声图像
    Mat gaussianNoise(const Mat& input, int avg, int sd) {
        Mat target = Mat::zeros(input.size(), input.type());
        //Opencv随机数类
        RNG rng(rand());
        //高斯分布的平均数或均匀分布的最小值
        int a = avg;
        //高斯分布的标准差或均匀分布的最大值
        int b = sd;
        //类型,NORMAL为高斯分布,UNIFORM为均匀分布
        int distType = RNG::NORMAL;
        //产生一个符合高斯分布(或均匀分布)的图像
        rng.fill(target, distType, a, b);
        return target;
    }

public:
    Noise(Image img) {
        this->src = img.getImage();
    }

    //添加胡椒噪声
    Image addPepperNoise(int n) {
        Mat output = saltAndPepperNoise(src, n, 0);
        string name = "胡椒噪声";
        if (src.channels() == 1)
            return GrayImage(output, name);
        else
            return  ColorImage(output, name);
    }

    //添加盐噪声
    Image addSaltNoise(int n) {
        Mat output = saltAndPepperNoise(src, n, 255);
        string name = "盐噪声";
        if (src.channels() == 1)
            return GrayImage(output, name);
        else
            return  ColorImage(output, name);
    }

    //添加椒盐噪声
    Image addSaltAndPepperNoise(int n) {
        Mat output = saltAndPepperNoise(saltAndPepperNoise(src, n / 2, 0), n / 2, 255);
        string name = "椒盐噪声";
        if (src.channels() == 1)
            return GrayImage(output, name);
        else
            return  ColorImage(output, name);
    }

    //添加高斯分布
    Image addGaussianNoise(int avg = 15, int sd = 30) {
        //高斯噪声图像
        Mat noise = gaussianNoise(src, avg, sd);
        //噪声图像与原图像叠加
        Mat output = handleImageAddition(src, noise);
        string name = "高斯噪声";
        if (src.channels() == 1)
            return GrayImage(output, name);
        else
            return  ColorImage(output, name);
    }
};

class Filter {
protected:
    Mat img;
    bool color;

    //对图像进行滤波处理
    Mat filterProcess(const Mat& img, uchar kernel(const Mat&, int, int, int, int), int size, int q = 1) {
        Mat target = img.clone();
        vector<Mat> channels;
        int m = size / 2;
        split(target, channels);
        for (Mat ch : channels) {
            Mat src = ch.clone();
            for (int i = src.rows - size; i >= 0; i--)
                for (int j = src.cols - size; j >= 0; j--) {
                    ch.at<uchar>(i + m, j + m) = kernel(src, size, i, j, q);
                }
        }
        merge(channels, target);
        return target;
    }

public:
    Filter(Image img) {
        this->img = img.getImage();
        this->color = this->img.channels() != 1;
    }    

};

class MeanFilter :public Filter {
private:
    //算术均值计算
    static uchar arithmeticMeanKernel(const Mat& img, int size, int i, int j, int q) {
        double res = 0;
        for (int r = i + size - 1; r >= i; r--) {
            for (int c = j + size - 1; c >= j; c--) {
                res += img.at<uchar>(r, c);
            }
        }
        return (uchar)(res / ((double)size * size));
    }
    //几何均值计算
    static uchar geometricMeanKernel(const Mat& img, int size, int i, int j, int q) {
        double res = 1;
        for (int r = i + size - 1; r >= i; r--) {
            for (int c = j + size - 1; c >= j; c--) {
                res *= img.at<uchar>(r, c);
            }
        }
        return (uchar)(pow(res, 1.0 / ((double)size * size)));
    }
    //谐波平均值计算
    static uchar harmonicMeanKernel(const Mat& img, int size, int i, int j, int q) {
        double res = 0;
        for (int r = i + size - 1; r >= i; r--) {
            for (int c = j + size - 1; c >= j; c--) {
                res += 1.0 / ((double)img.at<uchar>(r, c) + 1);
            }
        }
        return (uchar)(((double)size * size) / res - 1);
    }
    //反谐波平均值计算
    static uchar antiHarmonicMeanKernel(const Mat& img, int size, int i, int j, int q) {
        double res1 = 0, res2 = 0;
        for (int r = i + size - 1; r >= i; r--) {
            for (int c = j + size - 1; c >= j; c--) {
                res1 += pow(img.at<uchar>(r, c), q + 1);
                res2 += pow(img.at<uchar>(r, c), q);
            }
        }
        return (uchar)(res1 / res2);
    }

public:
    MeanFilter(Image img) :Filter(img) {

    }
    //算术平均滤波
    void handleArithmeticMean(int size, string type = "") {
        string name = type + "算术平均";
        name += (char)(size + '0');
        name += '*';
        name += (char)(size + '0');
        openWindows(name, filterProcess(img, arithmeticMeanKernel, size));
    }
    //几何平均滤波
    void handleGeometricMean(int size, string type = "") {
        string name = type + "几何平均";
        name += (char)(size + '0');
        name += '*';
        name += (char)(size + '0');
        openWindows(name, filterProcess(img, geometricMeanKernel, size));
    }
    //谐波平均滤波
    void handleHarmonicMean(int size, string type = "") {
        string name = type + "谐波平均";
        name += (char)(size + '0');
        name += '*';
        name += (char)(size + '0');
        openWindows(name, filterProcess(img, harmonicMeanKernel, size));
    }
    //反谐波平均滤波
    void handleAntiHrithmeticMean(int size, int q, string type = "", bool  twice = false) {
        string name = type + "反谐波平均";
        name += (char)(size + '0');
        name += '*';
        name += (char)(size + '0');
        Mat target = filterProcess(img, antiHarmonicMeanKernel, size, q);
        if (twice)
            target = filterProcess(target, antiHarmonicMeanKernel, size, -q);
        openWindows(name, target);
    }

    static void show(string type, Image img, int size, int q = 1, bool twice = false) {
        MeanFilter mf(img);
        mf.handleArithmeticMean(size, type);
        mf.handleGeometricMean(size, type);
        mf.handleHarmonicMean(size, type);
        mf.handleAntiHrithmeticMean(size, q, type, twice);
    }
};

class MedianFilter :public Filter {
protected:
    //快速选择中值
    static int quickSelect(vector<uchar>& vec, int left, int right, int target) {
        int t = vec[left], l = left, r = right;
        bool isLeft = true;
        while (l < r) {
            if (isLeft) {
                while (l < r && vec[r] >= t)
                    r--;
                vec[l] = vec[r];
            }
            else {
                while (l < r && vec[l] < t)
                    l++;
                vec[r] = vec[l];
            }
            isLeft = !isLeft;
        }
        vec[l] = t;
        if (l < target)
            return quickSelect(vec, l + 1, right, target);
        else if (l > target)
            return quickSelect(vec, left, l - 1, target);
        return vec[l];
    }

private:
    //中值计算
    static uchar medianKernel(const Mat& img, int size, int i, int j, int q) {
        vector<uchar> values;
        for (int r = i + size - 1; r >= i; r--) {
            for (int c = j + size - 1; c >= j; c--) {
                values.push_back(img.at<uchar>(r, c));
            }
        }
        int cnt = size * size;
        return quickSelect(values, 0, cnt - 1, cnt / 2);
    }

public:
    MedianFilter(Image img) :Filter(img) {

    }
    void handleMedian(int size, string type = "") {
        string name = type + "中值滤波";
        name += (char)(size + '0');
        name += '*';
        name += (char)(size + '0');
        openWindows(name, filterProcess(img, medianKernel, size));
    }
    static void show(string type, Image img, int size1, int size2) {
        MedianFilter mf(img);
        mf.handleMedian(size1, type);
        mf.handleMedian(size2, type);
    }
};

class AdaptiveMeanFilter :public MeanFilter {
private:
    static uchar adaptiveMeanKernel(const Mat& img, int size, int i, int j, int q) {
        double avg = 0, ds = 0;
        uchar u = img.at<uchar>(i + size / 2, j + size / 2);
        for (int r = i + size - 1; r >= i; r--) {
            for (int c = j + size - 1; c >= j; c--) {
                avg += img.at<uchar>(r, c);
            }
        }
        avg /= size*size;
        for (int r = i + size - 1; r >= i; r--) {
            for (int c = j + size - 1; c >= j; c--) {
                ds += pow(avg - img.at<uchar>(r, c), 2);
            }
        }
        double rate = q / ds;
        if (rate > 1.0)
            rate = 1.0;
        return (int)(u - rate * (u - avg));
    }
public:
    AdaptiveMeanFilter(Image img) :MeanFilter(img) {

    }
    //自适应均值
    void handleAdaptiveMeanKernel(int size, int q, string type = "") {
        string name = type + "自适应均值";
        name += (char)(size + '0');
        name += '*';
        name += (char)(size + '0');
        Mat res = filterProcess(img, adaptiveMeanKernel, size, q);
        openWindows(name, res);
    }
    static void show(string type, Image img, int size, int q) {
        AdaptiveMeanFilter mf(img);
        mf.handleAdaptiveMeanKernel(size, q, type);
    }
};

class AdaptiveMedianFilter :MedianFilter {
private:
    //自适应中值计算
    static uchar adaptiveMedianKernel(const Mat& img, int size, int i, int j, int max_size) {
        vector<uchar> values;
        for (int r = i + size - 1; r >= i; r--) {
            for (int c = j + size - 1; c >= j; c--) {
                values.push_back(img.at<uchar>(r, c));
            }
        }
        int cnt = size * size;
        int mid = quickSelect(values, 0, cnt - 1, cnt / 2);
        int min = quickSelect(values, 0, cnt - 1, 0);
        int max = quickSelect(values, 0, cnt - 1, cnt - 1);
        if (mid < max && min < mid) {
            uchar u = img.at<uchar>(i + size / 2, j + size / 2);
            return (min < u&& u < max) ? u : mid;
        }
        if (size == max_size || i == 0 || j == 0 || i + size + 1 >= img.rows || j + size + 1 >= img.cols)
            return mid;
        return adaptiveMedianKernel(img, size + 2, i - 1, j - 1, max_size);
    }
public:
    AdaptiveMedianFilter(Image img) :MedianFilter(img) {
        
    }
    void handleAdaptiveMedianKernel(int size, int max_size, string type = "") {
        string name = type + "自适应中值";
        name += (char)(size + '0');
        name += '*';
        name += (char)(size + '0');
        Mat res = filterProcess(img, adaptiveMedianKernel, size, max_size);
        openWindows(name, res);
    }
    static void show(string type, Image img, int size, int max_size) {
        AdaptiveMedianFilter mf(img);
        mf.handleAdaptiveMedianKernel(size, max_size, type);
    }
};

int main() {

    string name = "test.jpg";
    //灰度图像 
    GrayImage gray_img(name);
    //灰度图噪声生成器
    Noise noise(gray_img);
    //高斯噪声
    Image gaussianNoise = noise.addGaussianNoise(0, 30);
    //胡椒噪声
    Image pepperNoise = noise.addPepperNoise(5000);
    //盐噪声
    Image saltNoise = noise.addSaltNoise(5000);
    //椒盐噪声
    Image saltAndPepperNoise = noise.addSaltAndPepperNoise(5000);

    //均值滤波
    MeanFilter::show("高斯-", gaussianNoise, 5, 2);
    MeanFilter::show("胡椒-", pepperNoise, 5, 2);
    MeanFilter::show("盐-", saltNoise, 5, -2);
    MeanFilter::show("椒盐-", saltAndPepperNoise, 5, 2, true);

    //中值滤波
    MedianFilter::show("高斯-", pepperNoise, 5, 9);
    MedianFilter::show("胡椒-", pepperNoise, 5, 9);
    MedianFilter::show("盐-", saltNoise, 5, 9);
    MedianFilter::show("椒盐-", saltAndPepperNoise, 5, 9);

    //自适应均值滤波
    AdaptiveMeanFilter::show("高斯-", gaussianNoise, 7, 10000);

    //自适应中值滤波
    AdaptiveMedianFilter::show("高斯-", pepperNoise, 3, 7);
    AdaptiveMedianFilter::show("胡椒-", pepperNoise, 3, 7);
    AdaptiveMedianFilter::show("盐-", saltNoise, 3, 7);
    AdaptiveMedianFilter::show("椒盐-", saltAndPepperNoise, 7, 7);

    //彩色图像
    ColorImage color_img(name);

    //彩色图噪声生成器
    Noise c_noise(color_img);
    //高斯噪声
    Image c_gaussianNoise = c_noise.addGaussianNoise(0, 30);
    //胡椒噪声
    Image c_pepperNoise = c_noise.addPepperNoise(5000);
    //盐噪声
    Image c_saltNoise = c_noise.addSaltNoise(5000);
    //椒盐噪声
    Image c_saltAndPepperNoise = c_noise.addSaltAndPepperNoise(5000);

    //均值滤波
    MeanFilter::show("高斯-", c_gaussianNoise, 5, 2);
    MeanFilter::show("胡椒-", c_pepperNoise, 5, 2);
    MeanFilter::show("盐-", c_saltNoise, 5, -2);
    MeanFilter::show("椒盐-", c_saltAndPepperNoise, 5, 2, true);
    return 0;
}

posted @ 2021-06-28 22:11  wsxuehao  阅读(1919)  评论(0)    收藏  举报