OpenCV中findContours查找后获得轮廓数 轮廓包含点数 各点坐标的实践

本文介绍如何使用OpenCV进行轮廓检测,包括获取轮廓数量、各轮廓包含的点数及坐标等关键信息。通过实例演示了从图像处理到轮廓绘制的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                刚学OpenCV,并且不懂VC++,费了几天的气力在网上终于找到了findContours查找轮廓后,怎么获得轮廓数,轮廓内包含点数,以及每个点坐标值的方法,为了方便自己以后查阅,同时也为了像我一样的“门外汉”们节省点时间,把源代码晒一下,见笑了。

        主体源代码取自浅墨老师的书《OpenCV3编程入门》OpenC3版书本配套示例程序70,在此,向浅墨老师表示敬意和感谢!还有几行代码取自于网络,网址也做了标注,在此,一并向文章博主、楼主表示感谢!

        main.cpp

//---------------------------------【头文件、命名空间包含部分】----------------------------

//              描述:包含程序所使用的头文件和命名空间

//------------------------------------------------------------------------------------------------

#include"opencv2/highgui/highgui.hpp"

#include"opencv2/imgproc/imgproc.hpp"

#include <iostream>

#include <stdio.h>

using namespace cv;

using namespace std;

 

 

 

 

//-----------------------------------【宏定义部分】--------------------------------------------

//              描述:定义一些辅助宏

//------------------------------------------------------------------------------------------------

#define WINDOW_NAME1 "【原始图窗口】"                      //为窗口标题定义的宏

#define WINDOW_NAME2 "【轮廓图】"                                       //为窗口标题定义的宏

 

 

 

 

//-----------------------------------【全局变量声明部分】--------------------------------------

//              描述:全局变量的声明

//-----------------------------------------------------------------------------------------------

Mat g_srcImage;

Mat g_grayImage;

int g_nThresh = 80;

int g_nThresh_max = 255;

RNG g_rng(12345);

Mat g_cannyMat_output;

vector<vector<Point>>g_vContours;

vector<Vec4i> g_vHierarchy;

 

 

 

 

//-----------------------------------【全局函数声明部分】--------------------------------------

//              描述:全局函数的声明

//-----------------------------------------------------------------------------------------------

static void ShowHelpText();

void on_ThreshChange(int, void*);

 

 

 

 

//-----------------------------------【main( )函数】--------------------------------------------

//              描述:控制台应用程序的入口函数,我们的程序从这里开始执行

//-----------------------------------------------------------------------------------------------

int main(int argc, char** argv)

{

        //【0】改变console字体颜色

        system("color1F");

 

 

        //【0】显示欢迎和帮助文字

        ShowHelpText();

 

 

        //加载源图像

        g_srcImage= imread("..\\hlw.jpg", 1);

        if(!g_srcImage.data) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n");return false; }

 

 

        /*//对图像四周边7像素内的区域全部灰度化

        for(int row = 0; row < g_srcImage.rows; row++) {

                 for(int col = 0; col < g_srcImage.cols; col++) {

                         if(row < 8 || row>(g_srcImage.rows-8)) {

                                  g_srcImage.at<Vec3b>(row,col)[0] = 127;

                                  g_srcImage.at<Vec3b>(row,col)[1] = 127;

                                  g_srcImage.at<Vec3b>(row,col)[2] = 127;

                         }

                         if((row > 8 || row < (g_srcImage.rows-8))&& (col < 8 || col >(g_srcImage.cols - 8))) {

                                  g_srcImage.at<Vec3b>(row,col)[0] = 127;

                                  g_srcImage.at<Vec3b>(row,col)[1] = 127;

                                  g_srcImage.at<Vec3b>(row,col)[2] = 127;

                         }

                 }

        }

        imwrite("..\\hlwC.jpg",g_srcImage);*/ //查看切边效果

 

 

        //转成灰度并模糊化降噪

        cvtColor(g_srcImage,g_grayImage, COLOR_BGR2GRAY);

        blur(g_grayImage,g_grayImage, Size(3, 3));

 

 

        //创建窗口

        namedWindow(WINDOW_NAME1,WINDOW_AUTOSIZE);

        imshow(WINDOW_NAME1,g_srcImage);

 

 

        //创建滚动条并初始化

        createTrackbar("canny阈值",WINDOW_NAME1, &g_nThresh, g_nThresh_max, on_ThreshChange);

        on_ThreshChange(0,0);

 

 

        waitKey(0);

        return(0);

}

 

 

//-----------------------------------【on_ThreshChange()函数】------------------------------ 

//     描述:回调函数

//---------------------------------------------------------------------------------------------- 

