基于opencv的c++图像处理(图像边缘检测)

前言

基于opencv的c++接口,实现常用的图像边缘检测方法,包括了图像差分、拉普拉斯边缘检测、canny边缘检测、sobel边缘检测、prewitt边缘检测、roberts边缘检测以及Marr-Hildreth边缘检测。

相关的opencv接口解析

CV_EXPORTS_W void addWeighted(InputArray src1, double alpha, InputArray src2,
                              double beta, double gamma, OutputArray dst, int dtype = -1);

函数 addWeighted 用于计算两个数组的加权和,该函数可以替换为矩阵表达式:
dst = src1alpha + src2beta + gamma;
@param src1 第一个输入数组。
@param alpha 第一个数组元素的 alpha 权重。
@param src2 与 src1 大小和通道号相同的第二个输入数组。
@param beta 第二个数组元素的beta权重。
@param gamma 标量添加到每个总和。
@param dst 输出数组,其大小和通道数与输入数组相同。
@param dtype 输出数组的可选深度; 当两个输入数组具有相同的深度时,可以将 dtype 设置为 -1,这将等效于 src1.depth()。

CV_EXPORTS_W void Laplacian( InputArray src, OutputArray dst, int ddepth,
                             int ksize = 1, double scale = 1, double delta = 0,
                             int borderType = BORDER_DEFAULT );

该函数通过将使用 Sobel 算子计算的第二个 x 和 y 导数相加来计算源图像的拉普拉斯算子。

@param src 源图像。
@param dst 与 src 大小和通道数相同的目标图像。
@param ddepth 目标图像的所需深度。
@param ksize 用于计算二阶导数滤波器的孔径大小。 有关详细信息,请参阅#getDerivKernels。 大小必须是正数和奇数。
@param scale 计算的拉普拉斯值的可选比例因子。 默认情况下,不应用缩放。 有关详细信息,请参阅#getDerivKernels。
@param delta 在将结果存储到 dst 之前添加到结果中的可选 delta 值。
@param borderType 像素外推法,见#BorderTypes。 不支持#BORDER_WRAP。

