OpenCV 筆記(11):常用的邊緣檢測算子—— DoG

1. DoG 算子

DoG(Difference of Gaussians)高斯差分是計算機視覺中一種用于圖像增強角點檢測的算子。它是將兩個不同尺度的高斯模糊圖像進(jìn)行差分得到的。

高斯模糊可以平滑圖像中的噪聲和細(xì)節(jié),而差分可以突出圖像中的邊緣和角點。DoG 算子利用這兩個特性,在保留圖像邊緣和角點的同時,去除噪聲和細(xì)節(jié)。

DoG 在圖像處理領(lǐng)域有著廣泛的應(yīng)用,例如:

  • 圖像增強

  • 角點檢測

  • 邊緣檢測

  • 圖像分割

2. DoG 算子的推導(dǎo)

二維高斯函數(shù)的公式:

G(x,y) = \frac{1}{2\pi\delta^2}e^{-\frac{x^2+y^2}{2\delta^2}}

可寫成 G(x,y,\delta) = \frac{1}{2\pi\delta^2}e^{-\frac{x^2+y^2}{2\delta^2}} = \frac{1}{2\pi}\delta^{-2}e^{-\frac{x^2+y^2}{2}\delta^{-2}}

\delta求導(dǎo),則

\frac{\partial G}{\partial \delta}=\frac{\partial \frac{1}{2\pi}\delta^{-2}e^{-\frac{x^2+y^2}{2}\delta^{-2}}}{\partial \delta} \\=\frac{1}{2\pi}(-2\delta^{-3})e^{-\frac{x^2+y^2}{2}\delta^{-2}} + \frac{1}{2\pi}\delta^{-2}e^{-\frac{x^2+y^2}{2}\delta^{-2}}(-\frac{x^2+y^2}{2})(-2\delta^{-3}) \\=-\frac{1}{\pi\delta^{3}}e^{-\frac{x^2+y^2}{2\delta^2}} + \frac{x^2+y^2}{2\pi\delta^{5}}e^{-\frac{x^2+y^2}{2\delta^2}} \\=\frac{x^2+y^2-2\delta^2}{2\pi\delta^{5}}e^{-\frac{x^2+y^2}{2\delta^2}}

而在該系列的上一篇文章中,我們得到 LoG 算子的公式:

LoG(x,y,\delta) = \nabla^2G(x,y)= \frac{x^2+y^2-2\delta^2}{2\pi\delta^6}e^{-\frac{x^2+y^2}{2\delta^2}}

所以,

\frac{\partial G}{\partial \delta} = \delta*LoG

根據(jù)極限,又可得:

\frac{\partial G}{\partial \delta} = \lim_{\triangle\delta \to 0}\frac{G(x,y,\delta+\triangle\delta)-G(x,y,\delta)}{\triangle\delta} \\=\lim_{k \to 1}\frac{G(x,y,k\delta)-G(x,y,\delta)}{(k-1)\delta} = \delta*LoG

當(dāng) k \approx1 時,

\frac{G(x,y,k\delta)-G(x,y,\delta)}{(k-1)\delta} \approx \delta*LoG

G(x,y,k\delta)-G(x,y,\delta) \approx (k-1)\delta^2LoG

因此可以用 DoG 算子來近似 LoG 算子,DoG 算子和 LoG 算子在理論上是等價的。

DoG和LoG的對比.png

Marr 和 Hildreth 證明,如果兩個高斯模糊圖像的標(biāo)準(zhǔn)差比率為 1.6,那么 DoG 算子可以近似 LoG 算子。在實踐中,DoG 算子通常比 LoG 算子更容易實現(xiàn),而且計算速度更快。但是 LoG 算子的精確度更高,LoG 算子是使用一個特定的二維高斯函數(shù)來濾波圖像,該函數(shù)可以準(zhǔn)確地捕捉圖像中的邊緣和角點。

3. DoG 算子的實現(xiàn)

下面用高斯模糊和 absdiff() 函數(shù)變換來實現(xiàn) DoG :

int main(int argc,char *argv[])
{
    Mat src = imread(".../street.jpg");
    imshow("src",src);

    Mat gray;
    cvtColor(src, gray, cv::COLOR_BGR2GRAY); // 灰度化

    // 使用不同的 sigma ,實現(xiàn)不同尺度特征
    Mat blurred1, blurred2;
    double sigma1 = 1.6*2;
    double sigma2 = 2;
    GaussianBlur(gray, blurred1, Size(7,7), sigma1, sigma1);
    GaussianBlur(gray, blurred2, Size(7,7), sigma2, sigma2);

    // 通過差分計算 DoG 圖像
    Mat dst;
    absdiff(blurred1, blurred2, dst);
    normalize(dst, dst, 0, 255, NORM_MINMAX, CV_8UC1);

    imshow("DoG", dst);

    waitKey(0);
    return 0;
}
GaussianBlur實現(xiàn)的DoG.png

