0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

二值图像的欧拉数计算公式

OpenCV学堂 来源:OpenCV学堂 作者:OpenCV学堂 2022-06-30 11:08 次阅读

欧拉数定义

二值图像分析中欧拉数重要的拓扑特征之一,在图像分析与几何对象识别中有着十分重要的作用,二值图像的欧拉数计算公式表示如下:
E = N – H 其中
E表示计算得到欧拉数
N表示联通组件的数目
H表示在联通组件内部的洞的数目
下图是二值图像,白色背景,两个对象、分析计算得到欧拉数的例子:

01d926cc-f7bf-11ec-ba43-dac502259ad0.png

可以看到通过简单的欧拉数属性就可以对它们进行区分。左侧对象中有两个联通区域,所以N=2,没有洞孔区域,所以H=0, 计算得到欧拉数目为 2 – 0 = 。右侧是大写字母B,它只有一个联通区域所以N = 1, 内部有两个洞孔区域所以H = 2,最终计算得到欧拉数为 2 – 1 = -1。对于任意一个几何形状来说,如果我们要求得它的欧拉数,就首先要分析它的轮廓结构,然后根据轮廓层次结构计算得到N与H值。

欧拉数是图像几何识别中重要的属性,举例如下图中三个英文字母

01f57b74-f7bf-11ec-ba43-dac502259ad0.png 对字母A来说它的内部有一个黑色孔洞,所以它的H=1,其本身是一个联通组件所以N =1,最终计算得到欧拉数为 E = 1 -1 = 0,同样可以计算B与C它们的欧拉数分布为-1与1,可见通过欧拉数属性可以轻而易举的区分ABC三个英文字母。

二:轮廓层次信息获取

在OpenCV对二值图像进行轮廓分析输出的层次结构会保存在一个Vec4i的结构体中,这里有必要首先看一下轮廓发现API及其相关参数的解释:

voidcv::findContours(
InputOutputArrayimage,
OutputArrayOfArrayscontours,
OutputArrayhierarchy,
intmode,
intmethod,
Pointoffset=Point()
)
image参数表示输入的二值图像
contours表示所有的轮廓信息,每个轮廓是一系列的点集合
hierarchy表示对应的每个轮廓的层次信息,我们就是要用它实现对最大轮廓欧拉数的分析
mode表示寻找轮廓拓扑的方法,如果要寻找完整的层次信息,要选择参数RETR_TREE
method表示轮廓的编码方式,一般选择简单链式编码,参数CHAIN_APPROX_SIMPLE
offset表示是否有位移,一般默认是0

上面的参数中最重要的是hierarchy信息,它的输出是vector每个轮廓对应的Vec4i结构体里面四个值解释如下:

02138222-f7bf-11ec-ba43-dac502259ad0.png

上面的索引如果是负数就表示没有相关层次信息,如果是非负数就表示有相关的层次关系信息。此外轮廓发现函数对输入image图像的要求必须满足

  • 背景是黑色 ,0表示

  • 对象或者前景是白色,1表示

三:欧拉数计算方法

有了轮廓的层次信息与每个轮廓的信息之后,尝试遍历每个轮廓,首先通过调用findContours就可以获取二值图像的轮廓层次信息,然后遍历每个轮廓,进行层次遍历,获得每层子轮廓的总数,最终根据轮廓层级不同分为孔洞与连接轮廓的计数,二者想减得到每个独立外层轮廓的欧拉数。

二值化与轮廓发现的代码如下:

Matgray,binary;
cvtColor(src,gray,COLOR_BGR2GRAY);
threshold(gray,binary,0,255,THRESH_BINARY|THRESH_OTSU);
vectorhireachy;
vector<vector>contours;
findContours(binary,contours,hireachy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point());

获取同层轮廓的代码如下:

vector<int>current_layer_holes(vector<Vec4i>layers,intindex){
intnext=layers[index][0];
vector<int>indexes;
indexes.push_back(index);
while(next>=0){
indexes.push_back(next);
next=layers[next][0];
}
returnindexes;
}

使用队列迭代寻找遍历每层的代码如下:

while(!nodes.empty()){
//当前层总数目
if(index%2==0){//联通组件对象
n_total+=nodes.size();
}
else{//孔洞对象
h_total+=nodes.size();
}
index++;
//计算下一层所有孩子节点
intcurr_ndoes=nodes.size();
for(intn=0;n< curr_ndoes; n++) {
        intvalue=nodes.front();
nodes.pop();
//获取下一层节点第一个孩子
intchild=hireachy[value][2];
if(child>=0){
nodes.push(child);
}
}
}

