欢迎访问个人网络日志🌹🌹知行空间🌹🌹
获取对象的轮廓,一般最好先对图像进行灰度化再进行阈值处理,然后用来检测轮廓。
void cv::findContours(
InputOutputArray image,
OutputArrayOfArrays contours,
OutputArray hierarchy,
int mode,
int method,
Point offset = Point()
)
vector> vectorRETR_EXTERNAL/RETR_LIST/RETR_CCOMP/RETR_TREE四种roi上提取轮廓后想还原到原图上时比较有用。第四个参数mode,可以选择不同的轮廓提取算法,常用的有RETR_EXTERNAL/RETR_LIST/RETR_CCOMP/RETR_TREE四种。下面分别进行介绍。在findContours函数中,其第3个参数hierarchy表示的轮廓之间的层级关系,对于不同mode的轮廓提取算法,其返回的值是不同的。如下图【来自于OpenCV Doc】:

图中不同的轮廓有层级嵌入关系,我们将外部的轮廓称为父,内部的轮廓称为子,hierarchy就是表示轮廓的父子和邻近关系的矩阵。上图中有0/1/2/3/4/5/2a/3a 8 个轮廓,其中0,1,2是最外侧的轮廓,可记为它们在层级0hierarchy-0。而2a是轮廓2的子轮廓,记为其在层级1hierarchy 1。同样轮廓3是轮廓2a的子轮廓,记为其在层级2hierarchy 2。同样轮廓3a是轮廓3的子轮廓,记为其在层级3hierarchy 3。4/5是3a的子轮廓,其构成层级4hierarchy 4。 属于每个层的轮廓都有其自己的信息,如它的子轮廓是什么,父轮廓是什么,OpenCV通过一个四个元素的数组来表示每个轮廓与其他轮廓的关系,这个四维数组中的值分别表示**[Next, Previous, First_Child, Parent], Next表示属于同一个层级hierarchy的下一个轮廓,以上图轮廓0为例,0,1,2属于同个层级hierarchy-0的轮廓,因此0的Next是1,1的Next是2。同一个层级中轮廓2已经是最后一个了,因此其Next是-1。同样对于上图的轮廓4,与其同属于层级4hierarchy-4的轮廓是5,因此4的Next=5,而5的Next=-1。Previous表示同一层级中之前的的那个轮廓**,如上图,1的Previous=0, 2的Previous=1,0的Previous=-1。First_Child表示当前轮廓的第一个子轮廓,如上图,0的First_Child=-1,2的First_Child=2a,3a的First_Child=4。Parent表示当前轮廓的父轮廓,如上图,4和5的父轮廓都是3a,3a的父轮廓是3,3的父轮廓是2a,2的Parent=-1。
findContours方法中的mode参数会返回不同的hierarchy信息,因为有些算法会找出轮廓间的嵌套和邻近关系,有些则只是把轮廓找出来而不会解析轮廓之间的信息。
RETR_LIST算法只会返回轮廓信息,而没有轮廓间的嵌套信息。因此,所有的轮廓都属于同个层级hierarchy没有父子关系, hierarchy返回值中只有Next和Previous,Parent和First_Child都为-1。四维数组的第3和第4个元素都是-1。如上图运行findContours后的输出:
findContours(image, contours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);
>>> hierarchy
[1, -1, -1, -1]
[2, 0, -1, -1]
[3, 1, -1, -1]
[4, 2, -1, -1]
[5, 3, -1, -1]
[6, 4, -1, -1]
[7, 5, -1, -1]
[-1, 6, -1, -1]
这里的0/1/2/3/4/5/6/7对应的是轮廓在contours中的下标。
RETR_EXTERNAL算法,只会返回最外侧的轮廓信息,所有的子轮廓都不会返回,如上图,使用RETR_EXTERNAL算法将只会返回hierarchy-0层级0的3个轮廓。当然hierarchy中也只有3个轮廓之间的邻近信息,Parent和First_Child依然都为-1。

findContours(image, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
[1, -1, -1, -1]
[2, 0, -1, -1]
[-1, 1, -1, -1]
RETR_CCOMP算法会找到图中所有的轮廓,但只会将轮廓组织成两层hierarchy=2。物体的外轮廓属于hierarchy-0,内轮廓属于hierarchy-1,如上图0/1/2/3/4/5都属于hierarchy-0,而2a/3a属于hierarchy-1。
findContours(image, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);
[1, -1, -1, -1]
[2, 0, -1, -1]
[4, 1, 3, -1]
[-1, -1, -1, 2]
[6, 2, 5, -1]
[-1, -1, -1, 4]
[7, 4, -1, -1]
[-1, 6, -1, -1]
值得注意的是对于轮廓2a和3a,其hierarchy分别是[-1, -1, -1, 2]和[-1, -1, -1, 4]。这是因为2a和3a虽然属于hierarchy-1,但中间还隔着轮廓3,因此2a和3a之间没有邻近关系。
再来看个例子,如下图:【来自于OpenCV Doc】

轮廓0是外轮廓,1和2分别是轮廓0所围成物体的内轮廓,4属于内轮廓,3属于外轮廓,6属于内轮廓,5属于外轮廓,7和8也都属于外轮廓。对于轮廓0其属于hierarchy-1,两个内轮廓1和2属于hierarchy-2。故对于轮廓0,其Next=3,同个层级hierarchy level的下一个,previous=-1,‵First-Child=1,故轮廓0的hierarchy=[3,-1, 1, -1]`。
轮廓1属于层级2,hierarchy-2,其同一层级的下一个(与1在同个父外轮廓中)轮廓是2,其他均为-1,因此轮廓1的hierarchy=[2, -1, -1, 0]。
轮廓2属于层级2,hierarchy-2,其前一个轮廓是同个父外轮廓下的1,其余为-1,因此轮廓2的hierarchy=[-1, 1, -1, 0]。
轮廓3在hierarchy-1中的Next=5,Previous=0,First-Child=4,Parent=-1。
>>> hierarchy
array([[[ 3, -1, 1, -1],
[ 2, -1, -1, 0],
[-1, 1, -1, 0],
[ 5, 0, 4, -1],
[-1, -1, -1, 3],
[ 7, 3, 6, -1],
[-1, -1, -1, 5],
[ 8, 5, -1, -1],
[-1, 7, -1, -1]]])
RETR_TREE算法提取所有的轮廓,并返回所有轮廓之间的嵌套关系。如上图,使用RETR_TREE得到的轮廓hierarchy之间的关系为:【来自于OpenCV Doc】括号中的绿色字表示轮廓所属的层级hierarchy。

以轮廓0为例,其属于hierarchy-0,‵Next=7, Previous=-1, First_Child=1, Parent=-1`。
>>> hierarchy
array([[[ 7, -1, 1, -1],
[-1, -1, 2, 0],
[-1, -1, 3, 1],
[-1, -1, 4, 2],
[-1, -1, 5, 3],
[ 6, -1, -1, 4],
[-1, 5, -1, 4],
[ 8, 0, -1, -1],
[-1, 7, -1, -1]]])

findContours(image, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);
[3, -1, 1, -1]
[2, -1, -1, 0]
[-1, 1, -1, 0]
[5, 0, 4, -1]
[-1, -1, -1, 3]
[6, 3, -1, -1]
[-1, 5, -1, -1]
#include
#include
using namespace std;
int main(int argc, char **argv)
{
cv::RNG rng(12345);
cout << "Usage: " << argv[0] << "\n";
cv::String img_dir = "/images/OpenCV/2findContours/hole-hierarchy.png";
cv::Mat image = cv::imread(img_dir, cv::IMREAD_GRAYSCALE);
vector<cv::Vec4i> hierarchy;
vector<vector<cv::Point> > contours;
findContours(image, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);
cv::cvtColor(image, image, cv::COLOR_GRAY2BGR);
std::cout << "Contours Size: " << contours.size() << std::endl;
for(size_t i=0; i<contours.size(); i++)
{
cv::Scalar clr = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
cv::drawContours(image, contours, i, clr, 3);
}
for(auto &v : hierarchy)
{
std::cout << v << std::endl;
}
std::cout << image.channels() << std::endl;
cv::imwrite("contours.png", image);
cv::imshow("Img", image);
cv::waitKey(0);
return 0;
}s
欢迎访问个人网络日志🌹🌹知行空间🌹🌹