图像的Sobel滤波,OpenCV 、C++实现

原理

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

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

sobel滤波使用两个 3 ∗ 3 3 * 3 33的卷积核 一个代表水平方向上的变化,另一个代表垂直方向上的变化。用两个卷积核与原始图像做卷积运算,从而计算出导数的近似值。计算如下:
G x = [ − 1 0 1 − 2 0 2 − 1 0 1 ] ∗ A Gx = \begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2\\ -1 & 0 & 1\end{bmatrix} * A Gx=121000121A

G y = [ − 1 − 2 − 1 0 0 0 1 2 1 ] ∗ A \\ Gy = \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0\\ 1 & 2 & 1\end{bmatrix} * A Gy=101202101A
其中 A A A是原图像, G x Gx Gx G y Gy Gy分别是包含了图像像素在水平和垂直方向上的导数的近似值的图像。也就是原图像经过横向和纵向检测出的图像灰度值。
为了计算 G x Gx Gx G y Gy Gy,我们在输入图像上移动适当的内核(窗口),计算一个像素的值,然后向右移动一个像素。一旦到达该行的末尾,我们就向下移动到下一行的开头。
在网上截了一张计算 G x Gx Gx过程的图片,卷积核与上述略有出入:
在这里插入图片描述
同理可得 G y Gy Gy的计算。
在每个像素点处的灰度大小由 G x Gx Gx G y Gy Gy共同决定:
G = G x 2 + G y 2 G = \sqrt {Gx^2 + Gy^2} G=Gx2+Gy2
通常为了提高效率使用如下近似:
G = ∣ G x ∣ + ∣ G y ∣ G = |Gx| + |Gy| G=Gx+Gy

实现代码(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、原图:
在这里插入图片描述
2、灰度图:
在这里插入图片描述
3、水平方向
在这里插入图片描述
4、垂直方向
在这里插入图片描述
5、水平方向和垂直方向结合
滤波后的结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值