记录二Opencv和C++实现高斯核生成和卷积操作

图像处理中的卷积运算

所谓对图像进行卷积运算就是使用卷积核(卷积模板,一般为n*n的奇数方框)在数字图像中进行移动,如图所示,图像中的卷积核大小为3*3的方阵,在图像的滑动过程中卷积核中对应的数值与图像中的数值相乘并对九个数字求和得出中心像素的数值,然后继续滑动生成新的中心像素值(注意:已经计算出的中心像素值并不参与下一次的卷积运算),滑动的距离我们称为步长通常为1.当整幅图像都进行这样的操作后将会生成一幅根据卷积运算得出的新的图像.

边界填充问题

图像处理中二维离散卷积的运算比较直观也很好理解,仔细观察上述描述可能会发现一个问题,卷积运算后的新图像将会比原图像的大小要小,具体小多少和卷积核大小有关,卷积核尺寸越大新生成的图像越小,为了避免上述问题我们通常在卷积操作之前会扩充原图像的大小,或者说填充图像的边界,使其在经历卷积运算后尺寸任然和原图像大小一样.具体的填充策略有很多,举几个例子.

零填充:用0填充图像的边界,卷积处理之后图像容易出现黑边

00000
01230
04560
07890
00000

常数填充:选定常数进行填充

22222
21232
24562
27892
20002

 

夹取填充:复制或夹取图像边缘

11233
11233
44566
77899
77899

 

此外还有很多种填充方式在此就不一一列举了.

卷积核

应用不同的卷积核对图像进行卷积运算会得出不同的效果.以下为一些常用的卷积核

均值滤波

1/91/91/9
1/91/91/9
1/91/91/9

原图                                                  滤波后

图像锐化

-1-1-1
-19-1
-1-1-1

原图                                                  滤波后

Sobel

-101
-202
-101

原图                                                滤波后

高斯核

高斯核用于图像的模糊处理,和均值滤波的作用类似.但不同的是高斯滤波进行的是一种加权平均,越靠近核中心的数值权重越大

例如下表所示3*3大小σ为10的一个高斯核

0.110741
0.111296
0.110741
0.111296
0.111854
0.111296
0.110741
0.111296
0.110741

原图                                                       处理后

其中σ越大模糊效果越显著

获取高斯核

一维高斯函数数学表达式,σ为1为标准正态分布

二维高斯函数表达式:

根据二维高斯函数可以写出获取核函数的相关代码

//size核大小,越大平滑效果越明显
//sigma越大平滑效果越明显


Mat GetGaussianKernel( const int size,const double sigma)
{
    double **gaus=new double *[size];
    for(int i=0;i<size;i++)
    {
        gaus[i]=new double[size];  //动态生成矩阵
    }
    Mat Kernel(size,size,CV_64FC1,Scalar(0));
    const double PI=4.0*atan(1.0); //圆周率π赋值
    int center=size/2;
    double sum=0;
    for(int i=0;i<size;i++)
    {
        for(int j=0;j<size;j++)
        {
            gaus[i][j]=(1/(2*PI*sigma*sigma))*exp(-((i-center)*(i-center)+(j-center)*(j-center))/(2*sigma*sigma));//二维高斯函数
            sum+=gaus[i][j];
        }
    }


    for(int i=0;i<size;i++)
    {
        for(int j=0;j<size;j++)
        {
            gaus[i][j]/=sum;
            cout<<gaus[i][j]<<",  ";
            Kernel.at<double>(i,j) = gaus[i][j];//将数组转换为Mat

        }
        cout<<endl<<endl;
    }



     cout << "kernel[0][0] = " << dec << Kernel.at<double>(0,1) << endl;
    imshow("kernel", Kernel);
    return Kernel;
}

卷积运算的C++代码实现