void on_ThreshChange(int, void*)

{

 

 

        //用Canny算子检测边缘

        Canny(g_grayImage,g_cannyMat_output, g_nThresh, g_nThresh * 2, 3);

 

 

        //寻找轮廓

        findContours(g_cannyMat_output,g_vContours, g_vHierarchy, RETR_LIST, CHAIN_APPROX_NONE, Point(0, 0));

        cout<< "轮廓的总个数是 " << g_vContours.size() << endl;

       

        /*//  下面四行来自http://www.cnblogs.com/wsc36305/archive/2012/10/15/2725218.html

        vector<vector<Point>>::const_iteratoritContours = g_vContours.begin();

        for(; itContours != g_vContours.end(); ++itContours) {

                 cout<< "Size: " << itContours->size() << endl;//每个轮廓包含的点数

        }*/

 

 

        //绘出轮廓

        Matdrawing = Mat::zeros(g_cannyMat_output.size(), CV_8UC3);

        for(int i = 0; i< g_vContours.size(); i++)

        {

                 Scalarcolor = Scalar/*(255, 255, 255);// */ (g_rng.uniform(0, 255), g_rng.uniform(0,255), g_rng.uniform(0, 255));//任意值

                 drawContours(drawing,g_vContours, i, color, 1, 8, g_vHierarchy, 0, Point());

                 printf("\n输出 轮廓序号,第 个轮廓:   %d \n", i);

                 //下面一行来自 http://blog.youkuaiyun.com/dcrmg/article/details/51987348

                 cout<< "输出轮廓包含点的总数:  " <<g_vContours[i].size() << endl;    //试试看输出该轮廓点的总数

                 //  下面三行来自http://www.opencv.org.cn/forum.php?mod=viewthread&tid=38297&page=4

                 cout<< "输出轮廓第0个点的坐标  " <<g_vContours[i][0] << endl;     //试试看输出轮廓第一个点的坐标   x,y

                 //cout<< "输出轮廓第0个点的x坐标  " <<g_vContours[i][0].x << endl;  //试试看输出轮廓第一个点的横坐标 x

                 //cout<< "输出轮廓第0个点的y坐标  " <<g_vContours[i][0].y << endl;  //试试看输出轮廓第一个点的纵坐标 y

        }

 

 

        //显示效果图

        imwrite("..\\findContours.bmp",drawing);

        imshow(WINDOW_NAME2,drawing);

}

 

 

//-----------------------------------【ShowHelpText()函数】---------------------------------- 

//     描述:输出一些帮助信息 

//---------------------------------------------------------------------------------------------- 

static void ShowHelpText()

{

        //输出欢迎信息和OpenCV版本

        printf("\n\n\t\t\t非常感谢购买《OpenCV3编程入门》一书!\n");

        printf("\n\n\t\t\t此为本书OpenCV3版的第70个配套示例程序\n");

        printf("\n\n\t\t\t   当前使用的OpenCV版本为:"CV_VERSION);

        printf("\n\n ----------------------------------------------------------------------------\n");

 

 

        //输出一些帮助信息 

        printf("\n\n\t欢迎来到【在图形中寻找轮廓】示例程序~\n\n");

        printf("\n\n\t按键操作说明:\n\n"

                 "\t\t键盘按键任意键- 退出程序\n\n"

                 "\t\t滑动滚动条-改变阈值\n");

}

 

