在該系列的第八篇文章中,我們曾介紹過一階導(dǎo)數(shù)和二階導(dǎo)數(shù)對(duì)分析邊緣的結(jié)論:
一階導(dǎo)數(shù)通常在圖像中產(chǎn)生較粗的邊緣;
二階導(dǎo)數(shù)對(duì)精細(xì)細(xì)節(jié),如細(xì)線、孤立點(diǎn)和噪聲有較強(qiáng)的響應(yīng);
二階導(dǎo)數(shù)在灰度斜坡和灰度臺(tái)階過渡處會(huì)產(chǎn)生雙邊緣響應(yīng);
二階導(dǎo)數(shù)的符號(hào)可用于確定邊緣的過渡是從亮到暗還是從暗到亮。
一階導(dǎo)數(shù)算子(例如 Sobel 算子)通過對(duì)圖像求導(dǎo)來確定圖像的邊緣,數(shù)值絕對(duì)值較高的點(diǎn)對(duì)應(yīng)了圖像的邊緣。如果繼續(xù)求二階導(dǎo),原先數(shù)值絕對(duì)值較高的點(diǎn)對(duì)應(yīng)了過零點(diǎn)。因此,也可以通過找到二階導(dǎo)數(shù)的過零點(diǎn)來檢測邊緣。在某些情況下,找二階導(dǎo)數(shù)的過零點(diǎn)可能更容易。

1. Laplace 算子
之前我們曾介紹過二階導(dǎo)數(shù)的 Laplace 算子可以通過差分近似來簡化,其公式為
它的卷積核:

這是它的 4 鄰域卷積核。
1.1 Laplace 算子的擴(kuò)展
Laplace 算子是具有旋轉(zhuǎn)不變性的各向同性的算子。
將 4 鄰域的 Laplace 算子旋轉(zhuǎn) 45° 后,與原算子相加,就可以得到 8 鄰域的算子。

這是它的 8 鄰域卷積核。這個(gè)算子表示一個(gè)像素周圍一圈 8 個(gè)像素的和與中間像素 8 倍的差,作為拉普拉斯計(jì)算結(jié)果。
另外,還有兩個(gè)拉普拉斯卷積核,分別是對(duì) 4 鄰域卷積核和 8 鄰域卷積核取反。

1.2 圖像的模糊檢測
使用拉普拉斯變換對(duì)圖像進(jìn)行模糊檢測的步驟大致如下:
對(duì)圖像進(jìn)行拉普拉斯變換,檢測水平和垂直邊緣
然后對(duì)拉普拉斯變換后輸出的圖像求方差
如果圖像足夠清晰,輸出圖像的方差會(huì)大于給定閾值
如果圖像相對(duì)模糊,則拉普拉斯變換在圖像中并不能檢測到足夠的細(xì)節(jié),邊緣就越少,從而導(dǎo)致輸出圖像的方差小于給定閾值
該過程需要選擇合適的閾值。
拉普拉斯算子能突出顯示圖像中包含快速梯度變化的區(qū)域,這些區(qū)域往往與邊緣有關(guān)。因此,如果一幅圖像的方差較高,說明圖像中存在廣泛的邊緣響應(yīng),包括類邊和非類邊,這是一幅正常聚焦圖像的代表。但如果方差很低,那么表明圖像中的邊緣響應(yīng)很小,幾乎沒有邊緣存在。因此,通過比較方差與預(yù)設(shè)閾值的大小,可以判斷圖像是否模糊。
按照上面的步驟實(shí)現(xiàn)了一個(gè)模糊檢測的函數(shù):
bool isImageBlurry(const char* inputFile, double threshold)
{
Mat src = imread(inputFile);
if (src.empty()) {
printf("Image not loaded\n");
return false;
}
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
Mat dst, absDst;
cv::Laplacian(gray, dst, CV_16S, 3);
cv::convertScaleAbs(dst, absDst);
Mat mean, stddev;
double m = 0, sd = 0;
meanStdDev(absDst, mean, stddev);
m = mean.at<double>(0, 0);
sd = stddev.at<double>(0, 0);
double result = sd * sd;
std::cout << "m: " << m << std::endl;
std::cout << "StdDev: " << result << std::endl;
return result <= threshold;
}
然后寫一個(gè)程序來判斷一下這張圖是否是模糊的

