OpenCV(11)边缘检测、轮廓绘制 C++
2021/6/22 17:27:15
本文主要是介绍OpenCV(11)边缘检测、轮廓绘制 C++,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1.边缘检测
原文链接:http://blog.sina.com.cn/s/blog_154bd48ae0102weuk.html
边缘检测的一般步骤:
1.滤波
边缘检测的算法主要是基于图像的一阶和二阶导数。但是导数通常对噪声很敏感,所以首先要用滤波器降低噪声。常见的滤波方法主要是高斯滤波。
2.增强
增强边缘的基础是确定图像各点领域强度的变化值。增强算法可以将图像灰度点邻域强度值有显著变化的点凸现出来,在具体计算的过程中,可以通过计算梯度幅值来确定。
3.检测
经过增强的图像,往往领域中有很多点的梯度值比较大,而在特定的场合中,这些点并不是边缘点,所以应采用某种方式进行取舍,我们通常采取阈值化的方法来检测。
1.1.(cv :: canny)边缘检测
Canny边缘检测的步骤:
1.消除噪声(高斯滤波)
2.计算梯度幅值与方向(sobel滤波器)
3.非极大值抑制(排除一些非边缘像素)
4.滞后阈值
Sobel算子是计算图像梯度的,所以在canny和laplacian中都调用过sobel算子。
Sobel算子是一个主要用于边缘检测的离散微分算子,它结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。在图像的任意一点使用该算子,都将会产生对应的梯度矢量或者法向量。
拉普拉斯算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度的散度。
除了canny算子,其他都是有x和y两个方向的
Void canny(InputArray image,OutputArray edges,double threshold1,double threshold2,int apertureSize=3,bool L2gradient=false)
- image:输入图像
- edges:输出图像
- threshold1:第一个滞后性阈值
- threshold2:第二个滞后性阈值
- apertureSize:表示应用sobel算子孔径大小,默认值为3
- L2gradient:一个计算图像梯度幅值的标识,默认为false
1.2.(cv :: Sobel)边缘检测
需要注意的是,这个函数阈值1和阈值2中较小的值用于边缘连接,而较大的值用来控制强边缘的初始段。
Void Sobel(InputArray image,OutputArray edges,int ddepth,int dx,int dy,int ksize=3,double scale=1,double delta=0,int borderType=BORDER_DEFAULT)
- image:输入图像
- edges:输出图像
- ddepth:输出图像的深度
- dx:x方向的差分阶数
- dy:y方向的差分阶数
- ksize:Sobel的核大小,必须是1/3/5/7
- scale:计算导数值时可选的缩放因子
- delta:在结果存入目标图之前可选的delta值,默认0
- borderType:边界模式
1.3.(cv :: Laplacian)边缘检测
Laplacian(InputArray image,OutputArray edges,int ddepth,int ksize=1,double scale=1,double delta=0,int borderType=BORDER_DEFAULT)
- image:输入图像
- edges:输出图像
- ddepth:输出图像的深度
- ksize:用于计算二阶导数的滤波器的孔径尺寸,大小必须是正奇数,默认1
- scale:用来计算拉普拉斯值的时候可选的比例因子
- delta:在结果存入目标图之前可选的delta值,默认0
- borderType:边界模式
- Scharr滤波器
1.4.(cv :: Scharr)边缘检测
Void Scharr(InputArray image,OutputArray edges,int ddepth,int dx,int dy,double scale=1,double delta=0,int borderType=BORDER_DEFAULT)
- image:输入图像
- edges:输出图像
- ddepth:输出图像的深度
- dx:x方向的差分阶数
- dy:y方向的差分阶数
- scale:计算导数值时可选的缩放因子
- delta:在结果存入目标图之前可选的delta值,默认0
- borderType:边界模式
Scharr与soble只差一个参数,内核数,Scharr只作用于大小为3的内核,该函数和sobel函数一样快,但结果却更加精确。
//#include"stdafx.h"; #include"opencv2/opencv.hpp" //空间变量 using namespace cv; using namespace std; void main() { //显示原图像 Mat image = imread("./image/test3.jpg"); namedWindow("原图"); imshow("原图", image); //canny边缘检测的简单用法 Mat result; Canny(image, result, 150, 70); namedWindow("canny边缘检测后的图像"); imshow("canny边缘检测后的图像", result); //高阶的canny用法,转成灰度图,降噪,用canny,最后将得到的边缘作为掩码,拷贝原图到效果图上,得到彩色边缘图 Mat grayimage, edge; cvtColor(image, grayimage, COLOR_BGR2GRAY); boxFilter(grayimage, edge, -1, Size(3, 3)); Canny(edge, edge, 150, 70); Mat dst; dst = Scalar::all(123); image.copyTo(dst, edge); namedWindow("canny高阶边缘检测后的图像"); imshow("canny高阶边缘检测后的图像", dst); //sobel算子边缘检测 Mat x_result, y_result; Sobel(image, x_result, 0, 1, 0); Sobel(image, y_result, 0, 0, 1); addWeighted(x_result, 0.5, y_result, 0.5, 0, result); imshow("sobel边缘检测后x轴的图像", x_result); imshow("sobel边缘检测后y轴的图像", y_result); imshow("sobel边缘检测后的图像", result); //laplacian边缘检测 Laplacian(image, result, 0); imshow("laplacian边缘检测后的图像", result); //scharr滤波器 boxFilter(image, image, -1, Size(3, 3)); Scharr(image, x_result, 0, 1, 0); Scharr(image, x_result, 0, 0, 1); addWeighted(x_result, 0.5, y_result, 0.5, 0, result); imshow("scharr边缘检测后x轴的图像", x_result); imshow("scharr边缘检测后y轴的图像", y_result); imshow("scharr边缘检测后的图像", result); waitKey(); }
执行结果:
2.轮廓绘制
2.1.(cv :: findContours)查找轮廓
CV_EXPORTS_W void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset = Point());
- image:单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;
- contours:contours定义为 “vector<vector>contours”,是一个双重向量(向量内每个元素保存了一组由连续的Point构成的点的集合的向量),每一组点集就是一个轮廓,有多少轮廓,contours就有多少元素;
- hierarchy:hierarchy定义为“vector hierarchy”,
Vec4i的定义:typedef Vec<int, 4>Vec4i;(向量内每个元素都包含了4个int型变量),所以从定义上看,hierarchy是一个向量,向量内
每个元素都是一个包含4个int型的数组。向量hierarchy内的元素和轮廓向量contours内的元素是一一对应的,向量的容量相同hierarchy
内每个元素的4个int型变量是hierarchy[i][0] ~ hierarchy[i][3],分别表示当前轮廓 i 的后一个轮廓、前一个轮廓、父轮廓和内嵌轮廓的编
号索引。如果当前轮廓没有对应的后一个轮廓、前一个轮廓、父轮廓和内嵌轮廓,则相应的hierarchy[i][*]被置为-1。 - mode:定义轮廓的检索模式,取值如下:
- RETR_EXTERNAL:只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略;
- RETR_LIST:检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到;
- RETR_CCOMP: 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层;
- RETR_TREE: 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
- method:定义轮廓的近似方法,取值如下:
- CHAIN_APPROX_NONE:保存物体边界上所有连续的轮廓点到contours向量内;
- CHAIN_APPROX_SIMPLE:仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信
息点不予保留; - CHAIN_APPROX_TC89_L1:使用teh-Chinl chain 近似算法;
- CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain 近似算法。
- offset:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,并且Point还可
以是负值!
2.2.(cv :: drawContours )轮廓绘制函数
void drawContours(InputOutputArray image, InputOutputArrays contours, int contourIdx, const Scalar& color, int thickness = 1, int lineType = 8, InputArray hierarchy = noArray(), int maxLevel = INT_MAX, Point offset = Point());
- image:目标图像,填 Mat 类对象即可。
- contours:输入的轮廓,每个轮廓都是一组点集,可用 Point 类型的 vector 表示。
- contourIdx:轮廓的索引编号。若为负值,则绘制所有轮廓。
- color:轮廓颜色。
- thickness:轮廓线条的粗细程度,有默认值 1。若其为负值,便会填充轮廓内部空间。
- lineType:线条的类型,有默认值 8。可去类型如下:
类型 含义
8 8 连通线型
4 4 连通线型
LINE_AA 抗锯齿线型 - hierarchy:可选的层次结构信息,有默认值 noArray()。
- maxLevel:用于绘制轮廓的最大等级,有默认值 INT_MAX。
- offset:轮廓信息相对于目标图像对应点的偏移量,相当于在每一个轮廓点上加上该偏移量,有默认值 Point() 。在 ROI
区域(感兴趣区域)绘制轮廓时,这个参数便可派上用场。
#include <iostream> #include <vector> #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" using namespace std; using namespace cv; int main() { Mat img = imread("./image/test.jpg"); imshow("原图", img); cvtColor(img, img, COLOR_BGR2GRAY);//转化为灰度图 //大津法进行二值化 threshold(img, img, 100, 255, THRESH_OTSU); imshow("二值化", img); //提取二值化图像中的轮廓数据 vector<vector<Point> > contour_vec; vector<Vec4i> hierarchy; //1. RETR_EXTERNAL:只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略; //2. RETR_LIST:检测所有的轮廓 findContours(img, contour_vec, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE); cout << "contours number: " << contour_vec.size() << endl; // 以前常用的for循环绘制轮廓 /*Mat blkImg(binImg.size(), CV_8UC1, Scalar(0)); for(int i = 0; i < contour_vec.size(); i++) { drawContours(blkImg, contour_vec, i, Scalar(255), -1); } */ //绘制单通道轮廓图像,背景为白色,轮廓线条用黑色 Mat blkImg(img.size(), CV_8UC1, Scalar(255)); drawContours(blkImg, contour_vec, -1, Scalar(0), 2); imshow("单通道轮廓", blkImg); //绘制彩色轮廓图像,背景颜色为蓝绿色,轮廓线条为红色 Mat colorImg(img.size(), CV_8UC3, Scalar(255, 255, 0)); drawContours(colorImg, contour_vec, -1, Scalar(0, 0, 255), 3); imshow("彩色轮廓", colorImg); waitKey(0); return 0; }
执行结果:
2.3.图像反转、检测所有轮廓示例:
#include <iostream> #include <vector> #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" using namespace std; using namespace cv; int main() { Mat img = imread("./image/test17.jpg"); imshow("原图", img); //图像取反 Mat lookUpTable(1, 256, CV_8U); uchar* p = lookUpTable.data; for (int i = 0; i < 256; i++) p[i] = 255 - i; LUT(img, lookUpTable, img);//通过LUT函数实现图像取反 cvtColor(img, img, COLOR_BGR2GRAY);//转化为灰度图 //大津法进行二值化 threshold(img, img, 100, 255, THRESH_OTSU); imshow("二值化", img); //提取二值化图像中的轮廓数据 vector<vector<Point> > contour_vec; vector<Vec4i> hierarchy; findContours(img, contour_vec, hierarchy, RETR_LIST, CHAIN_APPROX_NONE); cout << "contours number: " << contour_vec.size() << endl; // 以前常用的for循环绘制轮廓 /*Mat blkImg(binImg.size(), CV_8UC1, Scalar(0)); for(int i = 0; i < contour_vec.size(); i++) { drawContours(blkImg, contour_vec, i, Scalar(255), -1); } */ //绘制单通道轮廓图像,背景为白色,轮廓线条用黑色 Mat blkImg(img.size(), CV_8UC1, Scalar(255)); drawContours(blkImg, contour_vec, -1, Scalar(0), 2); imshow("单通道轮廓", blkImg); //绘制彩色轮廓图像,背景颜色为蓝绿色,轮廓线条为红色 Mat colorImg(img.size(), CV_8UC3, Scalar(255, 255, 0)); drawContours(colorImg, contour_vec, -1, Scalar(0, 0, 255), 3); imshow("彩色轮廓", colorImg); waitKey(0); return 0; }
执行结果:
这篇关于OpenCV(11)边缘检测、轮廓绘制 C++的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-06-19EntBot.ai: AI Website Chatbot for Product Guides and Development Doc
- 2024-06-17zero-shot-learning-definition-examples-comparison
- 2024-06-06Package Easy(基于 NSIS 的打包exe安装包工具)使用方法-icode9专业技术文章分享
- 2024-06-06基于 casdoor 的 ELK 开源登录认证解决方案: elk-auth-casdoor-icode9专业技术文章分享
- 2024-05-29Elasticsearch慢查询日志配置
- 2024-05-29揭秘华为如此多成功项目的产品关键——Charter模板
- 2024-05-29海外IDC业务拓展的7大挑战
- 2024-05-29InLine Chat功能优化对标Github Copilot,CodeGeeX带来更高效、更直观的编程体验!
- 2024-05-29CodeGeeX 智能编程助手 6 项功能升级,在Visual Studio插件市场霸榜2周!
- 2024-05-29AutoMQ 生态集成 Apache Doris