《OpenCV3编程入门》学习笔记7 图像变换(一)基于OpenCV的边缘检测

第7章 图像变换

7.1 基于OpenCV的边缘检测

7.1.1 边缘检测的一般步骤

1.滤波:边缘检测算法主要基于图像强度的一阶和二阶导数,导数对噪声敏感,所以要滤波
2.增强:确定图像各点邻域强度的变化值,将有显著变化的点凸显,可通过计算梯度幅值确定
3.检测:某些特定应用中梯度值较大点不为边缘点,通过阈值化方法检测进行取舍

7.1.2 canny算子

1.主要评价标准:
(1)低错误率:减少噪声误报
(2)高定位性:接近实际边缘
(3)最小响应:边缘只标识一次
2.步骤:
(1)消除噪声:高斯滤波
(2)计算梯度幅值和方向
  1)运用一对卷积阵列分别作用于x和y方向
        在这里插入图片描述
  2)计算梯度幅值和方向(按照Sobel滤波器步骤进行)
             在这里插入图片描述
(3)非极大值抑制:排除非边缘像素,仅保留一些细线条
(4)滞后阈值:高阈值和低阈值(推荐高低阈值比2:1到3:1)
  1)像素幅值超过高阈值,保留为边缘像素
  2)像素幅值小于低阈值,排除
  3)像素幅值在高低之间,该像素连接到一个高于高阈值的像素时被保留
3.函数:Canny()函数
4.函数原型:

void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false)

5.参数说明:
(1)输入图像
(2)输出的边缘图
(3)第一个滞后性阈值,较小值用于边缘连接
(4)第二个滞后性阈值,较大值用于控制强边缘初始段
(5)应用Sobel算子的孔径大小,默认值3
(6)计算图像梯度幅值的标识,默认false
6.调用示例:

//将原图转成灰度图,降噪,用canny,将边缘图作为掩模拷贝到效果图上,得到彩色边缘图
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main()
{
	//载入原图
	Mat srcImage = imread("love.jpg");
	imshow("【原始图】", srcImage);

	Mat edgeImage, grayImage,dstImage;
	//【1】创建与src同类型和大小矩阵
	dstImage.create(srcImage.size(), srcImage.type());
	//【2】将原图转为灰度图
	cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);
	//【3】使用3*3内核降噪
	blur(grayImage, edgeImage, Size(3, 3));
	//【4】运行canny算子
	Canny(edgeImage, edgeImage, 3, 9, 3);
	//【5】将dstImage中所有元素设为0
	dstImage = Scalar::all(0);
	//【6】使用Canny算子输出的边缘图作为掩码将原图拷到目标图
	srcImage.copyTo(dstImage, edgeImage);
	//【7】显示效果图
	imshow("【效果图】Canny边缘检测", dstImage);

	waitKey(0);
	return 0;
}

运行效果:
在这里插入图片描述在这里插入图片描述

7.1.3 sobel算子

1.主要用于边缘检测的离散微分算子(discrete differentiation operator),结合高斯平滑和微分求导,计算图像灰度函数的近似梯度
2.计算过程:
(1)分别在x和y两个方向求导
水平:图像I与奇数大小的内核进行卷积,如内核大小为3:
                在这里插入图片描述
垂直:图像I与奇数大小的内核进行卷积,如内核大小为3:
                在这里插入图片描述
(2)在图像每一点,结合以上两个结果求出近似梯度
              在这里插入图片描述在这里插入图片描述
3.函数:Sobel()函数
4.函数原型:

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)

5.参数说明:
(1)输入图像
(2)目标图像
(3)输出图像深度,支持src.depth()和ddepth的组合:
    在这里插入图片描述
(4)x方向的差分阶数
(5)y方向的差分阶数
(6)Sobel核大小,取值:1、3(默认)、5、7,取值为1时一般使用3*1或1*3内核
   内核大小为3时,Sobel内核可能有明显误差,Scharr函数更精确,内核为:
            在这里插入图片描述
(7)计算导数时可选的缩放因子,默认1(无缩放)
(8)在结果存入目标图之前可选的delta值,默认0
(9)边界模式,默认BORDER_DEFAULT

使用sobel()函数时,取【xorder=1,yorder=0,ksize=3】计算图像X方向导数,取【xorder=0,yorder=1,ksize=3】计算图像Y方向导数

6.调用示例:

