Imgproc_4_图像轮廓

来源:互联网 发布:mac可以玩守望先锋吗 编辑:程序博客网 时间:2024/06/08 05:45

一图像轮廓

1,查找并绘制轮廓
1>查找轮廓

void findContours(InputOutputArray image,                   OutputArrayOfArrays contours,                   OutputArray hierarchy, int mode,                   int method, Point offset=Point())/*image,单通道8-bit,二值图像 *contours,检测到的轮廓,用vector<Point>代表每一个轮廓 *hierarchy,可选的输出向量,包含图像拓扑信息,对于每一个 *contours[i]对应4个,hierarchy[i][0]~hierarchy[i][3]分别 *表示后一个轮廓,前一个轮廓,父轮廓,内嵌轮廓的索引编号,如果没有 *对应项则设为负数 *mode(轮廓的检索模式): *CV_RETR_EXTERNAL,只检索外轮廓 *这种情况下hierarchy[i][2]=hierarchy[i][3]=-1 *CV_RETR_LIST,检索所有轮廓,但是不建立hierarchy *CV_RETR_CCOMP,检索所有轮廓,并将它们组织成双层hierarchy,顶 *层是外部边界,第二层为洞的边界。 *CV_RETR_TREE,检索所有轮廓,重构所有轮廓的hierarchy *method(轮廓的近似方法): *CV_CHAIN_APPROX_NONE:获取轮廓的点,相邻两个点位置差为1,   *max(abs(x1-x2),abs(y2-y1))==1 *CV_CHAIN_APPROX_SIMPLE:只保留水平,垂直,对角线方向的元素 *CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS: *Teh-Chin chain 近似算法的一种 *offset,每个轮廓点的可选偏移量,在图像ROI中提取,应用到整张图像 *时很有用 */                

2>绘制或填充轮廓

void drawContours(InputOutputArray image,                   InputArrayOfArrays contours,                   int contourIdx, const Scalar& color,                   int thickness=1, int lineType=8,                   InputArray hierarchy=noArray(),                   int maxLevel=INT_MAX,                   Point offset=Point() )/*image,目标图像 *contours,存储的所有轮廓,vector<vector<Point>> *contourIdx,指示要绘制第几个轮廓,如果为负,则全部绘制 *thickness,线的厚度,如果为负,则将轮廓填充 *maxLevel,绘制轮廓的最大等级,if0,只绘制contourIdx指定的 *轮廓,if1,绘制指定轮廓和所有嵌套轮廓,if2,绘制所有,需要 *hierarchy */                  

3>检测点是否在轮廓内

double pointPolygonTest(InputArray contour, Point2f pt,                         bool measureDist)//contour,输入轮廓,pt,为测试点//measureDist,if true,返回pt距离轮廓边最近距离,否则只检测是//否在轮廓内//在轮廓内,轮廓边上,轮廓外,分别返回正数,0,复数,当//measureDist=false时,返回1,0,,1                        

使用步骤:

//Canny边缘检测转化成二值图像cv::cvtColor(Image,Image,CV_BGR2GRAY);cv::GaussianBlur(Image,Image,cv::Size(3,3),0);cv::Canny(Image,Image,30,90);//查找轮廓std::vector<std::vector<cv::Point>> contours;std::vector<cv::Vec4i> hierarchy;cv::findContours(Image,contours,hierarchy,        CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE);//利用随机数,将轮廓一个一个画出来,便于分清。cv::RNG rng;result=cv::Mat::zeros(image.size(),CV_8UC3);for(int i=0;i<contours.size();i++){cv::Scalar color(rng.uniform(0,255),rng.uniform(0,255),                 rng.uniform(0,255));cv::drawContours(result,contours,i,color,1,8,hierarchy);}

效果图:
这里写图片描述

2,寻找图像的凸包
给定二位平面上的点集,凸包将最外层的点连接起来构成的凸多边形,理解物体形状或轮廓的一种比较有用的方法计算一个物体的凸包然后计算其凸缺陷。

void convexHull(InputArray points, OutputArray hull,                 bool clockwise=false,                 bool returnPoints=true )//points,输入的二位点集,可以为Mat或者vector//hull,找到的凸包//clockwise,操作方向,为true时,输出凸包为顺时针,否则为逆时针//returnPoints,当为true时返回凸包的各个点,否则返回凸包点的指//数,当hull为vector时此参数忽略                

使用步骤:

//第一步:findContours//第二步:针对每一个轮廓查找对应的凸包std::vector<std::vector<cv::Point>> hull(contours.size());for(int i=0;i<contours.size();i++){ cv::convexHull(contours[i],hull[i],false);}//第三步:绘制凸包cv::RNG rng;result=cv::Mat::zeros(image.size(),CV_8UC3);for(int i=0;i<contours.size();i++){cv::Scalar color(rng.uniform(0,255),rng.uniform(0,255),                 rng.uniform(0,255));cv::drawContours(result,hull,i,color,1,8,hierarchy);}

