图像实验3- 空域滤波

一、实验目的

掌握利用模板对图像进行空域滤波操作,熟练掌握常用空域模板的使用。
1、掌握图像平滑的空域方法,熟练掌握均值模板和高斯模板平滑图像
2、掌握图像锐化的空域方法,熟练掌握 Laplacian、Robert、Sobel 模板锐化
图像
3、掌握利用高提升滤波算法对图像进行增强

二、实验内容

1、利用均值模板平滑灰度图像。
具体内容:利用 OpenCV 对图像像素进行操作,分别利用 33、55 和 99
尺寸的均值模板平滑灰度图像
2、利用高斯模板平滑灰度图像。
具体内容:利用 OpenCV 对图像像素进行操作,分别利用 3
3、55 和 99
尺寸的高斯模板平滑灰度图像
3、利用 Laplacian、Robert、Sobel 模板锐化灰度图像。
具体内容:利用 OpenCV 对图像像素进行操作,分别利用 Laplacian、Robert、
Sobel 模板锐化灰度图像
4、利用高提升滤波算法增强灰度图像。
具体内容:利用 OpenCV 对图像像素进行操作,设计高提升滤波算法增
强图像
5、利用均值模板平滑彩色图像。
具体内容:利用 OpenCV 分别对图像像素的 RGB 三个通道进行操作,利
用 33、55 和 99 尺寸的均值模板平滑彩色图像
6、利用高斯模板平滑彩色图像。
具体内容:利用 OpenCV 分别对图像像素的 RGB 三个通道进行操作,分
别利用 3
3、55 和 99 尺寸的高斯模板平滑彩色图像
7、利用 Laplacian、Robert、Sobel 模板锐化灰度图像。
具体内容:利用 OpenCV 分别对图像像素的 RGB 三个通道进行操作,分
别利用 Laplacian、Robert、Sobel 模板锐化彩色图像

三、实验完成情况

1、利用均值模板平滑灰度图像。

核心代码

//均值滤波处理函数
Mat handleMeanFilter(int size) {
    //输出图像
    Mat target = Mat::zeros(img.size(), img.type());
    //滤波器尺寸为size*size
    Size ksize(size, size);
    //锚点,即滤波器中进行处理的点,默认(-1,-1)表示滤波器中心点
    Point p(-1, -1);
    //边界处理方式,取默认值BORDER_DEFAULT
    int borderType = BORDER_DEFAULT;
    //均值处理
    blur(img, target, ksize, p, borderType);
    return target;
}

实现截图

3*3
5*5
7*7

2、利用高斯模板平滑灰度图像。

核心代码

//高斯滤波处理函数
Mat handleGaussianFilter(int size, double sigama) {
    //输出图像
    Mat target = Mat::zeros(img.size(), img.type());
    //滤波器尺寸为size*size,必须为正奇数
    Size ksize(size, size);
    //X轴方向上的高斯半径/高斯核,若取0.0则由opencv调用getGaussianKernel通过滤波器大小计算高斯核
    double sigmaX = sigama;
    //Y轴方向上的高斯半径/高斯核,由于需要满足对称性,因此X、Y轴上的高斯半径相等,取默认值0即与sigma相等
    double sigmaY = 0.0;
    //边界处理方式,取默认值BORDER_DEFAULT
    int borderType = BORDER_DEFAULT;
    //高斯平滑处理
    GaussianBlur(img, target, ksize, sigmaX, sigmaY, borderType);
    return target;
}

实现截图

3*3
5*5
7*7

3、利用 Laplacian、Robert、Sobel 模板锐化灰度图像。

核心代码

//锐化图像叠加,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;
}

//Laplacian拉普拉斯滤波处理函数
Mat handleLaplacianFilter(int size) {
    //输出图像
    Mat target = Mat::zeros(img.size(), img.type());
    //图像深度,输入为CV_8U,为了防止溢出(>255/<0)采用CV_8U,也可使用-1自动选择
    int ddepth = CV_16S;
    //拉普拉斯核的大小,必须为正奇数
    int ksize = size;
    //拉普拉斯核导数计算结果的缩放系数,此处使用默认值1,表示不进行缩放,作用类似于对数变换
    double scale = 1.0;
    //拉普拉斯计算结果的偏移量,默认为0,不进行偏移
    double delta = 0.0;
    //边界处理方式,取默认值BORDER_DEFAULT
    int borderType = BORDER_DEFAULT;
    //拉普拉斯锐化处理
    Laplacian(img, target, ddepth, ksize, scale, delta, borderType);
    //对图像进行标定处理
    convertScaleAbs(target, target);
    return target;
}

