实验二 图像的空间域增强

实验二 图像的空间域增强

一、实验目的

  1. 进一步理解图像平滑和图像锐化等空间域增强方法的原理。
  2. 了解图像平滑和图像锐化的效果和作用。
  3. 掌握图像模板运算的流程。

二、实验内容

编程实现车牌图像的去噪和锐化处理。首先对图像去噪,分别采用均值滤波器和中值滤波器进行处理,比较两种滤波器效果;然后对去噪后的图像进行锐化,分别采用一阶导数和二阶导数,比较锐化效果。

三、实验报告要求

1.给出简要的设计思路(原理)

2.给出主要代码

3.给出实验结果的屏幕截图

  1. 实验过程中遇到的主要问题、心得体会或建议

四、报告内容

1.均值滤波:需要构建一个全为1的卷积核,对原图像每个像素进行卷积核计算求和后平均化。注意求和过程中可能会超出类型的最大范围,因此要扩大类型,采用Vec3d sum来存储,vec3f也可以

void averageFilter(const Mat& img, int kernelSize) {

    Mat averageFilter;

    averageFilter.create(img.size(), img.type());

    if (kernelSize % 2 == 0) kernelSize += 1; // 仅当kernelSize为偶数时增加1

    int init = kernelSize / 2;

    for (int y = init; y < img.rows - init; y++) {

        for (int x = init; x < img.cols - init; x++) {

            Vec3d sum = Vec3d(0, 0, 0);

            for (int i = -init; i <= init; i++) {

                for (int j = -init; j <= init; j++) {

                    Vec3b pixel = img.at<Vec3b>(y + i, x + j);

                    sum[0] += pixel[0];

                    sum[1] += pixel[1];

                    sum[2] += pixel[2];

                }

            }

            averageFilter.at<Vec3b>(y, x)[0] = static_cast<uchar>(sum[0] / (kernelSize * kernelSize));

            averageFilter.at<Vec3b>(y, x)[1] = static_cast<uchar>(sum[1] / (kernelSize * kernelSize));

            averageFilter.at<Vec3b>(y, x)[2] = static_cast<uchar>(sum[2] / (kernelSize * kernelSize));

        }

    }

    namedWindow("img");

    imshow("img", img);

    namedWindow("均值滤波");

    imshow("均值滤波", averageFilter);

}

2.中值滤波:对这个核内的每个像素排序,取中值作为最终原图像某像素的值,需注意的是由于排序只能对三通道某一个通道的具体值进行排序,因此要对每个通道都进行一遍取中值才能保持彩色图像

Mat mediumFilter(const Mat& img, int kernelSize) {

    Mat mediumfilter;

    mediumfilter.create(img.size(), img.type());

    if (kernelSize % 2 == 0) kernelSize += 1; // 仅当kernelSize为偶数时增加1

    int init = kernelSize / 2;

    for (int y = init; y < img.rows - init; y++) {

        for (int x = init; x < img.cols - init; x++) {

            vector<uchar> pixels0;

            vector<uchar> pixels1;

            vector<uchar> pixels2;

            for(int i=-init;i<=init;i++)

                for (int j = -init; j <= init; j++) {

                    pixels0.push_back(img.at<Vec3b>(y + i, x + j)[0]);

                    pixels1.push_back(img.at<Vec3b>(y + i, x + j)[1]);

                    pixels2.push_back(img.at<Vec3b>(y + i, x + j)[2]);

                }

            sort(pixels0.begin(), pixels0.end());

            sort(pixels1.begin(), pixels1.end());

            sort(pixels2.begin(), pixels2.end());

            uchar mediumpixel0 = pixels0[pixels0.size() / 2];

            uchar mediumpixel1 = pixels1[pixels1.size() / 2];

            uchar mediumpixel2 = pixels2[pixels2.size() / 2];

            mediumfilter.at<Vec3b>(y, x)[0] = mediumpixel0;

            mediumfilter.at<Vec3b>(y, x)[1] = mediumpixel1;

            mediumfilter.at<Vec3b>(y, x)[2] = mediumpixel2;

        }

    }

    namedWindow("中值滤波");

    imshow("中值滤波", mediumfilter);

    return mediumfilter;

}

3.sobel算子锐化:利用sobel算子核计算原图像的灰度图的x,y方向的梯度值最后求出最终梯度值,通过这两个方向的梯度,可以计算出边缘的方向和强度。对该梯度值要进行格式转换,然后与原图像加和,此处加和也要注意数值范围超限,做好类型转换

