实际比较filter2D和imfilter之间的关系
​     
            卷积运算是图像处理和增强中经常遇到的一种算法。由于很多优秀的开源算法都是采用matlab编写的,在我改写为c语言的时候就必然会遇到改写卷积算法的问题。在matlab中,卷积可以由imfilter来实现,在opencv中则是由filter2D来实现。它们之间的具体转化过程是什么?我通过一系列实验来研究。
          一、实验准备
          为了方便观察,仍然是采用分开来研究的方法。1)是输入数字作为卷积内容,直接观察结果;2)是采用小块图片作为卷积内容,仍然是比较结果;3)是采用真实的图片和真实的卷积核作为输入,对比最后处理图片的效果。
          那么,首先需要了解的就是在matlab中和opencv中如何将矩阵的内容进行比对?
          在matlab中可以直接打印到矩阵变量中去,而在opencv中可以这样直接打印到屏幕上面。然后将两者在matlab中做减法,直观地比较最后的结果。
          二、过程
          1)是输入数字作为卷积内容
        int _tmain(int argc, _TCHAR* argv[])
{
    //filter2d的卷积方法
    printf(    "filter2d的卷积方法\n");
    Mat srcMat(,,CV_32F);
    Mat dstMat(,,CV_32F);
    Mat srcH(,,CV_32F);
    srcH.at<float>(,) = -;
    srcH.at<float>(,) = -;
    srcH.at<float>(,) = ;
    srcH.at<float>(,) = ;
    srcH.at<float>(,) = ;
    srcH.at<float>(,) = ;
    srcH.at<float>(,) = ;
    srcH.at<float>(,) = ;
    srcH.at<float>(,) = ;
    printf(    "卷积核\n");
    for (int i=;i<srcH.rows;i++){
        for (int j=;j<srcH.cols;j++){
            printf("%f ",srcH.at<float>(i,j) );
        }
            printf("\n");
    }
    printf(    "输入\n");
    for (int i = ; i < ; i++){
        for (int j = ; j < ; j++)
          srcMat.at<float>(i,j) = i+;
    }
    for (int i = ; i < ; i++){
        for (int j = ; j < ; j++){
            printf("%.1f ",srcMat.at<float>(i,j));
        }
        printf("\n");
    }
    printf(    "输出\n");
    filter2D(srcMat,dstMat,srcMat.depth(),srcH);
    printf("\n"); printf("\n");
    for (int i = ; i < ; i++){
        for (int j = ; j < ; j++){
            printf("%.1f ",dstMat.at<float>(i,j));
        }
        printf("\n");
    }
    waitKey();
    return ;
}
 
            而在matlab中也有相关输入
          >> clear
>> H = [-2.000000 -1.000000 4.000000
3.000000 3.000000 3.000000
3.000000 2.000000 1.000000]
 
H =
 
    -2    -1     4
     3     3     3
     3     2     1
 
>> I=[1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0
3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.0
4.0 4.0 4.0 4.0 4.0 4.0 4.0 4.0 4.0 4.0
5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0
6.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0
7.0 7.0 7.0 7.0 7.0 7.0 7.0 7.0 7.0 7.0
8.0 8.0 8.0 8.0 8.0 8.0 8.0 8.0 8.0 8.0
9.0 9.0 9.0 9.0 9.0 9.0 9.0 9.0 9.0 9.0
10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.0]
 
I =
 
     1     1     1     1     1     1     1     1     1     1
     2     2     2     2     2     2     2     2     2     2
     3     3     3     3     3     3     3     3     3     3
     4     4     4     4     4     4     4     4     4     4
     5     5     5     5     5     5     5     5     5     5
     6     6     6     6     6     6     6     6     6     6
     7     7     7     7     7     7     7     7     7     7
     8     8     8     8     8     8     8     8     8     8
     9     9     9     9     9     9     9     9     9     9
    10    10    10    10    10    10    10    10    10    10
 
>> rst = imfilter(I,H)
 
rst =
 
    12    21    21    21    21    21    21    21    21    16
    24    37    37    37    37    37    37    37    37    24
    36    53    53    53    53    53    53    53    53    32
    48    69    69    69    69    69    69    69    69    40
    60    85    85    85    85    85    85    85    85    48
    72   101   101   101   101   101   101   101   101    56
    84   117   117   117   117   117   117   117   117    64
    96   133   133   133   133   133   133   133   133    72
   108   149   149   149   149   149   149   149   149    80
    87    99    99    99    99    99    99    99    99    33
 
 
 
