OpenCV的濾波與卷積

目錄

預備知識

濾波、核和卷積

邊界外推和邊界處理

閾值化操作

Otsu算法

自適應閾值

平滑

簡單模糊和方框型濾波器

中值濾波器

高斯濾波器

雙邊濾波器

導數和梯度

索貝爾導數

Scharr濾波器

拉普拉斯變換

圖像形態學

膨脹和腐蝕

通用形態學函數

開操作和閉操作

形態學梯度

頂帽和黑帽

自定義核

用任意線性濾波器做卷積

用cv::filter2D()進行卷積

通過cv::sepFilter2D使用可分核

生成卷積核


預備知識

濾波、核和卷積

濾波器指的是一種由一幅圖像 I(x,y)根據像素點x,y附近的區域計算得到一幅新圖像 I'(x,y)的算法。其中,模板規定了濾波器的形狀以及這個區域內像素的值的組成規律,也稱“濾波器”或“核”。在下面的介紹中多采用的是線性核,即 I'(x,y)的像素的值由 I(x,y)及其周圍的像素的值的加權相加得來的。可由以下方程表示:

I'(x, y)=\sum _{i, \: j\in kernal}k_{i,\: j}\cdot I(x+i,\: y+j)

\begin{bmatrix} 1 & 1 & 1& 1 & 1\\ 1 & 1& 1& 1 & 1\\ 1& 1 & \mathbf{1} & 1& 1\\ 1 & 1& 1 & 1 & 1\\ 1 & 1 & 1 & 1& 1 \end{bmatrix} \frac{1}{25}\begin{bmatrix} 1 & 1 & 1& 1 & 1\\ 1 & 1& 1& 1 & 1\\ 1& 1 & \mathbf{1} & 1& 1\\ 1 & 1& 1 & 1 & 1\\ 1 & 1 & 1 & 1& 1 \end{bmatrix} \begin{bmatrix} -1 & 0 &1 \\ -2 &\mathbf{0} & 2\\ -1& 0& 1 \end{bmatrix} \frac{1}{273}\begin{bmatrix} 1 & 4 & 7& 4 & 1\\ 4 & 16& 26& 16 & 4\\ 7& 26 & \mathbf{41} & 26& 7\\ 4 & 16& 26 & 16 & 4\\ 1 & 4 & 7 & 4& 1 \end{bmatrix}
(A)5×5盒狀核 (B)規範化的5×5盒狀核 (C)3×3的Sobel核 (D)5×5規範化高斯核

注:“錨點”均用粗體表示

邊界外推和邊界處理

自定義邊框

在處理圖像時,只要告訴調用的函數添加虛擬像素的規則,庫函數就會自動創建虛擬像素。cv::copyMakeBorder()就是一個爲圖像創建邊框的函數。

cv::copyMakeBorder(InputArray src, InputArray dst, int top, int bottom, int left, int right, int borderType, const cv::Scalar& value=cv::Scalar())

作用:通過指定兩幅圖像,同時指明填充方法,該函數就會將第一幅圖填補後的結果保存在第二幅圖像中。其中,src是原圖像,dst是填充後的圖像,top、bottom、left、right分別是四個方向上的尺寸,borderType是像素填充的方式,value是常量填充時的值。

borderType的取值及效果
取值 效果
cv::BORDER_CONSTANT 爲每個邊框像素賦予一個相同的值。
cv::BORDER_WRAP 類似於平鋪擴充
cv::BORDER_REPLICATE 複製邊緣的像素擴充
cv::BORDER_REFLECT 通過鏡像複製擴充
cv::BORDER_REFLECT_101 通過鏡像複製擴充,邊界像素除外
cv::BORDER_DEFAULT cv::BORDER_REFLECT_101

自定義外推

 int cv::borderInterpolate(int p, int len, int borderType)

作用:計算一個維度上的外推,p爲原圖上一個座標,len是p指維度上的大小,borderType是邊界類型。

