实际比较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. RabbitMQ确认机制问题处理

    现象: 手动在后台创建两个消息反馈队列 代码中监听到消息队列后,对消息进行处理并确认,代码为: 运行代码后,消息未从队列扔出去. 原因及解决方案:后台手动创建队列后,在监听消息中又对队列进行声明创建, ...

  2. 如何给 UILable 添加横线

    类似淘宝上的原价现价,原价上一般都会有一条横线,这种效果怎么实现呢?其实相当的简单,我们只需要重写自定义的lable的 - (void)drawRect:(CGRect)rect 方法就行了. 具体实 ...

  3. asp.net权限认证:摘要认证(digest authentication)

    asp.net权限认证系列 asp.net权限认证:Forms认证 asp.net权限认证:HTTP基本认证(http basic) asp.net权限认证:Windows认证 asp.net权限认证 ...

  4. 从C#到TypeScript - 接口

    总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...

  5. Swift 2.0 自定义cell和不同风格的cell

    昨天我们写了使用系统的cell怎样创建tableView,今天我们再细分一下,就是不同风格的cell,我们怎写代码.先自己创建一个cell,继承于UItableviewcell 我们看看 cell 里 ...

  6. static的加载先后顺序

    1.静态变量的声明和赋值是分开的,静态变量会先被声明,赋值操做被放在了静态代码块中. 2.静态变量的赋值和静态代码块的执行顺序和代码的先后书写顺序相关. 3.静态代码块优先执行,其次构造方法,最后普通 ...

  7. CSS之浏览器默认样式设置

    今天自己写css样式时,其中用到了<ul>标签,设置了一系列效果后运行,发现位置与设置有出入.chrome上打开检查项,发现<ul>标签的styles底部多了以下一段: ul, ...

  8. fiddler介绍

    先看fiddler 的使用界面和各模块的功能介绍 1请求列表:请求列表中包含了许多信息,从左至右依次为,#(序列号),Result(结果状态码),Prottocol(请求的协议),Host(请求的主机 ...

  9. dns服务搭建

    DNS 是域名系统 (Domain Name System) 的缩写,它是由解析器和域名服务器组成的. 域名服务器是指保存有该网络中所有主机的域名和对应IP地址,并具有将域名转换为IP地址功能的服务器 ...

  10. ABP Zero 单部署,单数据库,多租户架构

    首先,我们应该定义多租户系统中的两个条目: 租主(Host):租主是单例的(只有一个租主).租主会对创建和管理租户负责.因此,一个“租主用户”比所有的租户等级更高,并独立于所有租户,同时还能控制他们. ...