效果图:
这里写图片描述

补充:
找到凸包的对应轮廓的缺陷

void convexityDefects(InputArray contour,                       InputArray convexhull,                       OutputArray convexityDefects)//contour,convexhull,分别为对应的轮廓和凸包,用vector<Point>//convexityDefects,得到的缺陷vector<CvConvexityDefect>     
struct CvConvexityDefect{   CvPoint* start; //是contour上的缺陷起始坐标   CvPoint* end;   //是contour上的缺陷终止坐标   CvPoint* depth_point;  //上convexhull缺陷的最远点   float depth;  //最远距离};

这里写图片描述

3,使用多边形包围轮廓
1>boundingRect

Rect boundingRect(InputArray points)//points为二维点集,存在vector或Mat中

2>minAreaRect
返回包围二维点集的最小旋转矩阵

RotatedRect minAreaRect(InputArray points)//points为二维点集,存在vector或Mat中

3>minEnclosingCircle
返回包围二维点集的最小圆

void minEnclosingCircle(InputArray points, Point2f& center,                         float& radius)//center为圆心,radius为半径                        

4>fitEllipse
用椭圆拟合二位点集

RotatedRect fitEllipse(InputArray points)//此函数返回旋转矩阵,此旋转矩阵是椭圆的外切矩阵,可用来初始化椭圆

5>approxPolyDP
对于给定的点集,用指定精度逼近多边形曲线

void approxPolyDP(InputArray curve, OutputArray approxCurve,                   double epsilon, bool closed)/*curve,输入二位点集,vector<>或Mat *approxCurve,多边形逼近的结果,类型应该和curve一致 *epsilon,精度,原始曲线和近似曲线的最大距离 *closed,if true,则曲线为封闭,否则不封闭                  

应用:

//第一步:findContours//第二步:对每一个轮廓,得到相应的图形std::vector<cv::Rect> boundRect(contours.size());std::vector<cv::Point2f> center(contours.size());std::vector<float> radius(contours.size());std::vector<std::vector<cv::Point>>contours_poly(contours.size());for(int i=0;i<contours.size();i++){boundRect[i]=cv::boundingRect(contours[i]);cv::minEnclosingCircle(contours[i],center[i],radius[i]);cv::approxPolyDP(contours[i],contours_poly[i],3,true); }//第三部:绘制 cv::RNG rng;result=cv::Mat::zeros(image.size(),CV_8UC3);for(int i=0;i<contours.size();i++){ cv::Scalarcolor(rng.uniform(0,255),rng.uniform(0,255),                  rng.uniform(0,255)); cv::rectangle(result,boundRect[i],color); cv::circle(result,center[i],radius[i],color); cv::drawContours(result,contours_poly,i,color);}

效果图:
这里写图片描述

4,图像的距
一阶距与形状有关,二阶距显示曲线围绕直线平均值的拓展程度,三阶距则是关于平均值的对称性的测量。
1>距的计算

Moments moments(InputArray array, bool binaryImage=false )//array,光栅图像(单通道,8-bit或者浮点的二维数组)或//Point/Point2f的数组(1*N或N*1)//binaryImage,如果true,则图像中所有非0像素为1,这个参数仅对图//像使用

Moments类

class Moments{public:    Moments();    Moments(double m00, double m10, double m01,             double m20, double m11, double m02,             double m30, double m21, double m12,            double m03 );    Moments( const CvMoments& moments );    operator CvMoments() const;    //空间距    double  m00, m10, m01, m20, m11, m02, m30, m21, m12, m03;    //中央距    double  mu20, mu11, mu02, mu30, mu21, mu12, mu03;    //中央归一化距    double  nu20, nu11, nu02, nu30, nu21, nu12, nu03;}

空间距的计算:
这里写图片描述
中央距的计算:
这里写图片描述
则中心为:
这里写图片描述
m00是对应轮廓的面积,和contourArea效果相同

中央归一化距:
这里写图片描述

2>计算轮廓面积

double contourArea(InputArray contour, bool oriented=false )//contour,输入的二维点集,可以为vector或Mat//oriented,面向区域标识符,if true,返回带符号的面积值,正负取//决于轮廓是顺时针还是逆时针,false,表示绝对值返回//返回的值,和对应轮廓的Moments::m00相同

3>计算轮廓长度
计算曲线的周长

double arcLength(InputArray curve, bool closed)//curve,二维点集,vector或者Mat//closed,if true,表示曲线封闭,false表示不封闭

应用:

//第一步:findContours//针对每一个轮廓找到对应的距 std::vector<cv::Moments> mu(contours.size()); for(int i=0;i<contours.size();i++) { mu[i]=cv::moments(contours[i],false);}//利用得到的距,得到每个轮廓的中心点std::vector<cv::Point2f> mc(contours.size());for(int i=0;i<contours.size();i++){mc[i]=cv::Point2f(static_cast<float(mu[i].m10/mu[i].m00),                  static_cast<float>(mu[i].m01/mu[i].m00));}//绘制轮廓和中心点result=cv::Mat::zeros(image.size(),CV_8UC3);for(int i=0;i<contours.size();i++){cv::Scalar color(0,0,255);cv::drawContours(result,contours,i,color,1,8,hierarchy);cv::Scalar color1(0,255,0);cv::circle(result,mc[i],4,color1);}//得到每个轮廓的面积和周长for(int i=0;i<contours.size();i++){//两种得到面积的方法,结果相等 std::cout<<"Area:"<<mu[i].m00<<" "          <<cv::contourArea(contours[i])<<std::endl; std::cout<<"Length:"<<cv::arcLength(contours[i],true)           <<std::endl;}

效果:
这里写图片描述
这里写图片描述

5,分水岭算法
将图像进行区域分割,分两个步骤,一是排序过程,首先对 每个像素的灰度进行从低到高排序,二是淹没过程,对每一个局部极小值在h 阶高度的影响域采用采用先进先出结构进行判断及标注

void watershed(InputArray image, InputOutputArray markers)//image,三通道,8-bit//markers,单通道,32-bit,与image同size,输出结果

应用:

//第一步:findContoursresult=cv::Mat::zeros(image.size(),CV_32S);result=cv::Scalar::all(0);int color=0;//第二步:在result绘制轮廓,用color记录轮廓的数目for(int i=0;i>=0;i=hierarchy[i][0],color++){cv::drawContours(result,contours,i,                 cv::Scalar::all(1+color),-1,8,hierarchy);}//第三步:watershed,结果存储到result中cv::watershed(Image,result);cv::Mat water(result.size(),CV_8UC3);//第四步:用随机数颜色,便于区分区域std::vector<cv::Vec3b> colorTab;for(int i=0;i<color;i++){int b=cv::theRNG().uniform(0,255);int g=cv::theRNG().uniform(0,255);int r=cv::theRNG().uniform(0,255);colorTab.push_back(cv::Vec3b(b,g,r));}//第五步:针对result每个点的像素进行分类,标记for(int i=0;i<result.rows;i++)for(int j=0;j<result.cols;j++){int index=result.at<int>(i,j);if(index==-1)water.at<cv::Vec3b>(i,j)=cv::Vec3b(255,255,255);else if(index<=0||index>color)water.at<cv::Vec3b>(i,j)=cv::Vec3b(0,0,0);elsewater.at<cv::Vec3b>(i,j)=colorTab[index-1];}

效果图:
这里写图片描述

6,MSER提取特征区域
MSER使用的原理与分水岭算法相同:高度为0~255,逐渐淹没图像,随着水位的增高,被严格界定黑色区域会形成盆地,并且会在一段时间内有相对稳定的形状,这种盆地就是MSER

1>MSER类

[1]构造函数参数

delta :测量相对稳定性min_area :输出的最小面积max_area  :输出的最大面积max_variation :去除与它的子区域有相同大小的区域min_diversity :对于彩色图像,切断mser,如果多样性小于min_diversitymax_evolution :对于彩色图像的进化步奏area_threshold :对于彩色图像area_threshold导致重新初始化min_margin :对于彩色图像,忽略太小的边距edge_blur_size :对于彩色图像,滤波的孔径尺寸

[2]检测MSER区域

void MSER::operator()(const Mat& image,                       vector<vector<Point>>& msers,                       const Mat& mask=Mat() ) const//image,输入图像,8UC1/8UC3/8UC4//msers,检测到的区域点集                      

应用:

//进行MSER检测cv::MSER mser(5,200,3500);std::vector<std::vector<cv::Point>> points;mser(image,points);//输出图像初始化,并且赋值为255cv::Mat output(image.size(),CV_8UC3);output=cv::Scalar(255,255,255);//不同区域进行随机数标记cv::RNG rng;for(auto it=points.begin();it!=points.end();it++){cv::Vec3b c(rng.uniform(0,255),            rng.uniform(0,255),            rng.uniform(0,255));for(auto itpts=it->begin();itpts!=it->end();itpts++){//未被标记过的区域可以重新标记if(output.at<cv::Vec3b>(*itpts)[0]==255){  output.at<cv::Vec3b>(*itpts)=c;}}}

效果图:
这里写图片描述

0 0
原创粉丝点击