手撕OpenCV源码之filter2D(一)

本文深入探讨了OpenCV中的filter2D函数,详细解释了函数原型、Point anchor的概念及其在源码中的处理,以及边界处理方式。通过源码分析,揭示了anchor在实际计算中的定位和对填充的影响,提供了不同anchor设置下的计算示例。
摘要由CSDN通过智能技术生成

opencv中的filter2D

函数原型

  • 官方解释
    image

网站

CV_EXPORTS_W void filter2D( InputArray src, OutputArray dst, int ddepth,
                            InputArray kernel, Point anchor=Point(-1,-1),
                            double delta=0, int borderType=BORDER_DEFAULT );
  • ddepth是目标图像深度:每个像素所用的位数
    当前支持的图像深度为:
src.depth() = CV_8U, ddepth = -1/CV_16S/CV_32F/CV_64F
    src.depth() = CV_16U/CV_16S, ddepth = -1/CV_32F/CV_64F
    src.depth() = CV_32F, ddepth = -1/CV_32F/CV_64F
    src.depth() = CV_64F, ddepth = -1/CV_64F

ddepth为-1时,图像深度与源图像深度保持一致.

如果了解深度学习可能知道,深度学习中图像的直接卷积计算有4个参数,whck,但是在opencv
当前只支持k==1,c==1;如果图像为多通道,每个通道使用不同的kernel,则需要使用split()
函数将各通道拆开.

  • Point anchor:这个参数直接翻译叫做锚点,普通意义上,我们在计算卷积的时候,锚点是kernel的中心.中心与图像需要卷积的点对齐,计算改点及周围8个点的乘累加和,替代中心点.在OpenCV中可以设置其他点为锚点,这样就是以锚点和卷积的点对其,计算9个点.但是在Opencv中,对于anchor point做了计算,所以锚点的实际值与设置的值稍有差别,下文会详细介绍.

  • dalta:额外加的数值,就是在卷积过程中该数值会添加到每个像素上.

  • int borderType:表示对边界的处理方式.主要有以下几种:
/*
 Various border types, image boundaries are denoted with '|'
 * BORDER_REPLICATE:     aaaaaa|abcdefgh|hhhhhhh
 * BORDER_REFLECT:       fedcba|abcdefgh|hgfedcb
 * BORDER_REFLECT_101:   gfedcb|abcdefgh|gfedcba
 * BORDER_WRAP:          cdefgh|abcdefgh|abcdefg
 * BORDER_CONSTANT:      iiiiii|abcdefgh|iiiiiii  with some specified 'i'
 */

这是opencv中对5种宏的解释,例子描述的是比较形象的.
BORDER_REPLICATE: 表示对边界重复,来填充边界的扩展.
BORDER_REFLECTBORDER_REFLECT_101都是对边界的对称扩展,但是具体
方式不同,从例子可以很容易看出来.

Point anchor详解

关于Point anchor我在网上查了很多资料,解释都不够清楚,或者说在OpenCV中,按照常规解释,难以获得和OpenCV相同的计算结果.最终在OpenCV的源代码中找到了答案.先上源代码:
(opencv-2.4.9/modules/imgproc/src/filter.cpp)

void cv::filter2D( InputArray _src, OutputArray _dst, int ddepth,
                    InputArray _kernel, Point anchor,
                    double delta, int borderType )
{
     Mat src = _src.getMat(), kernel = _kernel.getMat();

     if( ddepth < 0 )
         ddepth = src.depth();</
以下是 OpenCV 中 `cv::filter2D` 函数的简化版源码: ```cpp void cv::filter2D( InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor = Point(-1,-1), double delta = 0, int borderType = BORDER_DEFAULT ) { // 获取输入图像和卷积核 Mat srcMat = src.getMat(); Mat kernelMat = kernel.getMat(); // 创建输出图像 dst.create(srcMat.size(), CV_MAKETYPE(ddepth, srcMat.channels())); Mat dstMat = dst.getMat(); // 根据卷积核的大小和锚点位置进行卷积计算 Point kernelAnchor(-1, -1); if (anchor.x >= 0 && anchor.y >= 0) kernelAnchor = Point(anchor.x * kernelMat.cols, anchor.y * kernelMat.rows); else kernelAnchor = Point((kernelMat.cols - 1) / 2, (kernelMat.rows - 1) / 2); // 对输入图像进行卷积计算 for (int i = 0; i < srcMat.channels(); ++i) { // 获取当前通道的输入图像和输出图像 Mat srcChannel = srcMat.channel(i); Mat dstChannel = dstMat.channel(i); // 对每个像素进行卷积操作 for (int y = 0; y < srcMat.rows; ++y) { for (int x = 0; x < srcMat.cols; ++x) { // 计算当前像素的卷积结果 float sum = 0; for (int ky = 0; ky < kernelMat.rows; ++ky) { for (int kx = 0; kx < kernelMat.cols; ++kx) { int srcX = x + kx - kernelAnchor.x; int srcY = y + ky - kernelAnchor.y; if (srcX >= 0 && srcX < srcMat.cols && srcY >= 0 && srcY < srcMat.rows) sum += srcChannel.at<float>(srcY, srcX) * kernelMat.at<float>(ky, kx); } } dstChannel.at<float>(y, x) = static_cast<float>(sum + delta); } } } // 边界处理 if (borderType != BORDER_CONSTANT) copyMakeBorder(dst, dst, kernelAnchor.y, kernelMat.rows - kernelAnchor.y - 1, kernelAnchor.x, kernelMat.cols - kernelAnchor.x - 1, borderType); } ``` 这是一个简化版的源码,省略了一些参数的处理和错误检查等细节。如果你想获得更完整和详细的源码,建议直接查阅 OpenCV 的源代码库中的 `imgproc/src/filter.cpp` 文件。 希望对你有帮助!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值