OpenCV 笔记(11):常用的边缘检测算子—— DoG

1. DoG 算子

DoG(Difference of Gaussians)高斯差分是计算机视觉中一种用于图像增强角点检测 的算子。它是将两个不同尺度的高斯模糊图像进行差分得到的。

高斯模糊可以平滑图像中的噪声和细节,而差分可以突出图像中的边缘和角点。DoG 算子利用这两个特性,在保留图像边缘和角点的同时,去除噪声和细节。

DoG 在图像处理领域有着广泛的应用,例如:

  • 图像增强

  • 角点检测

  • 边缘检测

  • 图像分割

2. DoG 算子的推导

二维高斯函数的公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> G ( x , y ) = 1 2 π δ 2 e − x 2 + y 2 2 δ 2 G(x,y) = \frac{1}{2\pi\delta^2}e^{-\frac{x^2+y^2}{2\delta^2}} </math>G(x,y) = 2πδ21e−2δ2x2+y2

可写成 <math xmlns="http://www.w3.org/1998/Math/MathML"> G ( x , y , δ ) = 1 2 π δ 2 e − x 2 + y 2 2 δ 2 = 1 2 π δ − 2 e − x 2 + y 2 2 δ − 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}} </math>G(x,y,δ) = 2πδ21e−2δ2x2+y2 = 2π1δ−2e−2x2+y2δ−2

对 <math xmlns="http://www.w3.org/1998/Math/MathML"> δ \delta </math>δ 求导,则

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∂ G ∂ δ = ∂ 1 2 π δ − 2 e − x 2 + y 2 2 δ − 2 ∂ δ = 1 2 π ( − 2 δ − 3 ) e − x 2 + y 2 2 δ − 2 + 1 2 π δ − 2 e − x 2 + y 2 2 δ − 2 ( − x 2 + y 2 2 ) ( − 2 δ − 3 ) = − 1 π δ 3 e − x 2 + y 2 2 δ 2 + x 2 + y 2 2 π δ 5 e − x 2 + y 2 2 δ 2 = x 2 + y 2 − 2 δ 2 2 π δ 5 e − x 2 + y 2 2 δ 2 \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}} </math>∂δ∂G=∂δ∂2π1δ−2e−2x2+y2δ−2=2π1(−2δ−3)e−2x2+y2δ−2 + 2π1δ−2e−2x2+y2δ−2(−2x2+y2)(−2δ−3)=−πδ31e−2δ2x2+y2 + 2πδ5x2+y2e−2δ2x2+y2=2πδ5x2+y2−2δ2e−2δ2x2+y2

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

<math xmlns="http://www.w3.org/1998/Math/MathML"> L o G ( x , y , δ ) = ∇ 2 G ( x , y ) = x 2 + y 2 − 2 δ 2 2 π δ 6 e − x 2 + y 2 2 δ 2 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}} </math>LoG(x,y,δ) = ∇2G(x,y)= 2πδ6x2+y2−2δ2e−2δ2x2+y2

所以,

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∂ G ∂ δ = δ ∗ L o G \frac{\partial G}{\partial \delta} = \delta*LoG </math>∂δ∂G = δ∗LoG

根据极限,又可得:

<math xmlns="http://www.w3.org/1998/Math/MathML"> ∂ G ∂ δ = lim ⁡ △ δ → 0 G ( x , y , δ + △ δ ) − G ( x , y , δ ) △ δ = lim ⁡ k → 1 G ( x , y , k δ ) − G ( x , y , δ ) ( k − 1 ) δ = δ ∗ L o G \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 </math>∂δ∂G = lim△δ→0△δG(x,y,δ+△δ)−G(x,y,δ)=limk→1(k−1)δG(x,y,kδ)−G(x,y,δ) = δ∗LoG

当 k <math xmlns="http://www.w3.org/1998/Math/MathML"> ≈ \approx </math>≈1 时,