例子:混合的邊界條件下計算一個特定像素的值,在一維中使用BORDER_REFLECT_101,在二維中使用BORDER_WRAP:

float val = img.at<float>(
    cv::borderInterpolate(100, img.rows, cv::BORDER_REFLECT_101),
    cv::borderInterpolate(-5, img.cols, cv::BORDER_WRAP)
)

閾值化操作

閾值化操作的原理是對於數組中每個值,根據其高於或低於某個閾值做出相對應的處理,OpenCV中提供了實現這種功能的方法cv::threshold()。

double cv::threshold(InputArray src, cv::OutputArray dst, double thresh, double maxValue, int thresholdType)

thresholdType的可選項及其操作
閾值類型 操作
cv::THRESH_BINARY DST_i=(SRC_i > thresh)\: ?\: MAXVALUE:0
cv::THRESH_BINARY_INV DST_i=(SRC_i > thresh)\: ?\: 0:MAXVALUE
cv::THRESH_TRUNC DST_i=(SRC_i > thresh)\: ?\: THRESH:SRC_i
cv::THRESH_TOZERO DST_i=(SRC_i > thresh)\: ?\: SRC_i:0
cv::THRESH_TOZERO_INV DST_i=(SRC_i > thresh)\: ?\: 0:SRC_i
不同閾值類型對應的結果

 

例一:將一幅圖像的三個通道相加並將像素值限制在100以內

#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;

void sum_rgb(const cv::Mat& src, cv::Mat& dst) {
	// 分通道
	vector<cv::Mat> planes;
	cv::split(src, planes);

	cv::Mat b = planes[0];
	cv::Mat g = planes[1];
	cv::Mat r = planes[2];
	cv::Mat s;

	// 加權融合,防止越界
	cv::addWeighted(r, 1. / 3, g, 1. / 3, 0.0, s);
	cv::addWeighted(s, 1., r, 1. / 3, 0.0, s);

	// 閾值化操作
	cv::threshold(s, dst, 100, 100, cv::THRESH_TRUNC);
}

void help() {
	cout << "Call: ./ch10_ex10_1 faceScene.jpg" << endl;
	cout << "Show use of alpha blending (addWeighted) and threshold" << endl;
}

int main()
{
	help();

	cv::Mat src = cv::imread("D:\\personal-data\\wallpapers\\test.png");
	cv::Mat dst;
	if (src.empty())
	{
		cout << "can not load the image" << endl;
		return -1;
	}

	sum_rgb(src, dst);
	cv::imshow("img", dst);
	cv::waitKey(0);
	cv::destroyAllWindows();
    return 0;
}

運行結果: 

例二:對於浮點型圖像進行三個通道相加並將像素值限制在100以內

#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;

void sum_rgb(const cv::Mat& src, cv::Mat& dst) {
	// 分通道
	vector<cv::Mat> planes;
	cv::split(src, planes);

	cv::Mat b = planes[0];
	cv::Mat g = planes[1];
	cv::Mat r = planes[2];

	// 全0初始化s矩陣
	cv::Mat s = cv::Mat::zeros(b.size(), CV_32F);
	// accumulate可將8位整型的圖像累加到一幅浮點型的圖像中
	cv::accumulate(b, s);
	cv::accumulate(g, s);
	cv::accumulate(r, s);

	// 閾值化操作
	cv::threshold(s, dst, 100, 100, cv::THRESH_TRUNC);
	s.convertTo(dst, b.type());
}

void help() {
	cout << "Call: ./ch10_ex10_1 faceScene.jpg" << endl;
	cout << "Show use of alpha blending (addWeighted) and threshold" << endl;
}

int main()
{
	help();

	cv::Mat src = cv::imread("D:\\personal-data\\wallpapers\\test.png");
	cv::Mat dst;
	if (src.empty())
	{
		cout << "can not load the image" << endl;
		return -1;
	}

	sum_rgb(src, dst);
	cv::imshow("img", dst);
	cv::waitKey(0);
	cv::destroyAllWindows();
	return 0;
}

