基於 Laplacian 實現簡單的圖像模糊檢測 業務背景 Laplacian 算子 圖像模糊檢測算法 總結

業務背景

從去年年底開始,我們團隊一直在做一款能夠給電商商品自動拍照的智能硬件。拍完照後,會將商品的套圖在電商平臺上進行展示。

對於要展示的商品圖片而言,我們對圖片本身的質量要求會比較高,例如不能將模糊不清的圖片進行展示。因此,需要一種圖像模糊檢測的方法,便於我們篩選出可用的圖片。

我們使用基於 Laplacian 的算法來檢測圖片是否模糊。調用它比較簡單,因爲 OpenCV 內置了 Laplacian 函數。

Laplacian 算子

求多元函數的二階導數的映射被稱爲 Laplacian 算子,它相當於二階 Sobel 算子的導數。

Laplacian 算子的定義:

我們分別對 Laplace 算子 x,y 兩個方向的二階導數進行差分就得到了離散函數的 Laplace 算子。

以 x 方向爲例:
一階差分:f'(x) = f(x) - f(x - 1)
二階差分:f''(x) = f'(x+1) - f'(x) = (f(x + 1) - f(x)) - (f(x) - f(x - 1))
化簡後:f''(x) = f(x - 1) - 2 f(x)) + f(x + 1)

提取前面的係數:[1, -2, 1]

同理,可得 y 方向的係數[1,-2,1]

疊加起來就得到了拉普拉斯矩陣


也就是拉普拉斯 3x3 卷積核。

圖像模糊檢測算法

算法的主要思想:先將圖像轉換成灰度圖像,然後單一通道的灰度圖像經過剛纔計算出來的拉普拉斯 3x3 卷積覈計算後會得到一個響應圖,最後再計算這個響應圖的方差。

基於該方差和按照經驗設定的閾值進行比較,就可以判斷圖像是否模糊。對於同一種類型的商品圖片,可以採用同一個閾值。不同的商品、不同環境拍攝的圖片可能需要調整閾值。

bool isImageBlurry(cv::Mat& img, double threshold)
{
    cv::Mat matImageGray;
    cv::cvtColor(img, matImageGray, COLOR_BGR2GRAY);
    cv::Mat dst, abs_dst;
    cv::Laplacian(matImageGray, dst, CV_16S, 3);
    cv::convertScaleAbs( dst, abs_dst );
    cv::Mat tmp_m, tmp_sd;
    double sd = 0;
    cv::meanStdDev(dst, tmp_m, tmp_sd);
    sd = tmp_sd.at<double>(0,0); // 方差
    return ((sd * sd) <= threshold);
}

找一張模糊的圖片,寫一個簡單的例子進行測試

using namespace std;
using namespace cv;

bool isImageBlurry(cv::Mat& img, double threshold=49.0);

int main(int argc,char *argv[])
{
    String imageName;
    cout << "Enter the image file name: " << endl;
    cin >> imageName;
    // read the image
    Mat image = imread(imageName);

    double time = (double)getTickCount();

    bool result = isImageBlurry(image);
    time = ((double)getTickCount() - time) / getTickFrequency();
    cout << "所用時間爲:" << time << "s" << endl;
    cout << "result:" << result << endl;
    return 0;
}

bool isImageBlurry(cv::Mat& img, double threshold)
{
    cv::Mat matImageGray;
    cv::cvtColor(img, matImageGray, COLOR_BGR2GRAY);
    cv::Mat dst, abs_dst;
    cv::Laplacian(matImageGray, dst, CV_16S, 3);
    cv::convertScaleAbs( dst, abs_dst );
    cv::Mat tmp_m, tmp_sd;
    double m = 0, sd = 0;
    cv::meanStdDev(dst, tmp_m, tmp_sd);
    m = tmp_m.at<double>(0,0); 
    sd = tmp_sd.at<double>(0,0);
    std::cout << "sd * sd: " << sd * sd << std::endl;
    return ((sd * sd) <= threshold);
}

執行結果:

Enter the image file name: 
test.jpeg
sd * sd: 31.0646
所用時間爲:0.0219034s
result:1

可以通過上述程序判斷出該圖片是模糊的。

最後,我們團隊主要使用的語言是 Java/Kotlin,還需要編寫一個 jni 來調用該函數。

總結

在無參考圖像的情況下,Laplacian 是一種常見的圖像模糊檢測的方式。除此之外,還可以採用 Brenner、Tenengrad、SMD、SMD2 等等。

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