//Robert模版处理函数
Mat handleRobertFilter() {
    //输出图像
    Mat target = img.clone();
    double min_value, max_value;
    vector<Mat> target_channels;
    split(target, target_channels);
    //手动计算robert算子
    for (int c = 0; c < target_channels.size(); c++) {
        Mat curr = target_channels[c];
        for (int i = 0; i < curr.rows - 1; i++)
            for (int j = 0; j < curr.cols - 1; j++)
                curr.at<uchar>(i, j) = saturate_cast<uchar>(abs(curr.at<uchar>(i + 1, j + 1) - curr.at<uchar>(i, j)) + abs(curr.at<uchar>(i + 1, j) - curr.at<uchar>(i, j + 1)));
        //区间缩放到 [0,255]
        minMaxLoc(curr, &min_value, &max_value);
        curr.convertTo(curr, CV_8U, 255.0 / (max_value - min_value), -255.0 * min_value / (max_value - min_value));
    }
    merge(target_channels, target);
    return target;
}

//Sobel锐化处理函数
Mat handleSobelFilter(int size, int x, int y, bool useScharr) {
    if (size != 3)
        useScharr = false;
    //图像深度,输入为CV_8U,为了防止溢出(>255/<0)采用CV_8U,也可使用-1自动选择
    int ddepth = CV_16S;
    //sobel核的大小,必须为正奇数
    int ksize = size;
    //x方向上的阶数,即x方向上的sobel算子的中心权值
    int dx = useScharr ? 1 : x;
    //y方向上的阶数,即y方向上的sobel算子的中心权值
    int dy = useScharr ? 1 : y;
    //sobel核导数计算结果的缩放系数,此处使用默认值1,表示不进行缩放,作用类似于对数变换
    double scale = 1.0;
    //sobel计算结果的偏移量,默认为0,不进行偏移
    double delta = 0.0;
    //边界处理方式,取默认值BORDER_DEFAULT
    int borderType = BORDER_DEFAULT;
    //锐化处理
    Mat target_x = Mat::zeros(img.size(), img.type());
    Mat target_y = Mat::zeros(img.size(), img.type());
    if (useScharr) {
        Scharr(img, target_x, ddepth, dx, 0, scale, delta, borderType);
        Scharr(img, target_y, ddepth, 0, dy, scale, delta, borderType);
    }
    else {
        Sobel(img, target_x, ddepth, dx, 0, ksize, scale, delta, borderType);
        Sobel(img, target_y, ddepth, 0, dy, ksize, scale, delta, borderType);
    }
    //对图像进行标定处理
    convertScaleAbs(target_x, target_x);
    convertScaleAbs(target_y, target_y);
    Mat target;
    addWeighted(target_x, 0.5, target_y, 0.5, 0, target);
    return target;
}

实现截图

Laplace图像
Laplace锐化后图像
Robert图像
Robert锐化后图像
Sobel图像
Sobel锐化后图像

4、利用高提升滤波算法增强灰度图像。

核心代码

//高提升滤波
Mat handleHighLiftFiltering(Mat filtered, double k) {
    return  k * handleImageAddition(this->img, filtered, false);
}
//高提升滤波
void highLiftFiltering(int type = 0, int k = 1) {
    Mat filtered;
    switch (type)
    {
    case 1:
        filtered = handleMeanFilter(3);
        break;
    default:
        filtered = handleGaussianFilter(3,0);
        break;
    }
    Mat highLift = handleHighLiftFiltering(filtered, k);
    openWindows("高提升模版", highLift);
    openWindows("高提升锐化后", handleImageAddition(this->img, highLift));
}

实现截图


5、利用均值模板平滑彩色图像。

核心代码

同灰度图像均值模版处理,已包含彩色图像的处理操作。

实现截图

3*3
5*5
7*7

6、利用高斯模板平滑彩色图像。

核心代码

同灰度图像高斯模版处理,已包含彩色图像的处理操作。

实现截图

3*3
5*5
7*7

7、利用 Laplacian、Robert、Sobel 模板锐化彩色图像。

核心代码

代码同灰度图像锐化处理,已包含彩色图像的处理操作。

实现截图

Laplace图像
Laplace锐化后图像
Robert图像
Robert锐化后图像
Sobel图像
Sobel锐化后图像

四、实验中的问题

  1. 不同于前两次实验,这次实验对于图像的操作更加细化,更具体,理解起来也更难一些。
  2. 由于牵扯到数学计算,因此难度较之前要大一些。
  3. 对于滤波操作还不是很熟悉,实际操作时碰到很多诸如是否需要划分通道、是否需要进行标定、图像的叠加方式等问题。

