findContours的使用
最近在做opencv相关的轮廓发现和分离。在opencv 中右两种方式使用findContours函数,第一个是检测轮廓和层次信息,第二个只检测轮廓。
//! retrieves contours and the hierarchical information from black-n-white image.
CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode,
int method, Point offset=Point());
//! retrieves contours from black-n-white image.
CV_EXPORTS void findContours( InputOutputArray image, OutputArrayOfArrays contours,
int mode, int method, Point offset=Point());
我需要的是第二种就直接调函数。
Mat image = imread(img_path, 1);
if (image.empty())
{
cout << "Couldn't load " << img_path << endl;
}
Mat timg(image);
medianBlur(image, timg, 9);
Mat gray0, gray;
vector<vector<Point> > contours; // 存储轮廓信息
// 灰度化+二值化
cvtColor(timg, gray0, CV_BGR2GRAY);
threshold(gray0, gray, 50, 255, THRESH_BINARY);
//imshow("threshold gray", gray);
// find contours and store them all as a list
findContours(gray, contours, CV_RETR_EXTERNAL, CHAIN_APPROX_NONE);
使用opencv3.4.14运行下面代码没有任何问题,能检测出来轮廓并且运行无报错。
项目中使用的是opencv 2.4.9。当我配置好opencv2.4.9后,将代码复制到该环境后,运行...,也能检测出来轮廓,但是报错了!!!弹出来一个警告,让我不得不中止程序。
搜索了很多解决方法,很多人也是直接换版本了,可是我现在要用2.4.9,然后又发现了一下几个解决方法:
方案一:
如果使用vector<vector<Point> > contours;作为findContours的参数,在运行时会得到
Assertion failed (mtype == type0 || (CV_MAT_CN(mtype) == CV_MAT_CN(type0) && ((1 << type0) & f… 原因是标准库里有std::vector 和 Point 和findContours里要用到的vector和Point不是一回事所以,声明的时候要用cv::vector和cv::Point就可以了。
方案二:
“修改了当前程序的vc运行库配置,问题解决。具体方法是:项目-属性-配置属性-C/C++-代码生成-运行库,将其改为“多线程调试(/MTd)”。”
方案三:
当一个DLL采用静态的方式链接到C运行时库时,会创建一个相对于该DLL的堆(Heap),而如果采用共享的方式链接到C运行时库的时候则使用的是应用程序的堆内存。而_CrtIsValidHeapPointer()在 DEBug模式下将确保传入的地址在本地的堆内存中。 因此就有理由相信,真有可能是静态链接的问 题。所以,我立即尝试将:
项目–属性–配置属性–常规–MFC的使用– 选择在共享DLL中使用MFC ;同时,
项目–属性–配置属性–C/C++–代码生成–运行库–选择 多线程DLL(/MD)。
以上方案都不能解决这个问题,直到我试了这个。
方案四:
在调用finContours函数之前,自己进行空间的分配。 于是,我对程序进行部分修改,修改程序如下:
vector<Mat> contours(100);
findContours(gray, contours, CV_RETR_EXTERNAL, CHAIN_APPROX_NONE);
注意,此时得到的每一个轮廓存放的方式不在是Point类型而是Mat类型,在操作轮廓坐标时使用的是Mat的操作方式,例如后续我用到了approxPolyDP函数。
Mat approxArray;
approxPolyDP(Mat(contours[maxLengthPosition]), approxArray, arcLength(Mat(contours[maxLengthPosition]), true) * 0.02, true);
其中,maxLengthPosition是最大轮廓的所在索引位置。
此时的approxArray也要用Mat接收,然后操作时使用:
approxArray.at<int>(i, j);
int 是Mat 定义的数据类型,如果出现错误,可以使用approxArray.type()来确定。approxArray.type()返回一个数值,该数值对应该图像是什么类型的,具体的对应可以去https://blog.csdn.net/kakiebu/article/details/79359867看下,写的很详细。
问题就这样解决了~