文章目录

  • 原理
  • 实现代码(C++实现,VS2015):
  • 实现效果


原理

sobel滤波主要用于边缘检测,通过测量图像中每个像素点强度的梯度,从而找到像素点强度值变化最快的方向以及这个方向的变化率。检测结果显示了图像强度值在每个像素处的变化大小,从而判断以及该像素点是否为图像的边缘。

在强度恒定的区域对像素进行滤波,得到的结果是近乎为零的向量。
如果再图像的边缘进行检测,则可以得到一个由强度较弱的像素指向强度较亮的向量。

sobel滤波使用两个sobel 需要高斯滤波吗_计算机视觉的卷积核 一个代表水平方向上的变化,另一个代表垂直方向上的变化。用两个卷积核与原始图像做卷积运算,从而计算出导数的近似值。计算如下:
sobel 需要高斯滤波吗_计算机视觉_02

sobel 需要高斯滤波吗_sobel 需要高斯滤波吗_03

其中sobel 需要高斯滤波吗_sobel 需要高斯滤波吗_04是原图像,sobel 需要高斯滤波吗_计算机视觉_05sobel 需要高斯滤波吗_卷积_06分别是包含了图像像素在水平和垂直方向上的导数的近似值的图像。也就是原图像经过横向和纵向检测出的图像灰度值。

为了计算sobel 需要高斯滤波吗_计算机视觉_05sobel 需要高斯滤波吗_卷积_06,我们在输入图像上移动适当的内核(窗口),计算一个像素的值,然后向右移动一个像素。一旦到达该行的末尾,我们就向下移动到下一行的开头。

在网上截了一张计算sobel 需要高斯滤波吗_计算机视觉_05过程的图片,卷积核与上述略有出入:

sobel 需要高斯滤波吗_sobel 需要高斯滤波吗_10


同理可得sobel 需要高斯滤波吗_卷积_06的计算。

在每个像素点处的灰度大小由sobel 需要高斯滤波吗_计算机视觉_05sobel 需要高斯滤波吗_卷积_06共同决定:

sobel 需要高斯滤波吗_边缘检测_14

通常为了提高效率使用如下近似:

sobel 需要高斯滤波吗_计算机视觉_15

实现代码(C++实现,VS2015):

#pragma once
#include <opencv2\opencv.hpp>
#include <iostream>
#include <cmath>

using namespace std;
cv::Mat BGR2GRAY(cv::Mat src) { //转换为灰度图片
	int rows = src.rows;
	int cols = src.cols;

	cv::Mat dst = cv::Mat::zeros(rows, cols, CV_8UC1);

	for (int i = 0; i < rows; i++) {
		for (int j = 0; j < cols; j++) {
			dst.at<uchar>(i, j) = 0.2126 * (float)src.at<cv::Vec3b>(i, j)[2]
				+ 0.7152 * (float)src.at<cv::Vec3b>(i, j)[1]
				+ 0.0722 * (float)src.at<cv::Vec3b>(i, j)[0];
		}
	}
	return dst;
}
void Sobelfilter(cv::Mat &src) {//sobel 滤波
	int rows = src.rows;
	int cols = src.cols;

	double dx = 0, dy = 0;
	cv::Mat Gx = cv::Mat::zeros(rows, cols, CV_8UC1);
	cv::Mat Gy = cv::Mat::zeros(rows, cols, CV_8UC1);
	cv::Mat G = cv::Mat::zeros(rows, cols, CV_8UC1);
	double v = 0, vx, vy;
	for (int i = 0; i < rows; i++) {
		for (int j = 0; j < cols; j++) {
			v = 0;
			if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1) {
				G.at<uchar>(i, j) = 0;
			}
			else {
				dx = src.at<uchar>(i - 1, j + 1) - src.at<uchar>(i - 1, j - 1)
					+ 2 * src.at<uchar>(i, j + 1) - 2 * src.at<uchar>(i, j - 1) +
					src.at<uchar>(i + 1, j + 1) - src.at<uchar>(i + 1, j - 1); 
				dy = src.at<uchar>(i + 1, j - 1) - src.at<uchar>(i - 1, j - 1)
					+ 2 * src.at<uchar>(i + 1, j) - 2 * src.at<uchar>(i - 1, j) +
					src.at<uchar>(i + 1, j + 1) - src.at<uchar>(i - 1, j + 1);
				v = abs(dx) + abs(dy); //G = |Gx| + |Gy|
				v = fmax(v, 0);
				v = fmin(v, 255);
				G.at<uchar>(i, j) = (uchar)v;
			}
		}
	}
	for (int i = 0; i < rows; i++) { // 水平方向
		for (int j = 0; j < cols; j++) {
			vx = 0;
			if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1)
				Gx.at<uchar>(i, j) = 0;
			else {
				dx = src.at<uchar>(i - 1, j + 1) - src.at<uchar>(i - 1, j - 1)
					+ 2 * src.at<uchar>(i, j + 1) - 2 * src.at<uchar>(i, j - 1) +
					src.at<uchar>(i + 1, j + 1) - src.at<uchar>(i + 1, j - 1);
				vx = abs(dx);
				vx = fmax(vx, 0); vx = fmin(vx, 255);
				Gx.at<uchar>(i, j) = (uchar)vx;
			}
		}
	}
	for (int i = 0; i < rows; i++) {// 垂直方向
		for (int j = 0; j < cols; j++) {
			vy = 0;
			if (i == 0 || j == 0 || i == rows - 1 || j == cols - 1)
				Gy.at<uchar>(i, j) = 0;
			else {
				dy = src.at<uchar>(i + 1, j - 1) - src.at<uchar>(i - 1, j - 1)
					+ 2 * src.at<uchar>(i + 1, j) - 2 * src.at<uchar>(i - 1, j) +
					src.at<uchar>(i + 1, j + 1) - src.at<uchar>(i - 1, j + 1);
				vy = abs(dy);
				vy = fmax(vy, 0); vx = fmin(vy, 255);
				Gy.at<uchar>(i, j) = (uchar)vy;
			}
		}
	}
	cv::imshow("Gx", Gx);// horizontal
	cv::imshow("Gy", Gy);// vertical
	cv::imshow("G", G);// gradient
}
int main(int argc, char** argv) {
	cv::Mat src, gray;
	src = cv::imread("C:/Users/Odysseus96/Pictures/Image/lena.jpg");
	cv::imshow("Input", src);

	gray = BGR2GRAY(src);
	cv::imshow("gray", gray);
	
	Sobelfilter(gray);
	cv::waitKey(0);
	cv::destroyAllWindows();
	return 0;
}

实现效果

1、原图:

sobel 需要高斯滤波吗_sobel 需要高斯滤波吗_16


2、灰度图:

sobel 需要高斯滤波吗_opencv_17


3、水平方向

sobel 需要高斯滤波吗_边缘检测_18


4、垂直方向

sobel 需要高斯滤波吗_sobel 需要高斯滤波吗_19


5、水平方向和垂直方向结合

sobel 需要高斯滤波吗_卷积_20