void Convlution(Mat &input_img,Mat  OutputImage, Mat Kernel)
{

    int border_x = Kernel.rows/2;
    int border_y = Kernel.cols/2;
    for(int img_y = 0; img_y < input_img.cols - 2*border_y; img_y++)
    {
        for(int img_x = 0; img_x < input_img.rows - 2*border_x; img_x++)
        {
            int end_value = 0;
            for(int kernel_y = 0; kernel_y < Kernel.cols; kernel_y++)
            {
                for(int kernel_x = 0; kernel_x < Kernel.rows; kernel_x++)
                {
                    int img_value = input_img.at<uchar>(img_y+kernel_y, img_x+kernel_x);
                    double Kernel_value = Kernel.at<double>(kernel_y, kernel_x);
                    end_value += img_value*Kernel_value;
                }
            }
            if(end_value > 256)
                end_value = 255;
            else if (end_value < 0)
                end_value = 0;
            OutputImage.at<char>(img_y+border_y, img_x+border_x) = (uchar)end_value;
//            OutputImage.at<uchar>(img_y+border_y, img_x+border_x) = saturate_cast<uchar>((int)end_value);

        }
    }

    input_img = OutputImage;

}

关于卷积运算部分讲解

老实说这代码我是参考了chaibubble的博客https://blog.csdn.net/chaipp0607/article/details/78277395修改了一点得出的

代码的主要思路是通过四次嵌套的循环完成卷积运算,最外的两层循环完成遍历图像的操作,内层两个循环完成对卷积核的遍历.

其中


                    int img_value = input_img.at<uchar>(img_y+kernel_y, img_x+kernel_x);
                    double Kernel_value = Kernel.at<double>(kernel_y, kernel_x);
                    end_value += img_value*Kernel_value;

img_value为图像中mask所对应的像素值,通过与图像坐标+核函数坐标(通过内层循环可获取一次卷积运算中所需要的所有的对应像素值)实现获取对应与核函数的像素值,Kernel_value显然就是核函数的数值,end_value为最后中心像素值.

if(end_value > 256)
                end_value = 255;
            else if (end_value < 0)
                end_value = 0;
            OutputImage.at<char>(img_y+border_y, img_x+border_x) = (uchar)end_value;
//            OutputImage.at<uchar>(img_y+border_y, img_x+border_x) = saturate_cast<uchar>((int)end_value);

此部分为最终像素值截取,防止溢出(超出255或小于0),注释掉的代码可取代以上操作,效果相同

所有测试代码

#include<opencv.hpp>
#include<iostream>

using namespace cv;
using namespace std ;

Mat src,gray,dst;
Mat GetGaussianKernel( const int size,const double sigma);
void Convlution(Mat &input_img,Mat  OutputImage, Mat Kernel);
Mat Kernel_test_3_3 = (Mat_<double>(3,3) <<
                       0.110741,  0.111296,  0.110741,

                       0.111296,  0.111854,  0.111296,

                       0.110741,  0.111296,  0.110741);
Mat Kernel_average_3_3 = (Mat_<double>(3,3) <<
                       0.111111,   0.111111,   0.111111,

                        0.111111,   0.111111,   0.111111,

                       0.111111,   0.111111,   0.111111);
Mat Kernel_ruihua_3_3 = (Mat_<double>(3,3) <<
                       -1,   -1,   -1,

                       -1,   9,   -1,

                       -1,   -1,   -1);
Mat Kernel_Sobel = (Mat_<double>(3,3) <<
                       -1,   0,   1,

                       -2,   0,   2,

                       -1,   0,   1);
