實際比較filter2D和imfilter之間的關系


實際比較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( 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");
    }
    waitKey( 100);
     return  0;
}
 
            而在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", 0);
    imwrite( "gray.jpg",gray);
    gray.convertTo(gray,CV_32F);
    Mat dst;
    filter2D(gray,dst,gray.depth(),srcH);
     for ( int i  =  0; i  < gray.rows; i ++){
         for ( int j  =  0; 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( 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");
    }
     //讀取圖片的處理的方法
    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) << " ";
        }
        ftxt <<endl;
    }
    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) << " ";
        }
         ftxt <<endl;
    }
    ftxt.close();
    imshow( "dst",dst);
    waitKey();
     return  0;
}
 
 
        結果令人滿意
        
            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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM