opencv程序五:轮廓检测

根据第一篇创建项目并添加两个文件

一.程序1

程序一:自己绘制简单的几何图形并检测轮廓

// 8Contours1.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <opencv2/opencv.hpp>  
using namespace std;  
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")  
int main( int argc, char** argv )  
{     
    const char *pstrWindowsSrcTitle = "原图";  
    const char *pstrWindowsOutLineTitle = "轮廓图";  
      
    const int IMAGE_WIDTH = 400;  
    const int IMAGE_HEIGHT = 200;  
  
    // 创建图像  
    IplImage *pSrcImage = cvCreateImage(cvSize(IMAGE_WIDTH, IMAGE_HEIGHT), IPL_DEPTH_8U, 3);  
    // 填充成白色  
    cvRectangle(pSrcImage, cvPoint(0, 0), cvPoint(pSrcImage->width, pSrcImage->height), CV_RGB(255, 255, 255), CV_FILLED);  
    // 画圆  
    CvPoint ptCircleCenter = cvPoint(IMAGE_WIDTH / 4, IMAGE_HEIGHT / 2);  
    int nRadius = 80;  
    cvCircle(pSrcImage, ptCircleCenter, nRadius, CV_RGB(255, 255, 0), CV_FILLED);  
    ptCircleCenter = cvPoint(IMAGE_WIDTH / 4, IMAGE_HEIGHT / 2);  
    nRadius = 30;  
    cvCircle(pSrcImage, ptCircleCenter, nRadius, CV_RGB(255, 255, 255), CV_FILLED);  
    // 画矩形  
    CvPoint ptLeftTop = cvPoint(IMAGE_WIDTH / 2 + 20, 20);  
    CvPoint ptRightBottom = cvPoint(IMAGE_WIDTH - 20, IMAGE_HEIGHT - 20);  
    cvRectangle(pSrcImage, ptLeftTop, ptRightBottom, CV_RGB(0, 255, 255), CV_FILLED);  
    ptLeftTop = cvPoint(IMAGE_WIDTH / 2 + 60, 40);  
    ptRightBottom = cvPoint(IMAGE_WIDTH - 60, IMAGE_HEIGHT - 40);  
    cvRectangle(pSrcImage, ptLeftTop, ptRightBottom, CV_RGB(255, 255, 255), CV_FILLED);  
    // 显示原图  
    cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE);  
    cvShowImage(pstrWindowsSrcTitle, pSrcImage);  
  
  
    // 转为灰度图  
    IplImage *pGrayImage =  cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);  
    cvCvtColor(pSrcImage, pGrayImage, CV_BGR2GRAY);  
    // 转为二值图  
    IplImage *pBinaryImage = cvCreateImage(cvGetSize(pGrayImage), IPL_DEPTH_8U, 1);  
    cvThreshold(pGrayImage, pBinaryImage, 250, 255, CV_THRESH_BINARY);  
  
  
    // 检索轮廓并返回检测到的轮廓的个数  
    CvMemStorage *pcvMStorage = cvCreateMemStorage();  
    CvSeq *pcvSeq = NULL;  
    cvFindContours(pBinaryImage, pcvMStorage, &pcvSeq, sizeof(CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));  
      
    // 画轮廓图  
    IplImage *pOutlineImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 3);  
	//轮廓最大层数 
    int nLevels = 5;  
    // 填充成白色  
    cvRectangle(pOutlineImage, cvPoint(0, 0), cvPoint(pOutlineImage->width, pOutlineImage->height), CV_RGB(255, 255, 255), CV_FILLED);  
    cvDrawContours(pOutlineImage, pcvSeq, CV_RGB(255,0,0), CV_RGB(0,255,0), nLevels, 2);  
    // 显示轮廓图  
    cvNamedWindow(pstrWindowsOutLineTitle, CV_WINDOW_AUTOSIZE);  
    cvShowImage(pstrWindowsOutLineTitle, pOutlineImage);  
  
  
    cvWaitKey(0);  
  
    cvReleaseMemStorage(&pcvMStorage);  
  
    cvDestroyWindow(pstrWindowsSrcTitle);  
    cvDestroyWindow(pstrWindowsOutLineTitle);  
    cvReleaseImage(&pSrcImage);  
    cvReleaseImage(&pGrayImage);  
    cvReleaseImage(&pBinaryImage);  
    cvReleaseImage(&pOutlineImage);  
    return 0;  
}  

关键函数

1.1  cvFindContours

函数功能:对图像进行轮廓检测,这个函数将生成一条链表以保存检测出的各个轮廓信息,并传出指向这条链表表头的指针。

函数原型:

int cvFindContours(

  CvArrimage,

  CvMemStoragestorage,

  CvSeq** first_contour  

  int header_size=sizeof(CvContour),

  int mode=CV_RETR_LIST  

  int method=CV_CHAIN_APPROX_SIMPLE,

  CvPoint offset=cvPoint(0,0)

);