運行結果:

Otsu算法

函數cv::threshold()也可以自動決定最優的閾值,只需將參數thresh傳遞值cv::THRESH_OTSU即可。

簡而言之,Otsu算法就是遍歷所有可能的閾值,然後對每個閾值結果的兩類像素(低於閾值和高於閾值兩類像素)計算方差\sigma _i^2,然後計算\sigma _w^2的值,取其最小的閾值。

\sigma _w^2\equiv w_1(t)\cdot \sigma _1^2+w_2(t)\cdot \sigma _2^2

式中,w_1(t)w_2(t)是根據兩類像素的數量計算而來的權重。由於要遍歷所有可能的閾值,所以這並不是一個相對高效的過程。

自適應閾值

自適應閾值方法中閾值在整個過程中自動產生變化,這由OpenCV中的cv::adaptiveThreshold()實現。

void cv::adaptiveThreshold(InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int threshType, int blockSize, double C)

該方法是逐個像素地計算自適應閾值T(x, y),具體計算過程是:計算每個像素位置周圍的blockSize×blockSize區域的加權平均值,然後減去常數C。求均值時所用權重和adaptiveMethod有關,若是cv::ADAPTIVE_THRESH_MEAN_C,則權重相等,若是cv::ADAPTIVE_THRESH_GAUSSIAN_C,則權重由高斯方差得到。

注:該方法只適應與單通道8位或浮點型圖像

#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;

int main()
{
	
	// 設置參數
	double fixed_threshold = 15;
	int threshold_type = 1 ? cv::THRESH_BINARY : cv::THRESH_BINARY_INV;
	int adaptive_method = 1 ? cv::ADAPTIVE_THRESH_MEAN_C : cv::ADAPTIVE_THRESH_GAUSSIAN_C;
	int block_size = 71;
	double offset = 15;
	// 以灰度圖形式加載圖片
	cv::Mat Igray = cv::imread("D:\\personal-data\\wallpapers\\test.png", cv::IMREAD_GRAYSCALE);

	// 判斷圖像是否加載成功
	if (Igray.empty()) { 
		cout << "Can not load " << "D:\\personal-data\\wallpapers\\test.png" << endl; 
		return -1; 
	}

	// 聲明輸出矩陣
	cv::Mat It, Iat;

	// 閾值化操作
	cv::threshold(
		Igray,
		It,
		fixed_threshold,
		255,
		threshold_type);
	// 自適應閾值
	cv::adaptiveThreshold(
		Igray,
		Iat,
		255,
		adaptive_method,
		threshold_type,
		block_size,
		offset
	);

	// 展示結果圖像
	cv::imshow("Raw", Igray);
	cv::imshow("Threshold", It);
	cv::imshow("Adaptive Threshold", Iat);
	cv::waitKey(0);
	cv::destroyAllWindows();
    return 0;
}

運行結果:

當threshold_type=1且adaptive_method=1

原圖

閾值化操作

自適應閾值化

當threshold_type=0且adaptive_method=0

原圖

閾值化操作

自適應閾值化

平滑

平滑也稱“模糊”,是一種簡單而又常用的圖像處理操作。平滑圖像的目的有很多,但通常都是爲了減少噪聲和僞影。在降低圖像分辨率的時候,平滑也十分重要,可以防止圖片出現鋸齒狀。

簡單模糊和方框型濾波器

void cv::blur(InputArray src,OutputArray dst,Size ksize,Point anchor=Point(-1,-1),int borderType=BORDER_DEFAULT)

作用:實現簡單的濾波,目標圖像中的每個值都是原圖像中相應位置一個窗口(核)中像素的平均值,窗口的尺寸由ksize聲明;anchaor指定計算時核與源圖像的對齊方式,默認情況下anchor爲cv::Point(-1, -1),表示核相對於濾波器居中。

void cv::boxFilter(InputArray src, OutputArray dst, cv::Size ksize, cv::Point anchor=cv::Point(-1,-1), bool normalize=true;  int borderType=cv::BORDER_DEFAULT)