五、实验结果

源码

lab3.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(string path) {
        img = openImage(path, IMREAD_COLOR);
        openWindows("彩色图像", img);
    }
};

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

class SpatialFiltering {
private:
    Mat img;
    bool color;

    //均值滤波处理函数
    Mat handleMeanFilter(int size) {
        //输出图像
        Mat target = Mat::zeros(img.size(), img.type());
        //滤波器尺寸为size*size
        Size ksize(size, size);
        //锚点,即滤波器中进行处理的点,默认(-1,-1)表示滤波器中心点
        Point p(-1, -1);
        //边界处理方式,取默认值BORDER_DEFAULT
        int borderType = BORDER_DEFAULT;
        //均值处理
        blur(img, target, ksize, p, borderType);
        return target;
    }

    //高斯滤波处理函数
    Mat handleGaussianFilter(int size, double sigama) {
        //输出图像
        Mat target = Mat::zeros(img.size(), img.type());
        //滤波器尺寸为size*size,必须为正奇数
        Size ksize(size, size);
        //X轴方向上的高斯半径/高斯核,若取0.0则由opencv调用getGaussianKernel通过滤波器大小计算高斯核
        double sigmaX = sigama;
        //Y轴方向上的高斯半径/高斯核,由于需要满足对称性,因此X、Y轴上的高斯半径相等,取默认值0即与sigma相等
        double sigmaY = 0.0;
        //边界处理方式,取默认值BORDER_DEFAULT
        int borderType = BORDER_DEFAULT;
        //高斯平滑处理
        GaussianBlur(img, target, ksize, sigmaX, sigmaY, borderType);
        return target;
    }

