EdgeDetection
C++实现的主流边缘检测算法(Canny、Sobel、Laplace、Roberts等)
Install / Use
/learn @yooongchun/EdgeDetectionREADME
摘要:本文主要介绍了几种主流的图像边缘检测算法,包括Canny算子、Sobel算子、Laplace算子、Roberts算子、Krisch算子、Prewitt算子、Susan角点检测算法等。另外也介绍了相应衍生的概念如图像噪声、图像滤波等。
文中所有代码均在本人电脑上正常运行!
测试环境:VS2013+opevCV2.49
[TOC]
一、图像噪声
1.图像噪声分类
1.1 图像噪声分类
噪声的分类和该噪声的分布符合什么模型有关,常见的噪声有高斯白噪声、椒盐噪声、泊松分布噪声、指数分布噪声等。
1.2 图像滤波器
图像滤波器有空域滤波器,比如均值滤波器、中值滤波器、低通滤波器、高斯滤波等;频域滤波器,比如小波变换、傅里叶变换、余弦变换等;形态学滤波器,主要是通过膨胀和腐蚀等形态学操作进行去噪。 一般平时见的比较多是是高斯白噪声,像用均值滤波、中值滤波、高斯滤波可以去噪。除此以外,像椒盐噪声,一般用中值滤波基本可以去噪。
2.给图像添加噪声
以上说了图像噪声的分类,以下则针对常见的高斯白噪声和椒盐噪声进行详解
2.1 高斯白噪声
高斯白噪声中的高斯是指概率分布是正态函数,而白噪声是指它的二阶矩不相关,一阶矩为常数,是指先后信号在时间上的相关性。这是考查一个信号的两个不同方面的问题。
2.2 椒盐噪声
椒盐噪声其实是椒噪声和盐噪声的统称,椒噪声即图像中灰度值为255的像素点,盐噪声则为0。
2.3 编程实现
/*-----------CreateNoise类-------------------*/
//CreateNoise.h文件
//功能:产生图像噪声
//使用:调用成员函数void AddGaussianNoise(Mat&)添加高斯噪声
//调用成员函数void AddPepperNoise(Mat&int n)添加高斯噪声
//调用成员函数void AddSaitNoise(Mat&,int n)添加高斯噪声
#ifndef _CREATENOISE
#define _CREATENOISE
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#define TWO_PI 6.2831853071795864769252866
using namespace cv;
using namespace std;
class CreateNoise{
private:
double generateGaussianNoise();//创建高斯噪声函数原型
public:
void AddPepperNoise(Mat &image, int n);//添加椒噪声
void AddSaltNoise(Mat &image, int n);//添加盐噪声
void AddGaussianNoise(Mat&);//添加高斯噪声
};
#endif
//CreateNoise.cpp文件
/*给图像添加高斯噪声*/
#include"com.fanyu.createNoise.h"
//产生高斯噪声
double CreateNoise::generateGaussianNoise(){
static bool hasSpare = false;
static double rand1, rand2;
if (hasSpare)
{
hasSpare = false;
return sqrt(rand1) * sin(rand2);
}
hasSpare = true;
rand1 = rand() / ((double)RAND_MAX);
if (rand1 < 1e-100) rand1 = 1e-100;
rand1 = -2 * log(rand1);
rand2 = (rand() / ((double)RAND_MAX)) * TWO_PI;
return sqrt(rand1) * cos(rand2);
}
//高斯噪声
void CreateNoise::AddGaussianNoise(Mat& I){
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
int channels = I.channels();
int nRows = I.rows;
int nCols = I.cols * channels;
if (I.isContinuous()){
nCols *= nRows;
nRows = 1;
}
int i, j;
uchar* p;
for (i = 0; i < nRows; ++i){
p = I.ptr<uchar>(i);
for (j = 0; j < nCols; ++j){
double val = p[j] + generateGaussianNoise() * 32;
if (val < 0)
val = 0;
if (val > 255)
val = 255;
p[j] = (uchar)val;
}
}
}
//盐噪声
void CreateNoise::AddSaltNoise(Mat &image, int n) {//n为噪点数目
int i, j;
for (int k = 0; k<n / 2; k++) {
// rand() is the random number generator
i = std::rand() % image.cols; // % 整除取余数运算符,rand=1022,cols=1000,rand%cols=22
j = std::rand() % image.rows;
if (image.type() == CV_8UC1) { // gray-level image
image.at<uchar>(j, i) = 255; //at方法需要指定Mat变量返回值类型,如uchar等
}
else if (image.type() == CV_8UC3) { // color image
image.at<cv::Vec3b>(j, i)[0] = 255; //cv::Vec3b为opencv定义的一个3个值的向量类型
image.at<cv::Vec3b>(j, i)[1] = 255; //[]指定通道,B:0,G:1,R:2
image.at<cv::Vec3b>(j, i)[2] = 255;
}
}
}
//椒噪声
void CreateNoise::AddPepperNoise(Mat &image, int n) {//n为噪点数目
int i, j;
for (int k = 0; k<n; k++) {
// rand() is the random number generator
i = std::rand() % image.cols; // % 整除取余数运算符,rand=1022,cols=1000,rand%cols=22
j = std::rand() % image.rows;
if (image.type() == CV_8UC1) { // gray-level image
image.at<uchar>(j, i) = 0; //at方法需要指定Mat变量返回值类型,如uchar等
}
else if (image.type() == CV_8UC3) { // color image
image.at<cv::Vec3b>(j, i)[0] = 0; //cv::Vec3b为opencv定义的一个3个值的向量类型
image.at<cv::Vec3b>(j, i)[1] = 0; //[]指定通道,B:0,G:1,R:2
image.at<cv::Vec3b>(j, i)[2] = 0;
}
}
}
二、图像滤波
1. 图像滤波说明【来源搜狗词条】
图像滤波,即在尽量保留图像细节特征的条件下对目标像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。 由于成像系统、传输介质和记录设备等的不完善,数字图像在其形成、传输记录过程中往往会受到多种噪声的污染。另外,在图像处理的某些环节当输入的像对象并不如预想时也会在结果图像中引入噪声。这些噪声在图像上常表现为一引起较强视觉效果的孤立像素点或像素块。一般,噪声信号与要研究的对象不相关它以无用的信息形式出现,扰乱图像的可观测信息。对于数字图像信号,噪声表为或大或小的极值,这些极值通过加减作用于图像像素的真实灰度值上,在图像造成亮、暗点干扰,极大降低了图像质量,影响图像复原、分割、特征提取、图识别等后继工作的进行。要构造一种有效抑制噪声的滤波机必须考虑两个基本问题能有效地去除目标和背景中的噪声;同时,能很好地护图像目标的形状、大小及特定的几何和拓扑结构特征。
2. 滤波器分类
对于滤波器的分类按需求可有多种分类方式,滤波方法也各不相同,在此主要根据图像噪声处理介绍一种分类:
不同的滤波器用于不同的噪声,很难说某一个降噪滤波器能符所有的噪声。 首先,说一下噪声的类型吧,噪声的分类和该噪声的分布符合什么模型有关,常见的噪声有高斯白噪声、椒盐噪声、泊松分布噪声、指数分布噪声等。 其次,采用的滤波器有空域滤波器,比如均值滤波器、中值滤波器、低通滤波器、高斯滤波等;频域滤波器,比如小波变换、傅里叶变换、余弦变换等;形态学滤波器,主要是通过膨胀和腐蚀等形态学操作进行去噪。 第三,对应场合。一般平时见的比较多是是高斯白噪声,像用均值滤波、中值滤波、高斯滤波可以去噪。还有在低照度下,比如晚上拍照时的图像,一般属于泊松分布的噪声,可以采用一些3d去噪算法,比如效果不错的BM3D算法。像椒盐噪声,一般用中值滤波基本可以去噪。
作者:chaoren xiongmao 链接:http://www.zhihu.com/question/20095134/answer/14158067 来源:知乎 著作权归作者所有,转载请联系作者获得授权。
3. 滤波算法
3.1 高斯滤波算法
openCV中封装了高斯滤波函数
C++: void GaussianBlur(InputArray src,OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, intborderType=BORDER_DEFAULT )
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。它可以是单独的任意通道数的图片,但需要注意,图片深度应该为CV_8U,CV_16U, CV_16S, CV_32F 以及 CV_64F之一。 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。 第三个参数,Size类型的ksize高斯内核的大小。其中ksize.width和ksize.height可以不同,但他们都必须为正数和奇数。或者,它们可以是零的,它们都是由sigma计算而来。 第四个参数,double类型的sigmaX,表示高斯核函数在X方向的的标准偏差。 第五个参数,double类型的sigmaY,表示高斯核函数在Y方向的的标准偏差。若sigmaY为零,就将它设为sigmaX,如果sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height计算出来。 为了结果的正确性着想,最好是把第三个参数Size,第四个参数sigmaX和第五个参数sigmaY全部指定到。 第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。应用:
/*线性滤波:高斯滤波*/
bool GaussianBlur(String srcAdress, String dstAdress = "E:/opencv/averageFilter.jpg"){
Mat srcImage = imread(srcAdress, 1);
Mat dstImage;
if (!srcImage.data) {
cout << "图像读取出错!";
return false;
}
//滤波操作
GaussianBlur(srcImage, dstImage, Size(7,7),0,0);
//导出
imwrite(dstAdress, dstImage);
//显示
imshow("原图:", srcImage);
imshow("高斯滤波效果:", dstImage);
waitKey(0);
return true;
}
效果展示:

3.2 方框滤波算法
openCV中封装该函数,函数原型为
C++: void boxFilter(InputArray src,OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), boolnormalize=true, int borderType=BORDER_DEFAULT )
参数详解: 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。该函数对通道是独立处理的,且可以处理任意通道数的图片,但需要注意,待处理的图片深度应该为CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。 第三个参数,int类型的ddepth,输出图像的深度,-1代表使用原图深度,即src.depth()。 第四个参数,Size类型(对Size类型稍后有讲解)的ksize,内核的大小。一般这样写Size( w,h )来表示内核的大小( 其中,w 为像素宽度, h为像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小 第五个参数,Point类型的anchor,表示锚点(即被平滑的那个点),注意他有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。 第六个参数,bool类型的normalize,默认值为true,一个标识符,表示内核是否被其区域归一化(normalized)了。 第七个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。
编程实现:
/*线性滤波:方框滤波*/
int kenelValue = 3;
int MaxkenelValue = 50;
String srcAdress, dstAdress;
//回调函数
static void callBackboxFilter(int, void*){
Mat srcImage = imread(srcAdress,1);
Mat dstImage;
if (!srcImage.data){
cout << "图像读取出错";
return;
}
//滤波操作
boxFilter(srcImage, dstImage, -1, Size(kenelValue+1, kenelValue+1));
//显示
imshow("方框滤波效果预览:", dstImage);
}
//创建进度条
int boxFilter(String ssrcAdress, String sdstAdress = "E:/opencv/boxFilter.jpg"){
srcAdress = ssrcAdress;
dstAdress=sdstAdress;
namedWindow("方框滤波效果预览:",WINDOW_AUTOSIZE);
//创建进度条
createTrackbar("内核大小:", "方框滤波效果预览:", &kenelValue, MaxkenelValue, callBackboxFilter);
//回调
callBackboxFilter(kenelValue, 0);
waitKey(0);
return 0;
}
效果展示

3.3 均值滤波算法
openCV中封装了均值滤波算法函数
C++: void blur(InputArray src, OutputArraydst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
参数详解 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。该函数对通道是独立处理的,且可以处理任意通道数的图片,但需要注意,待处理的图片深度应该为CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。 第三个参数,Size类型(对Size类型稍后有讲解)的ksize,内核的大小。一般这样写Size( w,h )来表示内核的大小( 其中,w 为像素宽度, h为像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小 第四个参数,Point类型的anchor,表示锚点(即被平滑的那个点),注意他有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。 第五个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。
应用
/*线性滤波:均值滤波*/
bool blur(String srcAdress, String dstAdress = "E:/opencv/averageFilter.jpg"){
Mat srcImage = imread(srcAdress, 1);
Mat dstImage;
if (!srcImage.data) {
cout << "图像读取出错!";
return false;
}
//滤波操作
blur(srcImage, dstImage, Size(7, 7));
//导出
imwrite(dstAdress, dstImage);
//显示
imshow("原图:", srcImage);
imshow("均值滤波效果:", dstImage);
waitKey(0);
return true;
}
效果

3.4 中值滤波算法
openCV封装了该算法
C++: void medianBlur(InputArray src,OutputArray dst, int ksize)
参数详解: 第一个参数,InputArray类型的src,函数的输入参数,填1、3或者4通道的Mat类型的图像;当ksize为3或者5的时候,图像深度需为CV_8U,CV_16U,或CV_32F其中之一,而对于较大孔径尺寸的图片,它只能是CV_8U。 第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。我们可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。 第三个参数,int类型的ksize,孔径的线性尺寸(aperture linear size),注意这个参数必须是大于1的奇数,比如:3,5,7,9 ...
应用
/*非线性滤波:中值滤波*/
bool medianBlur(String src,String dst){
Mat srcImage = imread(src,1);
Mat dstImage;
if (!srcImage.data)
return false;
//滤波操作[注意:中间值取奇数]
medianBlur(srcImage,dstImage,7);
//显示
imshow("原图:",srcImage);
imshow("中值滤波效果图:",dstImage);
//导出
imwrite(dst,dstImage);
waitKey(0);
return true;
}
效果

3.5 双边滤波算法
openCV封装了该函数
C++: void bilateralFilter(InputArray src, OutputArraydst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT)
参数详解: 第一个参数,InputArray类型的src,输入图像,即源图像,需要为8位或者浮点型单通道、三通道的图像。 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。 第三个参数,int类型的d,表示在过滤过程中每个像素邻域的直径。如果这个值我们设其为非正数,那么OpenCV会从第五个参数sigmaSpace来计算出它来。 第四个参数,double类型的sigmaColor,颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,
Related Skills
node-connect
341.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
341.0kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.4kCommit, push, and open a PR
