Sobel算子是一种典型的用于边缘检测的线性滤波器,它基于两个简单的3*3内核,内核结构如下图所示
水平方向:
-1 | 0 | 1 |
-2 | 0 | 2 |
-1 | 0 | 1 |
垂直方向:
-1 | -2 | -1 |
0 | 0 | 0 |
1 | 2 | 1 |
如果把图像看做二维函数,那么sobel算子就是图像在水平和垂直方向变化的速度。在数学属于中,这种速度称为梯度。它是一个二维向量,向量的元素是横竖两个方向的函数的一阶导数:
grad(I)=
sobel算子在水平和垂直方向计算像素值的差分,得到图像梯度的近似值。它在像素周围的一定范围内进行运算,以减少噪声带来的影响。
使用水平方向和垂直方向滤波器检测边缘:
#include <iostream>
#include <iomanip>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
//#include "laplacianZC.h"
int main()
{
// Read input image
cv::Mat image = cv::imread("E:\\OpenCV\\OpenCV3源码\\images\\boldt.jpg", 0);
if (!image.data)
return 0;
// Display the image
cv::namedWindow("Original Image");
cv::imshow("Original Image", image);
// Compute Sobel X derivative
cv::Mat sobelX;
cv::Sobel(image, // input
sobelX, // output
CV_8U, // image type
1, 0, // kernel specification
3, // size of the square kernel
0.4, 128); // scale and offset(偏移量)
// Display the image
cv::namedWindow("Sobel X Image");
cv::imshow("Sobel X Image", sobelX);
// Compute Sobel Y derivative
cv::Mat sobelY;
cv::Sobel(image, // input
sobelY, // output
CV_8U, // image type
0, 1, // kernel specification
3, // size of the square kernel
0.4, 128); // scale and offset
// Display the image
cv::namedWindow("Sobel Y Image");
cv::imshow("Sobel Y Image", sobelY);
因为梯度是一个二维向量,所以它有范数和方向。梯度向量的范数表示变化的振幅,计算时通常被当做欧几里得范数(也成L2范数):
=
但是在图像处理领域,通常把绝对值之和作为范数进行计算。这称为L1范数。
梯度向量总是指向变化最剧烈的方向。对于一幅图像来说,这意味着梯度方向与边缘垂直,从较暗区域指向较亮区域。梯度的角度用下面的公式计算:
∠grade(I)=arctan()
使用L1范数边缘检测:
// Compute norm of Sobel
cv::Sobel(image, sobelX, CV_16S, 1, 0);
cv::Sobel(image, sobelY, CV_16S, 0, 1);
cv::Mat sobel;
//compute the L1 norm
sobel = abs(sobelX) + abs(sobelY);
double sobmin, sobmax;
cv::minMaxLoc(sobel, &sobmin, &sobmax);
std::cout << "sobel value range: " << sobmin << " " << sobmax << std::endl;
// Compute Sobel X derivative (7x7)
cv::Sobel(image, sobelX, CV_8U, 1, 0, 7, 0.001, 128);
// Display the image
cv::namedWindow("Sobel X Image (7x7)");
cv::imshow("Sobel X Image (7x7)", sobelX);
// Print window pixel values
for (int i = 0; i<12; i++) {
for (int j = 0; j<12; j++)
std::cout << std::setw(5) << static_cast<int>(sobel.at<short>(i + 79, j + 215)) << " ";
std::cout << std::endl;
}
std::cout << std::endl;
std::cout << std::endl;
std::cout << std::endl;
// Conversion to 8-bit image
// sobelImage = -alpha*sobel + 255
cv::Mat sobelImage;
sobel.convertTo(sobelImage, CV_8U, -255. / sobmax, 255);
// Display the image
cv::namedWindow("Sobel Image");
cv::imshow("Sobel Image", sobelImage);
threshold()函数用法:https://blog.youkuaiyun.com/u012566751/article/details/77046445
对梯度幅值进行阈值化,可得到一个二值边缘分布图。
// Apply threshold to Sobel norm (low threshold value)
cv::Mat sobelThresholded;
cv::threshold(sobelImage, sobelThresholded, 225, 255, cv::THRESH_BINARY);
// Display the image
cv::namedWindow("Binary Sobel Image (low)");
cv::imshow("Binary Sobel Image (low)", sobelThresholded);
// Apply threshold to Sobel norm (high threshold value)
cv::threshold(sobelImage, sobelThresholded, 190, 255, cv::THRESH_BINARY);
// Display the image
cv::namedWindow("Binary Sobel Image (high)");
cv::imshow("Binary Sobel Image (high)", sobelThresholded);