void sobelOperatorSharpening(const Mat& img) {

    Mat gray_img;

    cvtColor(img, gray_img, COLOR_BGR2GRAY); // 转换为灰度图

    Mat grad_img = Mat::zeros(gray_img.size(), CV_32F); // 创建一个用于存储梯度幅值的图像

    //sobel算子核

    int sobel_x[3][3] = { {-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1} };

    int sobel_y[3][3] = { {-1, -2, -1}, {0, 0, 0}, {1, 2, 1} };

    for (int y = 1; y < img.rows - 1; y++) {

        for (int x = 1; x < img.cols - 1; x++) {

            float grad_x = 0.0, grad_y = 0.0;

            for (int i = -1; i <= 1; i++) {

                for (int j = -1; j <= 1; j++) {

                    uchar pixel = gray_img.at<uchar>(y + i, x + j);

                    grad_x += pixel * sobel_x[i + 1][j + 1];

                    grad_y += pixel * sobel_y[i + 1][j + 1];

                }

            }

            grad_img.at<float>(y, x) = sqrt(grad_x * grad_x + grad_y * grad_y);

        }

    }

    // 转换梯度图格式

    Mat grad_img_uchar;

    normalize(grad_img, grad_img_uchar, 0, 255, NORM_MINMAX);

    grad_img_uchar.convertTo(grad_img_uchar, CV_8U);

    // 与原图片相加实现边缘增强

    Mat output = img.clone();

    for (int y = 0; y < img.rows; y++) {

        for (int x = 0; x < img.cols; x++) {

            for (int i = 0; i < 3; i++) {

                int val = img.at<Vec3b>(y, x)[i] + grad_img_uchar.at<uchar>(y, x);

                //防止加和后uchar数值超过255做的类型转换

                output.at<Vec3b>(y, x)[i] = saturate_cast<uchar>(val);

            }

        }

    }

    namedWindow("Sobel算子锐化");

    imshow("Sobel算子锐化", output);

}                  

4. 拉普拉斯算子锐化:和上面的sobel算子锐化本质上的区别就在于它不需要关注梯度值,是二阶导数的计算,只需要一个核

void LaplacianSharpening(const Mat& img) {

    Mat gray_img,output0,output1,output2;

    cvtColor(img, gray_img, COLOR_BGR2GRAY); // 转换为灰度图

    output0= Mat::zeros(gray_img.size(), CV_32F);

    // 拉普拉斯

    int kernel0[3][3] = {

        {1, 1, 1},

        {1, -8, 1},

        {1, 1, 1}

    };

    int kernel1[3][3] = {

        {0, 1, 0},

        {1, -4, 1},

        {0, 1, 0}

    };

    for (int y = 1; y < img.rows - 1; y++) {

        for (int x = 1; x < img.cols - 1; x++) {

            float sum = 0.0;

            for (int k = -1; k <= 1; k++) {

                for (int l = -1; l <= 1; l++) {

                    sum += gray_img.at<uchar>(y + k, x + l) * kernel1[k + 1][l + 1];

                }

            }

            output0.at<float>(y, x) = sum;

        }

    }

    convertScaleAbs(output0, output1);// 转换为8位图像

    cvtColor(output1, output2, COLOR_GRAY2BGR); // 将灰度图转换为三通道图像以匹配原图

    output2 = img + output2;

    namedWindow("拉普拉斯算子锐化");

    imshow("拉普拉斯算子锐化", output2);

}          

实验结果:

可以看出,对于原图像有很多噪点的椒盐噪声利用中值滤波处理的最好,画面尤为干净。两种算子的锐化结果对比看,拉普拉斯锐化的噪点更加明显

#include <opencv2/opencv.hpp>
#include <algorithm>
using namespace cv;
using namespace std;