在這里,使用 absdiff() 函數(shù)實現(xiàn)差分,就是將兩幅圖像作差。使用歸一化函數(shù) normalize() 的作用是將 DoG 響應(yīng)的圖像范圍從其最小值和最大值映射到 0-255 的范圍內(nèi)。這樣,可以方便地使用 OpenCV 庫中的函數(shù)對圖像進(jìn)行可視化。

不使用 OpenCV 的 GaussianBlur() 函數(shù),按照高斯函數(shù)的公式來實現(xiàn)一個 GaussianFilter()

// x,y 方向聯(lián)合實現(xiàn)獲取高斯模板
void generateGaussMask(Mat& mask, Size size, double sigma) {
    mask.create(size, CV_64F);
    int h = size.height;
    int w = size.width;
    int center_h = (h - 1) / 2;
    int center_w = (w - 1) / 2;
    double sum = 0.0;
    double x, y;
    for (int i = 0; i < h; ++i) {
        y = pow(i - center_h, 2);
        for (int j = 0; j < w; ++j) {
            x = pow(j - center_w, 2);
            //常數(shù)部分不計算,因為最后都要歸一化
            double g = exp(-(x + y) / (2 * sigma*sigma));
            mask.at<double>(i, j) = g;
            sum += g;
        }
    }
    mask = mask / sum;
}

//按二維高斯函數(shù)實現(xiàn)高斯濾波
void GaussianFilter(Mat& src, Mat& dst, Mat window) {
    int hh = (window.rows - 1) / 2;
    int hw = (window.cols - 1) / 2;
    dst = Mat::zeros(src.size(), src.type());
    //邊界填充
    Mat newSrc;
    copyMakeBorder(src, newSrc, hh, hh, hw, hw, BORDER_REPLICATE);//邊界復(fù)制

    //高斯濾波
    for (int i = hh; i < src.rows + hh; ++i) {
        for (int j = hw; j < src.cols + hw; ++j) {
            double sum[3] = { 0 };

            for (int r = -hh; r <= hh; ++r) {
                for (int c = -hw; c <= hw; ++c) {
                    if (src.channels() == 1) {
                        sum[0] = sum[0] + newSrc.at<uchar>(i + r, j + c) * window.at<double>(r + hh, c + hw);
                    } else if (src.channels() == 3) {
                        Vec3b rgb = newSrc.at<Vec3b>(i + r, j + c);
                        sum[0] = sum[0] + rgb[0] * window.at<double>(r + hh, c + hw);//B
                        sum[1] = sum[1] + rgb[1] * window.at<double>(r + hh, c + hw);//G
                        sum[2] = sum[2] + rgb[2] * window.at<double>(r + hh, c + hw);//R
                    }
                }
            }

            if (src.channels() == 1)
            {
                dst.at<uchar>(i - hh, j - hw) = static_cast<uchar>(sum[0]);
            } else if (src.channels() == 3) {
                Vec3b rgb = { static_cast<uchar>(sum[0]), static_cast<uchar>(sum[1]), static_cast<uchar>(sum[2]) };
                dst.at<Vec3b>(i - hh, j - hw) = rgb;
            }
        }
    }
}

int main(int argc,char *argv[])
{
    Mat src = imread(".../street.jpg");
    imshow("src",src);

    Mat gray;
    cvtColor(src, gray, cv::COLOR_BGR2GRAY); // 灰度化

    Mat mask1, mask2, blurred1, blurred2;
    double sigma1 = 1.6*2;
    double sigma2 = 2;
    generateGaussMask(mask1, Size(7, 7), sigma1);
    generateGaussMask(mask2, Size(7, 7), sigma2);

    GaussianFilter(src, blurred1, mask1);
    GaussianFilter(src, blurred2, mask2);

    // 通過差分計算 DoG 圖像
    Mat dst;
    absdiff(blurred1, blurred2, dst);
    normalize(dst, dst, 0, 255, NORM_MINMAX, CV_8UC1);

    imshow("DoG", dst);

    waitKey(0);
    return 0;
}
自定義GaussianFilter實現(xiàn)的DoG.png

4. 總結(jié)

DoG 算子在一定程度上可以替代 LoG 算子,DoG 算子是 LoG 算子的一種近似實現(xiàn),而且具有計算速度快、對噪聲敏感性低等優(yōu)點。

DoG 算子在圖像處理領(lǐng)域有廣泛應(yīng)用,例如圖像角點檢測、圖像特征提取(例如 SIFT 算法、FAST 算法)等。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容