图像卷积

由于采集图像的设备可能受到光子噪声、暗电流噪声等干扰,使得采集到的图像具有噪声,另外图像信号在传输过程中也可能产生噪音

因此去除图像中的噪声是图像预处理中十分重要的步骤,图像滤波是图像噪声去除的重要方式

图像卷积filter2D()

卷积常用在信号处理中,而图像数据可以看作一种信号数据,例如图像中每一行可以看作测量亮度变化的信号数据

因此,可以对图像进行卷积操作,在信号处理中,卷积操作需要给出一个卷积函数与信号进行计算

图像的卷积需要给出一个卷积模板与原始图像进行卷积计算,整个过程可以看成是一个卷积模板在另外一个大的图像上移动

对每个卷积模板覆盖的区域进行点乘,得到的值作为中心像素点的输出值

卷积首先需要将卷积模板旋转180°,之后从图像的左上角开始移动旋转后的卷积模板,从左到右,从上到下,依次进行卷积计算

最终得到卷积后的图形,卷积模板又称为卷积核或者内核,是一个固定大小的二维矩阵,矩阵存放着预先设定的数值

图像卷积过程可以大致分为五步

第一步:将卷积模板旋转180°,由于多数情况中卷积模板中的数据是中心对称的,因此有时这步可以省略,但是如果卷积模板不是中心对称的,必须将模板进行旋转

第二步:将卷积模板中心放在原图像中需要计算卷积的像素上,卷积模板中其余部分对应在原图像相应的像素上,如图5-1所示,卷积模板和待卷积矩阵中黄色区域分别是卷积模板的中心和对应点,定位结果中阴影区域为模板覆盖的区域

第三步:用卷积模板中的系数乘以图像中对应位置的像素数值,并对所有结果求和,针对图5-1表示的卷积步骤,其计算过程如式所示,最终计算结果为84

第四步:将计算结果存放在原图像中与卷积模板中心点像对应的像素处,即图5-1里待卷积矩阵中的黄色像素处,结果如图5-2所示

第五步:将卷积模板在图像中从左至右从上到下移动,重复以上3个步骤,直到处理完所有的像素值,每一次循环的处理结果如图5-3所示

这种方法只能对图像中心区域进行卷积,而由于卷积模板中心无法放置在图像的边缘像素处,因此图像边缘区域没有进行卷积运算

为了解决这个问题,可主动将图像的边缘外推出去(例:用0在原始图像周围增加一层像素)

从而解决模板图像中部分数据没有对应像素的问题

最后一个像素值已经接近CV_8U数据类型的最大值,因此,如果卷积模板选取不当,极有可能造成卷积结果超出数据范围的情况发生

因此,图像卷积操作常将卷积模板通过缩放使得所有数值的和为1,进而解决卷积后数值越界的情况发生

OpenCV4提供了filter2D()函数实现图像和卷积模板之间的卷积运算

filter2D()函数原型:

void filter2D(InputArray src,
              OutputArray dst,
              int ddepth,
              InputArray kernel,
              Point anchor = Point(-1,-1),
              int boderType = BORDER_DEFAULT
              )
  • src:输入图像
  • dst:输出图像
  • ddepth:输出图像的数据类型(深度),根据输入图像的数据类型不同,拥有不同的取值范围

    当赋值为-1时,输出图像的数据类型自动选择

  • kernel:卷积核,CV_32FC1的矩阵
  • anchor:内核的基准点(锚点),默认值(-1,-1)代表内核基准点位于kernel的中心位置

    基准点是卷积核中与进行处理的像素点的像素点重合的点,位置必须在卷积核的内部

  • delta:偏值,在计算结果中加上偏值
  • borderType:像素外推法选择标志,默认参数为BORDER_DEFAULT,表示不包含边界值倒序填充

该函数用于实现图像和卷积模板之间的卷积运算,函数第一个参数为输入的待卷积图像,允许输入图像为多通道图像

如果需要用不同的卷积模板对不同的通道进行菊娜姬操作,就需要先使用split()函数将多个通道分离之后单独对每一个通道

求取卷积运算,第二个参数为输出图像,尺寸和通道数与第一个参数保持一致,输出图像的数据类型由第三个参数进行选择

第四个参数为卷积模板矩阵,第五个参数可以指定卷积模板的中心位置,卷积偏值表示在卷积步骤第二步计算结果上再加上偏值作为结果

注意:filter()函数不会将卷积模板进行旋转,热狗卷积模板不对称,那么需要将卷积模板旋转180°后再输入第四个参数

filter2D()函数输出图像数据类型与输入图像数据类型联系

输入图像数据类型 输出图像数据类型
CV_8U -1 / CV_16S / CV_32F / CV_64F
CV_16U / CV_16S -1 / CV_32F / CV_64F
CV_32F -1 / CV_32F / CV_64F
CV_64F -1 / CV_64F

示例:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
    //待卷积矩阵
    uchar points[25]= {
            1,2,3,4,5,
            6,7,8,9,10,
            11,12,13,14,15,
            16,17,18,19,20,
            21,22,23,24,25
    };
    Mat img(5,5,CV_8UC1,points);
    //卷积模板
    Mat kernel = (Mat_<float>(3,3) << 1,2,1,
            2,0,2,
            1,2,1);
    Mat kernel_norm = kernel / 12; //卷积模板归一化
    //未归一化卷积结果和归一化卷积结果
    Mat result,result_norm;
    filter2D(img,result,CV_32F,kernel,Point(-1,-1),2,BORDER_CONSTANT);
    filter2D(img,result_norm,CV_32F,kernel_norm,Point(-1,-1),2,BORDER_CONSTANT);
    cout << "result=" << endl << result << endl;
    cout << "result_norm" << endl << result_norm << endl;
    //图像卷积
    Mat lena = imread("E:\\CLion\\opencv_xin\\SG\\SG_0.jpg");
    if (lena.empty()) {
        cout << "Error" << endl;
        return -1;
    }
    Mat lena_filter;
    filter2D(lena,lena_filter,-1,kernel_norm,Point(-1,-1),2,BORDER_CONSTANT);
    imshow("lena_filter",lena_filter);
    imshow("lena",lena);
    waitKey(0);
    return 0;
}

注:ucahr:图像处理中常常使用的一种数据类型uchar,一般它指的就是unsigned char,是一种8-bit无符号整形数据,范围为[0, 255]

results matching ""

    No results matching ""