作用:方框濾波器是一種矩形的並且濾波器中所有值k_{i,\: j}全部相等的濾波器。通常,所有的k_{i,\: j}爲1或者1/A,其中A是濾波器的面積。後一種濾波器稱爲“歸一化方框型濾波器”,下面所示的是一個5×5的模糊濾波器,也稱“歸一化方框型濾波器”。

\frac{1}{25}\begin{bmatrix} 1 & 1 & 1& 1 & 1\\ 1 & 1& 1& 1 & 1\\ 1& 1 & \mathbf{1} & 1& 1\\ 1 & 1& 1 & 1 & 1\\ 1 & 1 & 1 & 1& 1 \end{bmatrix}

通過上述介紹,可以發現cv::boxFilter()是一種一般化的形式,而cv::blur()是一種特殊化的形式。但前者可以以非歸一化形式調用,並且輸出圖像深度可以控制,但後者智能以歸一化形式調用,且輸出圖像深度必須和原圖像保持一致。

中值濾波器

中值濾波器(Median Filter)將每個像素替換爲圍繞這個像素的矩形領域內的中值或“中值”像素(相對於平均像素)。通過均值濾波器對噪聲圖像,尤其是有較大孤立的異常值非常敏感,少量具有較大偏差的點也會嚴重影響到均值濾波器。中值濾波器可以採用取其中間點的方式來消除異常值。

void cv::medianBlur(InputArray src, OutputArray dst, Size ksize)

高斯濾波器

關於高斯濾波器,在之前的文章中已做了詳細介紹,可以參考OpenCV高斯濾波GaussianBlur

雙邊濾波器

雙邊濾波器是一種比較大的圖像分析算子,也就是邊緣保持平滑。高斯平滑的模糊過程是減緩像素在空間上的變化,因此與鄰近的關係緊密,而隨機噪聲在像素間的變化幅度又會非常的大(即噪聲不是空間相關的)。基於這種前提高斯平滑很好地減弱了噪聲並且保留了小信號,但是卻破壞了邊緣信息,邊緣也模糊了。

和高斯平滑類似,雙邊濾波對每個像素及其領域內的像素進行了加權平均。其權重由兩部分組成,第一部分同高斯平滑,第二部分也是高斯權重,但是它不是基於空間距離而是色彩強度差計算而來的,在多通道(色彩)圖像上強度差由各分量的加權累加代替。可將其當做高斯平滑,指示相似程度更高的像素權值更高,邊緣更加明顯,對比度更高。

cv::bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType= cv::BORDER_DEFAULT)

參數:d是像素鄰近的最大距離,處理視頻時一般不大於5,非實時應用時可放大到9;sigmaColor是色彩空間濾波器的sigma值,該值越大,則色彩強度越大,不連續性越強;sigmaSpace是座標空間濾波器的sigma值。

導數和梯度

卷積中最重要也是最基本的部分就是(近似)計算導數。

索貝爾導數

一般來說,用來表示微分的最常用的算子是索貝爾(Sobel)導數算子,可以實現任意階導數和混合偏導數。

void sv::Sobel(InputArray src, OutputArray dst, int ddepth, int xorder, int yorder, cv::Size ksize=3, double scale=1, double delta=0, int borderType=cv::BORDER_DEFAULT)

參數:ddepth指明目標圖像的深度或類型;xorder和yorder是求導順序,其取值可爲0,、1或2,其中0代表在該方向不求導,kszie是一個奇數,表示濾波器核的大小, 目前最大到31;閾值和偏移量在結果存入dst前進行調用,公式如下:

dst_i=scale\cdot \left \{ \sum _{i,\: j\in sobel\_kernel}k_{i,\: j}*I(x+i, y+j) \right \}+delta

Sobel算子的好處是可以將覈定義爲各種大小,並且可以快速迭代式地構造這些核。大的核可以更好地近似導數,因爲可以消除噪聲影響。其缺點是如果導數在空間上變化劇烈,核太大會使結果發生偏差,並且核比較小時準確度不高。

