前言
基于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,
BORDER_REPLICATE = 1,
BORDER_REFLECT = 2,
BORDER_WRAP = 3,
BORDER_REFLECT_101 = 4,
BORDER_TRANSPARENT = 5,
BORDER_REFLECT101 = BORDER_REFLECT_101,
BORDER_DEFAULT = BORDER_REFLECT_101,
BORDER_ISOLATED = 16
};
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);
int SobelEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage, double vValue, double hValue, double fixedValue);
int PrewittEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage, double vValue, double hValue, double fixedValue);
Mat roberts(Mat srcImage);
int RobertsEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage);
void marrEdge(const Mat src, Mat& result, int kerValue, double delta);
int MarrEdgeDetect(cv::Mat srcImage, cv::Mat &dstImage, int kerValue, double delta);
};
}
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);
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;
}
Mat dstImage0;
Laplacian(srcImage, dstImage0, CV_16S, 3, 1, 0, BORDER_DEFAULT);
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(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;
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);
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;
}
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));
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;
}
void ImgEnhance::EdgeDetect::marrEdge(const Mat src, Mat& result, int kerValue, double delta)
{
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;
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);
}
}
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::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;
ImgE.CannyEdgeDetect(srcGray, cannyImage, 30, 90);
cv::namedWindow("canny边缘检测结果图", 0);
cv::imshow("canny边缘检测结果图", cannyImage);
Mat sobelImage;
ImgE.SobelEdgeDetect(srcGray, sobelImage, 0.5, 0.5, 0);
cv::namedWindow("sobel边缘检测结果图", 0);
cv::imshow("sobel边缘检测结果图", sobelImage);
Mat prewittImage;
ImgE.PrewittEdgeDetect(srcGray, prewittImage, 0.5, 0.5, 0);
cv::namedWindow("prewitt边缘检测结果图", 0);
cv::imshow("prewitt边缘检测结果图", prewittImage);
Mat robertsImage;
ImgE.RobertsEdgeDetect(srcGray, robertsImage);
cv::namedWindow("roberts边缘检测结果图", 0);
cv::imshow("roberts边缘检测结果图", robertsImage);
Mat marrImage;
ImgE.MarrEdgeDetect(srcGray, marrImage, 9, 1.6);
cv::namedWindow("marr边缘检测结果图", 0);
cv::imshow("marr边缘检测结果图", marrImage);
cv::waitKey(0);
return 0;
}
结果展示
|