<think>好的,我现在要教用户关于OpenCV Python中的findContours和drawContours函,特别是轮廓查找与筛选的方法。首先,我需要回忆这两个函的基本用法和参,确保自己理解正确。 用户可能已经知道轮廓是图像中物体的边界,但可能需要了解如何找到并绘制它们。首先,我应该解释findContours的作用,它用于在二值图像中检测轮廓。需要注意的是,原图会被这个函修改,所以通常使用拷贝来处理。参方面,mode方法有RETR_EXTERNAL、RETR_LIST等,method有CHAIN_APPROX_SIMPLE等,这些需要详细说明。 然后,drawContours用于绘制检测到的轮廓。参包括图像、轮廓列表、索引(-1表示全部)、颜色和厚度。要提醒用户轮廓是在原图上绘制,所以可能需要复制图像。 接下来是筛选轮廓的部分。用户可能想知道如何根据面积、周长等特征筛选。比如使用cv2.contourArea计算面积,cv2.arcLength计算周长,然后设定阈值过滤。这需要给出具体的代码示例,比如遍历所有轮廓,保留符合条件的。 需要检查是否要提到轮廓的层次结构,即hierarchy参。对于初学者来说,可能暂时不需要深入,但可以简要提及,以备后续学习。 还要注意常见的错误,比如确保图像是二值化的,否则findContours可能不会正确工作。此外,绘制轮廓时索引的使用,比如用-1来画所有,或者指定某个索引。 结构方面,按照用户的请求,分步骤讲解:1)准备工作,2)查找轮廓,3)绘制轮廓,4)筛选轮廓。每个步骤给出代码片段,并解释参和注意事项。 最后,可能需要一个完整的示例代码,从读取图像、灰度化、二值化、查找、筛选到绘制,帮助用户整体理解流程。同时提醒实际应用中可能需要调整阈值和方法。 现在要确保语言清晰,用中文,代码中的LaTeX公式正确,比如学表达式用$...$,例如面积阈值用$A > 100$。独立公式如果有的话,用$$...$$,但可能这部分内容较少,重在代码和参说明。 检查是否有遗漏,比如轮廓近似方法的影响,比如CHAIN_APPROX_SIMPLE会减少点数,节省内存。或者不同检索模式的区别,比如RETR_EXTERNAL只找最外层轮廓,RETR_TREE建立层次结构。 总结时,强调实践的重要性,建议用户尝试不同的参和筛选条件,观察结果变化。这样可以帮助他们更好地理解如何应用这些函解决实际问题。</think>下面将分步讲解OpenCV中`findContours`和`drawContours`的使用方法及轮廓筛选技巧: --- ### **1. 准备工作** ```python import cv2 import numpy as np # 读取图像并转为灰度图 img = cv2.imread('image.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 二值化处理(阈值根据实际情况调整) _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) ``` --- ### **2. 查找轮廓findContours** ```python # 查找轮廓(注意会修改原图,使用copy()) contours, hierarchy = cv2.findContours( image=thresh.copy(), # 输入二值图像 mode=cv2.RETR_EXTERNAL, # 轮廓检索模式 method=cv2.CHAIN_APPROX_SIMPLE # 轮廓近似方法 ) ``` #### **关键参解析** - **mode**(轮廓检索模式): - `cv2.RETR_EXTERNAL`:只检测最外层轮廓 - `cv2.RETR_TREE`:检测所有轮廓并建立层级关系树 - `cv2.RETR_LIST`:检测所有轮廓不建立层级 - **method**(轮廓近似方法): - `cv2.CHAIN_APPROX_SIMPLE`:压缩水平/垂直/对角线段,仅保留端 - `cv2.CHAIN_APPROX_NONE`:存储所有轮廓 --- ### **3. 绘制轮廓:drawContours** ```python # 创建原图拷贝用于绘制 draw_img = img.copy() # 绘制所有轮廓(-1表示绘制全部) cv2.drawContours( image=draw_img, contours=contours, contourIdx=-1, color=(0, 255, 0), # BGR颜色 thickness=2 ) ``` #### **参说明** - `contourIdx`:指定绘制轮廓的索引(输入`0`绘制第一个轮廓) - `thickness`:轮廓线粗细(设为`-1`表示填充轮廓) --- ### **4. 轮廓筛选技巧** #### **4.1 按面积筛选** ```python min_area = 100 filtered_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > min_area] ``` #### **4.2 按周长/长宽比筛选** ```python # 计算周长 perimeter = cv2.arcLength(cnt, closed=True) # 长宽比计算 x, y, w, h = cv2.boundingRect(cnt) aspect_ratio = w / float(h) ``` #### **4.3 按凸性筛选** ```python # 判断是否为凸轮廓 is_convex = cv2.isContourConvex(cnt) ``` --- ### **5. 完整示例代码** ```python # 读取图像并预处理 img = cv2.imread('shapes.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY) # 查找轮廓 contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 筛选面积>100的轮廓 filtered = [cnt for cnt in contours if cv2.contourArea(cnt) > 100] # 绘制筛选结果 result = img.copy() cv2.drawContours(result, filtered, -1, (0,255,0), 2) # 显示结果 cv2.imshow('Result', result) cv2.waitKey(0) ``` --- ### **常见问题** 1. **为什么`findContours`返回空列表?** - 检查输入图像是否已正确二值化 - 确认阈值处理是否合理(可用自适应阈值`cv2.adaptiveThreshold`) 2. **如何保存筛选后的轮廓?** ```python # 保存绘制结果 cv2.imwrite('output.jpg', result) ``` 3. **如何获取轮廓坐标?** ```python for cnt in contours: # 轮廓坐标为Nx1x2组 print(cnt[:,0,:]) # 提取所有的(x,y)坐标 ``` 建议通过调整阈值和筛选条件进行实验,观察不同参对结果的影响。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值