實際上,由於Sobel算子定義在離散空間上,所以它並不是真正的導數,而是一個多項式,即在x方向上進行Sobel運算表示的並不是二階導數,而是對拋物線函數的局部擬合。

Scharr濾波器

爲了將圖像內的信息聯繫起來,可能需要測量一幅圖像:在處理過程中,通過在目標附近組織一幅梯度直方圖來收集其形狀信息,這些直方圖是許多形狀分類器訓練和使用的基礎。因此,梯度角的誤差會降低分類器識別的效果。

對於3×3的Sobel濾波器,梯度角距離水平或垂直方向越遠,誤差越明顯。在OpenCV中,調用cv::Sobel()時設置ksize爲cv::SCHARR,即可消除3×3這樣小但是快的Sobel導數濾波器所帶來的誤差。Scharr濾波器核Sobel濾波器同樣很快,但是前者精度更高。因此選擇3×3的濾波器時,應當使用Scharr濾波器。

\begin{bmatrix} -3 & 0 & +3\\ -10 & 0 &+10 \\ -3& 0 & +3 \end{bmatrix}     或     \begin{bmatrix} -3 & -10 & -3\\ 0 & 0 &0 \\ +3& +10 & +3 \end{bmatrix}

拉普拉斯變換

OpenCV中的函數Laplacian實現了對拉普拉斯算子的離散近似:

Laplace(f)=\frac{\partial^2 f}{\partial x^2} + \frac{\partial^2 f}{\partial y^2}

void sv::Laplacian(InputArray src, OutputArray dst, int ddepth, cv::Size ksize=3, double scale=1, double delta=0, int borderType=cv::BORDER_DEFAULT)

只要ksize不爲1,Laplacian算子的實現就是直接計算Sobel算子響應之和。當ksize=1時的卷積核如下所示:

\begin{bmatrix} 0 & 1 & 0\\ 1 & 4 &1 \\ 0& 1 & 0 \end{bmatrix}

Laplacian算子可應用於各種場景處理,一種常見的應用就是匹配“斑點”。Laplacian算子就是圖像在x和y軸方向的導數之和,這意味着一個被較大值包圍的點或小斑點(比ksize小)處的值將會變得很大。相反,被較小值包圍的點或小斑點處的值將在負方向上變得很大。

Laplacian算子同樣可以用於邊緣檢測,函數一階導數在原函數變化大的地方,值會相應變大,同樣在圖像邊緣處也同樣變化,所有導數在這些地方將變得很大。因此可以在二階導數爲0的地方搜尋這麼一個極大值,原圖像中的邊緣通過Laplacian算子運算後會變成0。對於有些不是邊緣也變成0的問題,可以通過濾掉Sobel一階導數中較大值的點解決。

圖像形態學

OpenCV提供了一種高效且易用的圖像形態學變換接口。其中有很多形態學方法,但基本上所有的形態學操作都基於兩種原始操作——膨脹與腐蝕。

膨脹和腐蝕

膨脹和腐蝕是最基本的形態學變換,可用於消除噪聲、元素分割和連接等。基於這兩種操作,可以實現更復雜的形態學操作,用來定位強度峯值或孔洞、另一種形式的圖像梯度等。

膨脹是一種卷積操作,它將目標像素的值替換爲卷積核覆蓋區域的局部最大值,擴張了明亮區域,填充凹面。此卷積核是一個非線性核,是一個四邊形或圓形的實心核,其錨點在中心。與膨脹對應,腐蝕是與之相反的操作,腐蝕操作計算的是核覆蓋範圍內的局部最小值縮減了明亮區,消除凸起。

原圖

膨脹

腐蝕

void cv::erode(InputArray src, OutputArray dst, InputArray element, cv::Point anchor=cv::Point(-1, -1), int iterations=1 int borderType=cv::BORDER_CONSTANT, const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue())

