S1.2 如何自定义核?

S1.2 如何自定义核?


我们知道很多的卷积核,例如sobel,均值滤波核,高斯核,拉普拉斯变换核。这些核一般是3x3的矩阵。也有5x5的。
均 值 1 9 [ 1 , 1 , 1 1 , 1 , 1 1 , 1 , 1 ] 均值 \frac{1}{9}\begin{bmatrix} 1,1,1\\ 1,1,1\\ 1,1,1\\ \end{bmatrix} 911,1,11,1,11,1,1

S o b e l [ − 1 , 0 , 1 − 2 , 0 , 2 − 1 , 0 , 1 ] S c h a r r [ − 3 , 0 , 3 − 10 , 0 , 10 − 3 , 0 , 3 ] Sobel \begin{bmatrix} -1,&0,&1\\ -2,&0,&2\\ -1,&0,&1\\ \end{bmatrix} Scharr \begin{bmatrix} -3,&0,&3\\ -10,&0,&10\\ -3,&0,&3\\ \end{bmatrix} Sobel1,2,1,0,0,0,121Scharr3,10,3,0,0,0,3103

5 x 5 高 斯 [ 1 , 4 , 7 , 4 , 1 4 , 16 , 26 , 16 , 4 7 , 26 , 41 , 26 , 7 4 , 16 , 26 , 16 , 4 1 , 4 , 7 , 4 , 1 ] 5x5高斯 \begin{bmatrix} 1,&4,&7,&4,&1\\ 4,&16,&26,&16,&4\\ 7,&26,&41,&26,&7\\ 4,&16,&26,&16,&4\\ 1,&4,&7,&4,&1\\ \end{bmatrix} 5x51,4,7,4,1,4,16,26,16,4,7,26,41,26,7,4,16,26,16,4,14741

拉 普 拉 斯 [ 0 , 1 , 0 1 , − 4 , 1 0 , 1 , 0 ] 拉普拉斯 \begin{bmatrix} 0,&1,&0\\ 1,&-4,&1\\ 0,&1,&0\\ \end{bmatrix} 0,1,0,1,4,1,010

(ps:什么是卷积?S0.7 卷积和相关)

那么问题来了:能不能自己定义卷积核呢?这些卷积核都是怎么设计的呢?如何理解这些卷积核呢?

这篇blog博主小白就斗胆探究一下怎样设计卷积核-。-

构建自定义核程式

我们用OpenCV自定义核函数filter2D,来作为我们的源码。当然你可以把部分功能封装成一个函数。

//25. 自定义卷积核
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;

int main()
{
    Mat src = imread("images/favorite/Lena.jpg",0);
    Mat dst;
    imshow("src", src);

    double matrix[3][3] = { {1, 1, 1},
                            {1, 1, 1},
                            {1, 1, 1}};

    Mat filter0(Size(3,3), CV_64F, matrix);

    filter0 = filter0/9;

//    std::cout << filter0 << std::endl;
    filter2D(src, dst, CV_64F, filter0);

    dst.convertTo(dst, CV_8U);

    imshow("dst", dst);

    waitKey(0);
    return 0;
}

分析不同卷积核效果

均值核,高斯核

这两个核里面都是正数,无非就是像素值加权平均,最终只会把像素变的越来越相同,产生的图像会模糊,但会有滤波效果。总结起来就是,如果相邻是正数,表示权值大小关系

中心为锚点(实际值所在的地方),中心越大,图像越不模糊,滤波效果减弱。

拉普拉斯变换

拉普拉斯常用于边缘提取,它的特点是中心是负数,而且核的sum=0。

由于负数在卷积核中对应梯度操作(之后会讲),我们得到的图像其实是那些图像对x和y梯度的叠加。知道这个,我们可以把拉普拉斯变换变成两个:一个是对x的梯度,一个是对y的梯度:
对 x [ 0 , 0 , 0 1 , − 2 , 1 0 , 0 , 0 ] 对 y [ 0 , 1 , 0 0 , − 2 , 0 0 , 1 , 0 ] 对x \begin{bmatrix} 0,&amp;0,&amp;0\\ 1,&amp;-2,&amp;1\\ 0,&amp;0,&amp;0\\ \end{bmatrix} 对y \begin{bmatrix} 0,&amp;1,&amp;0\\ 0,&amp;-2,&amp;0\\ 0,&amp;1,&amp;0\\ \end{bmatrix} x0,1,0,0,2,0,010y0,0,0,1,2,1,000
如果我们想得到既对x,也对y的就把两个核产生的图像加起来。