void averageFilter(const Mat& img, int kernelSize) {
    Mat averageFilter;
    averageFilter.create(img.size(), img.type());
    if (kernelSize % 2 == 0) kernelSize += 1; // 仅当kernelSize为偶数时增加1
    int init = kernelSize / 2;
    for (int y = init; y < img.rows - init; y++) {
        for (int x = init; x < img.cols - init; x++) {
            Vec3d sum = Vec3d(0, 0, 0); 
            for (int i = -init; i <= init; i++) {
                for (int j = -init; j <= init; j++) {
                    Vec3b pixel = img.at<Vec3b>(y + i, x + j);
                    sum[0] += pixel[0];
                    sum[1] += pixel[1];
                    sum[2] += pixel[2];
                }
            }
            averageFilter.at<Vec3b>(y, x)[0] = static_cast<uchar>(sum[0] / (kernelSize * kernelSize));
            averageFilter.at<Vec3b>(y, x)[1] = static_cast<uchar>(sum[1] / (kernelSize * kernelSize));
            averageFilter.at<Vec3b>(y, x)[2] = static_cast<uchar>(sum[2] / (kernelSize * kernelSize));
        }
    }
    namedWindow("img");
    imshow("img", img);
    namedWindow("均值滤波");
    imshow("均值滤波", averageFilter);
}
Mat mediumFilter(const Mat& img, int kernelSize) {
    Mat mediumfilter;
    mediumfilter.create(img.size(), img.type());
    if (kernelSize % 2 == 0) kernelSize += 1; // 仅当kernelSize为偶数时增加1
    int init = kernelSize / 2;
    for (int y = init; y < img.rows - init; y++) {
        for (int x = init; x < img.cols - init; x++) {
            vector<uchar> pixels0;
            vector<uchar> pixels1;
            vector<uchar> pixels2;
            for(int i=-init;i<=init;i++)
                for (int j = -init; j <= init; j++) {
                    pixels0.push_back(img.at<Vec3b>(y + i, x + j)[0]);
                    pixels1.push_back(img.at<Vec3b>(y + i, x + j)[1]);
                    pixels2.push_back(img.at<Vec3b>(y + i, x + j)[2]);
                }
            sort(pixels0.begin(), pixels0.end());
            sort(pixels1.begin(), pixels1.end());
            sort(pixels2.begin(), pixels2.end());
            uchar mediumpixel0 = pixels0[pixels0.size() / 2];
            uchar mediumpixel1 = pixels1[pixels1.size() / 2];
            uchar mediumpixel2 = pixels2[pixels2.size() / 2];
            mediumfilter.at<Vec3b>(y, x)[0] = mediumpixel0;
            mediumfilter.at<Vec3b>(y, x)[1] = mediumpixel1;
            mediumfilter.at<Vec3b>(y, x)[2] = mediumpixel2;
        }
    }
    namedWindow("中值滤波");
    imshow("中值滤波", mediumfilter);
    return mediumfilter;
}
void sobelOperatorSharpening(const Mat& img) {
    Mat gray_img;
    cvtColor(img, gray_img, COLOR_BGR2GRAY); // 转换为灰度图
    Mat grad_img = Mat::zeros(gray_img.size(), CV_32F); // 创建一个用于存储梯度幅值的图像
    //sobel算子核
    int sobel_x[3][3] = { {-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1} };
    int sobel_y[3][3] = { {-1, -2, -1}, {0, 0, 0}, {1, 2, 1} };
    for (int y = 1; y < img.rows - 1; y++) {
        for (int x = 1; x < img.cols - 1; x++) {
            float grad_x = 0.0, grad_y = 0.0;
            for (int i = -1; i <= 1; i++) {
                for (int j = -1; j <= 1; j++) {
                    uchar pixel = gray_img.at<uchar>(y + i, x + j);
                    grad_x += pixel * sobel_x[i + 1][j + 1];
                    grad_y += pixel * sobel_y[i + 1][j + 1];
                }
            }
            grad_img.at<float>(y, x) = sqrt(grad_x * grad_x + grad_y * grad_y);
        }
    }
    // 转换梯度图格式
    Mat grad_img_uchar;
    normalize(grad_img, grad_img_uchar, 0, 255, NORM_MINMAX);
    grad_img_uchar.convertTo(grad_img_uchar, CV_8U);
    // 与原图片相加实现边缘增强
    Mat output = img.clone();
    for (int y = 0; y < img.rows; y++) {
        for (int x = 0; x < img.cols; x++) {
            for (int i = 0; i < 3; i++) {
                int val = img.at<Vec3b>(y, x)[i] + grad_img_uchar.at<uchar>(y, x);
                //防止加和后uchar数值超过255做的类型转换
                output.at<Vec3b>(y, x)[i] = saturate_cast<uchar>(val);
            }
        }
    }
    namedWindow("Sobel算子锐化");
    imshow("Sobel算子锐化", output);
}
void LaplacianSharpening(const Mat& img) {
    Mat gray_img,output0,output1,output2;
    cvtColor(img, gray_img, COLOR_BGR2GRAY); // 转换为灰度图
    output0= Mat::zeros(gray_img.size(), CV_32F);
    // 拉普拉斯核
    int kernel0[3][3] = {
        {1, 1, 1},
        {1, -8, 1},
        {1, 1, 1}
    };
    int kernel1[3][3] = {
        {0, 1, 0},
        {1, -4, 1},
        {0, 1, 0}
    };
    for (int y = 1; y < img.rows - 1; y++) {
        for (int x = 1; x < img.cols - 1; x++) {
            float sum = 0.0;
            for (int k = -1; k <= 1; k++) {
                for (int l = -1; l <= 1; l++) {
                    sum += gray_img.at<uchar>(y + k, x + l) * kernel1[k + 1][l + 1];
                }
            }
            output0.at<float>(y, x) = sum;
        }
    }
    convertScaleAbs(output0, output1);// 转换为8位图像
    cvtColor(output1, output2, COLOR_GRAY2BGR); // 将灰度图转换为三通道图像以匹配原图
    output2 = img + output2;
    namedWindow("拉普拉斯算子锐化");
    imshow("拉普拉斯算子锐化", output2);
}
int main() {
	Mat	img = imread("H://文档//Tencent Files//ASE//噪声图像.bmp");
    Mat img1;
	averageFilter(img, 3);
    img1=mediumFilter(img, 3);
    
    sobelOperatorSharpening(img1);
    LaplacianSharpening(img1);
    waitKey(0);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值