#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main()
{
	//【0】创建grad_x和grad_y矩阵
	Mat grad_x, grad_y;
	Mat abs_grad_x, abs_grad_y, dstImage;
	//【1】载入原图
	Mat srcImage = imread("love.jpg");
	//【2】显示原图
	imshow("【原始图】sobel边缘检测", srcImage);
	//【3】求X方向梯度
	Sobel(srcImage, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
	convertScaleAbs(grad_x, abs_grad_x);
	imshow("【效果图】x方向Sobel", abs_grad_x);
	//【4】求Y方向梯度
	Sobel(srcImage, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
	convertScaleAbs(grad_y, abs_grad_y);
	imshow("【效果图】y方向Sobel", abs_grad_y);
	//【5】合并梯度(近似)
	addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dstImage, -1);
	//【6】显示效果图
	imshow("【效果图】整体方向Sobel", dstImage);

	waitKey(0);
	return 0;
}

运行效果:
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述

7.1.4 Laplacian算子

1.二阶导数可以用来检测边缘, Laplacian算子是n维欧几里得空间中的一个二阶微分算子,定义为梯度grad的散度div,一幅图像减去它的Laplacian算子可以增强对比度
2.如果f是二阶可微的实函数,则f的拉普拉斯算子定义为笛卡尔坐标系xi中的所有非混合二阶偏导数求和:
                在这里插入图片描述
3.函数:Laplacian()函数
4.函数原型:

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

5.参数说明:
(1)输入图像
(2)输出的边缘图
(3)输出图像深度
(4)计算二阶导数的滤波器的孔径尺寸,大小必须为正奇数,默认1
(5)计算拉普拉斯值得时候可选的比例因子,默认1
(6)在结果存入目标图之前可选的delta值,默认0
(7)边界模式,默认BORDER_DEFAULT
6.函数主要利用sobel算子的运算,通过加上sobel算子运算的图像x方向和y方向上的导数,来得到载入图像的拉普拉斯变换,sobel算子如下:
                在这里插入图片描述
ksize=1时,Laplacian()函数采用3*3孔径:
                  在这里插入图片描述
7.调用示例:

#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main()
{
	//【0】变量定义
	Mat srcImage, grayImage, dstImage, abs_dstImage;
	//【1】载入原图
	srcImage = imread("love.jpg");
	//【2】显示原图
	imshow("【原始图】图像Laplace变换", srcImage);
	//【3】使用高斯滤波消除噪声
	GaussianBlur(srcImage, srcImage, Size(3, 3), 0, 0, BORDER_DEFAULT);
	//【4】转换为灰度图
	cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);
	//【5】使用Laplace函数
	Laplacian(grayImage, dstImage, CV_16S, 3, 1, 0, BORDER_DEFAULT);
	//【6】计算绝对值,将结果转换成8位
	convertScaleAbs(dstImage, abs_dstImage);
	//【7】显示效果图
	imshow("【效果图】图像Laplacian变换", abs_dstImage);
	waitKey(0);
	return 0;
}

运行效果:
在这里插入图片描述在这里插入图片描述

7.1.5 scharr滤波器

1.计算图像差分:Scharr()函数,与Sobel基本一样,没有ksize核大小:

Scharr(src,dst,ddepth,dx,dy,scale,delta,broderType)

=
Sobel(src,dst,ddepth,dx,dy,CV_SCHARR,scale,delta,borderType)

2.调用示例:

#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main()
{
	//创建grad_x和grad_y矩阵
	Mat grad_x, grad_y;
	Mat abs_grad_x, abs_grad_y, dstImage;
	//载入原图
	Mat srcImage = imread("love.jpg");
	//显示原图
	imshow("【原始图】Scharr滤波器", srcImage);
	//求x方向梯度
	Scharr(srcImage, grad_x, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT);
	convertScaleAbs(grad_x, abs_grad_x);
	imshow("【效果图】X方向Scharr", abs_grad_x);
	//求y方向梯度
	Scharr(srcImage, grad_y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT);
	convertScaleAbs(grad_y, abs_grad_y);
	imshow("【效果图】Y方向Scharr", abs_grad_y);
	//合并梯度(近似)
	addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dstImage);
	//显示效果图
	imshow("【效果图】合并梯度后Scharr", dstImage);

	waitKey(0);
	return 0;
}

运行结果:
在这里插入图片描述 在这里插入图片描述
在这里插入图片描述 在这里插入图片描述

7.1.6 综合示例