enum BorderTypes {
    
    
    BORDER_CONSTANT    = 0, //!< `iiiiii|abcdefgh|iiiiiii`  with some specified `i`
    BORDER_REPLICATE   = 1, //!< `aaaaaa|abcdefgh|hhhhhhh`
    BORDER_REFLECT     = 2, //!< `fedcba|abcdefgh|hgfedcb`
    BORDER_WRAP        = 3, //!< `cdefgh|abcdefgh|abcdefg`
    BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba`
    BORDER_TRANSPARENT = 5, //!< `uvwxyz|abcdefgh|ijklmno`

    BORDER_REFLECT101  = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
    BORDER_DEFAULT     = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
    BORDER_ISOLATED    = 16 //!< do not look outside of ROI
};     
CV_EXPORTS_W void Canny( InputArray image, OutputArray edges,
                         double threshold1, double threshold2,
                         int apertureSize = 3, bool L2gradient = false );

该函数在输入图像中查找边缘,并使用 Canny 算法在输出地图边缘中标记它们。 threshold1 和 threshold2 之间的最小值用于边缘链接。 这最大值用于查找强边缘的初始段。 看http://en.wikipedia.org/wiki/Canny_edge_detector
@param image 8 位输入图像。
@param edges 输出边缘图; 单通道 8 位图像,其大小与 image 相同。
@param threshold1 滞后过程的第一个阈值。
@param threshold2 滞后过程的第二个阈值。
@param apertureSize Sobel 算子的孔径大小。
@param L2gradient 一个标志,表示是否更准确的L2范数应该用于计算图像梯度幅度 (
L2gradient=true ),或者默认L1范数(L2gradient=false )。

CV_EXPORTS_W void Sobel( InputArray src, OutputArray dst, int ddepth,
                         int dx, int dy, int ksize = 3,
                         double scale = 1, double delta = 0,
                         int borderType = BORDER_DEFAULT );

该函数通过将使用 Sobel 算子计算图像x和y方向的梯度。
@param src 输入图像。
@param dst 输出与 src 大小和通道数相同的图像。
@param ddepth 输出图像深度,见@ref filter_depths “combinations”;在 8 位输入图像的情况下,它将导致截断导数。
@param dx 导数 x 的顺序。
@param dy 导数 y 的顺序。
@param ksize 扩展 Sobel 内核的大小;它必须是 1、3、5 或 7。
@param scale 计算导数值的可选比例因子;默认情况下,没有缩放
已应用(有关详细信息,请参阅#getDerivKernels)。
@param delta 在将结果存储到 dst 之前添加到结果中的可选 delta 值。
@param borderType 像素外推方法,参见#BorderTypes。不支持#BORDER_WRAP。

示例代码

edge.h

#pragma once
#include <iostream>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>

using namespace std;
using namespace cv;
#define PROCESS_IMG_SUCESS 0
#define PROCESS_IMG_FAIL 1

namespace ImgEnhance
{
    
    
	//边缘检测
	class EdgeDetect
	{
    
    
	public:
		EdgeDetect() {
    
     cout << "EdgeDetect is being created" << endl; } // 这是构造函数声明
		~EdgeDetect() {
    
     cout << "EdgeDetect is being deleted" << endl; } // 这是析构函数声明
		void diffOperation(const cv::Mat srcImage, cv::Mat& edgeXImage, cv::Mat& edgeYImage);// 图像差分操作
		int ImgDifference(cv::Mat srcImage, cv::Mat &dstImage, double vValue, double hValue, double fixedValue);//图像差分
		int LaplaceEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage);//拉普拉斯边缘检测
		int CannyEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage, double threshold1, double threshold2);//canny边缘检测
		int SobelEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage, double vValue, double hValue, double fixedValue);//sobel边缘检测
		int PrewittEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage, double vValue, double hValue, double fixedValue);//prewitt边缘检测																										
		Mat roberts(Mat srcImage);//Roberts算子实现
		int RobertsEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage);//roberts边缘检测												   
		void marrEdge(const Mat src, Mat& result, int kerValue, double delta);//Marr-Hildreth检测算法实现(高斯平滑滤波器和拉普拉斯锐化器结合起来)
		int MarrEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage, int kerValue, double delta);//marr边缘检测

	};
}

edge.cpp

#include"edge.h"

// 图像差分操作
void ImgEnhance::EdgeDetect::diffOperation(const cv::Mat srcImage, cv::Mat& edgeXImage, cv::Mat& edgeYImage)
{
    
    
	cv::Mat tempImage = srcImage.clone();
	int nRows = tempImage.rows;
	int nCols = tempImage.cols;
	for (int i = 0; i < nRows - 1; i++)
	{
    
    
		for (int j = 0; j < nCols - 1; j++)
		{
    
    
			// 计算垂直边边缘
			edgeXImage.at<uchar>(i, j) =
				abs(tempImage.at<uchar>(i + 1, j) -
					tempImage.at<uchar>(i, j));
			// 计算水平边缘
			edgeYImage.at<uchar>(i, j) =
				abs(tempImage.at<uchar>(i, j + 1) -
					tempImage.at<uchar>(i, j));
		}
	}
}

int ImgEnhance::EdgeDetect::ImgDifference(cv::Mat srcImage, cv::Mat &dstImage, double vValue, double hValue, double fixedValue)
{
    
    
	if (srcImage.empty())
	{
    
    
		printf("cannot load!!\n");
		return 1;
	}
	cv::Mat edgeXImage(srcImage.size(), srcImage.type());
	cv::Mat edgeYImage(srcImage.size(), srcImage.type());
	// 计算查分图像
	diffOperation(srcImage, edgeXImage, edgeYImage);
	cv::imshow("edgeXImage", edgeXImage);
	cv::imshow("edgeYImage", edgeYImage);
	//cv::Mat edgeImage(srcImage.size(), srcImage.type());
	// 水平与垂直边缘图像叠加
	addWeighted(edgeXImage, vValue, edgeYImage, hValue, fixedValue, dstImage);
	return 0;
}

int ImgEnhance::EdgeDetect::LaplaceEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage)
{
    
    
	if (srcImage.empty())
	{
    
    
		printf("cannot load!!\n");
		return 1;
	}
	//使用Laplace函数
	//第三个参数:目标图像深度;第四个参数:滤波器孔径尺寸;第五个参数:比例因子;第六个参数:表示结果存入目标图
	Mat dstImage0;
	Laplacian(srcImage, dstImage0, CV_16S, 3, 1, 0, BORDER_DEFAULT);
	//计算绝对值,并将结果转为8位
	convertScaleAbs(dstImage0, dstImage);
	return 0;

}

int ImgEnhance::EdgeDetect::CannyEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage, double threshold1, double threshold2)
{
    
    
	if (srcImage.empty())
	{
    
    
		printf("cannot load!!\n");
		return 1;
	}
	//运行canny算子
	Canny(srcImage, dstImage, threshold1, threshold2, 3);
	return 0;
}

int ImgEnhance::EdgeDetect::SobelEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage, double vValue, double hValue, double fixedValue)
{
    
    
	if (srcImage.empty())
	{
    
    
		printf("cannot load!!\n");
		return 1;
	}


	Mat grad_x, grad_y;
	Mat abs_grad_x, abs_grad_y;

	//求x方向梯度
	Sobel(srcImage, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
	convertScaleAbs(grad_x, abs_grad_x);
	imshow("x方向soble", abs_grad_x);

	//求y方向梯度
	Sobel(srcImage, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
	convertScaleAbs(grad_y, abs_grad_y);
	imshow("y向soble", abs_grad_y);


	// 水平与垂直边缘图像叠加
	addWeighted(abs_grad_x, vValue, abs_grad_y, hValue, fixedValue, dstImage);
	return 0;
}

int ImgEnhance::EdgeDetect::PrewittEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage, double vValue, double hValue, double fixedValue)
{
    
    
	if (srcImage.empty())
	{
    
    
		printf("cannot load!!\n");
		return 1;
	}
	Mat Kernelx, Kernely;
	Kernelx = (Mat_<double>(3, 3) << 1, 1, 1, 0, 0, 0, -1, -1, -1);
	Kernely = (Mat_<double>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);

	Mat grad_x, grad_y;
	Mat abs_grad_x, abs_grad_y;

	filter2D(srcImage, grad_x, CV_16S, Kernelx, Point(-1, -1));
	filter2D(srcImage, grad_y, CV_16S, Kernely, Point(-1, -1));
	convertScaleAbs(grad_x, abs_grad_x);
	convertScaleAbs(grad_y, abs_grad_y);
	imshow("y方向prewitt", abs_grad_x);
	imshow("x方向prewitt", abs_grad_y);

	// 水平与垂直边缘图像叠加
	addWeighted(abs_grad_x, vValue, abs_grad_y, hValue, fixedValue, dstImage);
	return 0;

}

//Roberts算子实现
Mat ImgEnhance::EdgeDetect::roberts(Mat srcImage)
{
    
    
	Mat dstImage = srcImage.clone();
	int nRows = dstImage.rows;
	int nCols = dstImage.cols;
	for (int i = 0; i < nRows - 1; i++) {
    
    
		for (int j = 0; j < nCols - 1; j++) {
    
    
			//根据公式计算
			int t1 = (srcImage.at<uchar>(i, j) -
				srcImage.at<uchar>(i + 1, j + 1))*
				(srcImage.at<uchar>(i, j) -
					srcImage.at<uchar>(i + 1, j + 1));
			int t2 = (srcImage.at<uchar>(i + 1, j) -
				srcImage.at<uchar>(i, j + 1))*
				(srcImage.at<uchar>(i + 1, j) -
					srcImage.at<uchar>(i, j + 1));
			//计算g(x,y)
			dstImage.at<uchar>(i, j) = (uchar)sqrt(t1 + t2);
		}
	}
	return dstImage;
}

int ImgEnhance::EdgeDetect::RobertsEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage)
{
    
    
	if (srcImage.empty())
	{
    
    
		printf("cannot load!!\n");
		return 1;
	}
	dstImage = roberts(srcImage);
	return 0;
}

//Marr-Hildreth检测算法实现(高斯平滑滤波器和拉普拉斯锐化器结合起来)
void ImgEnhance::EdgeDetect::marrEdge(const Mat src, Mat& result, int kerValue, double delta)
{
    
    
	// 计算LOG算子
	Mat kernel;
	// 半径
	int kerLen = kerValue / 2;
	kernel = Mat_<double>(kerValue, kerValue);
	// 滑窗
	for (int i = -kerLen; i <= kerLen; i++)
	{
    
    
		for (int j = -kerLen; j <= kerLen; j++)
		{
    
    
			// 核因子生成
			kernel.at<double>(i + kerLen, j + kerLen) =
				exp(-((pow(j, 2) + pow(i, 2)) /
					(pow(delta, 2) * 2)))
				* (((pow(j, 2) + pow(i, 2) - 2 *
					pow(delta, 2)) / (2 * pow(delta, 4))));
		}
	}
	// 输出参数设置
	int kerOffset = kerValue / 2;
	Mat laplacian = (Mat_<double>(src.rows - kerOffset * 2,
		src.cols - kerOffset * 2));
	result = Mat::zeros(src.rows - kerOffset * 2,
		src.cols - kerOffset * 2, src.type());
	double sumLaplacian;
	// 遍历计算卷积图像的Lapace算子
	for (int i = kerOffset; i < src.rows - kerOffset; ++i)
	{
    
    
		for (int j = kerOffset; j < src.cols - kerOffset; ++j)
		{
    
    
			sumLaplacian = 0;
			for (int k = -kerOffset; k <= kerOffset; ++k)
			{
    
    
				for (int m = -kerOffset; m <= kerOffset; ++m)
				{
    
    
					// 计算图像卷积
					sumLaplacian += src.at<uchar>(i + k, j + m) *
						kernel.at<double>(kerOffset + k,
							kerOffset + m);
				}
			}
			// 生成Lapace结果
			laplacian.at<double>(i - kerOffset,
				j - kerOffset) = sumLaplacian;
		}
	}
	// 过零点交叉 寻找边缘像素
	for (int y = 1; y < result.rows - 1; ++y)
	{
    
    
		for (int x = 1; x < result.cols - 1; ++x)
		{
    
    
			result.at<uchar>(y, x) = 0;
			// 邻域判定
			if (laplacian.at<double>(y - 1, x) *
				laplacian.at<double>(y + 1, x) < 0)
			{
    
    
				result.at<uchar>(y, x) = 255;
			}
			if (laplacian.at<double>(y, x - 1) *
				laplacian.at<double>(y, x + 1) < 0)
			{
    
    
				result.at<uchar>(y, x) = 255;
			}
			if (laplacian.at<double>(y + 1, x - 1) *
				laplacian.at<double>(y - 1, x + 1) < 0)
			{
    
    
				result.at<uchar>(y, x) = 255;
			}
			if (laplacian.at<double>(y - 1, x - 1) *
				laplacian.at<double>(y + 1, x + 1) < 0)
			{
    
    
				result.at<uchar>(y, x) = 255;
			}
		}
	}
}

int ImgEnhance::EdgeDetect::MarrEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage, int kerValue, double delta)
{
    
    
	if (srcImage.empty())
	{
    
    
		printf("cannot load!!\n");
		return 1;
	}
	marrEdge(srcImage, dstImage, kerValue, delta);
	return 0;
}

test.cpp

#include"edge.h"

ImgEnhance::EdgeDetect ImgE;//边缘检测

int main()
{
    
    
	// 读取源图像及判断
	cv::Mat srcImage = cv::imread("flower.jpg");
	if (!srcImage.data)
	{
    
    
		return 1;
	}
	/*cv::namedWindow("原始图", 0);
	cv::imshow("原始图", srcImage);*/
	// 转化为灰度图像
	cv::Mat srcGray;
	if (srcImage.channels() == 3)
	{
    
    
		cv::cvtColor(srcImage, srcGray, COLOR_RGB2GRAY);
	}
	else
	{
    
    
		srcGray = srcImage.clone();
	}
	cv::namedWindow("灰度图", 0);
	cv::imshow("灰度图", srcGray);
	
	Mat difImage;
	//图像差分
	ImgE.ImgDifference(srcGray, difImage, 0.5, 0.5, 0);
	cv::namedWindow("图像差分结果图", 0);
	cv::imshow("图像差分结果图", difImage);

	Mat laplaceImage;
	//拉普拉斯边缘检测
	ImgE.LaplaceEdgeDetect(srcGray, laplaceImage);
	cv::namedWindow("拉普拉斯边缘检测结果图", 0);
	cv::imshow("拉普拉斯边缘检测结果图", laplaceImage);

	Mat cannyImage;
	//canny边缘检测
	ImgE.CannyEdgeDetect(srcGray, cannyImage, 30, 90);
	cv::namedWindow("canny边缘检测结果图", 0);
	cv::imshow("canny边缘检测结果图", cannyImage);

	Mat sobelImage;
	//sobel边缘检测
	ImgE.SobelEdgeDetect(srcGray, sobelImage, 0.5, 0.5, 0);
	cv::namedWindow("sobel边缘检测结果图", 0);
	cv::imshow("sobel边缘检测结果图", sobelImage);

	Mat prewittImage;
	//prewitt边缘检测
	ImgE.PrewittEdgeDetect(srcGray, prewittImage, 0.5, 0.5, 0);
	cv::namedWindow("prewitt边缘检测结果图", 0);
	cv::imshow("prewitt边缘检测结果图", prewittImage);

	Mat robertsImage;
	//roberts边缘检测
	ImgE.RobertsEdgeDetect(srcGray, robertsImage);
	cv::namedWindow("roberts边缘检测结果图", 0);
	cv::imshow("roberts边缘检测结果图", robertsImage);

	Mat marrImage;
	//marr边缘检测
	ImgE.MarrEdgeDetect(srcGray, marrImage, 9, 1.6);
	cv::namedWindow("marr边缘检测结果图", 0);
	cv::imshow("marr边缘检测结果图", marrImage);

	cv::waitKey(0);

	return 0;

}

结果展示

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_40118285/article/details/127002334