则计算两者之差
rst3 =
 
   -11    -2    -2    -2    -2    -2    -2    -2    -2    -7
   -13     0     0     0     0     0     0     0     0   -13
   -17     0     0     0     0     0     0     0     0   -21
   -21     0     0     0     0     0     0     0     0   -29
   -25     0     0     0     0     0     0     0     0   -37
   -29     0     0     0     0     0     0     0     0   -45
   -33     0     0     0     0     0     0     0     0   -53
   -37     0     0     0     0     0     0     0     0   -61
   -41     0     0     0     0     0     0     0     0   -69
   -66   -54   -54   -54   -54   -54   -54   -54   -54  -120
结论是在边界会有所不同,这个应该是不同算法对于边界的处理不同而已。那么主体成分是完全一样的。
 
 
            2)是采用小块图片作为卷积内容
         那么准备了小块的灰度图片作为卷积内容
             //读取图片的处理的方法
    Mat gray = imread("test.jpg",);
    imwrite("gray.jpg",gray);
    gray.convertTo(gray,CV_32F);
    Mat dst;
    filter2D(gray,dst,gray.depth(),srcH);
    for (int i = ; i < gray.rows; i++){
        for (int j = ; j < gray.cols; j++){
            printf("%f ",dst.at<float>(i,j));
        }
        printf("\n");
    }         
          
             同样matlab
             I = im2double(imread('gray.jpg'))
             H =
              -2    -1     4
               3     3     3
               3     2     1
>> rst = imfilter(I,H)
              
            对比相关结果,这里可以发现,在matlab中图像是归一化存储的。
           
           在输入之前归一化,这样就会得到比较好的结果。
           结果比较像
 
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace std;
using namespace cv;
int _tmain(int argc, _TCHAR* argv[])
{
    ////filter2d的卷积方法
    printf(    "filter2d的卷积方法\n");
    //Mat srcMat(10,10,CV_32F);
    //Mat dstMat(10,10,CV_32F);
    Mat srcH(,,CV_32F);
    srcH.at<float>(,) = -;
    srcH.at<float>(,) = -;
    srcH.at<float>(,) = ;
    srcH.at<float>(,) = ;
    srcH.at<float>(,) = ;
    srcH.at<float>(,) = ;
    srcH.at<float>(,) = ;
    srcH.at<float>(,) = ;
    srcH.at<float>(,) = ;
    printf(    "卷积核\n");
    for (int i=;i<srcH.rows;i++){
        for (int j=;j<srcH.cols;j++){
            printf("%f ",srcH.at<float>(i,j) );
        }
        printf("\n");
    }
    //读取图片的处理的方法
    Mat gray = imread("test.jpg",);
    imwrite("gray.jpg",gray);
    gray.convertTo(gray,CV_32F);
    gray = gray/; //归一化处理
    fstream ftxt;
    ftxt.open("src.txt",ios::out); //写入的方式,同时是append模式的就不会覆盖掉前面的东西了。 
    for (int i = ; i < gray.rows; i++){
        for (int j = ; j < gray.cols; j++){
            ftxt<<gray.at<float>(i,j)<<" ";
        }
        ftxt<<endl;
    }
    ftxt.close();
    Mat dst;
    filter2D(gray,dst,gray.depth(),srcH);
    
    ftxt.open("rst.txt",ios::out); //写入的方式,同时是append模式的就不会覆盖掉前面的东西了。 
    for (int i = ; i < gray.rows; i++){
        for (int j = ; j < gray.cols; j++){
             ftxt<<dst.at<float>(i,j)<<" ";
        }
         ftxt<<endl;
    }
    ftxt.close();
    imshow("dst",dst);
    waitKey();
    return ;
}
 
 
        结果令人满意
       
           3)是采用真实的图片和真实的卷积核作为输入
          结论除了在图片的边界有差异外,在其他的地方,这个差异在小数点后4位,应该说是非常相似的,可以用于实际生成。
          三、小结和反思
          最后的结论是可以正常使用,但是在输入之前,需要将图片归一化处理。那么,通过这个实验,除了获得面上的这个知识之外,更多的应该是一种实验的方法。很多时候,无论是做改写还是其他的事情之前,将相关可能发生的情况设计好;力图获得稳定准确的结果。这些对于最终获得设计之中的结论是非常重要和有价值的。
          感谢阅读到此,希望能够有所帮助。
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace std;
using namespace cv; int _tmain(int argc, _TCHAR* argv[])
{
////filter2d的卷积方法
printf( "filter2d的卷积方法\n");
//Mat srcMat(10,10,CV_32F);
//Mat dstMat(10,10,CV_32F);
Mat srcH(3,3,CV_32F);
srcH.at<float>(0,0) = -2;
srcH.at<float>(0,1) = -1;
srcH.at<float>(0,2) = 4;
srcH.at<float>(1,0) = 3;
srcH.at<float>(1,1) = 3;
srcH.at<float>(1,2) = 3;
srcH.at<float>(2,0) = 3;
srcH.at<float>(2,1) = 2;
srcH.at<float>(2,2) = 1;
printf( "卷积核\n");
for (int i=0;i<srcH.rows;i++){
for (int j=0;j<srcH.cols;j++){
printf("%f ",srcH.at<float>(i,j) );
}
printf("\n");
}
//printf( "输入\n");
//for (int i = 0; i < 10; i++){
// for (int j = 0; j < 10; j++)
// srcMat.at<float>(i,j) = i+1;
//}
//for (int i = 0; i < 10; i++){
// for (int j = 0; j < 10; j++){
// printf("%.1f ",srcMat.at<float>(i,j));
// }
// printf("\n");
//}
//printf( "输出\n");
//filter2D(srcMat,dstMat,srcMat.depth(),srcH);
//printf("\n"); printf("\n");
//for (int i = 0; i < 10; i++){
// for (int j = 0; j < 10; j++){
// printf("%.1f ",dstMat.at<float>(i,j));
// }
// printf("\n");
//}
//读取图片的处理的方法
Mat gray = imread("test.jpg",0);
imwrite("gray.jpg",gray);
gray.convertTo(gray,CV_32F);
gray = gray/255; //归一化处理
fstream ftxt;
ftxt.open("src.txt",ios::out); //写入的方式,同时是append模式的就不会覆盖掉前面的东西了。
for (int i = 0; i < gray.rows; i++){
for (int j = 0; j < gray.cols; j++){
ftxt<<gray.at<float>(i,j)<<" ";
//printf("%.1f ",dst.at<float>(i,j));
}
ftxt<<endl;
//printf("\n");
}
ftxt.close();
Mat dst;
filter2D(gray,dst,gray.depth(),srcH); ftxt.open("rst.txt",ios::out); //写入的方式,同时是append模式的就不会覆盖掉前面的东西了。
for (int i = 0; i < gray.rows; i++){
for (int j = 0; j < gray.cols; j++){
ftxt<<dst.at<float>(i,j)<<" ";
//printf("%.1f ",dst.at<float>(i,j));
}
ftxt<<endl;
//printf("\n");
}
ftxt.close();
imshow("dst",dst); waitKey();
return 0;
}