图中src原图,dst0拉普拉斯变换图,dst1、dst2对x梯度对y的梯度,dst3为dst1+dst2。

在这里插入图片描述

  • 只要有负数就可以提取边缘?

是的,我们演示三个自己写的核。
d s t 0 [ 1 , 0 , 1 0 , 0 , 0 0 , − 2 , 0 ] dst0 \begin{bmatrix} 1,&amp;0,&amp;1\\ 0,&amp;0,&amp;0\\ 0,&amp;-2,&amp;0\\ \end{bmatrix} dst01,0,0,0,0,2,100

d s t 1 [ 0 , − 1 , 0 1 , 1 , 1 0 , − 2 , 0 ] dst1\begin{bmatrix} 0,&amp;-1,&amp;0\\ 1,&amp;1,&amp;1\\ 0,&amp;-2,&amp;0\\ \end{bmatrix} dst10,1,0,1,1,2,010

d s t 2 [ 0 , 0 , 3 2 , 0 , 0 0 , − 5 , 0 ] dst2\begin{bmatrix} 0,&amp;0,&amp;3\\ 2,&amp;0,&amp;0\\ 0,&amp;-5,&amp;0\\ \end{bmatrix} dst20,2,0,0,0,5,300

在这里插入图片描述

第一个提取的边缘比较厚,第二个比较薄。这是因为第一个由于有一行0,导致边缘附近的也可以检测到边缘,导致边缘变大,这样有利于放大 小的边缘。

  • 只是中心变成负数?

[ 1 , 1 , 1 1 , − 8 , 1 1 , 1 , 1 ] \begin{bmatrix} 1,&amp;1,&amp;1\\ 1,&amp;-8,&amp;1\\ 1,&amp;1,&amp;1\\ \end{bmatrix} 1,1,1,1,8,1,111

我们得到了一个提取边缘的卷积核,其实这个核和拉普拉斯没啥区别,感觉就像考虑4邻域和8邻域的图像边缘提取。

在这里插入图片描述

  • 换一下?边缘变成负数?

[ 0 , − 1 , 0 − 1 , 4 , − 1 0 , − 1 , 0 ] \begin{bmatrix} 0,&amp;-1,&amp;0\\ -1,&amp;4,&amp;-1\\ 0,&amp;-1,&amp;0\\ \end{bmatrix} 0,1,0,1,4,1,010

在这里插入图片描述
其实没多大区别。

Sobel核

在很多教材中会提到Sobel其实可以表示为两个一维矩阵的卷积:
S o b e l [ − 1 , 0 , 1 − 2 , 0 , 2 − 1 , 0 , 1 ] Sobel \begin{bmatrix} -1,&amp;0,&amp;1\\ -2,&amp;0,&amp;2\\ -1,&amp;0,&amp;1\\ \end{bmatrix} Sobel1,2,1,0,0,0,121

其实根据之前讨论的零行就是扩大边缘,sobel其实理解起来并没有什么问题。
不 就 是 [ 0 , 0 , 0 − 2 , 0 , 2 0 , 0 , 0 ] 的 增 强 版 么 ? 不就是 \begin{bmatrix} 0,&amp;0,&amp;0\\ -2,&amp;0,&amp;2\\ 0,&amp;0,&amp;0\\ \end{bmatrix} 的增强版么? 0,2,0,0,0,0,020

自己动手DIY自己的卷积核

。。。

卷积核的数学分析

首先,我们定义卷积函数f在x方向和y方向的的二阶导:
∂ 2 f ∂ x 2 = f ( x + 1 , y ) + f ( x − 1 , y ) − 2 f ( x , y ) ∂ 2 f ∂ y 2 = f ( x , y + 1 ) + f ( x , y − 1 ) − 2 f ( x , y ) \frac{\partial^2 f}{\partial x^2} = f(x+1,y)+f(x-1,y)-2f(x,y)\\ \frac{\partial^2 f}{\partial y^2} = f(x,y+1)+f(x,y-1)-2f(x,y) x22f=f(x+1,y)+f(x1,y)2f(x,y)y22f=f(x,y+1)+f(x,y1)2f(x,y)
我们的拉普拉斯算子就是两者相加:
∇ 2 f ( x , y ) = f ( x + 1 , y ) + f ( x − 1 , y ) + f ( x , y + 1 ) + f ( x , y − 1 ) − 4 f ( x , y ) \nabla^2f(x,y)=f(x+1,y)+f(x-1,y)+f(x,y+1)+f(x,y-1)-4f(x,y) 2f(x,y)=f(x+1,y)+f(x1,y)+f(x,y+1)+f(x,y1)4f(x,y)

