标签:11 int 检测 image C++ OpenCV result 图像 轮廓
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;
}
执行结果:
标签:11,int,检测,image,C++,OpenCV,result,图像,轮廓 来源: https://blog.csdn.net/qq_35831134/article/details/118107095
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。