p.s 转一篇有用博文,时间久了原始链接已经丢失,抱歉

Overview:
imfill是matlab的一个函数,在http://www.mathworks.cn/cn/help/images/ref/imfill.html 中有详细的讲解。这个函数有好几种不同的签名。在这里我的侧重点是imfill(m, 'holes'),以及如何用openCV来实现imfill一样的功能。本文有三部分组成。
 
1. 使用Matlab 的imfill 进行填充图像
在Matlab中简单的几行代码就能实现:
1
2
3
4
5
6
7
8
 
clc;
clear;
BW = im2bw( imread('imfilltest.tif'));
imshow(BW);
holes = imfill(BW, 'holes');

BW(~holes) = 1; 
figure,imshow(holes);

左图为填充前的图像,右图是填充后的图像:
2. 用opencv来实现imfill(bw, 'holes')
opencv 在这里不像matlab那么好用了,matlab调用下。
 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
 
#include 
#include 
#include

using namespace std;
using namespace cv;

void my_imfillholes(Mat &src)
{
   // detect external contours
   //
   vector > contours;
   vector hierarchy;
   findContours(src, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
   //
   // fill external contours
   //
   if( !contours.empty() && !hierarchy.empty() )
   {
      for (int idx=0;idx < contours.size();idx++)
      {
         drawContours(src,contours,idx,Scalar::all(255),CV_FILLED,8);
      }
   }
}

void test_my_imfillholes()
{
   Mat m = imread(filltestName,IMREAD_GRAYSCALE);
   //threshold, (i,j)>100 -->255
   Mat th_m;
   threshold(m, th_m, 100, 255, THRESH_BINARY);
   my_imfillholes(th_m);
   namedWindow(WinName, CV_WINDOW_AUTOSIZE);
   imshow(WinName, th_m);
   waitKey(0); 
}

void main()
{
   test_my_imfillholes();
   system("pause");
}

3. imfill 和opencv实现的imfill 对矩阵进行操作的对比
我仍有点不放心,觉得尽管2幅图看起来差不多,但是是不是完全一样呢,然后我觉得用个矩阵试一下。
m = [1, 1, 1, 0, 0, 0, 0, 0;
         1, 0, 1, 0, 1, 1, 0, 0;
         1, 0, 1, 0, 1, 1, 0, 0;
         1, 1, 1, 0, 1, 0, 1, 0;
         1, 0, 1, 0, 1, 0, 1, 0;
         1, 1, 1, 0, 1, 0, 1, 0;
         1, 0, 1, 0, 0, 1, 1, 0;
         1, 1, 1, 0, 0, 0, 0, 0];
without_holes = imfill(m, 'holes')
 
得到结果:
without_holes =
     1     1     1     0     0     0     0     0
     1     1     1     0     1     1     0     0
     1     1     1     0     1     1     0     0
     1     1     1     0     1     1     1     0
     1     1     1     0     1     1     1     0
     1     1     1     0     1     1     1     0
     1     1     1     0     0     1     1     0
     1     1     1     0     0     0     0     0
然后用第2部分所说的opencv的方法也试一下,结果发现是这样的:
without_holes =
     0     0     0     0     0     0     0     0
     0     1     1     0     1     1     0     0
     0     1     1     0     1     1     0     0
     0     1     1     0     1     1     1     0
     0     1     1     0     1     1     1     0
     0     1     1     0     1     1     1     0
     0     1     1     0     0     1     1     0
     0     0     0     0     0     0     0     0
是不一样的。这个问题折腾了我一个晚上,终于,我在
中的 findContours找到了这样的一个note:
Note:
Source image is modified by this function. Also, the function does not take into account 1-pixel border of the image (it’s filled with 0’s and used for neighbor analysis in the algorithm), therefore the contours touching the image border will be clipped.
它的意思是,findCountours 是不会包含1-pixel的边界的。所以这就是为啥opencv计算的结果中边界的1都消失的原因。我想,既然边界不被包含,那么我给它上下左右加一个1-pixel的框,这样边界点就变成了内部点,就能成功的用findCountours了。于是乎:我写了下面的code:
C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 
#include 
#include 
#include

using namespace std;
using namespace cv;

void my_imfillholes_v2()
{
   //step 1: make a border
   Mat m(8, 8, CV_8UC1, data);
   Mat m_with_border;
   copyMakeBorder(m, m_with_border, 1, 1, 1, 1, BORDER_CONSTANT, Scalar());
   cout<<m_with_border<<endl;

//setp 2: find the contour fill holes
   vector > contours;
   vector hierarchy;
   findContours(m_with_border, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
   //
   // fill external contours
   // 
   if( !contours.empty() && !hierarchy.empty() )
   {
      for (int idx=0;idx < contours.size();idx++)
      {
         drawContours(m_with_border,contours,idx,Scalar::all(1),CV_FILLED,8);
      }
   }
   //cout<<m_with_border<<endl;
   //step 3: remove the border
   m_with_border = m_with_border.rowRange(Range(1, m_with_border.rows-1));
   //cout<<m_with_border<<endl;
   m_with_border = m_with_border.colRange(Range(1, m_with_border.cols-1));
   cout<<m_with_border<<endl;
}

void main()
{
   my_imfillholes_v2();
   system("pause");
}

 
先加一个全0的1-pixel的框,然后在findCountours填充,最后把框给去了。这样结果就完全和matlab中的imfill一致了。
result =
     1     1     1     0     0     0     0     0
     1     1     1     0     1     1     0     0
     1     1     1     0     1     1     0     0
     1     1     1     0     1     1     1     0
     1     1     1     0     1     1     1     0
     1     1     1     0     1     1     1     0
     1     1     1     0     0     1     1     0
     1     1     1     0     0     0     0     0

实际比较filter2D和imfilter之间的关系的更多相关文章

  1. .NET Core与.NET Framework、Mono之间的关系

    随着微软的.NET开源的推进,现在在.NET的实现上有了三个.NET Framework,Mono和.NET Core.经常被问起Mono的稳定性怎么样,后续Mono的前景如何,要回答这个问题就需要搞 ...

  2. .NET Core 和 .NET Framework 之间的关系

    引用一段描述:Understanding the relationship between .NET Core and the .NET Framework. .NET Core and the .N ...

  3. 实体之间的关系【Entity Relationships】(EF基础系列篇9)

    Here, you will learn how entity framework manages the relationships between entities. Entity framewo ...

  4. php CGI、Fastcgi、PHP-FPM的详细介绍与之间的关系

    以下PHP CGI.Fastcgi.PHP-FPM的一些信息归纳和汇总----->详细介绍与之间的关系 一:CGI是干嘛的?CGI是为了保证web server传递过来的数据是标准格式的 web ...

  5. [转] valuestack,stackContext,ActionContext.之间的关系

    三者之间的关系如下图所示: ActionContext  一次Action调用都会创建一个ActionContext  调用:ActionContext context = ActionContext ...

  6. angular源码阅读,依赖注入的原理:injector,provider,module之间的关系。

    最开始使用angular的时候,总是觉得它的依赖注入方式非常神奇. 如果你跳槽的时候对新公司说,我曾经使用过angular,那他们肯定会问你angular的依赖注入原理是什么? 这篇博客其实是angu ...

  7. JavaScript和Java之间的关系

    今天来简单而又详细地说说JavaScript和Java的关系. 开门见山总结性一句话,它们之间的关系 = 雷锋和雷峰塔之间的关系,换句话说:它们之间没什么关系. 但往往有不少初学者甚至中级者认为它们之 ...

  8. PHP类和对象之间的关系

    类是对象的描述: 类和对象之间的关系类似于建房子的图纸和房子: 创建类--new对象--用对象: 一.对象的两个特性: --对象的行为(功能):可以对对象施加操作,例如,电视机的开.关.转换频道: - ...

  9. 关于计算机的ID和用户ID之间的关系

    关于计算机的ID和用户ID之间的关系 计算机安装完系统后就会生成计算机ID,然后系统会以计算机ID为前缀附加数字创建Administrator(500)和Guest(501)用户ID,其他用户的ID将 ...

随机推荐

  1. 在点击div中的p时,如何阻止事件冒泡?

    今天整理笔记,发现在学习javaScript的过程中,遇到过一个在当时看来很棘手的问题,现在特地总结一下,也希望能帮助到曾像我一样迷惘的初学者. 我还是以一个案例来说明问题,html代码如下: < ...

  2. ArcGIS制图表达Representation实战篇4-自由式制图表达

    ArcGIS制图表达Representation实战篇4-自由式制图表达 by 李远祥 上一章节关于制图表达的控制点中已经介绍过制图表达的编辑功能,利用制图表达的编辑功能,可以实现一些规则以外的效果. ...

  3. maven 配置安装

    1.下载maven http://maven.apache.org/   2.windows安装maven 解压包后配置环境变量 PATH:%M2_HOME%\bin M2_HOME:D:\soft\ ...

  4. JDBC连接数据库(二)——连接池

    参考博客:http://www.cnblogs.com/xdp-gacl/p/4002804.html 一. 应用程序直接建立数据库连接模型 应用程序直接每次访问数据库时,都建立创建一个数据库的链接, ...

  5. ios跳转到系统设置

    在项目中,我们经常会碰到使用位置的需求.当用户设置app不允许使用位置的时候,最好的用户体验就是直接调转到系统的位置设置界面,进行设置. 第一 跳转到自己项目(在需要调转的按钮动作中添加如下的代码,就 ...

  6. php查询,多条件查询

    单条件查询: 1.先要有一张表,显示出表中的数据: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ...

  7. Keepalived安装与配置

      下载并解压Keepalived安装包到两台nginx所在的服务器   192.168.200.1   192.168.200.2     执行编译安装(安装目录设置为 /usr/local/kee ...

  8. 《C++ Primer》学习笔记 :命名空间的using声明

    最近在学C++,在<C++ Primer>第五版的3.1节中说到使用using声明来使用命名空间中的成员,<C++ Primer>中这样写道: 有了using声明就无须专门的前 ...

  9. ASP.NET通用权限框架 权限管理系统源码jquery 精美UI源码

    软件技术开发,合作请联系QQ:858-048-581 开发工具 VS2010 .sql2005.2008等(在Sql server数据执行脚本即可)  VS2010 打开保证本地运行成功(数据库.源代 ...

  10. [Qt初级] 解决 中QMainWindow和QDockWidget添加布局失败问题

    初接触Qt,使用的教程是陆文周编写的<Qt5开发及实例>一书. 其中有关于QDockWidget.QStackedWidget这些类的介绍和使用实例. 要首先说明的是书上讲的非常的清楚,代 ...