详解OpenCV的函数filter2D(),并提醒大家它做的运算并不是卷积运算而是相关运算

Posted 昊虹图像算法

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解OpenCV的函数filter2D(),并提醒大家它做的运算并不是卷积运算而是相关运算相关的知识,希望对你有一定的参考价值。

卷积运算和相关运算是咱们图像处理中最基本的两种线性运算,可以说,图像处理中的绝大部分算法,特别是与滤波有关的算法都是建立这两种运算之上的。

关于图像(矩阵)卷积运算和相关运算的详细知识,大家可以参考博文 https://blog.csdn.net/wenhao_ir/article/details/124109222

MATLAB提供了函数imfilter()实现对这两种运算的支持,其语法如下:

B = imfilter(A,h)
B = imfilter(A,h,options,...)

options的可选值如下:

所以当我们需要作卷积操作时可以像下面这样写:

imfilter(A,h,'conv')

当我们需要作相关操作时可以像下面这样写:

imfilter(A,h,'corr')

OpenCV作为图像处理的强有力工具,自然也少不了对这两种运算的支持。
OpenCV提供了函数filter2D()作图像(矩阵)的相关运算,注意是相关运算,而不是卷积运算。

它的运算公式如下:

dst ( x , y ) = ∑ 0 ≤ y ′ < kernel.rows 0 ≤ x ′ < kernel.cols , kernel ( x ′ , y ′ ) ∗ src ( x + x ′ − anchor.x , y + y ′ − anchor.y ) \\textttdst (x,y) = \\sum _ \\stackrel0\\leq x' < \\textttkernel.cols,0\\leq y' < \\textttkernel.rows \\textttkernel (x',y')* \\textttsrc (x+x'- \\textttanchor.x ,y+y'- \\textttanchor.y ) dst(x,y)=0y<kernel.rows0x<kernel.cols,kernel(x,y)src(x+xanchor.x,y+yanchor.y)

上面这个式子看起来很复杂,实际上就是博文https://blog.csdn.net/wenhao_ir/article/details/124109222中介绍的相关运算的公式化表达。公式中的anchor代表kernel核的锚点,关于锚点的介绍,可以参考我的博文 https://blog.csdn.net/wenhao_ir/article/details/124173092
大家如果通过阅读上面两篇博文理解了相关运算及锚点的概念,那么这个公式是不难理解的。

那如果我们要做卷积运算怎么办呢?
根据卷积运算的定义,我们将核旋转180度再做相关运算就是卷积运算了。那么怎么将核旋转180度呢?根据博文https://blog.csdn.net/wenhao_ir/article/details/51443330我们知道将矩阵180度等效于先作一个水平镜像,再作一个垂直镜像,矩阵的镜像运算我们可以用函数flip()完成,函数flip()的原型如下:

void cv::flip(InputArray src, OutputArray dst, int flipCode)	

这个函数的使用示例见博文 https://blog.csdn.net/wenhao_ir/article/details/51442792
我们只需要将参数flipCode的值写为负数值,即可实现对矩阵进行180度的旋转。

接下来看下函数filter2D()的原型:
C++原型如下:

void cv::filter2D(	InputArray 	src,
					OutputArray dst,
					int 	ddepth,
					InputArray 	kernel,
					Point 	anchor = Point(-1,-1),
					double 	delta = 0,
					int 	borderType = BORDER_DEFAULT)	

Python原型如下:

dst	= cv.filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]])

各参数意义如下:
src—输入图像
dst—目标图像
ddepth—目标图像的数据深度
kernel—相关运算的运算核
anchor—kernel的锚点
delta—对运算结果的偏移处理,比如某个像素的相关运算结果为1,delta=2,那么最终在目标图像中这个像素的值为1+2=3。
borderType—边界扩展标志位,关于边界扩展,可以参考博文 https://blog.csdn.net/wenhao_ir/article/details/124177989

示例代码如下:

# 博主微信/QQ 2487872782
# 有问题可以联系博主交流
# 有图像处理需求也请联系博主
# 图像处理技术交流QQ群 271891601

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# OpenCV的版本为4.1

import numpy as np
import cv2 as cv

A1 = np.array([[1, 1, 7, 3],
              [5, 5, 2, 7],
              [9, 9, 6, 1],
              [7, 7, 4, 9]], dtype='uint8')

kernel1 = np.array([[1, 1, 1],
                    [1, 1, 1],
                    [1, 1, 1]], dtype='uint8')

B1 = cv.filter2D(A1, cv.CV_8U, kernel1, anchor=(0, 0), borderType=cv.BORDER_CONSTANT)

B2 = cv.filter2D(A1, cv.CV_8U, kernel1, anchor=(-1, -1), borderType=cv.BORDER_CONSTANT)

运行结果如下:



结果分析:B1和B2的不同在于核的锚点不一样,B1用的是核左上角的点,B2用的是核的中心点。边界扩展方式为填充0值处理。
手工验证结果如下:

B1[0,0]=1*1+1*1+7*1+5*1+5*1+2*1+9*1+9*1+6*1=45

B1[0,3]=0*1+0*1+0*1+7*1+3*1+0*1+2*1+7*1+0*1=11


可见,B1的手工结果和程序运行的结果一致。

再来看B2:

B2[0,0]=0*1+0*1+0*1+0*1+1*1+1*1+0*1+5*1+5*1=12

B2[0,3]=0*1+0*1+0*1+0*1+1*1+1*1+0*1+5*1+5*1=19

可见,B12的手工结果也和程序运行的结果一致。

补充说明:
函数filter2D()还支持in-place operation(就地操作)哦,关于这个操作的详情见博文 https://blog.csdn.net/wenhao_ir/article/details/125211437

开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系

以上是关于详解OpenCV的函数filter2D(),并提醒大家它做的运算并不是卷积运算而是相关运算的主要内容,如果未能解决你的问题,请参考以下文章

卷积处理过程模拟:用Python实现OpenCV函数filter2D等效的卷积功能

手撕OpenCV源码之filter2D

OpenCV:filter2D函数的计算效率

opencv filter2D()函数(卷积)计算原理

Python-OpenCV中的filter2D()函数

OpenCV filter2D和sepFilter2D