int main(int argc, char *argv[])
{

    cout.setf(ios::fixed,ios::floatfield);
    src =imread("/home/qinzihang/opencv-4.2.0/samples/data/lena.jpg");
    cvtColor(src,src,COLOR_BGR2GRAY);
    Mat dstImage(src.rows,src.cols,CV_8UC1,Scalar(0));
    imshow("GG",src);
    int size=5; //定义卷积核大小
    double **gaus=new double *[size];
    for(int i=0;i<size;i++)
    {
        gaus[i]=new double[size];  //动态生成矩阵
    }

    Mat Gauss_Kernel = GetGaussianKernel(3,10);
    Convlution(src,dstImage,Gauss_Kernel);
    imshow("after", src);
    waitKey(0);
}
Mat GetGaussianKernel( const int size,const double sigma)
{
    double **gaus=new double *[size];
    for(int i=0;i<size;i++)
    {
        gaus[i]=new double[size];  //动态生成矩阵
    }
    Mat Kernel(size,size,CV_64FC1,Scalar(0));
    const double PI=4.0*atan(1.0); //圆周率π赋值
    int center=size/2;
    double sum=0;
    for(int i=0;i<size;i++)
    {
        for(int j=0;j<size;j++)
        {
            gaus[i][j]=(1/(2*PI*sigma*sigma))*exp(-((i-center)*(i-center)+(j-center)*(j-center))/(2*sigma*sigma));
            sum+=gaus[i][j];
        }
    }


    for(int i=0;i<size;i++)
    {
        for(int j=0;j<size;j++)
        {
            gaus[i][j]/=sum;
            cout<<gaus[i][j]<<",  ";
            Kernel.at<double>(i,j) = gaus[i][j];

        }
        cout<<endl<<endl;
    }



     cout << "kernel[0][0] = " << dec << Kernel.at<double>(0,1) << endl;
    imshow("kernel", Kernel);
    return Kernel;
}
void Convlution(Mat &input_img,Mat  OutputImage, Mat Kernel)
{

    int border_x = Kernel.rows/2;
    int border_y = Kernel.cols/2;
    for(int img_y = 0; img_y < input_img.cols - 2*border_y; img_y++)
    {
        for(int img_x = 0; img_x < input_img.rows - 2*border_x; img_x++)
        {
            int end_value = 0;
            for(int kernel_y = 0; kernel_y < Kernel.cols; kernel_y++)
            {
                for(int kernel_x = 0; kernel_x < Kernel.rows; kernel_x++)
                {
                    int img_value = input_img.at<uchar>(img_y+kernel_y, img_x+kernel_x);
                    double Kernel_value = Kernel.at<double>(kernel_y, kernel_x);
                    end_value += img_value*Kernel_value;
                }
            }
            if(end_value > 256)
                end_value = 255;
            else if (end_value < 0)
                end_value = 0;
            OutputImage.at<char>(img_y+border_y, img_x+border_x) = (uchar)end_value;
//            OutputImage.at<uchar>(img_y+border_y, img_x+border_x) = saturate_cast<uchar>((int)end_value);

        }
    }

    input_img = OutputImage;

}

以上便是本文的所有内容,如有错误疏漏欢迎大家指出.

 

 

  • 3
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
高斯滤波是一种常用的图像滤波算法,可以用于去除图像中的噪声。OpenCV库提供了高斯滤波的实现,下面是使用OpenCVC++实现高斯滤波的示例代码: ```c++ #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main(int argc, char** argv) { // 读取图像 Mat image = imread("test.jpg"); if (image.empty()) { cout << "Could not open or find the image" << endl; return -1; } // 高斯滤波 Mat gaussian_image; GaussianBlur(image, gaussian_image, Size(3, 3), 0); // 显示原图和滤波后的图像 namedWindow("Original Image", WINDOW_NORMAL); namedWindow("Gaussian Image", WINDOW_NORMAL); imshow("Original Image", image); imshow("Gaussian Image", gaussian_image); waitKey(0); return 0; } ``` 在上述代码中,首先使用`imread`函数读取待处理的图像,然后调用`GaussianBlur`函数对图像进行高斯滤波,最后使用`imshow`函数显示原图和滤波后的图像。 `GaussianBlur`函数的参数列表如下: ```c++ void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT); ``` - `src`:输入图像。 - `dst`:输出图像。 - `ksize`:高斯的大小,即卷积的大小。它的值必须是正奇数,例如(3, 3)、(5, 5)、(7, 7)等。 - `sigmaX`:在X方向上的高斯标准差。 - `sigmaY`:在Y方向上的高斯标准差。如果sigmaY的值为0,则使用sigmaX的值。 - `borderType`:表示边界填充方式,具体取值可以参考OpenCV文档。 注意,高斯滤波会使图像变模糊,所以需要根据实际需求选择适当的滤波参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值