int main(int argc,char *argv[])
{
string fileName = ".../test.jpeg";
bool result = isImageBlurry(fileName.c_str(),11.0);
cout << "result =" << result <<endl;
return 0;
}
輸出結(jié)果:
m: 2.5213
StdDev: 6.31374
result = 1
說明是模糊的圖片。
Laplace 算子對(duì)噪聲敏感,通常不適用于存在噪聲的圖像。
2. LoG 算子
LoG(Laplacian of Gaussian)邊緣檢測算子是 David Courtnay Marr 和 Ellen Hildreth 在 1980 年共同提出的,也稱為 Marr-Hildreth 算子,它根據(jù)圖像的信噪比來求檢測邊緣的最優(yōu)濾波器。該算法先對(duì)圖像進(jìn)行高斯平滑處理,然后再與 Laplacian 算子進(jìn)行卷積。稍后來解釋為何是這樣的。
先來回顧一下二維高斯函數(shù)的公式:
高斯函數(shù)的一階導(dǎo)數(shù)和二階導(dǎo)數(shù),在很多算子中都會(huì)用到。例如一階導(dǎo)數(shù)應(yīng)用在 Canny 算子,二階導(dǎo)數(shù)應(yīng)用在 LoG 算子等等。
簡單推導(dǎo)一下它的一階導(dǎo)數(shù):
同理:
還有推導(dǎo)一下它的二階導(dǎo)數(shù):
同理:
將高斯函數(shù)代入拉普拉斯算子,可得 LoG 算子:
Marr-Hildreth 算法如下:
- 首先讓 LoG 核與一幅輸入圖像卷積:
- 尋找 g(x,y) 的過零點(diǎn)來確定 f(x,y) 的邊緣位置。因?yàn)槔绽棺儞Q和卷積都是線性運(yùn)算,因此上式可以改成
其中,f(x,y) 是輸入圖像,g(x,y) 是輸出圖像。
這樣正好解釋了之前說的,該算法先對(duì)圖像進(jìn)行高斯平滑處理,然后再與 Laplacian 算子進(jìn)行卷積。因?yàn)橄仁褂酶咚篂V波器對(duì)圖像進(jìn)行平滑處理,可以減少噪聲和細(xì)節(jié),然后使用拉普拉斯算子對(duì)濾波后的圖像進(jìn)行邊緣檢測。
它的優(yōu)點(diǎn)是可以有效去除噪聲,同時(shí)保留圖像中的真實(shí)邊緣。相比 Laplace 算子,LoG 算子具有更好的邊緣定位能力和抗噪聲。但是它也存在一些缺點(diǎn),計(jì)算量相對(duì)較大。
下圖是負(fù) LoG 算子的三維圖像,看上去很像“墨西哥草帽”。所以,在業(yè)界也被稱為墨西哥草帽小波(Mexican hat wavelet)。


負(fù) LoG 算子可用 5*5 的模版近似表示

下面用高斯模糊和拉普拉斯變換來實(shí)現(xiàn) LoG :
int main(int argc,char *argv[])
{
Mat src = imread(".../street.jpg");
imshow("src",src);
Mat dst, gray, edge;
cv::GaussianBlur(src, dst, cv::Size(3, 3), 0 ,0); // 高斯模糊 去除噪聲
cv::cvtColor(dst, gray, cv::COLOR_BGR2GRAY); // 灰度化
cv::Laplacian(gray, edge, CV_16S, 3); // 使用拉普拉斯算子提取邊緣
cv::convertScaleAbs(edge, edge);
imshow("LoG", edge);
waitKey(0);
return 0;
}

3. 總結(jié)
本文介紹了 Laplace 算子、LoG 算子,它們都是二階導(dǎo)數(shù)的邊緣算子。
特別是 LoG 算子在 Laplace 算子的基礎(chǔ)上引入了高斯濾波,可以在一定程度上克服噪聲的影響。但它仍舊有一定的局限性,不過這種思想的引入對(duì)后續(xù)圖像特征研究起到了積極作用,被很多后續(xù)的算法所采納。