【C++】图像处理中的微分算子原理与实现(五)

简介: 一阶微分边缘算子:经典算子比如:Roberts(罗伯特)、Prewitt(普鲁伊特)、Sobel(索贝尔),Canny(坎尼)等。二阶微分边缘算子:Laplacian算子,LoG( Laplace of Gaussian function)边缘检测算子和DoG(Difference of Gaussian)高斯差分算子。

LoG算子

1980年,LoG边缘检测算子是David Courtnay Marr和Ellen Hildreth共同提出的,因此,也称为Marr & Hildreth 边缘检测算法或Marr & Hildreth算子。该算法首先对图像做高斯滤波,然后再求其拉普拉斯(Laplacian)二阶导数。即图像与 Laplacian of the Gaussian function 进行滤波运算。最后,可以通过检测滤波结果的零交叉(Zero crossings)获得图像或物体的边缘。因而,也被业界简称为Laplacian-of-Gaussian (LoG)算子。Log算子的表达式如下:

370a8681133e4c7898fc0cb42e4d2e71.gif

常用的卷积模板是5*5的模板:

28085b1978454e2eba7cc76f8bea4218.gif

voidquick_opencv::LOG_Demo(Mat&img)
{
//高斯-拉普拉斯算子 二阶微分 用于边缘检测Matsrc_gray;
intkernel_size=3;
constchar*window_name="LOG Demo";
GaussianBlur(img, img, Size(3, 3), 0, 0, BORDER_DEFAULT);
cvtColor(img, src_gray, COLOR_RGB2GRAY);
namedWindow(window_name, WINDOW_AUTOSIZE);
Matdst, abs_dst;
Laplacian(src_gray, dst, CV_16S, kernel_size);
convertScaleAbs(dst, abs_dst);
imshow(window_name, abs_dst);
}

DoG算子

1980年,高斯差分(DoG)算子,Marr and Hildreth指出,使用高斯差分(DOG)来近似Log算子是可能的:

b6f28c54092747e7b6282f1e2aa94e00.gif

LoG算子和DoG算子的函数波形对比如下图所示,由于高斯差分的计算更加简单,因此可用DoG算子近似替代LoG算子:

45fc615cc85f4807b9b5f7ae6ea85901.jpg

//x,y方向联合实现获取高斯模板voidgenerateGaussMask(Mat&Mask, Sizewsize, doublesigma) {
Mask.create(wsize, CV_64F);
inth=wsize.height;
intw=wsize.width;
intcenter_h= (h-1) /2;
intcenter_w= (w-1) /2;
doublesum=0.0;
doublex, y;
for (inti=0; i<h; ++i) {
y=pow(i-center_h, 2);
for (intj=0; j<w; ++j) {
x=pow(j-center_w, 2);
//因为最后都要归一化的,常数部分可以不计算,也减少了运算量doubleg=exp(-(x+y) / (2*sigma*sigma));
Mask.at<double>(i, j) =g;
sum+=g;
        }
    }
Mask=Mask/sum;
}
//按二维高斯函数实现高斯滤波voidGaussianFilter(Mat&src, Mat&dst, Matwindow) {
inthh= (window.rows-1) /2;
inthw= (window.cols-1) /2;
dst=Mat::zeros(src.size(), src.type());
//边界填充MatNewsrc;
copyMakeBorder(src, Newsrc, hh, hh, hw, hw, BORDER_REPLICATE);//边界复制//高斯滤波for (inti=hh; i<src.rows+hh; ++i) {
for (intj=hw; j<src.cols+hw; ++j) {
doublesum[3] = { 0 };
for (intr=-hh; r<=hh; ++r) {
for (intc=-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);
                    }
elseif (src.channels() ==3) {
Vec3brgb=Newsrc.at<Vec3b>(i+r, j+c);
sum[0] =sum[0] +rgb[0] *window.at<double>(r+hh, c+hw);//Bsum[1] =sum[1] +rgb[1] *window.at<double>(r+hh, c+hw);//Gsum[2] =sum[2] +rgb[2] *window.at<double>(r+hh, c+hw);//R                    }
                }
            }