void cv::dilate(InputArray src, OutputArray dst, InputArray element, cv::Point anchor=cv::Point(-1, -1), int iterations=1 int borderType=cv::BORDER_CONSTANT, const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue())

以上兩個函數分別是膨脹和腐蝕對應的函數,第三個參數是核,可以傳遞一個未初始化的cv::Mat,會使用默認的錨點在中心的3×3的核。

erode(x, y)=\underset{(i,\: j)\in kernel}{min}src(x+i,\: y+j)

dilate(x, y)=\underset{(i,\: j)\in kernel}{max}src(x+i,\: y+j)

腐蝕操作通常用於消除圖中斑點一樣的噪聲,原理是斑點經過腐蝕後會消失,而大的可見區域不會受影響。膨脹操作通常用於發生連通分支。

通用形態學函數

當處理的對象是二值圖像時,像素只能是開(>0)或關(=0)的圖像掩膜時,基本的腐蝕和膨脹操作就夠用了。需要對灰度圖或者彩色圖進行處理時,一些其他操作就非常有用了,這些操作可以通過cv::morphologyEx()實現。

void cv::morphologyEx(InputArray src, OutputArray dst, int op, InputArray element, cv::Point anchor=cv::Point(-1, -1), int iterations=1, int borderType=cv::BORDER_CONSTANT, const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue())

cv::morphologyEx()操作op選項
操作值 形態學操作 是否需要臨時圖像
cv::MORPH_OPEN 開操作
cv::MORPH_CLOSE 閉操作
cv::MORPH_GRADIENT 形態學梯度 總是需要
cv::MORPH_TOPHAT 頂帽操作 就地調用需要(src = dst)
cv::MORPH_BLACKHAT 地貌操作 就地調用需要(src = dst)

開操作和閉操作

開操作先將圖像進行腐蝕,然後對腐蝕的結果進行膨脹。開操作常用語對二值圖像中的區域進行計算。

閉操作想將圖像進行膨脹,然後對膨脹的結果進行腐蝕。閉操作用於複雜連通分支算法中減少無用或噪聲驅動的片段。

對於連通分支,通常先進行腐蝕或閉操作消除噪聲,然後通過開操作連接相互靠近的大型區域。

對於一幅非布爾型圖像進行形態學操作時,閉操作最明顯的效果是消除值小於鄰域內的點的孤立異常,而開操作消除的是大於鄰域內點的孤立異常值。

形態學梯度

gradient(src)=dilate(src)-erode(src)

梯度操作的結果(擴張亮域)減腐蝕操作的結果(縮減亮域)產生了原圖像中的目標邊緣。對於灰度圖像,其結果就是計算明暗變換的趨勢。形態學梯度通常用於顯示明亮區域的邊界,然後便可以將他們看作目標或者目標的部分。用擴張的圖像減去了收縮的圖像便得到完整的邊界。與計算梯度不同,它並不會關注某個物體的周圍。

頂帽和黑帽

TopHat(src)=src-open(src)

BlackHat(src)=close(src)-src

頂帽用於顯示與其鄰近相比更亮的部分;黑帽用於顯示與其鄰近相比更暗的部分。

#include "stdafx.h"
#include <opencv2/opencv.hpp>