/*
效果:
	实现canny边缘检测,滑动条控制阈值
	实现Sobel边缘检测,滑动条控制内核大小
	实现Scharr滤波器
*/
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
//全局变量
Mat g_srcImage, g_grayImage, g_dstImage;
//Canny边缘检测相关变量
Mat g_cannyDetectedEdges;
int g_cannyLowThreshold = 1;//Trackbar位置参数
//Sobel边缘检测相关变量
Mat g_sobelGradient_X, g_sobelGradient_Y;
Mat g_sobelAbsGradient_X, g_sobelAbsGradient_Y;
int g_sobelKernelSize = 1;//Trackbar位置参数
//Scharr滤波器相关变量
Mat g_scharrGradient_X, g_scharrGradient_Y;
Mat g_scharrAbsGradient_X, g_scharrAbsGradient_Y;
//全局函数
static void on_Canny(int, void*);//canny边缘检测窗口滚动条回调函数
static void on_Sobel(int, void*);//Sobel边缘检测窗口滚动条回调函数
void Scharr();//封装了Scharr边缘检测相关代码的函数
int main()
{
	//改变console字体颜色
	system("color 2F");
	//载入原图
	g_srcImage = imread("girl.jpg");
	if (!g_srcImage.data)
	{
		printf("载入原图失败~!\n");
		return false;
	}
	//显示原图
	namedWindow("【原始图】");
	imshow("【原始图】", g_srcImage);
	//创建与src同类型和大小的矩阵
	g_dstImage.create(g_srcImage.size(), g_srcImage.type());
	//将原图转换为灰度图
	cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
	//创建显示窗口
	namedWindow("【效果图】Canny边缘检测", WINDOW_AUTOSIZE);
	namedWindow("【效果图】Sobel边缘检测", WINDOW_AUTOSIZE);
	namedWindow("【效果图】Scharr滤波器", WINDOW_AUTOSIZE);

	//创建滑动条
	createTrackbar("参数值", "【效果图】Canny边缘检测", &g_cannyLowThreshold, 120, on_Canny);
	createTrackbar("参数值", "【效果图】Sobel边缘检测", &g_sobelKernelSize, 3, on_Sobel);
	//调用回调函数
	on_Canny(g_cannyLowThreshold, 0);
	on_Sobel(g_sobelKernelSize, 0);
	//调用封装了Scharr边缘检测代码的函数
	Scharr();
	
	while ((char)waitKey(1) != 27) {}
	return 0;
}
//canny边缘检测窗口滚动条回调函数
static void on_Canny(int, void*)
{
	//3*3内核降噪
	blur(g_grayImage, g_cannyDetectedEdges, Size(3, 3));
	//运行Canny算子
	Canny(g_cannyDetectedEdges, g_cannyDetectedEdges, g_cannyLowThreshold, g_cannyLowThreshold * 3, 3);
	//将g_dstImage内所有元素设为0
	g_dstImage = Scalar::all(0);
	//使用Canny算子输出的边缘图g_cannyDetectedEdges作为掩模,将原图拷贝到目标图
	g_srcImage.copyTo(g_dstImage, g_cannyDetectedEdges);
	//显示效果图
	imshow("【效果图】Canny边缘检测", g_dstImage);
}
static void on_Sobel(int, void*)
{
	//求X方向梯度
	Sobel(g_srcImage, g_sobelGradient_X, CV_16S, 1, 0, 2*g_sobelKernelSize+1, 1, 1, BORDER_DEFAULT);
	convertScaleAbs(g_sobelGradient_X, g_sobelAbsGradient_X);
	//求Y方向梯度
	Sobel(g_srcImage, g_sobelGradient_Y, CV_16S, 0, 1, 2*g_sobelKernelSize+1, 1, 1, BORDER_DEFAULT);
	convertScaleAbs(g_sobelGradient_Y, g_sobelAbsGradient_Y);
	//合并梯度
	addWeighted(g_sobelAbsGradient_X, 0.5, g_sobelAbsGradient_Y, 0.5, 0, g_dstImage);
	//显示效果图
	imshow("【效果图】Sobel边缘检测", g_dstImage);
}
void Scharr()
{
	//求X方向梯度
	Scharr(g_srcImage, g_scharrGradient_X, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT);
	convertScaleAbs(g_scharrGradient_X, g_scharrAbsGradient_X);
	//求Y方向梯度
	Scharr(g_srcImage, g_scharrGradient_Y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT);
	convertScaleAbs(g_scharrGradient_Y, g_scharrAbsGradient_Y);
	//合并梯度
	addWeighted(g_scharrAbsGradient_X, 0.5, g_scharrAbsGradient_Y, 0.5, 0, g_dstImage);
	//显示效果图
	imshow("【效果图】Scharr滤波器", g_dstImage);
}

运行效果:
在这里插入图片描述
在这里插入图片描述 在这里插入图片描述
在这里插入图片描述 在这里插入图片描述
在这里插入图片描述 在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ccchenxi/article/details/85023298