四:运行与测试结果

测试图一(ABC)与运行结果:

022be9de-f7bf-11ec-ba43-dac502259ad0.jpg

测试图二与运行结果

024b4b1c-f7bf-11ec-ba43-dac502259ad0.jpg

五:完整源代码

#include
#include

usingnamespacecv;
usingnamespacestd;

vector<int>current_layer_holes(vectorlayers,intindex);

intmain(intargc,char**argv){
Matsrc=imread("D:/holes.png");
if(src.empty()){
printf("couldnotloadimage...
");
return-1;
}
namedWindow("input",CV_WINDOW_AUTOSIZE);
imshow("input",src);

Matgray,binary;
cvtColor(src,gray,COLOR_BGR2GRAY);
threshold(gray,binary,0,255,THRESH_BINARY|THRESH_OTSU);

vectorhireachy;
vector<vector>contours;
findContours(binary,contours,hireachy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point());
Matresult=Mat::zeros(src.size(),src.type());
for(size_tt=0;t< contours.size(); t++) {
        intnext=hireachy[t][0];//nextatthesamehierarchicallevel
intprev=hireachy[t][1];//prevatthesamehierarchicallevel
intchild=hireachy[t][2];//firstchild
intparent=hireachy[t][3];//parent
printf("next%d,previous%d,children:%d,parent:%d
",next,prev,child,parent);
drawContours(result,contours,t,Scalar(0,255,0),2,8);
//startcalculateeulernumber
inth_total=0;
intn_total=1;
intindex=1;
vector<int>all_children;
if(child>=0&&parent< 0){
//计算当前层
queue<int>nodes;
vector<int>indexes=current_layer_holes(hireachy,child);
for(inti=0;i< indexes.size(); i++) {
                nodes.push(indexes[i]);
            }
            while(!nodes.empty()){
//当前层总数目
if(index%2==0){//联通组件对象
n_total+=nodes.size();
}
else{//孔洞对象
h_total+=nodes.size();
}
index++;
//计算下一层所有孩子节点
intcurr_ndoes=nodes.size();
for(intn=0;n< curr_ndoes; n++) {
                    intvalue=nodes.front();
nodes.pop();
//获取下一层节点第一个孩子
intchild=hireachy[value][2];
if(child>=0){
nodes.push(child);
}
}
}
printf("holenumber:%d
",h_total);
printf("connectionnumber:%d
",n_total);
//计算欧拉数
inteuler_num=n_total-h_total;
printf("numberofeuler:%d
",euler_num);
drawContours(result,contours,t,Scalar(0,0,255),2,8);
//显示欧拉数
Rectrect=boundingRect(contours[t]);
putText(result,format("euler:%d",euler_num),rect.tl(),FONT_HERSHEY_SIMPLEX,1.0,Scalar(255,255,0),2,8);
}
if(child< 0&&parent< 0){
printf("holenumber:%d
",h_total);
printf("connectionnumber:%d
",n_total);
inteuler_num=n_total-h_total;
printf("numberofeuler:%d
",euler_num);
drawContours(result,contours,t,Scalar(255,0,0),2,8);
Rectrect=boundingRect(contours[t]);
putText(result,format("euler:%d",euler_num),rect.tl(),FONT_HERSHEY_SIMPLEX,1.0,Scalar(255,255,0),2,8);
}

}

imshow("result",result);
waitKey(0);
return0;
}

vector<int>current_layer_holes(vectorlayers,intindex){
intnext=layers[index][0];
vector<int>indexes;
indexes.push_back(index);
while(next>=0){
indexes.push_back(next);
next=layers[next][0];
}
returnindexes;
}

PS:代码未经更多严格测试,仅供参考!

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 二值图像
    +关注

    关注

    0

    文章

    14

    浏览量

    8685
  • OpenCV
    +关注

    关注

    29

    文章

    611

    浏览量

    40782
  • 欧拉
    +关注

    关注

    1

    文章

    12

    浏览量

    1794

原文标题:OpenCV轮廓层次分析实现欧拉数计算

文章出处:【微信号:CVSCHOOL,微信公众号:OpenCV学堂】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    电能的计算公式 电能的计算公式中时间的单位

    电能是电力消耗的度量,它表示单位时间内消耗的电功率。电能的计算公式为: 电能(E)= 电功率(P)× 时间(t) 其中,电功率可以通过以下公式计算: 电功率(P)= 电压(U)× 电流(I) 通过
    的头像 发表于 02-22 10:00 1908次阅读

    功率的计算公式w怎么求

    功率的计算公式是根据物理学中的定义和公式推导而来的,下面将介绍功率的概念、单位以及计算公式。 一、功率的基本概念 在物理学中,功率是描述物体进行功的速率。它表示单位时间内所做的功。功率越高,表示单位
    的头像 发表于 01-17 11:12 1269次阅读

    永磁电机计算公式是什么

    永磁电机是一种利用永磁体产生的恒定磁场来产生电磁力的电机。其结构简单、体积小、效率高,因此在很多领域中得到了广泛应用。本文将详细介绍永磁电机的计算公式。 永磁电机的计算公式主要涉及到其电磁转矩
    的头像 发表于 01-11 10:38 1469次阅读

    电机扭矩的计算公式和转速计算公式

    在电机学中,电机转速和扭矩是非常重要的参数,在实际应用中,电机转速和扭矩的计算公式也使用得非常频繁,本文详细介绍扭矩的计算公式和转速计算公式
    发表于 12-25 09:41 1391次阅读

    LTC7545具体的增益计算公式是什么?

    LTC7545按照下面的电路连接,这个是非官方标准的接法,想问下具体的增益计算公式是什么,VOUT和VIN以及数字code之间的关系。
    发表于 12-04 07:27

    在AD7134数据手册将动态范围的计算公式时,有效噪声的定义是什么?

    在AD7134数据手册将动态范围的计算公式时,里边提到的有效噪声是将模拟输入端短路后,ADC测到的噪声的峰峰值除以根号2吗?
    发表于 11-30 07:50

    rc充放电时间计算公式

    电子发烧友网站提供《rc充放电时间计算公式.zip》资料免费下载
    发表于 11-20 14:27 2次下载
    rc充放电时间<b class='flag-5'>计算公式</b>

    电气计算公式和实例说明

    掌握实用的计算公式是电气工作者应具备的能力,但公式繁多应用时查找不方便,下面将整理和收集的一些常用的实用公式和口诀整理出来,并用实例说明和解释。
    的头像 发表于 11-17 14:46 482次阅读

    CAN波特率计算公式详解

    CAN波特率计算公式详解
    发表于 10-20 07:57

    无刷电机常用计算公式分享

    无刷电机常用计算公式
    发表于 09-28 08:14

    共模差模输入计算公式详解

    共模差模输入计算公式详解  共模差模输入是电路中常见的一个概念,常用于放大运算器及其他电路中。本文将详细介绍共模差模输入的概念、计算公式及其应用。 一、共模和差模信号 在电路中,信号可以分为
    的头像 发表于 09-19 17:23 4860次阅读

    差分放大器增益计算公式

    差分放大器增益计算公式 差分放大器的增益计算公式是用来计算差分放大电路输出电压与输入电压之间的比例关系的。这个公式在差分放大器电路的设计和优化中起着至关重要的作用,因为它可以帮助工程师
    的头像 发表于 09-04 17:18 2208次阅读

    卷积神经网络计算公式

    积神经网络计算公式 神经网络是一种类似于人脑的神经系统的计算模型,它是一种可以用来进行模式识别、分类、预测等任务的强大工具。在深度学习领域,深度神经网络已成为最为重要的算法之一。在本文中,我们将重点
    的头像 发表于 08-21 16:49 1209次阅读

    标准工时计算公式是什么?标准工时是如何计算公式的?

    标准工时对企业的生产运作起到至关重要的作用,现在的企业都在寻求计算一个合理的标准工时的办法,本篇就跟大家介绍一下标准工时的计算公式是什么样子的?到底如何计算标准工时?并为大家介绍一款适合企业进行精益
    发表于 08-03 13:57

    PCB翘曲原因及解决办法 PCB翘曲度的计算公式

    今天给大家分享的是 PCB 翘曲原因及解决办法、PCB 翘曲度的计算公式
    发表于 07-02 10:10 4294次阅读
    PCB翘曲原因及解决办法 PCB翘曲度的<b class='flag-5'>计算公式</b>