int main()
{
	cv::namedWindow("image", cv::WINDOW_NORMAL);
	cv::namedWindow("erosion", cv::WINDOW_NORMAL);
	cv::namedWindow("dilation", cv::WINDOW_NORMAL);
	cv::namedWindow("opening", cv::WINDOW_NORMAL);
	cv::namedWindow("closing", cv::WINDOW_NORMAL);
	cv::namedWindow("gradient", cv::WINDOW_NORMAL);
	cv::namedWindow("topHat", cv::WINDOW_NORMAL);
	cv::namedWindow("blackHat", cv::WINDOW_NORMAL);
	cv::Mat img = cv::imread("D:\\personal-data\\wallpapers\\test.png");
	cv::Mat erosion, dilation, opening, closing, gradient, topHat, blackHat;
	cv::erode(img, erosion, cv::Mat());
	cv::dilate(img, dilation, cv::Mat());
	cv::morphologyEx(img, opening, cv::MORPH_OPEN, cv::Mat());
	cv::morphologyEx(img, closing, cv::MORPH_CLOSE, cv::Mat());
	cv::morphologyEx(img, gradient, cv::MORPH_GRADIENT, cv::Mat());
	cv::morphologyEx(img, topHat, cv::MORPH_TOPHAT, cv::Mat());
	cv::morphologyEx(img, blackHat, cv::MORPH_BLACKHAT, cv::Mat());
	cv::imshow("image", img);
	cv::imshow("erosion", erosion);
	cv::imshow("dilation", dilation);
	cv::imshow("opening", opening);
	cv::imshow("closing", closing);
	cv::imshow("gradient", gradient);
	cv::imshow("topHat", topHat);
	cv::imshow("blackHat", blackHat);
	cv::waitKey(0);
	cv::destroyAllWindows();
    return 0;
}

自定義核

在形態學上,核常常稱爲“構造元素”,OpenCV提供了創建自定義形態學核的函數cv::getStructuringElement()。

cv::Mat cv::getStructuringElement(int shape, cv::Size ksize, cv::Point anchor=cv::Point(-1, -1))

cv::getStructuringElement()的元素形狀
形狀值 元素 描述
cv::MOEPH_RECT 矩形 E_{i,\: j}=1,\forall i,j
cv::MOEPH_ELLIPSE 橢圓形 以ksize.width和ksize.height爲兩個半徑做橢圓
cv::MOEPH_CROSS 交叉

E_{i,\: j}=1,當 i == anchor.y或 j == anchor.x

用任意線性濾波器做卷積

\begin{bmatrix} -1 & 0 & +1\\ -2& 0 & +2\\ -1& 0 & +1 \end{bmatrix}=\begin{bmatrix} 1\\ 2\\ 1 \end{bmatrix}\bigotimes \begin{bmatrix} -1 & 0 & +1 \end{bmatrix}                    \begin{bmatrix} 0 & 1 & 0\\ 1 &1 &1 \\ 0 & 1 & 0 \end{bmatrix}

上述兩個核中,左邊的核是可分的,右邊的是不可分的。一個可分核可以理解成兩個一維核,在卷積時先調用x內核,然後再調用y內核。兩個矩陣進行卷積所產生的消耗可以用兩個矩陣的面積之積近似。如此一來,用n×n的核對面積爲A的圖像進行卷積所需要的時間是An^2,但如果分解爲n×1和1×n的兩個核,那麼代價就是An+An=2An。由此可見,分解卷積核可以提高卷積計算的效率。

用cv::filter2D()進行卷積

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

注:如果定義了錨點的位置,那麼核的大小可以是偶數,否則必須是奇數。

通過cv::sepFilter2D使用可分核

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

注:兩個核的大小應當是n1×1和1×n2,n1和n2不一定相等。

生成卷積核

void cv::getDerivKernel(OutputArray kx, OutputArray ky, int dx, int dy, int ksize, bool normalize=true, int ktype=CV_32F)

作用:生成可分解核,如Sobel和Scharr核。dx和dy是求導順序;ksize是核的大小,可以爲1、3、5、7或cv::SCHARR;normalize指示是否核元素規範化,如果是浮點型圖像,設爲true,反之設爲false;ktype表示濾波器的類型,可以使CV_32F和CV_64F。

cv::Mat cv::getGaussianKernel(int ksize, double sigma, int ktype=CV_32F)

作用:生成高斯核。

k_i=\alpha \cdot e^{-\frac{(i-(ksize-1)^2)^2}{(2\sigma )^2}}

α在濾波器需要規範化的時候才起作用。sigma可以爲-1,這樣將自動計算,其中\sigma =0.3\cdot (\frac{ksize-1}{2}-1)+0.8

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章