    //锐化图像叠加,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;
    }

    //Laplacian拉普拉斯滤波处理函数
    Mat handleLaplacianFilter(int size) {
        //输出图像
        Mat target = Mat::zeros(img.size(), img.type());
        //图像深度,输入为CV_8U,为了防止溢出(>255/<0)采用CV_8U,也可使用-1自动选择
        int ddepth = CV_16S;
        //拉普拉斯核的大小,必须为正奇数
        int ksize = size;
        //拉普拉斯核导数计算结果的缩放系数,此处使用默认值1,表示不进行缩放,作用类似于对数变换
        double scale = 1.0;
        //拉普拉斯计算结果的偏移量,默认为0,不进行偏移
        double delta = 0.0;
        //边界处理方式,取默认值BORDER_DEFAULT
        int borderType = BORDER_DEFAULT;
        //拉普拉斯锐化处理
        Laplacian(img, target, ddepth, ksize, scale, delta, borderType);
        //对图像进行标定处理
        convertScaleAbs(target, target);
        return target;
    }

    //Robert模版处理函数
    Mat handleRobertFilter() {
        //输出图像
        Mat target = img.clone();
        double min_value, max_value;
        vector<Mat> target_channels;
        split(target, target_channels);
        //手动计算robert算子
        for (int c = 0; c < target_channels.size(); c++) {
            Mat curr = target_channels[c];
            for (int i = 0; i < curr.rows - 1; i++)
                for (int j = 0; j < curr.cols - 1; j++)
                    curr.at<uchar>(i, j) = saturate_cast<uchar>(abs(curr.at<uchar>(i + 1, j + 1) - curr.at<uchar>(i, j)) + abs(curr.at<uchar>(i + 1, j) - curr.at<uchar>(i, j + 1)));
            //区间缩放到 [0,255]
            minMaxLoc(curr, &min_value, &max_value);
            curr.convertTo(curr, CV_8U, 255.0 / (max_value - min_value), -255.0 * min_value / (max_value - min_value));
        }
        merge(target_channels, target);
        return target;
    }

    //Sobel锐化处理函数
    Mat handleSobelFilter(int size, int x, int y, bool useScharr) {
        if (size != 3)
            useScharr = false;
        //图像深度,输入为CV_8U,为了防止溢出(>255/<0)采用CV_8U,也可使用-1自动选择
        int ddepth = CV_16S;
        //sobel核的大小,必须为正奇数
        int ksize = size;
        //x方向上的阶数,即x方向上的sobel算子的中心权值
        int dx = useScharr ? 1 : x;
        //y方向上的阶数,即y方向上的sobel算子的中心权值
        int dy = useScharr ? 1 : y;
        //sobel核导数计算结果的缩放系数,此处使用默认值1,表示不进行缩放,作用类似于对数变换
        double scale = 1.0;
        //sobel计算结果的偏移量,默认为0,不进行偏移
        double delta = 0.0;
        //边界处理方式,取默认值BORDER_DEFAULT
        int borderType = BORDER_DEFAULT;
        //锐化处理
        Mat target_x = Mat::zeros(img.size(), img.type());
        Mat target_y = Mat::zeros(img.size(), img.type());
        if (useScharr) {
            Scharr(img, target_x, ddepth, dx, 0, scale, delta, borderType);
            Scharr(img, target_y, ddepth, 0, dy, scale, delta, borderType);
        }
        else {
            Sobel(img, target_x, ddepth, dx, 0, ksize, scale, delta, borderType);
            Sobel(img, target_y, ddepth, 0, dy, ksize, scale, delta, borderType);
        }
        //对图像进行标定处理
        convertScaleAbs(target_x, target_x);
        convertScaleAbs(target_y, target_y);
        Mat target;
        addWeighted(target_x, 0.5, target_y, 0.5, 0, target);
        return target;
    }

    //高提升滤波
    Mat handleHighLiftFiltering(Mat filtered, double k) {
        return  k * handleImageAddition(this->img, filtered, false);
    }

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

    //均值滤波
    void meanFilter(int size = 3) {
        string name = string("均值滤波") + (char)(size + '0') + '*' + (char)(size + '0');
        openWindows(name, handleMeanFilter(size));
    }

    //高斯滤波
    void gaussianFilter(int size = 3, double sigma = 0.0) {
        if (!(size & 1) || size <= 0) {
            cout << "高斯滤波器大小必须为正奇数" << endl;
            return;
        }
        string name = string("高斯滤波") + (char)(size + '0') + '*' + (char)(size + '0');
        openWindows(name, handleGaussianFilter(size, sigma));
    }

    //拉普拉斯滤波
    void laplacian(int size = 3) {
        if (!(size & 1) || size <= 0) {
            cout << "滤波器大小必须为正奇数" << endl;
            return;
        }
        Mat laplace = handleLaplacianFilter(size);
        openWindows("Laplace模版", laplace);
        openWindows("Laplace锐化后", handleImageAddition(this->img, laplace));
    }

    //robert滤波
    void robert() {
        Mat robert = handleRobertFilter();
        openWindows("Robert模版", robert);
        openWindows("Robert锐化后", handleImageAddition(this->img, robert));
    }

    //Sobel滤波
    void sobel(int size = 3, bool useScharr = true, int dx = 1, int dy = 1) {
        if (!(size & 1) || size <= 0) {
            cout << "滤波器大小必须为正奇数" << endl;
            return;
        }
        string name = useScharr ? "Scharr" : "Sobel";
        Mat sobel = handleSobelFilter(size, dx, dy, useScharr);
        openWindows(name + "模版", sobel);
        openWindows(name + "锐化后", handleImageAddition(this->img, sobel));
    }

    //高提升滤波
    void highLiftFiltering(int type = 0, int k = 1) {
        Mat filtered;
        switch (type)
        {
        case 1:
            filtered = handleMeanFilter(3);
            break;
        default:
            filtered = handleGaussianFilter(3,0);
            break;
        }
        Mat highLift = handleHighLiftFiltering(filtered, k);
        openWindows("高提升模版", highLift);
        openWindows("高提升锐化后", handleImageAddition(this->img, highLift));
    }

};


int main() {

    string name = "test.jpg";

    //灰度图像 
    GrayImage gray_img(name);
   
    //滤波对象
    SpatialFiltering gray_filter(gray_img);
 
    //均值滤波
    gray_filter.meanFilter(3);
    gray_filter.meanFilter(5);
    gray_filter.meanFilter(7);

    //高斯滤波
    gray_filter.gaussianFilter(3);
    gray_filter.gaussianFilter(5);
    gray_filter.gaussianFilter(7);

    //laplacian锐化
    gray_filter.laplacian();

    //robert锐化
    gray_filter.robert();

    //sobel锐化
    gray_filter.sobel(3,true);
    //高提升锐化
    gray_filter.highLiftFiltering(0,5);

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

    //滤波对象
    SpatialFiltering color_filter(color_img);
    
    //均值滤波
    color_filter.meanFilter(3);
    color_filter.meanFilter(5);
    color_filter.meanFilter(7);

    //高斯滤波
    color_filter.gaussianFilter(3);
    color_filter.gaussianFilter(5);
    color_filter.gaussianFilter(7);

    //laplacian锐化
    color_filter.laplacian();

    //robert锐化
    color_filter.robert();

    //sobel锐化
    color_filter.sobel(3, false);

    return 0;
}

posted @ 2021-06-22 20:55  wsxuehao  阅读(1450)  评论(0)    收藏  举报