函数说明:

第一个参数表示输入图像,必须为一个8位的二值图像。图像的二值化请参见《【OpenCV入门指南】第四篇图像的二值化》。

 

第二参数表示存储轮廓的容器。为CvMemStorage类型,定义在OpenCV\core\types_c.h中。

 

第三个参数为输出参数,这个参数将指向用来存储轮廓信息的链表表头。

 

第四个参数表示存储轮廓链表的表头大小,当第六个参数传入CV_CHAIN_CODE时,要设置成sizeof(CvChain),其它情况统一设置成sizeof(CvContour)。

 

第五个参数为轮廓检测的模式,有如下取值:

CV_RETR_EXTERNAL:只检索最外面的轮廓;

  CV_RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中;

  CV_RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;

CV_RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次,可以参见下图。

opencv程序五:轮廓检测_第1张图片

第六个参数用来表示轮廓边缘的近似方法的,常用值如下所示:

CV_CHAIN_CODE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。

  CV_CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。

 

第七个参数表示偏移量,比如你要从图像的(100, 0)开始进行轮廓检测,那么就传入(100, 0)。

 

使用cvFindContours函数能检测出图像的轮廓,将轮廓绘制出来则需要另一函数——cvDrawContours来配合了。下面介绍cvDrawContours函数。

 

1.2  cvDrawContours

函数功能:在图像上绘制外部和内部轮廓

函数原型:

void cvDrawContours(

  CvArr *img,

  CvSeqcontour,

  CvScalar external_color,

  CvScalar hole_color,

  int max_level,

  int thickness=1,

  int line_type=8,

  CvPoint offset=cvPoint(0,0)

);

第一个参数表示输入图像,函数将在这张图像上绘制轮廓。

第二个参数表示指向轮廓链表的指针。

第三个参数和第四个参数表示颜色,绘制时会根据轮廓的层次来交替使用这二种颜色。

第五个参数表示绘制轮廓的最大层数,如果是0,只绘制contour;如果是1,追加绘制和contour同层的所有轮廓;如果是2,追加绘制比contour低一层的轮廓,以此类推;如果值是负值,则函数并不绘制contour后的轮廓,但是将画出其子轮廓,一直到abs(max_level) - 1层。

第六个参数表示轮廓线的宽度,如果为CV_FILLED则会填充轮廓内部。

第七个参数表示轮廓线的类型。

第八个参数表示偏移量,如果传入(1020),那绘制将从图像的(1020)处开始。


运行结果如下图所示:



由图可以看出,轮廓线已经按层次交替的绘制成功了,读者可以修改程序中的cvDrawContours中的nLevels参数,看看图形会有什么变化。


二.程序2

程序二:检测任意图片轮廓,加载的图片不是自己画的了

// 9Contours2.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <opencv2/opencv.hpp>  
using namespace std;  
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")  
IplImage *g_pGrayImage = NULL;  
const char *pstrWindowsBinaryTitle = "二值图";  
const char *pstrWindowsOutLineTitle = "轮廓图";  
CvSeq *g_pcvSeq = NULL;  
  
void on_trackbar(int pos)  
{  
    // 转为二值图  
    IplImage *pBinaryImage = cvCreateImage(cvGetSize(g_pGrayImage), IPL_DEPTH_8U, 1);  
    cvThreshold(g_pGrayImage, pBinaryImage, pos, 255, CV_THRESH_BINARY);  
    // 显示二值图  
    cvShowImage(pstrWindowsBinaryTitle, pBinaryImage);  
  
    CvMemStorage* cvMStorage = cvCreateMemStorage();  
    // 检索轮廓并返回检测到的轮廓的个数  
    cvFindContours(pBinaryImage,cvMStorage, &g_pcvSeq);  
  
    IplImage *pOutlineImage = cvCreateImage(cvGetSize(g_pGrayImage), IPL_DEPTH_8U, 3);  
	//轮廓最大层数
    int _levels = 5;  
    cvZero(pOutlineImage);  
    cvDrawContours(pOutlineImage, g_pcvSeq, CV_RGB(255,0,0), CV_RGB(0,255,0), _levels);  
    cvShowImage(pstrWindowsOutLineTitle, pOutlineImage);  
  
    cvReleaseMemStorage(&cvMStorage);  
    cvReleaseImage(&pBinaryImage);  
    cvReleaseImage(&pOutlineImage);  
}  
  
