前言
基于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;
}