美文网首页
33.制作你自己的线性过滤器!(filter2D/imgcode

33.制作你自己的线性过滤器!(filter2D/imgcode

作者: 小嗷_a2e2 | 来源:发表于2018-07-03 12:21 被阅读125次

本文作者:小嗷

微信公众号:aoxiaoji

吹比QQ群:736854977

链接:https://mp.weixin.qq.com/s?__biz=MzU1MTgxNjQyMg==&mid=2247483898&idx=1&sn=92ef5b6715cbd79235dba7e219955699&chksm=fb8adc73ccfd5565d82d5c3277466c39d4355ecea88dfc7ed53292360dfc886ae2be7f38a44f#rd


image image image.gif

小嗷再问一次自己,什么是线性滤波?

  1. 首先图像和核卷积是必须的。

  2. 均值的话,卷积以后的总数除以核的大小(如3*3=9,9个数相加后总数/9=所求的像素值)

  3. 高斯的话,先进行高斯函数的运算,然后把算出来的核核图像进行卷积。再加起来。

即: 核与图像卷积 + 核内数相加(不懂的话,自行看推荐文章。)

图像、相加、卷积、都是固定不变。自定义线性滤波,只能改变核(核也称元素结构),呵呵哒

  • 大家可以参考第28篇代码有自定义核的内容

在本篇中,您将学习:

使用OpenCV函数filter2D()创建自己的线性过滤器。


本文你会找到以下问题的答案:

  1. filter2D()

  2. imgcodecs

  3. char **argv是什么用法啊?argv[0] 什么意思啊?


2.1 理论

下面的解释属于Bradski和Kaehler的《学习OpenCV》一书(英文书)

2.1.1 相关性

在一般意义上,相关性是图像的每个部分和操作符(内核)之间的操作。

2.1.2 什么是内核?

内核本质上是一个固定大小的数值系数数组,以及该数组中通常位于中心的锚点。

(其实小嗷都不想写,这玩意说的N遍)

image

2.1.3 与内核的关联是如何工作的?

假设您希望知道映像(图像)中某个特定位置的结果值。相关值的计算方法如下:

  1. 将内核锚点放在一个确定的像素上,其余的内核将在图像中覆盖相应的本地像素。

  2. 将核系数乘以相应的图像像素值,并对结果进行求和。

  3. 将结果放置到输入图像中锚点的位置。

  4. 通过扫描整个图像的内核,对所有像素重复这个过程。

不得不说,相比小嗷的介绍,鬼佬的解释更专业更能装逼

用方程的形式表示上述过程,我们将得到:

image

看不懂公式正常,请点击第24篇自定义中值滤波处理的代码。

image.gif
src.at<Vec3b>(i + 1, j + 1)[0]
src.at<Vec3b>(i + 1, j)[0]
src.at<Vec3b>(i, j + 1)[0]

I(XXX)代表就是锚点附近的值,K(i,j)代表核

幸运的是,OpenCV为您提供了函数filter2D(),因此您不必编写所有这些操作。

2.1.4 这个程序做什么?

  1. 加载一个图像

  2. 执行规范化的框过滤器。例如,对于大小为3的内核,内核为:

image

该程序将使用3、5、7、9和11大小的内核执行筛选操作。

  • 过滤器输出(与每个内核一起)将在500毫秒内显示
image

3.1 filter2D

将图像与内核卷积。

该函数对图像应用任意线性滤波器。就地操作支持。当孔径位于图像外部,根据指定的边框模式插入离群像素值。该函数根据指定的边界模式插入离群点像素值。

这个函数实际上计算的是相关性,而不是卷积:

image

也就是说kernel并不是中心点的镜像,如果需要一个正真的卷积,使用函数flip()并将中心点设置为(kernel.cols - anchor.x - 1, kernel.rows - anchor.y -1).

该函数在大核(11x11或更大)的情况下使用基于DFT的算法,而在小核情况下使用直接算法(使用createLinearFilter()检索得到).

参数详情:

  • src:输入图像

  • dst:输出图像的大小和数量与src相同。

  • ddepth:目标图像深度,如果没写将生成与原图像深度相同的图像。原图像和目标图像支持的图像深度如下:

  • 1src.depth() = CV_8U, ddepth = -1/CV_16S/CV_32F/CV_64F2src.depth() = CV_16U/CV_16S, ddepth = -1/CV_32F/CV_64F3src.depth() = CV_32F, ddepth = -1/CV_32F/CV_64F4src.depth() = CV_64F, ddepth = -1/CV_64F
    

当ddepth输入值为-1时,目标图像和原图像深度保持一致。

  • kernel:卷积核(或者是相关核),一个单通道浮点型矩阵。如果想在图像不同的通道使用不同的kernel,可以先使用split()函数将图像通道事先分开。

  • anchor:内核的基准点(anchor),其默认值为(-1,-1)说明位于kernel的中心位置。基准点即kernel中与进行处理的像素点重合的点。

  • delta:在储存目标图像前可选的添加到像素的值,默认值为0

  • borderType:像素向外逼近的方法,默认值是BORDER_DEFAULT,即对全部边界进行计算。

3.2 imgcodecs

用于图片的读写。//2.4里没有这块(2.49)

OpenCV3开始图片、视频编解码从highgui模块分离出来,组成了imgcodecs和videoio。Linux环境下需要注意一下,其他没啥。

3.3 char **argv是什么用法啊?argv[0] 什么意思啊?

 1int argc, char **argv 用于运行时,把命令行参数传入主程序。 2argc -- 命令行参数 总个数,包括 可执行程序名。 3argv[i] --  第 i 个参数。 4argv[0] -- 可执行程序名。 5例如运行: 6abc.exe 7argc 等于 1, argv[0] 是 "abc.exe" 8例如运行: 9rec.exe  4  5.210argc 等于 3, argv[0] 是 "rec.exe", argv[1] 是 "4", argv[2] 是 "5.2".11主函数里若有:12int x;13float y;14char s[80];15strcpy(s,argv[0]);  // 程序名存入 了 s16sscanf(argv[1],"%d",&x);  // x 得到数值417sscanf(argv[2],"%f",&y);  // y 得到数值 5.2
image.gif

本篇文章的代码如下所示。

 1//首先我们需要导入相关的包,例如图像处理imgpro和输入输出图片imgcodecs,highgui则是图形界面 2#include "opencv2/imgproc.hpp" 3#include "opencv2/imgcodecs.hpp" 4#include "opencv2/highgui.hpp" 5 6using namespace cv; 7 8int main(int argc, char** argv) 9{10    //声明变量11    Mat src, dst;12    Mat kernel;13    //Point锚点14    Point anchor;15    //像素值16    double delta;17    //图像深度18    int ddepth;19    //核的大小20    int kernel_size;21    //常量即: 窗口命名22    const char* window_name = "filter2D Demo";  23    //设置imageName的名字等于argc大于等于2成立就argv[1],否则就输出图片24    const char* imageName = argc >= 2 ? argv[1] : "D://8line.jpg";2526    // 加载图片(选择彩色模式)27    src = imread(imageName, IMREAD_COLOR); // Load an image28    //如果为空就打印,退出29    if (src.empty())30    {31        printf(" Error opening image\n");32        printf(" Program Arguments: [image_name -- default ../data/lena.jpg] \n");33        return -1;34    }3536    // 为过滤器初始化参数37    anchor = Point(-1, -1);38    delta = 0;39    ddepth = -1;4041    //循环 - 每隔0.5秒过滤不同内核大小的图像42    int ind = 0;43    for (;;)44    {45        // 更新归一化方框过滤器的内核大小(均值)46        kernel_size = 3 + 2 * (ind % 5);47        //3*3矩阵,图像色彩是1,再除以948        kernel = Mat::ones(kernel_size, kernel_size, CV_32F) / (float)(kernel_size*kernel_size);4950        // 应用过滤器(原图、输出图、深度一样,卷积核,锚点中心,像素值,默认边界)51        filter2D(src, dst, ddepth, kernel, anchor, delta, BORDER_DEFAULT);52        imshow(window_name, dst);5354        char c = (char)waitKey(500);55        // Press 'ESC' to exit the program56        if (c == 27)57        {58            break;59        }6061        ind++;62    }6364    return 0;65}

源图:

image

效果图:(就是均值滤波(不停的扩大核的大小))

image

解释一下:

让我们来看看这个项目的总体结构:

加载一个图像

1const char* imageName = argc >=2 ? argv[1] : "../data/lena.jpg";2// Loads an image3src = imread( imageName, IMREAD_COLOR ); // Load an image4if( src.empty() )5{6    printf(" Error opening image\n");7    printf(" Program Arguments: [image_name -- default ../data/lena.jpg] \n");8    return -1;9}

初始化参数

1 // Initialize arguments for the filter2anchor = Point( -1, -1 );3delta = 0;4ddepth = -1;

循环

执行一个无限循环更新内核大小,并对输入图像应用线性过滤器。让我们更详细地分析一下:

  • 首先我们定义过滤器将要使用的内核。这里是:
1    // Update kernel size for a normalized box filter2    kernel_size = 3 + 2*( ind%5 );3    kernel = Mat::ones( kernel_size, kernel_size, CV_32F )/ (float)(kernel_size*kernel_size);

第一行是将kernel_size更新为范围内的奇数值:[3,11]。第二行实际上构建了内核。通过设置它的值与1′s矩阵填充和正常化把它除以元素的个数。

  • 设置内核后,我们可以使用函数filter2D()生成过滤器:
1    // Apply filter2    filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_DEFAULT );

我们的程序将会实现一个while循环,每个500 ms的内核大小的过滤器将会在指定的范围内更新。

在编译上面的代码之后,您可以执行它,将图像的路径作为参数。结果应该是一个窗口,该窗口显示被规范化过滤器模糊的图像。内核大小每0.5秒就会发生变化。

image
  1. 本人是抱着玩一玩的心态,学习opencv(其实深度学习没有外界说的这么高深,小嗷是白板,而且有工作在身并且于代码无关)

  2. 大家可以把我的数学水平想象成初中水平,毕竟小嗷既不是代码靠吃饭又不是靠数学吃饭,毕业N年

  3. 写文章主要是为了后人少走点弯路,多交点朋友,一起学习

  4. 如果有好的图像识别群拉我进去QQ:631821577

  5. 就我一个白板,最后还是成的,你们别怕,慢慢来吧

image

分享可以无数次,转载成自己文章QQ邮箱通知一下,未经授权请勿转载。

邮箱:631821577@qq.com

QQ群:736854977

有什么疑问公众号提问,下班或者周六日回答,ths

感言

本文就是为了后面做【铺垫】,filter2D卷积函数 + Mat::one创建核(元素结构)

推荐文章:

18.图像处理之线性滤波(空间域/高低频/方框/均值/高斯) --- OpenCV从零开始到图像(人脸 + 物体)识别系列

17.图像处理之线性滤波(线性运算/卷积) --- OpenCV从零开始到图像(人脸 + 物体)识别系列

20.方差/标准差/数学期望/正态分布/高斯函数(数学篇)--- OpenCV从零开始到图像(人脸 + 物体)识别系列

24.双边滤波和中值滤波器(自定义中值滤波器去除椒盐噪声、exp含义)-- OpenCV从零开始到图像(人脸 + 物体)识别系列

28.击中击不中变换(二值图像/结构元素/集合/convertTo/saturate_cast/moveWindow)

32.色彩分割(inRange/cvtColor/iostream/max/min)涉及图像深度

9.基本的图像容器(Mat详解)

代码链接:

https://pan.baidu.com/s/1wJnWJmseiJLE6ls0nQ6ehA

密码: ihyq

相关文章

网友评论

      本文标题:33.制作你自己的线性过滤器!(filter2D/imgcode

      本文链接:https://www.haomeiwen.com/subject/qvmduftx.html