int main( int argc, char** argv )  
{     
    const char *pstrWindowsSrcTitle = "原图";  
    const char *pstrWindowsToolBarName = "二值化";  
  
    // 从文件中加载原图  
    IplImage *pSrcImage = cvLoadImage("beautiful.jpg", CV_LOAD_IMAGE_UNCHANGED);  
    // 显示原图  
    cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE);  
    cvShowImage(pstrWindowsSrcTitle, pSrcImage);  
  
    // 转为灰度图  
    g_pGrayImage =  cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);  
    cvCvtColor(pSrcImage, g_pGrayImage, CV_BGR2GRAY);  
  
    // 创建二值图和轮廓图窗口  
    cvNamedWindow(pstrWindowsBinaryTitle, CV_WINDOW_AUTOSIZE);  
    cvNamedWindow(pstrWindowsOutLineTitle, CV_WINDOW_AUTOSIZE);  
  
  
    // 滑动条    
    int nThreshold = 0;  
    cvCreateTrackbar(pstrWindowsToolBarName, pstrWindowsBinaryTitle, &nThreshold, 254, on_trackbar);  
  
    on_trackbar(1);  
  
    cvWaitKey(0);  
  
    cvDestroyWindow(pstrWindowsSrcTitle);  
    cvDestroyWindow(pstrWindowsBinaryTitle);  
    cvDestroyWindow(pstrWindowsOutLineTitle);  
    cvReleaseImage(&pSrcImage);  
    cvReleaseImage(&g_pGrayImage);  
    return 0;  
}  

程序运行结果如下所示:

opencv程序五:轮廓检测_第2张图片


三.程序3

轮廓检测另外还有一个重要的函数cvApproxPoly,它的函数原型如下所示

CVAPI(CvSeq*)  cvApproxPoly(

  const voidsrc_seq,

  int header_size,

  CvMemStoragestorage,

  int method,

  double parameter,

  int parameter2 CV_DEFAULT(0)

);

是一个轮廓的多边形轮廓逼近函数,这个函数用指定精度逼近一个或多个 曲线,并返回逼近结果。一开始觉得这个函数没什么必要,因为如果只是要简单的提取和显示图像轮廓的话只要cvFindContours函数就够了,那么为什么还要这个函数呢?后来百度了下,在opencv论坛上找到了答案。

首先,轮廓的多边形逼近指的是:使用多边形来近似表示一个轮廓。其次,多边形逼近的目的是为了减少轮廓的顶点数目。但多边形逼近的结果依然是一个轮廓,只是这个轮廓相对要粗旷一些。

下面看下测试代码:

// 10Contours3_cvApproxPoly.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <opencv2/opencv.hpp> 
int main()  
{  
    IplImage* src = NULL;  
    IplImage* img = NULL;  
    IplImage* dst = NULL;  
  
    CvMemStorage* storage = cvCreateMemStorage (0);  
    CvMemStorage* storage1 = cvCreateMemStorage (0);  
    CvSeq* contour = 0;  
    CvSeq* cont;  
    CvSeq* mcont;  
  
    src = cvLoadImage ("8.jpg", 1);  
    img = cvCreateImage (cvGetSize(src), IPL_DEPTH_8U, 1);  
    dst = cvCreateImage (cvGetSize(src), src->depth, src->nChannels);  
	cvNamedWindow ("原图", 1);  
    cvShowImage ("原图", src);
    cvCvtColor (src, img, CV_BGR2GRAY);  
    cvThreshold (img, img, 100, 200, CV_THRESH_BINARY);  
    cvNamedWindow ("二值化图", 1);  
    cvShowImage ("二值化图", img);   
    cvFindContours (img, storage, &contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); 
	IplImage *pOutlineImage = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3); 
    int _levels = 5;  
    cvDrawContours(pOutlineImage, contour, CV_RGB(255,0,0), CV_RGB(0,255,0), _levels,2);  
	 cvNamedWindow ("cvFindContours", 1);  
    cvShowImage ("cvFindContours", pOutlineImage);
    if (contour)  
    {  
        CvTreeNodeIterator iterator;  
        cvInitTreeNodeIterator (&iterator, contour,  1);  
        while (0 != (cont = (CvSeq*)cvNextTreeNode (&iterator)))  
        {  
            mcont = cvApproxPoly (cont, sizeof(CvContour), storage1, CV_POLY_APPROX_DP, cvContourPerimeter(cont)*0.02,0);  
            cvDrawContours (dst, mcont, CV_RGB(255,0,0),CV_RGB(0,0,100),1,2,8,cvPoint(0,0));  
        }  
    }  
  
    cvNamedWindow ("Contour", 1);  
    cvShowImage ("Contour", dst);  
  
    cvWaitKey (0);  
  
    cvReleaseMemStorage (&storage);  
    cvReleaseImage (&src);  
    cvReleaseImage (&img);  
    cvReleaseImage (&dst);  
  
    return 0;  
}  
程序运行结果如下所示:



对比两个图(轮廓颜色不用管),可以发现多边形逼近的结果确实相对粗旷点。



你可能感兴趣的:(opencv,轮廓检测,cvApproxPoly,cvDrawContours,cvFindContours)