图像处理基础 (四)边缘提取之 LOG 和 DOG 算子
原理Laplace
对连续二元标量函数 https://www.zhihu.com/equation?tex=f%28x%2Cy%29 ,其拉普拉斯算子是梯度的散度,即
https://www.zhihu.com/equation?tex=%5Cbegin%7Baligned%7D+%5Cnabla+%5Ccdot+%7B%5Cnabla%7Bf%7D%7D+++%3D+%5Cfrac%7B%5Cpartial%5E2%7Bf%7D%7D%7B%5Cpartial%7Bx%5E2%7D%7D%2B+%5Cfrac%7B%5Cpartial%5E2%7Bf%7D%7D%7B%5Cpartial%7By%5E2%7D%7D%5Cend%7Baligned%7D
对于离散图像而言,
https://www.zhihu.com/equation?tex=%5Cbegin%7Baligned%7D+%5Cnabla+%5Ccdot+%7B%5Cnabla%7Bf%7D%7D+++%3D++f%28x+%2B+1%2Cy%29+%2B+f%28x+-+1%2Cy%29+%2B+f%28x%2C+y+%2B+1%29+%2B+f%28x%2C+y+-+1%29+-+4f%28x%2Cy%29+%5Cend%7Baligned%7D
对应的滤波模板是
如果结果 < 0,说明滤波中心是局部极大值,可能是噪声点,也可能是边缘上的点;结果 = 0,说明滤波中心很可能处于平坦区域;结果 > 0,说明滤波中心是局部极小值,可能是噪声点,也可能是边缘。
很明显,laplace 可以用于定位局部极值,用于寻找边缘
cv::Mat laplace_extract_edges(const cv::Mat& source) {
// 获取信息
const int H = source.rows;
const int W = source.cols;
// padding
const auto padded_image = make_pad(source, 1, 1);
const int W2 = padded_image.cols;
// 准备结果
auto result = source.clone();
// 开始滤波
for(int i = 0;i < H; ++i) {
const uchar* row_ptr = padded_image.data + (1 + i) * W2 + 1;
uchar* const res_ptr = result.data + i * W;
for(int j = 0;j < W; ++j) {
// 每个点, 找上下左右四个点
const uchar u = row_ptr, d = row_ptr, l = row_ptr, r = row_ptr;
res_ptr = cv::saturate_cast<uchar>(std::abs(u + d + l + r - 4 * row_ptr));
}
}
return result;
}
Laplace 边缘检测的例子
但是 laplace 对于噪声点也能检测出来,所以给上面的图像加一些高斯噪声,边缘检测结果就会差得多
Laplace 边缘检测 - 对噪声图像的处理
LOG
为了解决上面的问题,一个很简单的想法就是先对图像做高斯平滑,去除一些噪声,再使用 laplace 处理图像。这两步都可以是卷积的方式。连续的卷积,可以先求高斯平滑函数的 laplace,然后再对图像做滤波,证明略。
因此,接下来求高斯平滑函数的 laplace 算子
二元连续的高斯函数https://www.zhihu.com/equation?tex=G%28x%2Cy%2C%5Csigma%29+%3D+%5Cfrac%7B1%7D%7B2+%5Cpi+%5Csigma%5E2%7D+e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Csigma%5E2%7D%7D ,求 laplace, 梯度的散度,需要求两次偏导
https://www.zhihu.com/equation?tex=%5Cfrac%7B%5Cpartial%7BG%7D%7D%7B%5Cpartial%7Bx%7D%7D+%3D+%5Cfrac%7B1%7D%7B2%5Cpi+%5Csigma%5E2%7D+%5Ccdot+-%5Cfrac%7B1%7D%7B2+%5Csigma%5E2%7D2x+%5Ccdot+e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Csigma%5E2%7D%7D+%3D+-%5Cfrac%7Bx%7D%7B2%5Cpi+%5Csigma%5E4%7D%5Ccdot+e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Csigma%5E2%7D%7D
https://www.zhihu.com/equation?tex=%5Cbegin%7Baligned%7D+%5Cfrac%7B%5Cpartial%5E2%7BG%7D%7D%7B%5Cpartial%7Bx%7D%5E2%7D+%26%3D++-%5CBig%28++%5Cfrac%7B1%7D%7B2%5Cpi+%5Csigma%5E4%7D%5Ccdot+e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Csigma%5E2%7D%7D+-%5Cfrac%7B2x%7D%7B2%5Csigma%5E2%7D+%5Ccdot++e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Csigma%5E2%7D%7D+%5Ccdot+%5Cfrac%7Bx%7D%7B2%5Cpi%5Csigma%5E4%7D+++++%5CBig%29+%5C%5C++%26%3D+-+++%5Cfrac%7B1%7D%7B2%5Cpi+%5Csigma%5E4%7D%5Ccdot+e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Csigma%5E2%7D%7D++++%2B+%5Cfrac%7Bx%5E2%7D%7B2%5Cpi%5Csigma%5E6%7D+%5Ccdot++e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Csigma%5E2%7D%7D+%5C%5C++%26%3D+%5Cfrac%7Bx%5E2+-+%5Csigma%5E2%7D%7B2%5Cpi+%5Csigma%5E6%7D+%5Ccdot++e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Csigma%5E2%7D%7D+%5Cend%7Baligned%7D+++
由于 https://www.zhihu.com/equation?tex=x%2Cy 在函数中是对称的,因此,很容易可以得到
https://www.zhihu.com/equation?tex=%5Cfrac%7B%5Cpartial%7BG%7D%7D%7B%5Cpartial%7By%7D%7D++%3D+-%5Cfrac%7By%7D%7B2%5Cpi+%5Csigma%5E4%7D%5Ccdot+e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Csigma%5E2%7D%7D
https://www.zhihu.com/equation?tex=%5Cbegin%7Baligned%7D+%5Cfrac%7B%5Cpartial%5E2%7BG%7D%7D%7B%5Cpartial%7By%7D%5E2%7D+++%26%3D+%5Cfrac%7By%5E2+-+%5Csigma%5E2%7D%7B2%5Cpi+%5Csigma%5E6%7D+%5Ccdot++e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Csigma%5E2%7D%7D+%5Cend%7Baligned%7D+++
由此可得,高斯函数的 laplace ,LOG
https://www.zhihu.com/equation?tex=%5Cbegin%7Baligned%7D+%5Cnabla+%5Ccdot+%7B%5Cnabla%7BG%28x%2Cy%2C%5Csigma%29%7D%7D+++%3D+%5Cfrac%7B%5Cpartial%5E2%7BG%7D%7D%7B%5Cpartial%7Bx%5E2%7D%7D%2B+%5Cfrac%7B%5Cpartial%5E2%7BG%7D%7D%7B%5Cpartial%7By%5E2%7D%7D%5Cend%7Baligned%7D+%3D++%5Cfrac%7Bx%5E2+%2B+y%5E2+-+2%5Csigma%5E2%7D%7B2%5Cpi+%5Csigma%5E6%7D+%5Ccdot++e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Csigma%5E2%7D%7D
https://www.zhihu.com/equation?tex=5+%5Ctimes++5 的 LOG 模板如下
5x5 高斯拉普拉斯 LOG 模板
在图像处理中,平坦区域的 laplace 为 0,因为没有梯度。上面的 https://www.zhihu.com/equation?tex=5+%5Ctimes+5 模板是离散的,和不为 0,因此需要略微修改,简单的做法是统一减去一个微小量,使得和为 0。为了加快处理速度,也可以全转成 int,但会损失一些精度。
高斯拉普拉斯 LOG 函数
上图是二元连续 LOG 函数,可以看到,从滤波中心往外扩散,有一个先增大后减小的过程。对图像滤波时,分三种情况
平坦区域
因为没有梯度,因此不管怎么加权相加减,LOG 模板的响应值为 0
平坦区域,一维切面的 LOG 响应
边缘
LOG 模板和图像无关,考虑下图灰度剧烈变化的区域
灰度剧烈变化区域,一维切面的 LOG 响应
黑色线表示灰度值,从一个较低的平坦区域到一个较高的平坦区域,两侧的平坦区域处(蓝色)的 LOG 响应值都为 0;灰度跃升时,考虑红色部分,刚开始由于右边加入了一些高区域(绿色)的灰度,所以,LOG 响应值 > 0。
灰度剧烈变化区域,一维切面的 LOG 响应
继续向右移动,由于还剩下一部分在低灰度(绿色),此时,LOG 响应值 < 0。
从 > 0 渐变到 < 0,LOG 模板在从低灰度区域到高灰度区域移动的过程中,存在某个时刻的响应值 = 0,但由于是离散的,这个点不一定是图像中的点,粗略检测边缘的时候,可以检查两侧 LOG 响应值符号是否相反,如果一正一负,说明中间存在灰度剧烈变化的点,可以看作边缘。
一正一负,在边缘的两侧,靠近低灰度的区域 LOG 响应值 > 0,靠近高灰度的区域 LOG 区域响应值 < 0。
噪声点
如果 LOG 遇到的是噪声点,考虑上面的情况,如果噪声处在 LOG 模板 < 0的区域(绿色),那么 LOG 响应值一定 < 0,因为突然减去了一个高灰度值,而其它平坦区域不变。
如果考虑噪声处在 LOG 模板 > 0 的区域,那么 LOG 响应值是 > 0 的,同时考虑另一侧的情况
也是一样的,平坦区域为 0,相比之下加入了一个高灰度值,LOG 滤波结果 > 0,因此 LOG 对于噪声的响应值变化如下图
两侧的平坦区域,LOG 响应值 = 0;噪声处于 LOG > 0 区域时,LOG 响应值 > 0;噪声处于 LOG< 0区域时,LOG 响应值 < 0。
结论是:按照之前边缘检测的方法,找两侧一正一负,LOG 依旧无法避免噪声。。???
综上,可以得到 LOG 检测边缘的具体步骤
[*]根据方差和滤波核大小确定 LOG 模板
[*]LOG 模板对图像做滤波
[*]在滤波结果中,找两侧响应值分别一正一负的点
[*]设置一个阈值 t,两侧像素之差 > t 的点被视作真正的边缘
具体实现时,考虑以下 4 种一正一负的情况
寻找过 0 点,一正一负的四种情况
代码
std::vector<double> laplace_of_gaussi(const cv::Mat& source, const int radius, const double sigma) {
// padding 处理边缘
const auto padded_image = make_pad(source, radius, radius);
const int W2 = padded_image.cols;
// 准备一个 LOG 模板
const int window_len = (radius << 1) + 1;
const int window_size = window_len * window_len;
const double sigma_2 = sigma * sigma;
const double sigma_6 = sigma_2 * sigma_2 * sigma_2;
double LOG;
int LOG_offset;
int offset = 0;
for(int i = -radius; i <= radius; ++i) {
for(int j = -radius; j <= radius; ++j) {
const double distance = i * i + j * j;
LOG = (distance - 2 * sigma_2) / sigma_6 * std::exp(-distance / (2 * sigma_2));
LOG_offset = i * W2 + j;
++offset;
}
}
/*
0.001 0.0110.0440.0670.044 0.011 0.001
0.011 0.1000.2720.3260.272 0.100 0.011
0.044 0.2720.088 -0.6290.088 0.272 0.044
0.067 0.326 -0.629 -2.460 -0.629 0.326 0.067
0.043 0.2720.088 -0.6290.088 0.272 0.044
0.011 0.1000.2720.3260.272 0.100 0.011
0.001 0.0110.0440.0670.044 0.011 0.001
*/
// 平坦区域, LOG 响应为 0
double sum_value = 0.0;
for(int i = 0;i < offset; ++i) sum_value += LOG;
for(int i = 0;i < offset; ++i) LOG -= sum_value / offset;
// 收集原始图像信息
const int H = source.rows;
const int W = source.cols;
const int length = H * W;
// LOG 模板扫过
std::vector<double> LOG_result(length, 0);
for(int i = 0;i < H; ++i) {
const uchar* const row_ptr = padded_image.data + i * W2;
double* const res_ptr = LOG_result.data() + i * W;
for(int j = 0;j < W; ++j) {
double conv_sum = 0;
for(int k = 0;k < offset; ++k)
conv_sum += LOG * row_ptr];
res_ptr = conv_sum;
}
}
return LOG_result;
}
cv::Mat laplace_of_gaussi_edge_detection(const cv::Mat& source, const int radius, const double sigma, const double threshold) {
// 获取 LOG 滤波的结果
const auto LOG_result = laplace_of_gaussi(source, radius, sigma);
const int H = source.rows;
const int W = source.cols;
// 准备结果
auto result_image = source.clone();
// 现在 LOG_result 里面是结果, 开始找两侧一正一负的点
for(int i = 1;i < H - 1; ++i) {
const double* const row_ptr = LOG_result.data() + i * W;
uchar* const res_ptr = result_image.data + i * W;
for(int j = 1;j < W - 1; ++j) {
// 开始找四个方向
if((row_ptr * row_ptr < 0 and std::abs(row_ptr - row_ptr) > threshold) or
(row_ptr * row_ptr < 0 and std::abs(row_ptr - row_ptr) > threshold) or
(row_ptr * row_ptr < 0 and std::abs(row_ptr - row_ptr) > threshold) or
(row_ptr * row_ptr < 0 and std::abs(row_ptr - row_ptr) > threshold)) {
res_ptr = 255;
}
else res_ptr = 0;
}
}
return result_image;
}
LOG 边缘检测结果
对于之前的噪声图像
laplace of gaussi(LOG)
虽然无法完全去除噪声,但是和之前直接用 laplace 的效果相比,还是减弱了一些
laplace
DOG
二元连续高斯函数 https://www.zhihu.com/equation?tex=G%28x%2Cy%2C%5Csigma%29+%3D+%5Cfrac%7B1%7D%7B2%5Cpi+%7D%5Csigma%5E%7B-2%7D+e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%7D+%5Csigma%5E%7B-2%7D%7D 对方差求导,可以得到
https://www.zhihu.com/equation?tex=%5Cbegin%7Baligned%7D+%5Cfrac%7B%5Cpartial%7BG%7D%7D%7B%5Cpartial%7B%5Csigma%7D%7D+%26%3D+%5Cfrac%7B1%7D%7B2%5Cpi%7D+%5Ccdot+%28-2%29+%5Ccdot+%5Csigma%5E%7B-3%7D+%5Ccdot+e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%7D+%5Csigma%5E%7B-2%7D%7D+-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%7D+%5Ccdot+%28-2%29+%5Ccdot+%5Csigma%5E%7B-3%7D+%5Ccdot+e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%7D+%5Csigma%5E%7B-2%7D%7D++%5Ccdot+%5Cfrac%7B1%7D%7B2%5Cpi%7D%5Csigma%5E%7B-2%7D+%5C%5C++%26%3D+%5CBig%28+-%5Cfrac%7B1%7D%7B%5Cpi%5Csigma%5E%7B3%7D%7D+%2B+%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Cpi%5Csigma%5E5%7D+%5CBig%29+%5Ccdot+e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%7D+%5Csigma%5E%7B-2%7D%7D+%5C%5C++%26%3D+%5Cfrac%7Bx%5E2+%2B+y%5E2+-+2%5Csigma%5E2%7D%7B2%5Cpi%5Csigma%5E5%7D++%5Ccdot+e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Csigma%5E%7B2%7D%7D+%7D+%5C%5C+%5Cend%7Baligned%7D
很巧地,前面得到的
https://www.zhihu.com/equation?tex=%5Cbegin%7Baligned%7D+LOG%28x%2Cy%2C%5Csigma%29+%3D+%5Cfrac%7B%5Cpartial%5E2%7BG%7D%7D%7B%5Cpartial%7Bx%5E2%7D%7D%2B+%5Cfrac%7B%5Cpartial%5E2%7BG%7D%7D%7B%5Cpartial%7By%5E2%7D%7D%5Cend%7Baligned%7D+%3D++%5Cfrac%7Bx%5E2+%2B+y%5E2+-+2%5Csigma%5E2%7D%7B2%5Cpi+%5Csigma%5E6%7D+%5Ccdot++e%5E%7B-%5Cfrac%7Bx%5E2+%2B+y%5E2%7D%7B2%5Csigma%5E2%7D%7D ,
二者就差一个,即
https://www.zhihu.com/equation?tex=%5Cfrac%7B%5Cpartial%7BG%7D%7D%7B%5Cpartial%7B%5Csigma%7D%7D++%3D+%5Csigma+%5Ccdot+LOG
根据极限,又可以得到
https://www.zhihu.com/equation?tex=%5Cbegin%7Baligned%7D+%5Cfrac%7B%5Cpartial%7BG%7D%7D%7B%5Cpartial%7B%5Csigma%7D%7D+%26%3D+%5Clim_%7B%5CDelta%5Csigma%5Crightarrow0%7D%7B%5Cfrac%7BG%28x%2C+y%2C+%5Csigma+%2B+%5CDelta%5Csigma%29+-+G%28x%2C+y%2C+%5Csigma%29%7D%7B%5CDelta%5Csigma%7D%7D%5C%5C++%26%3D+%5Clim_%7Bk%5Crightarrow1%7D%7B%5Cfrac%7BG%28x%2C+y%2C+k+%5Csigma+%29+-+G%28x%2C+y%2C+%5Csigma%29%7D%7B%28k+-+1%29%5Ccdot+%5Csigma%7D%7D%5C%5C+%5Cend%7Baligned%7D
结合前面两式,当 https://www.zhihu.com/equation?tex=k+%5Capprox+1 ,有
https://www.zhihu.com/equation?tex=%5Cbegin%7Baligned%7D+%5Cfrac%7BG%28x%2C+y%2C+k+%5Csigma+%29+-+G%28x%2C+y%2C+%5Csigma%29%7D%7B%28k+-+1%29%5Ccdot+%5Csigma%7D+%26%5Capprox+%5Csigma+%5Ccdot+LOG+%5C%5C++G%28x%2C+y%2C+k+%5Csigma+%29+-+G%28x%2C+y%2C+%5Csigma%29+%26%5Capprox+%28k+-+1%29%5Ccdot+%5Csigma%5E2%5Ccdot+LOG+%5Cend%7Baligned%7D
两个不同方差的高斯函数相减,记作 https://www.zhihu.com/equation?tex=DOG 即可近似得到 https://www.zhihu.com/equation?tex=LOG ,可见,当方差确定时,二者的函数曲线增减趋势是十分相近的,如下图
DOG 和 LOG 函数,一维切面(图片来源于网络)
可以认为,DOG 近似 LOG。
边缘检测时,二者相比
[*]DOG 只需要两个高斯模板分别对图像滤波,然后相减,计算上可以加速,例如分离成两个一维的模板
[*]LOG 更为精确。
#include &#34;faster_gaussi_filter.h&#34;
std::vector<double> difference_of_gaussi(const cv::Mat& source, const int radius, const double sigma, const double k) {
// 两个高斯卷积
const auto lhs = faster_2_gaussi_filter_channel(source, 2 * radius + 1, k * sigma, k * sigma);
const auto rhs = faster_2_gaussi_filter_channel(source, 2 * radius + 1, sigma, sigma);
// 准备结果
const int length = source.rows * source.cols;
std::vector<double> result_double(length, 0);
for(int i = 0;i < length; ++i) result_double = lhs.data - rhs.data;//
returnresult_double;
}
cv::Mat laplace_of_gaussi_edge_detection(const cv::Mat& source, const int radius, const double sigma, const double threshold) {
// 获取 LOG 结果
// const auto LOG_result = laplace_of_gaussi(source, radius, sigma);
const auto LOG_result = difference_of_gaussi(source, radius, sigma, 1.1);
// ...... 定位过零的点作为边缘
}
DOG 检测含噪声图像的边缘
极值点检测
DOG 比较著名的应用就是在 sift 中定位特征点。每个金字塔都包含不同尺度的高斯模糊之后的图像,下图是某个金字塔(某个分辨率)的 5 个尺度高斯模糊的图像组,相减得到 4 个 DOG 高斯差分图像,
DOGin sift
之后,相邻三层 DOG 之间,寻找中间那一层的极值点,如下
比较同一个 DOG 以及上下相邻的 DOG,在邻域 3x3 内共 26 个点,判定是否为极大值或者极小值,如果在多个尺度下都能保证是极值,则可以认为是稳定的特征点,以下是一个粗略的实现
// 单通道关键点检测
std::pair< keypoints_type, keypoints_type > difference_of_gaussi_keypoints_detection(
const cv::Mat& source, const int radius,
const std::vector< double > sigma_list,
const double threshold) {
const int C = source.channels();
if(C != 1) {
std::cout << &#34;只支持单通道图像 !&#34; << std::endl;
return std::pair< keypoints_type, keypoints_type >();
}
// 四次高斯模糊, 3 张 DOG 图
assert(sigma_list.size() == 4);
// 根据 sigma_list 的方差分别做高斯模糊
const int kernel_size = (radius << 1) + 1;
const auto gaussi_1 = faster_2_gaussi_filter_channel(source, kernel_size, sigma_list, sigma_list);
const auto gaussi_2 = faster_2_gaussi_filter_channel(source, kernel_size, sigma_list, sigma_list);
const auto gaussi_3 = faster_2_gaussi_filter_channel(source, kernel_size, sigma_list, sigma_list);
const auto gaussi_4 = faster_2_gaussi_filter_channel(source, kernel_size, sigma_list, sigma_list);
// 分别求 DOG
const int H = source.rows;
const int W = source.cols;
const int length = H * W;
std::vector<double> DOG_down(length), DOG_mid(length), DOG_up(length);
for(int i = 0;i < length; ++i) DOG_down = gaussi_1.data - gaussi_2.data;
for(int i = 0;i < length; ++i) DOG_mid = gaussi_2.data - gaussi_3.data;
for(int i = 0;i < length; ++i) DOG_up = gaussi_3.data - gaussi_4.data;
// 准备结果, 返回极大值与极小值的点
keypoints_type max_points;
keypoints_type min_points;
// 三层之间找极值
for(int i = 1;i < H - 1; ++i) {
double* const down = DOG_down.data() + i * W;
double* const mid = DOG_mid.data() + i * W;
double* const up = DOG_up.data() + i * W;
for(int j = 1;j < W - 1; ++j) {
// 中间这个点的值, 和最近的 26 个点比较大小
const auto center = mid;
// 极大值
if(center > mid and center > mid and
center > mid and center > mid and center > mid and
center > mid and center > mid and center > mid and
center > down and center > down and center > down and
center > down and center > down and center > down and
center > down and center > down and center > down and
center > up and center > up and center > up and
center > up and center > up and center > up and
center > up and center > up and center > up) {
//保留响应值比较强的点作为特征点
if(std::abs(center) > threshold) max_points.emplace_back(i, j);
}
// 极小值
if(center < mid and center < mid and
center < mid and center < mid and center < mid and
center < mid and center < mid and center < mid and
center < down and center < down and center < down and
center < down and center < down and center < down and
center < down and center < down and center < down and
center < up and center < up and center < up and
center < up and center < up and center < up and
center < up and center < up and center < up) {
if(std::abs(center) > threshold) min_points.emplace_back(i, j);
}
}
}
return std::make_pair(max_points, min_points);
}
void demo_5() {
// 读取图像
const std::string image_path(&#34;../images/detail/a0015-DSC_0081.png&#34;);
auto origin_image = cv::imread(image_path);
cv::Mat detected_result = origin_image.clone();
// BGR -> Gray 支持灰度图像
cv::cvtColor(origin_image, origin_image, cv::COLOR_BGR2GRAY);
// LOG 检测边缘
std::pair<keypoints_type, keypoints_type > key_points;
key_points = difference_of_gaussi_keypoints_detection(origin_image, 3, {0.3, 0.4, 0.5, 0.6}, 6);
// 极大值
for(const auto point : key_points.first) cv::circle(detected_result, cv::Point(point.second, point.first), 3, CV_RGB(255, 0, 0));
// 极小值
for(const auto point : key_points.second) cv::circle(detected_result, cv::Point(point.second, point.first), 3, CV_RGB(0, 255, 0));
// 展示结果, 保存
auto comparison_results = cv_concat({detected_result});
cv_show(comparison_results);
cv_write(comparison_results, &#34;./images/output/LOG_keypoints_detection.png&#34;);
}
结果
四个尺度的高斯模糊结果,方差分别是0.3,0.4,0.5,0.6
得到的 3 个相邻尺度的 DOG 图像
粗略检测到的特征点,极大值(红),极小值(绿)
看起来效果还可以。
另外测试几张图像
后续将在 sift 中优化。
问题
[*]LOG 检测边缘时,寻找两侧响应值相反的时候,是离散的点,太过粗略
改进
[*]double 改成 int,损失一些精度,加快计算
[*]LOG 模板也是对称的,而且可以分离成两个一维的 LOG 加快计算
参考资料
[*]https://homepages.inf.ed.ac.uk/rbf/HIPR2/log.htm
[*]https://homepages.inf.ed.ac.uk/rbf/HIPR2/zeros.htm
[*]https://senitco.github.io/2017/06/20/image-feature-LoG-DoG/
[*]https://www.cnblogs.com/YiXiaoZhou/p/5891645.html
[*]https://blog.csdn.net/gnehcuoz/article/details/52793654
[*]https://blog.csdn.net/u014453898/article/details/106461500
[*]SIFT - Lowe D G. Distinctive image features from scale-invariant keypoints. International journal of computer vision, 2004, 60(2): 91-110.
[*]测试图片来源 - MIT-Adobe-5K
马克,慢慢看。 我做个总结,如果有写的不对的地方,还望指正[捂脸]。边缘检测的效果不太好,让我一度怀疑我是不是哪一步推错了,或是哪里理解错了
页:
[1]