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

    浏览量

    8717
  • OpenCV
    +关注

    关注

    29

    文章

    626

    浏览量

    41247
  • 欧拉
    +关注

    关注

    1

    文章

    13

    浏览量

    1816

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

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

收藏 人收藏

    评论

    相关推荐

    云端弹性计算公式有哪些内容?

    云端弹性计算公式有哪些内容?云端弹性计算公式涵盖资源分配、性能监控、自动伸缩及积分计算等方面。资源分配依据虚拟机配置和实际需求动态调整;性能监控实时监控CPU、内存等关键指标;自动伸缩根据预设阈值
    的头像 发表于 11-15 11:04 79次阅读

    平衡流量计计算公式

    流量计计算公式的重要性及应用你了解吗? 一、管道流速公式 这是平衡流量计中最基本的计算公式之一,它基于流体的质量守恒定律。通过测量管道中的压力差和密度,可以计算出流体的流速。
    的头像 发表于 10-25 14:14 107次阅读
    平衡流量计<b class='flag-5'>计算公式</b>

    VCA820的增益计算公式是什么?

    我看了手册,貌似VCA820没有给出增益计算公式,请解答!!谢谢
    发表于 09-26 06:43

    工业镜头焦距计算公式

    掌握工业镜头焦距计算公式是确保工业视觉系统高效、准确运行的重要一步。
    的头像 发表于 09-25 16:36 502次阅读
    工业镜头焦距<b class='flag-5'>计算公式</b>

    请问VCA820的增益计算公式是什么?

    VCA820的增益计算公式是什么?就是输入与增益之间的计算公式。官网资料里面好像没有介绍,不知道是不是我看的不够仔细
    发表于 09-06 08:04

    示波器周期的计算公式

    示波器作为电子测量领域的重要工具,其能够直观地显示电压信号随时间变化的波形,对于电路分析、信号检测等领域具有不可替代的作用。在示波器的使用中,我们经常需要计算信号的周期,以了解信号的特性。本文将详细介绍示波器周期的计算公式及其应用,并通过实例进行说明,以期为读者提供更为深
    的头像 发表于 05-17 17:25 2034次阅读

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

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

    功率的计算公式w怎么求

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

    电流计算公式简单方法

    电流计算公式是描述电流与电压和电阻之间的关系的数学公式。根据欧姆定律,电流通过一个导体时与电压成正比,与电阻成反比。这意味着,当电压增加或电阻减小时,电流也会增加。 电流的计算公式是I = V/R
    的头像 发表于 01-16 14:28 8335次阅读

    单相电机功率计算公式

    单相电机是一种常见的电动机类型,广泛应用于家用电器、小型机械设备和办公设备等领域。在实际应用中,正确计算单相电机的功率是非常重要的。本文将详细介绍单相电机功率的计算公式,并对其背后的原理和相关概念
    的头像 发表于 01-16 10:16 3624次阅读

    永磁电机计算公式是什么

    、磁场强度、磁链和电流之间的关系。下面将分别介绍这些公式。 电磁转矩(Te)的计算公式 电磁转矩是永磁电机产生的机械转矩,计算公式为: Te = Kc * B * Ia * sin(θm - θa) 其中,Kc是定子槽
    的头像 发表于 01-11 10:38 5729次阅读

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

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

    阶巴特沃斯滤波器电路截止频率计算公式

    阶巴特沃斯滤波器是一种常用于电子电路中的滤波器,它能够通过改变截止频率来实现对信号的滤波功能。在本文中,我们将详细介绍阶巴特沃斯滤波器的截止频率的计算公式。 截止频率的计算公式
    的头像 发表于 12-15 11:23 8667次阅读

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

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

    if函数如何嵌套计算公式

    嵌套if函数可以在Excel等电子表格软件中实现复杂的条件判断和计算公式。本文将详细介绍if函数的嵌套使用方法,包括语法、常见应用场景和实例演示等,以及注意事项和进阶技巧。 一、if函数的基本语法
    的头像 发表于 11-30 16:55 6475次阅读