OPENCV图像轮廓检测

Posted 邓小俊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OPENCV图像轮廓检测相关的知识,希望对你有一定的参考价值。

前面在图像转换的时候学到canny算子,可以检测出图像的轮廓信息,但是,该算子检测到的轮廓信息还需要我们手动的用眼睛去识别,而实际工程应用中,我们需要得到轮廓的具体数学信息,这就涉及到今天的主题,图像轮廓检测.

         一.图像轮廓检测

         在opencv中,轮廓对应着一系列的点的集合,opencv提供了一个函数,用来获得这些点的集合

         API:void finContours(输入图像,输出轮廓点集,输出向量,int 轮廓检索模式,int 轮廓近似方法,Point 轮廓点的可选偏移量)

         注:1.输入图像,是单通道八位阈值化图像,也就是对应canny检测之后的图像,如果不是阈值化图像,算法会将图像中不为0的点当成1,0点当成0处理,我们可以通过canny threshold adaptiveThreshold来处理,

            2.该函数在检测轮廓的时候会修改源图像的数据,所以最好不要拿源图像直接检测轮廓,最好是用拷贝

            3.输出点集的形式为vector<vector<Point>>contours,每个向量包含有多个向量,包含的向量一个就是一条轮廓,而包含的向量是由一个个点构成的.

                  4.输出向量包含了图像轮廓的拓扑信息,比如这个轮廓的前一个轮廓编号,后一个轮廓编号,父轮廓编号以及子轮廓编号,形式为vector<vec4i>h,h[0]为后一个轮廓编号 h[1]后一个轮廓编号 h[2]父轮廓编号 h[3]内嵌轮廓编号

                  5.轮廓的检索模式包含有如下几种选择RETR_EXTERNAL只检测最外围的轮廓  RETR_LIST提取所有的轮廓,不建立上下等级关系,只有兄弟等级关系  RETR_CCOMP提取所有轮廓,建立为双层结构 RETR_TREE提取所有轮廓,建立网状结构

                  6.轮廓的近似方法有以下取值 CHAIN_APPROX_NONE获取轮廓的每一个像素,像素的最大间距不超过1 CHAIN_APPROX_SIMPLE压缩水平垂直对角线的元素,只保留该方向的终点坐标(也就是说一条中垂线a-b,中间的点被忽略了) CHAIN_APPROX_TC89_LI使用TEH_CHAIN逼近算法中的LI算法  CHAIN_APPROX_TC89_KCOS使用TEH_CHAIN逼近算法中的KCOS算法

                  7.可选偏移量,对ROI区域中获得的轮廓要在整个图像中分析的时候,该参数可以派上用场

         二.图像轮廓绘制

         该函数可以很方便的绘制出我们找到的轮廓点集和拓扑结构构成的轮廓图.

         API:void drawContours(源图像,轮廓点集,int 绘制指示,scalar 轮廓颜色,int 轮廓粗细,int 轮廓线形,inputarray 轮廓的拓扑结构信息,int 轮廓的最大绘制等级,int 可选的轮廓偏移参数)

         注:1.如果绘制指示为负值,则绘制所有轮廓,轮廓粗细默认值为1,为负值的化,绘制在轮廓的内部进行,使用filled,填充轮廓,线条类型默认为8,LINE_AA将绘制抗锯齿的线型,轮廓绘制等级,默认值是INT_MAX,指示最多可以绘制几层轮廓,=.

         轮廓检测的基本步骤为1.图像转换为灰度图像,2.图像的滤波,降噪,3.图像的canny二值化或者其他二值化方式,4.寻找轮廓findContours

代码例子如下

//边À?缘¦Ì轮?廓¤a查¨¦找¨°
//先¨¨进?行D边À?缘¦Ì滤?波¡§ 然¨?后¨®用®?canny算?法¤¡§得Ì?到Ì?二t值¦Ì化¡¥图ª?像?
//滤?波¡§算?法¤¡§选?择?均¨´值¦Ì滤?波¡§ 可¨¦调Ì¡Â的Ì?是º?孔¡Á径?尺?寸ä?
//canny算?法¤¡§可¨¦调Ì¡Â的Ì?上¦?下?限T和¨ªsobel算?子Á¨®孔¡Á径?3 5 7
Mat srcImage,blurImage,grayBlurImage,cannyImage,contourImage;
vector<vector<Point>>g_vContours;
vector<Vec4i>g_vHierarchy;
RNG g_rng(12345);

const int g_BlurMax = 100;
int g_BlurValue;
void onBlurTrackBar(int pos,void* userData);

const int g_sobelSizeMax = 2;//sobel孔¡Á径?
int g_sobelValue;

const int g_lowThresholdMax = 80;//边À?缘¦Ì检¨¬测a低̨ª阈D值¦Ì
int g_lowThresholdValue;
int g_upThresholdValue;

void onTrackBarSobelSize(int pos,void* userData);
void onTrackBarLowThresholdSize(int pos,void* userData);


int main(int argc,char* argv[])
{
    srcImage = imread("F:\\opencv\\OpenCVImage\\findContours.jpg");

    g_BlurValue = 4;
    g_sobelValue = 1;
    g_lowThresholdValue = 80;
    g_upThresholdValue = 240;
    namedWindow("canny Image");
    namedWindow("contours Image");
    createTrackbar("blur size value ", "canny Image", &g_BlurValue, g_BlurMax,onBlurTrackBar,0);
    createTrackbar("sobel size", "canny Image", &g_sobelValue, g_sobelSizeMax,onTrackBarSobelSize,0);
    createTrackbar("low threshold", "canny Image", &g_lowThresholdValue, g_lowThresholdMax,onTrackBarLowThresholdSize,0);
    onBlurTrackBar(g_BlurValue, 0);


    imshow("src image", srcImage);
    moveWindow("src image", 0, 0);
    moveWindow("canny Image", srcImage.cols, 0);
    moveWindow("contour image", srcImage.cols*2, 0);

    waitKey(0);
    return 0;
}

//修T改?了¢?滤?波¡§参?数ºy
void onBlurTrackBar(int pos,void* userData)
{
    int blurSize = g_BlurValue*2+1;
    blur(srcImage, blurImage, Size(blurSize,blurSize));

    int sobelValue = g_sobelValue*2 +3;
    if (g_lowThresholdValue == 0) {
        g_lowThresholdValue = 1;
    }
    int lowThresholdValue = g_lowThresholdValue;
    int upThresholdValue = lowThresholdValue*3;
    cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);
    //计?算?canny
    Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);

    contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);
    findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));
    for(int i = 0;i<g_vHierarchy.size();i++)
    {
        Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));
        drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));
    }

    imshow("contour image", contourImage);
    imshow("canny Image",cannyImage);
}

//修T改?了¢?sobel孔¡Á径?参?数ºy
void onTrackBarSobelSize(int pos,void* userData)
{
    int blurSize = g_BlurValue*2+1;
    blur(srcImage, blurImage, Size(blurSize,blurSize));

    int sobelValue = g_sobelValue*2 +3;
    if (g_lowThresholdValue == 0) {
        g_lowThresholdValue = 1;
    }
    int lowThresholdValue = g_lowThresholdValue;
    int upThresholdValue = lowThresholdValue*3;
    cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);
    //计?算?canny
    Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);

    contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);
    findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));
    for(int i = 0;i<g_vHierarchy.size();i++)
    {
        Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));
        drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));
    }

    imshow("contour image", contourImage);
    imshow("canny Image",cannyImage);
}

//修T改?了¢?阈D值¦Ì参?数ºy
void onTrackBarLowThresholdSize(int pos,void* userData)
{
    int blurSize = g_BlurValue*2+1;
    blur(srcImage, blurImage, Size(blurSize,blurSize));

    int sobelValue = g_sobelValue*2 +3;
    if (g_lowThresholdValue == 0) {
        g_lowThresholdValue = 1;
    }
    int lowThresholdValue = g_lowThresholdValue;
    int upThresholdValue = lowThresholdValue*3;
    cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);
    //计?算?canny
    Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);

    contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);
    findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));
    for(int i = 0;i<g_vHierarchy.size();i++)
    {
        Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));
        drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));
    }

    imshow("contour image", contourImage);
    imshow("canny Image",cannyImage);
}

二.凸包

         凸包是指,给定一个二维平面上的点集,凸包就是将这个点集最外层的点连接起来的构成的凸多边形

         计算一个物体的凸包,然后计算凸包的凹缺陷,是理解物体轮廓与形状的有效方式,可以用于典型的相似物体查找中.

         API:void convertHull(输入二维点集,输出凸包,bool 操作方向标识符,bool 返回点类型)

         注:1.操作方向标识符,是指在笛卡尔坐标中,当为true的时候,起始点到结束点顺时针,否则,逆时针.

                  2返回点类型为真时,返回凸包的各个定点,否则,返回凸包各点的指数,当输出为vector<point>时,这个参数被忽略.

                  3.返回的二维点集形态类是vector<point>,得到的凸包类型,也是vector<point>hull,hull.size()是凸包的点的个数.

         一般寻找凸包,主要是先对图像二值化,后寻找轮廓,然后寻找一条指定轮廓的凸包.

示例代码如下

//对?一°?张?图ª?片?进?行D轮?廓¤a查¨¦找¨°,并¡é对?每?一°?个?查¨¦找¨°出?来¤¡ä的Ì?轮?廓¤a运?行D凸ª1包㨹查¨¦找¨°程¨¬序¨°,生¦¨²成¨¦最Á?终?显?示º?图ª?片?
//边À?缘¦Ì轮?廓¤a查¨¦找¨°
//先¨¨进?行D边À?缘¦Ì滤?波¡§ 然¨?后¨®用®?canny算?法¤¡§得Ì?到Ì?二t值¦Ì化¡¥图ª?像?
//滤?波¡§算?法¤¡§选?择?均¨´值¦Ì滤?波¡§ 可¨¦调Ì¡Â的Ì?是º?孔¡Á径?尺?寸ä?
//canny算?法¤¡§可¨¦调Ì¡Â的Ì?上¦?下?限T和¨ªsobel算?子Á¨®孔¡Á径?3 5 7
Mat srcImage,blurImage,grayBlurImage,cannyImage,contourImage,hullImage;
vector<vector<Point>>g_vContours;
vector<Vec4i>g_vHierarchy;
RNG g_rng(12345);

const int g_BlurMax = 100;
int g_BlurValue;
void onBlurTrackBar(int pos,void* userData);

const int g_sobelSizeMax = 2;//sobel孔¡Á径?
int g_sobelValue;

const int g_lowThresholdMax = 80;//边À?缘¦Ì检¨¬测a低̨ª阈D值¦Ì
int g_lowThresholdValue;
int g_upThresholdValue;

void onTrackBarSobelSize(int pos,void* userData);
void onTrackBarLowThresholdSize(int pos,void* userData);


int main(int argc,char* argv[])
{
   srcImage = imread("F:\\opencv\\OpenCVImage\\convexHull.jpg");
   
   g_BlurValue = 4;
   g_sobelValue = 1;
   g_lowThresholdValue = 80;
   g_upThresholdValue = 240;
   namedWindow("canny Image");
   namedWindow("contours Image");
   namedWindow("hull image");
   createTrackbar("blur size value ", "canny Image", &g_BlurValue, g_BlurMax,onBlurTrackBar,0);
   createTrackbar("sobel size", "canny Image", &g_sobelValue, g_sobelSizeMax,onTrackBarSobelSize,0);
   createTrackbar("low threshold", "canny Image", &g_lowThresholdValue, g_lowThresholdMax,onTrackBarLowThresholdSize,0);
   onBlurTrackBar(g_BlurValue, 0);

   moveWindow("src image", 0, 0);
   moveWindow("canny Image", srcImage.cols, srcImage.rows);
   moveWindow("contour image", srcImage.cols*2, 0);
   moveWindow("hull image", srcImage.cols*2, srcImage.rows);

   waitKey(0);
   return 0;
}

//修T改?了¢?滤?波¡§参?数ºy
void onBlurTrackBar(int pos,void* userData)
{
   imshow("src image", srcImage);
   
   int blurSize = g_BlurValue*2+1;
   blur(srcImage, blurImage, Size(blurSize,blurSize));

   int sobelValue = g_sobelValue*2 +3;
   if (g_lowThresholdValue == 0) {
       g_lowThresholdValue = 1;
   }
   int lowThresholdValue = g_lowThresholdValue;
   int upThresholdValue = lowThresholdValue*3;
   cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);
   //计?算?canny
   Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);

   contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);
   findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));
   for(int i = 0;i<g_vHierarchy.size();i++)
   {
       Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));
       drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));
   }
   
   vector<vector<Point>>hull(g_vContours.size());
   hullImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);
   for(int i = 0; i < g_vContours.size(); i++)
   {
       convexHull(Mat(g_vContours[i]), hull[i],false);
       Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));
       drawContours(hullImage, hull, i, color,1,8,vector<Vec4i>(),0,Point(0,0));
   }

   imshow("hull image", hullImage);
   imshow("contour image", contourImage);
   imshow("canny Image",cannyImage);
}