卷积核的频域体现(傅里叶变换卷积定理)

我们来做一道题:

找出一个等价的滤波器H(u,v),它在频率域实现拉普拉斯变换等价操作。

在空域中,滤波后的图像用函数表示:
g ( x , y ) = f ( x + 1 , y ) + f ( x − 1 , y ) + f ( x , y + 1 ) + f ( x , y − 1 ) − 4 f ( x , y ) g(x,y)=f(x+1,y)+f(x-1,y)+f(x,y+1)+f(x,y-1)-4f(x,y) g(x,y)=f(x+1,y)+f(x1,y)+f(x,y+1)+f(x,y1)4f(x,y)
我们可以很简单的证明:
f ( x + y 0 , x + y 0 ) 经 过 傅 里 叶 变 换 变 成 F ( u , v ) e − 2 π i ( u x 0 M + v y 0 N ) f(x+y_0,x+y_0)经过傅里叶变换变成 F(u,v) e^{-2\pi i(\frac{ux_0}{M}+\frac{vy_0}{N})} f(x+y0,x+y0)F(u,v)e2πi(Mux0+Nvy0)
我们假设频域变换前后图像有如下关系:
G ( u , v ) = H ( u , v ) F ( u , v ) G(u,v)=H(u,v)F(u,v) G(u,v)=H(u,v)F(u,v)
我们把第一个式子两边都傅里叶变换一下,解频域中的变换关系H:
F ( u , v ) [ e − 2 π i u M + e 2 π i u M + e − 2 π i v N + e 2 π i v N ] = H ( u , v ) F ( u , v ) H ( u , v ) = e − 2 π i u M + e 2 π i u M + e − 2 π i v N + e 2 π i v N F(u,v)[e^{-2\pi i\frac{u}{M}}+e^{2\pi i\frac{u}{M}}+e^{-2\pi i\frac{v}{N}}+e^{2\pi i\frac{v}{N}}]=H(u,v)F(u,v)\\ H(u,v)=e^{-2\pi i\frac{u}{M}}+e^{2\pi i\frac{u}{M}}+e^{-2\pi i\frac{v}{N}}+e^{2\pi i\frac{v}{N}} F(u,v)[e2πiMu+e2πiMu+e2πiNv+e2πiNv]=H(u,v)F(u,v)H(u,v)=e2πiMu+e2πiMu+e2πiNv+e2πiNv
根据欧拉定理:
H ( u , v ) = 2 c o s ( 2 π u M ) + 2 c o s ( 2 π v N ) H(u,v)=2cos(\frac{2\pi u}{M})+2cos(\frac{2\pi v}{N}) H(u,v)=2cos(M2πu)+2cos(N2πv)

其他无关的东西

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

using namespace cv;

//输入卷积核,显示结果
Mat filter_show(Mat src, double matrix[3][3], int m, int n, String name)
{
    Mat dst;
    Mat filter(Size(n,m), CV_64F, matrix);

    double sum = 0;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < m; j++)
            sum += matrix[i][j];
    if(sum != 0)
        filter = filter/sum;

    filter2D(src, dst, CV_64F, filter);

    dst.convertTo(dst, CV_8U);

    return dst;
//    imshow(name, dst);
}

int main()
{
    Mat src = imread("images/favorite/Lena.jpg",0);
    imshow("src", src);
    int m = 3;
    int n = 3;

    double matrix0[3][3] = { {0, 0, 0},
                             {-2, 0, 2},
                             {0, 0, 0}};
    double matrix1[3][3] = { {0, 1, 0},
                             {1, -4, 1},
                             {0, 1, 0}};
    double matrix2[3][3] = { {1, 1, 1},
                             {1, -8, 1},
                             {1, 1, 1}};

//    std::cout << filter0 << std::endl;
    Mat dst0 = filter_show(src, matrix0, n, m, "dst0");
    Mat dst1 = filter_show(src, matrix1, n, m, "dst1");
    Mat dst2 = filter_show(src, matrix2, n, m, "dst2");

    imshow("dst0", dst0);
    imshow("dst1", dst1);
    imshow("dst2", dst2);
//    imshow("dst3", dst1+dst2);

    waitKey(0);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值