在本教程中,您将学习如何:
使用 OpenCV 函数 filter2D() 创建您自己的线性滤波器。
笔记
下面的解释属于 Bradski 和 Kaehler 的《Learning OpenCV》一书。
相关性
在非常一般的意义上,相关性是图像的每个部分与运算符(内核)之间的操作。
内核本质上是一个固定大小的数值系数数组以及该数组中的一个anchor point锚点,该锚点通常位于中心。
与内核的相关性如何工作?
假设您想知道图像中特定位置的结果值。 相关值的计算方式如下:
将内核锚点放置在确定的像素之上,内核的其余部分覆盖图像中相应的局部像素。
将内核系数乘以相应的图像像素值并对结果求和。
将结果放在输入图像中锚点的位置。
通过在整个图像上扫描内核来对所有像素重复该过程。
以方程的形式表达上述过程,我们将有:
幸运的是,OpenCV 为您提供了 filter2D() 函数,因此您不必编写所有这些操作。
加载图像
执行标准化的盒式过滤器normalized box filter。 例如,对于 size=3 的内核,内核将是:
该程序将使用大小为 3、5、7、9 和 11 的内核执行过滤操作。
过滤器输出(每个内核)将在 500 毫秒内显示
教程代码显示在下面的行中。
你也可以从这里raw.githubusercontent.com 下载
/**
* @file filter2D_demo.cpp
* @brief Sample code that shows how to implement your own linear filters by using filter2D function
* 展示如何使用 filter2D 函数实现您自己的线性过滤器的示例代码
* @author OpenCV team
*/
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
/**
* @function main
*/
int main ( int argc, char** argv )
{
// 声明变量
Mat src, dst;
Mat kernel;//内核
Point anchor;//锚点
double delta;
int ddepth;
int kernel_size;
const char* window_name = "filter2D Demo";
//![load]
const char* imageName = argc >=2 ? argv[1] : "lena.jpg";
// 加载图像
src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an image
if( src.empty() )
{
printf(" Error opening image\n");
printf(" Program Arguments: [image_name -- default lena.jpg] \n");
return EXIT_FAILURE;
}
//![load]
//![init_arguments]
// 初始化过滤器的参数
anchor = Point( -1, -1 );
delta = 0;
ddepth = -1;
//![init_arguments]
// Loop - Will filter the image with different kernel sizes each 0.5 seconds
//循环 - 每 0.5 秒过滤一次具有不同内核大小的图像
int ind = 0;
for(;;)
{
//![update_kernel]
// Update kernel size for a normalized box filter
// 更新归一化box过滤器的内核大小
kernel_size = 3 + 2*( ind%5 );//内核尺寸在 3 5 7 9 11 之间切换
kernel = Mat::ones( kernel_size, kernel_size, CV_32F )/ (float)(kernel_size*kernel_size);//归一化
//![update_kernel]
//![apply_filter]
// 应用过滤器 Apply filter
filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_DEFAULT );
//![apply_filter]
imshow( window_name, dst );//显示滤波后图像
char c = (char)waitKey(500);//等待输入 0.5秒
// 按“ESC”退出程序
if( c == 27 )
{ break; }
ind++;
}
return EXIT_SUCCESS;
}
Load an image 加载图像
const char* imageName = argc >=2 ? argv[1] : "lena.jpg";
// Loads an image
src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an image
if( src.empty() )
{
printf(" Error opening image\n");
printf(" Program Arguments: [image_name -- default lena.jpg] \n");
return EXIT_FAILURE;
}
Initialize the arguments
// 初始化过滤器的参数
anchor = Point( -1, -1 );
delta = 0;
ddepth = -1;
Loop
执行无限循环更新内核大小并将我们的线性滤波器应用于输入图像。 让我们更详细地分析一下:
// 更新归一化盒过滤器的内核大小
kernel_size = 3 + 2*( ind%5 );
kernel = Mat::ones( kernel_size, kernel_size, CV_32F )/ (float)(kernel_size*kernel_size);
第一行是将 kernel_size 更新为范围内的奇数值:[3,11]。 第二行实际上是通过将其值设置为一个填充有 1 的矩阵并通过将其除以元素数量来对其进行归一化来构建内核。
// Apply filter
filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_DEFAULT );
src:源图像
dst:目标图像
ddepth:dst 的深度。 负值(例如 -1)表示深度与源相同。
kernel:要通过镜像扫描的内核
anchor:锚点相对于其内核的位置。 位置 Point(-1, -1) 默认表示中心。
delta:在相关过程中要添加到每个像素的值。 默认为 0
BORDER_DEFAULT:我们默认设置这个值(更多细节在后面的教程中)