//修T改?了¢?sobel孔¡Á径?参?数ºy
void onTrackBarSobelSize(int pos,void* userData)
{
   imshow("src image", srcImage);

   int blurSize = g_BlurValue*2+1;
   blur(srcImage, blurImage, Size(blurSize,blurSize));

   int sobelValue = g_sobelValue*2 +3;
   if (g_lowThresholdValue == 0) {
       g_lowThresholdValue = 1;
   }
   int lowThresholdValue = g_lowThresholdValue;
   int upThresholdValue = lowThresholdValue*3;
   cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);
   //计?算?canny
   Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);

   contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);
   findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));
   for(int i = 0;i<g_vHierarchy.size();i++)
   {
       Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));
       drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));
   }
   vector<vector<Point>>hull(g_vContours.size());
   hullImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);
   for(int i = 0; i < g_vContours.size(); i++)
   {
       convexHull(Mat(g_vContours[i]), hull[i],false);
       Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));
       drawContours(hullImage, hull, i, color,1,8,vector<Vec4i>(),0,Point(0,0));
   }
   
   imshow("hull image", hullImage);
   imshow("contour image", contourImage);
   imshow("canny Image",cannyImage);
}

//修T改?了¢?阈D值¦Ì参?数ºy
void onTrackBarLowThresholdSize(int pos,void* userData)
{
   imshow("src image", srcImage);

   int blurSize = g_BlurValue*2+1;
   blur(srcImage, blurImage, Size(blurSize,blurSize));

   int sobelValue = g_sobelValue*2 +3;
   if (g_lowThresholdValue == 0) {
       g_lowThresholdValue = 1;
   }
   int lowThresholdValue = g_lowThresholdValue;
   int upThresholdValue = lowThresholdValue*3;
   cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);
   //计?算?canny
   Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);

   contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);
   findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));
   for(int i = 0;i<g_vHierarchy.size();i++)
   {
       Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));
       drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));
   }
   vector<vector<Point>>hull(g_vContours.size());
   hullImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);
   for(int i = 0; i < g_vContours.size(); i++)
   {
       convexHull(Mat(g_vContours[i]), hull[i],false);
       Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));
       drawContours(hullImage, hull, i, color,1,8,vector<Vec4i>(),0,Point(0,0));
   }
   
   imshow("hull image", hullImage);
   imshow("contour image", contourImage);
   imshow("canny Image",cannyImage);
}

三.对于轮廓代表的二维点集的其他处理方式

         获取点集的外围矩形边界

         API:Rect boundingRect(输入点集)

         返回点集的最小包围矩形

         API:RotatedRect minAreaRect(输入点集)

         寻找点集的最小包围圆心

         API:void minEnclosingCircle(输入点集,Point2f& 圆心,float& 半径)

         椭圆拟合二维点集

         API:RatatedRect fitEllipse(输入点集)

         逼近多边形曲线

         API:void approxPolyDp(输入二维点集,输出多边形逼近结果,double epsilon,bool close是否封闭)

         注:epsilon为原始曲线和近似曲线之间的最大值

            closed为真,则封闭,为假,得到的多边形不封闭

         以上各个API使用例程如下