<math xmlns="http://www.w3.org/1998/Math/MathML"> G ( x , y , k δ ) − G ( x , y , δ ) ( k − 1 ) δ ≈ δ ∗ L o G \frac{G(x,y,k\delta)-G(x,y,\delta)}{(k-1)\delta} \approx \delta*LoG </math>(k−1)δG(x,y,kδ)−G(x,y,δ) ≈ δ∗LoG

<math xmlns="http://www.w3.org/1998/Math/MathML"> G ( x , y , k δ ) − G ( x , y , δ ) ≈ ( k − 1 ) δ 2 L o G G(x,y,k\delta)-G(x,y,\delta) \approx (k-1)\delta^2LoG </math>G(x,y,kδ)−G(x,y,δ) ≈ (k−1)δ2LoG

因此可以用 DoG 算子来近似 LoG 算子,DoG 算子和 LoG 算子在理论上是等价的。

Marr 和 Hildreth 证明,如果两个高斯模糊图像的标准差比率为 1.6,那么 DoG 算子可以近似 LoG 算子。在实践中,DoG 算子通常比 LoG 算子更容易实现,而且计算速度更快。但是 LoG 算子的精确度更高,LoG 算子是使用一个特定的二维高斯函数来滤波图像,该函数可以准确地捕捉图像中的边缘和角点。

3. DoG 算子的实现

下面用高斯模糊和 absdiff() 函数变换来实现 DoG :

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

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

    // 使用不同的 sigma ,实现不同尺度特征
    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;
}

在这里,使用 absdiff() 函数实现差分,就是将两幅图像作差。使用归一化函数 normalize() 的作用是将 DoG 响应的图像范围从其最小值和最大值映射到 0-255 的范围内。这样,可以方便地使用 OpenCV 库中的函数对图像进行可视化。

不使用 OpenCV 的 GaussianBlur() 函数,按照高斯函数的公式来实现一个 GaussianFilter()

ini 复制代码
// x,y 方向联合实现获取高斯模板
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);
            //常数部分不计算,因为最后都要归一化
            double g = exp(-(x + y) / (2 * sigma*sigma));
            mask.at<double>(i, j) = g;
            sum += g;
        }
    }
    mask = mask / sum;
}

//按二维高斯函数实现高斯滤波
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);//边界复制

    //高斯滤波
    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;
}

4. 总结

DoG 算子在一定程度上可以替代 LoG 算子,DoG 算子是 LoG 算子的一种近似实现,而且具有计算速度快、对噪声敏感性低等优点。

DoG 算子在图像处理领域有广泛应用,例如图像角点检测、图像特征提取(例如 SIFT 算法、FAST 算法)等。

相关推荐
CV-King7 小时前
opencv实战项目(三十):使用傅里叶变换进行图像边缘检测
人工智能·opencv·算法·计算机视觉
jndingxin9 小时前
OpenCV视频I/O(14)创建和写入视频文件的类:VideoWriter介绍
人工智能·opencv·音视频
FL162386312912 小时前
[C++]使用纯opencv部署yolov11旋转框目标检测
opencv·yolo·目标检测
方世恩15 小时前
【进阶OpenCV】 (5)--指纹验证
人工智能·opencv·目标检测·计算机视觉
FL162386312915 小时前
[C++]使用纯opencv部署yolov11-pose姿态估计onnx模型
c++·opencv·yolo
A_lvvx15 小时前
OpenCV透视变换
人工智能·opencv·计算机视觉
鲸~屿16 小时前
计算机视觉 第十章OpenCV
人工智能·opencv·计算机视觉
菜就多练_082820 小时前
《深度学习》OpenCV 摄像头OCR 过程及案例解析
人工智能·深度学习·opencv·ocr
红米煮粥2 天前
OpenCV-图像拼接
人工智能·opencv·计算机视觉
jndingxin2 天前
OpenCV视频I/O(8)视频采集类VideoCapture之从视频源中读取一帧图像函数read()的使用
人工智能·opencv·音视频