for (intk=0; k<src.channels(); ++k) {
if (sum[k] <0)
sum[k] =0;
elseif (sum[k] >255)
sum[k] =255;
            }
if (src.channels() ==1)
            {
dst.at<uchar>(i-hh, j-hw) =static_cast<uchar>(sum[0]);
            }
elseif (src.channels() ==3)
            {
Vec3brgb= { static_cast<uchar>(sum[0]), static_cast<uchar>(sum[1]), static_cast<uchar>(sum[2]) };
dst.at<Vec3b>(i-hh, j-hw) =rgb;
            }
        }
    }
}
//DOG高斯差分voidDOG1(Mat&src, Mat&dst, Sizewsize, doublesigma, doublek=1.6) {
MatMask1, Mask2, gaussian_dst1, gaussian_dst2;
generateGaussMask(Mask1, wsize, k*sigma);//获取二维高斯滤波模板1generateGaussMask(Mask2, wsize, sigma);//获取二维高斯滤波模板2//高斯滤波GaussianFilter(src, gaussian_dst1, Mask1);
GaussianFilter(src, gaussian_dst2, Mask2);
dst=gaussian_dst1-gaussian_dst2-1;
threshold(dst, dst, 0, 255, THRESH_BINARY);
}
//DOG高斯差分--使用opencv的GaussianBlurvoidDOG2(Mat&src, Mat&dst, Sizewsize, doublesigma, doublek=1.6) {
Matgaussian_dst1, gaussian_dst2;
//高斯滤波GaussianBlur(src, gaussian_dst1, wsize, k*sigma);
GaussianBlur(src, gaussian_dst2, wsize, sigma);
dst=gaussian_dst1-gaussian_dst2;
threshold(dst, dst, 0, 255, cv::THRESH_BINARY);
}
// 实现voidquick_opencv::DOG_Demo(Mat&img)
{
if (img.channels() >1) cv::cvtColor(img, img, COLOR_BGR2GRAY);
Matedge1, edge2;
DOG1(img, edge1, cv::Size(7, 7), 2);
DOG2(img, edge2, cv::Size(7, 7), 2);
namedWindow("My_DOG", WINDOW_NORMAL);
imshow("My_DOG", edge1);
namedWindow("Opencv_DOG", WINDOW_NORMAL);
imshow("Opencv_DOG", edge2);
}
目录
相关文章
|
2月前
|
C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
2月前
|
编译器 C++ 开发者
【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
2月前
|
存储 C语言 C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(一)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
2月前
|
C++
C++番外篇——虚拟继承解决数据冗余和二义性的原理
C++番外篇——虚拟继承解决数据冗余和二义性的原理
46 1
|
6月前
|
存储 自然语言处理 安全
C++ STL标准库 《string原理与实战分析》
C++ STL标准库 《string原理与实战分析》
99 0
|
7月前
|
小程序 编译器 Linux
C++ 异常原理:以一个小程序为例
作者在调查某个 bug 时涉及到 C++ 异常,借此机会以本文把 C++ 异常机制梳理清楚供大家参考。
|
7月前
|
设计模式 算法 C++
【C++】STL之迭代器介绍、原理、失效
【C++】STL之迭代器介绍、原理、失效
141 2
|
6月前
|
大数据 C++ 索引
C++ STL标准库 《vector向量原理与实战分析》
C++ STL标准库 《vector向量原理与实战分析》
60 0
|
6月前
|
C++ 容器
C++ STL标准库 《queue单向队列原理与实战分析》
C++ STL标准库 《queue单向队列原理与实战分析》
53 0
|
7月前
|
编译器 C++ 容器
C++模板的原理及使用
C++模板的原理及使用