//首º¡Á先¨¨查¨¦找¨°轮?廓¤a,再¨´用®?多¨¤边À?形?逼À?近¨¹轮?廓¤a
//然¨?后¨®依°¨¤靠?多¨¤边À?形?轮?廓¤a获?得Ì?包㨹围¡ì多¨¤边À?形?轮?廓¤a的Ì?圆2形? 最Á?小?矩?形? 矩?形?边À?界?
//需¨¨要°a变À?更¨¹的Ì?只?有®D二t值¦Ì化¡¥操¨´作Á¡Â时º¡À的Ì?阈D值¦Ì
//阈D值¦Ì最Á?大䨮值¦Ì 255 最Á?小?值¦Ì可¨¦变À?
Mat srcImage,srcCopyImage,srcGrayImage,srcThresholdImage,DstImage;
vector<vector<Point>>contours;
vector<Vec4i> hierarchys;
const int g_lowThresholdMax = 254;
int g_lowThresholdValue;
int g_upThresholdValue;
void onTrackBarLowThreshold(int pos,void* userData);
RNG g_rng(12345);
int main(int argc,char* argv[])
{
   srcImage = imread("F:\\opencv\\OpenCVImage\\contour.jpg");
   srcCopyImage = srcImage.clone();
   //转Áa化¡¥RGB为a灰¨°度¨¨图ª?像?
   if(srcImage.channels() == 3)
   {
       cvtColor(srcImage, srcGrayImage, CV_RGB2GRAY);
   }
   else
   {
       srcGrayImage = srcImage.clone();
   }
   blur(srcGrayImage, srcGrayImage,Size(3,3));
   
   namedWindow("src image");
   namedWindow("threshold image");
   namedWindow("dst image");
   
   g_lowThresholdValue = 80;
   g_upThresholdValue = 255;
   createTrackbar("low threshold value", "threshold image", &g_lowThresholdValue, g_lowThresholdMax,onTrackBarLowThreshold,0);
   onTrackBarLowThreshold(g_lowThresholdValue, 0);
   
   imshow("src image", srcImage);
   
   moveWindow("src image", 0, 0);
   moveWindow("threshold image", srcImage.cols, 0);
   moveWindow("dst image", srcImage.cols*2, 0);
   
   waitKey(0);
   return 0;
}


void onTrackBarLowThreshold(int pos,void* userData)
{
   if(g_lowThresholdValue == 0)g_lowThresholdValue = 1;
   threshold(srcGrayImage, srcThresholdImage, g_lowThresholdValue, g_upThresholdValue, THRESH_BINARY);
   //二t值¦Ì化¡¥完ª¨º成¨¦,寻¡ã找¨°轮?廓¤a
   findContours(srcThresholdImage, contours, hierarchys, RETR_TREE, CHAIN_APPROX_SIMPLE,Point(0,0));
   //多¨¤边À?形?毕À?竟1轮?廓¤a,先¨¨生¦¨²成¨¦变À?量¢?
   vector<vector<Point>>contours_polys(contours.size());//多¨¤边À?形?
   vector<Rect>boundRect(contours.size());//轮?廓¤a最Á?外ªa层?矩?形?边À?界?
   vector<Point2f>center(contours.size());//最Á?小?面?积y包㨹围¡ì圆2
   vector<float>radius(contours.size());
   
   DstImage = srcCopyImage.clone();
   for(int i = 0; i < contours.size();i++)
   {
       //逼À?近¨¹多¨¤边À?形?
       approxPolyDP(Mat(contours[i]), contours_polys[i], 3, true);//逼À?近¨¹精?度¨¨3且¨°封¤a闭À?
       //从䨮逼À?近¨¹到Ì?的Ì?多¨¤边À?形?得Ì?到Ì?最Á?外ªa层?矩?形?
       boundRect[i] = boundingRect(Mat(contours_polys[i]));
       //从䨮逼À?近¨¹的Ì?多¨¤边À?形?得Ì?到Ì?最Á?小?圆2形?
       minEnclosingCircle(Mat(contours_polys[i]), center[i], radius[i]);
   }
   //先¨¨绘?制?轮?廓¤a
   drawContours(DstImage, contours, -1, Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255)));
   //依°¨¤次ä?在¨²轮?廓¤a上¦?绘?制?矩?形?和¨ª圆2形?
   for(int i = 0; i < contours.size(); i++)
   {
       rectangle(DstImage, boundRect[i].tl(), boundRect[i].br(), Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255)),2,8,0);
       circle(DstImage, center[i], (int)radius[i], Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255)),2,8,0);
   }
   
   imshow("threshold image", srcThresholdImage);
   imshow("dst image", DstImage);
}

四.图像的矩

         图像说到底还是一个矩阵,而矩阵在不同的空间大小的情况下,进行分析的时候就需要用到矩阵的矩,也就是图像的矩,矩函数在图像分析中具有重要的作用,模式识别,目标分类,目标识别方位估计,图像编码重构等都需要用到图像的矩.

         图像的一阶矩与图像的形状相关,二阶矩显示图像中曲线围绕直线平均值的扩展程度,三阶矩是关于平均值的对称性的测量,由二阶矩和三阶矩可以到处七个不变矩,也即是7Hu不变矩.

         不变矩是图像的统计特征,满足平移,旋转,伸缩均不变的不变性,可以用于图像识别.

         另外,通过中心矩也可以计算图像的轮廓长度以及面积.

         API:Moments moments(源图像,bool 非0像素是否全看做1);

         注:源可以是二维数组或者单通道,八位或浮点型

                  第二个参数默认为false,非零像素不全部看作为1

         计算轮廓面积

         API:double contourArea(输入,bool 面向区域标识符)

         输入输入为向量或者是二维点,也可以说Mat,一般是查找到的图像的轮廓点集或者是根据轮廓点集拟合出的矩形,椭圆,圆,多边形,也可以使图像的凸包vector<poit2f>,返回图像的面积,默认为false,表示返回图像的面积是绝对值,不带符号.

         计算轮廓长度

         API:double arcLength(输入点集,bool 指示曲线是否封闭)

         注:输入点集类型与上一个api一致,默认曲线是封闭的.

测试使用图像的中心矩和opencv提供的算法,来计算图像轮廓面积,代码如下

//图ª?像?的Ì?矩?
//canny算?法¤¡§获?得Ì?二t值¦Ì图ª?像? 二t值¦Ì图ª?像?获?得Ì?边À?缘¦Ì 边À?缘¦Ì获?得Ì?边À?缘¦Ì矩? 边À?缘¦Ì矩?获?得Ì?中D心?距¨¤
//通ª¡§过y中D心?距¨¤计?算?图ª?像?的Ì?轮?廓¤a的Ì?面?积y和¨ª距¨¤离¤?

Mat srcImage,srcGrayImage,srcBlurImage,srcThresholdImage,srcCopyImage;

vector<vector<Point>>contours;
vector<Vec4i> hierarchys;

const int g_lowThresholdMax = 85;
int g_lowThresholdValue;
int g_upThresholdValue;
void onTrackBarLowThreshold(int pos,void* userData);

int main(int argc,char* argv[])
{
   srcImage = imread("F:\\opencv\\OpenCVImage\\mement.jpg");
   
   if(srcImage.channels() == 3)
   {
       cvtColor(srcImage, srcGrayImage, CV_RGB2GRAY);
   }
   else
   {
       srcGrayImage = srcImage.clone();
   }
   
   blur(srcGrayImage,srcBlurImage,Size(3,3));
   
   namedWindow("canny image");
   g_lowThresholdValue = 80;
   g_upThresholdValue = g_lowThresholdValue*3;
   createTrackbar("low threshold value", "canny image", &g_lowThresholdValue, g_lowThresholdMax,onTrackBarLowThreshold,0);
   onTrackBarLowThreshold(g_lowThresholdValue, 0);
 
   imshow("src image", srcImage);
   
   moveWindow("src image", 0, 0);
   moveWindow("canny image", srcBlurImage.cols, 0);
   moveWindow("dst image", srcBlurImage.cols*2, 0);
   
   waitKey(0);
   return 0;
}



void onTrackBarLowThreshold(int pos,void* userData)
{
   Canny(srcBlurImage, srcThresholdImage, g_lowThresholdValue, g_upThresholdValue,3);
   //二t进?制?图ª?像?获?取¨?轮?廓¤a
   findContours(srcThresholdImage, contours, hierarchys, RETR_TREE, CHAIN_APPROX_SIMPLE,Point(0,0));
   //计?算?矩?
   vector<Moments>mu(contours.size());
   for(int i = 0; i < contours.size(); i++)
   {
       mu[i] = moments(contours[i],false);
   }
   //计?算?中D心?矩?
   vector<Point2f>mc(contours.size());
   for(int i = 0; i < contours.size(); i++)
   {
       mc[i] = Point2f(static_cast<float>(mu[i].m10/mu[i].m00),static_cast<float>(mu[i].m01/mu[i].m00));
   }
   //绘?制?轮?廓¤a
   //srcCopyImage = srcImage.clone();
   srcCopyImage = Mat(srcImage.rows,srcImage.cols,CV_8UC1,Scalar::all(0));
   drawContours(srcCopyImage, contours, -1, Scalar(255));
   for(int i = 以上是关于OPENCV图像轮廓检测的主要内容,如果未能解决你的问题,请参考以下文章

youcans 的 OpenCV 例程200篇194.寻找图像轮廓(cv.findContours)

youcans 的 OpenCV 例程200篇194.寻找图像轮廓(cv.findContours)

图像轮廓检测错误:OpenCV、C++

求C++中利用opencv计算轮廓图像傅里叶描述子的代码

OPENCV图像轮廓检测

OpenCV 图像轮廓检测