C++ 非单连通二值图像的周长

C++ 非单连通二值图像的周长,c++,image-processing,geometry,C++,Image Processing,Geometry,我试图在二值图像中计算一个区域的周长。当一个区域是简单连接的,即它没有洞,一切都很简单:我只是检查每个像素是否属于该区域,并且至少有一个不属于该区域的邻居。。。我有一个变量,用于计算满足此条件的像素数 对于有孔的区域,我使用不同的方法。我从边界中的一个像素开始,跳到一个相邻的像素,如果它本身是边界像素,则增加一个计数器。这个过程还有一些奇怪之处,当我回到初始像素时就结束了。大概是这样的: int iPosCol = iStartCol, int iPosRow = iStartRow; do

我试图在二值图像中计算一个区域的周长。当一个区域是简单连接的,即它没有洞,一切都很简单:我只是检查每个像素是否属于该区域,并且至少有一个不属于该区域的邻居。。。我有一个变量,用于计算满足此条件的像素数

对于有孔的区域,我使用不同的方法。我从边界中的一个像素开始,跳到一个相邻的像素,如果它本身是边界像素,则增加一个计数器。这个过程还有一些奇怪之处,当我回到初始像素时就结束了。大概是这样的:

int iPosCol = iStartCol, int iPosRow = iStartRow;
do 
{
  //check neighbors, pick point on the perimeter
  //condition: value == label, pixel at the border.
  check8Neighbors(iPosCol, iPosRow);
  updatePixPosition(iPosCol, iPosRow);
}
while ( iPosC != iStartC || iPosR != iStartR );
问题是,如果该区域中的孔接近边界1像素距离,则该方法将不起作用


是否有计算非单连通区域周长的标准方法,或者我是以错误的方式处理问题吗?

也许最简单的方法是运行一个连接组件算法,然后填充漏洞。

也许最简单的方法是运行一个连接组件算法,然后填充漏洞。

所以我的建议是: 为了简单起见,我们假设您希望找到黑色区域的边界。 首先在图像的所有侧面添加一个额外的白色列和一个额外的白色行。这样做是为了简化角落案例,我将尝试解释它的帮助

接下来,从您所在区域的任何像素进行广度优先搜索。图形中的边定义为以黑色连接相邻单元。通过执行此BFS,您将找到区域中的所有像素。现在选择最底部,你可以线性地找到它,如果有许多最底部,只需选择其中的任何一个。选择它下面的像素-这个像素肯定是白色的,因为:我们选择了我们区域中最底部的像素,如果像素是黑色的,BFS会访问它。此外,由于我们添加了额外的行和列,在最底部的像素下方还有一个像素

现在做另一个BFS这一次通过白色的nighbouring像素。事实上,我们添加了额外的行和列有助于这里。通过这种方式,我们可以在我们感兴趣的黑色区域周围找到一个白色区域。现在,与新发现的白色区域中的任何像素相邻的原始黑色区域中的所有像素都是边界的一部分,并且只有它们是边界的一部分。你数一数这些像素,你就得到了周长

由于我们不想将孔的边界计算为周长的一部分,因此解决方案变得复杂。如果不存在这种情况,我们可以只计算初始黑色区域中与任何白色像素或图像边界相邻的所有像素。在这里,我们不需要添加行和列


希望这个答案有帮助。

所以我的建议是: 为了简单起见,我们假设您希望找到黑色区域的边界。 首先在图像的所有侧面添加一个额外的白色列和一个额外的白色行。这样做是为了简化角落案例,我将尝试解释它的帮助

接下来,从您所在区域的任何像素进行广度优先搜索。图形中的边定义为以黑色连接相邻单元。通过执行此BFS,您将找到区域中的所有像素。现在选择最底部,你可以线性地找到它,如果有许多最底部,只需选择其中的任何一个。选择它下面的像素-这个像素肯定是白色的,因为:我们选择了我们区域中最底部的像素,如果像素是黑色的,BFS会访问它。此外,由于我们添加了额外的行和列,在最底部的像素下方还有一个像素

现在做另一个BFS这一次通过白色的nighbouring像素。事实上,我们添加了额外的行和列有助于这里。通过这种方式,我们可以在我们感兴趣的黑色区域周围找到一个白色区域。现在,与新发现的白色区域中的任何像素相邻的原始黑色区域中的所有像素都是边界的一部分,并且只有它们是边界的一部分。你数一数这些像素,你就得到了周长

由于我们不想将孔的边界计算为周长的一部分,因此解决方案变得复杂。如果不存在这种情况,我们可以只计算初始黑色区域中与任何白色像素或图像边界相邻的所有像素。在这里,我们不需要添加行和列


希望这个答案有帮助。

正如JCooper所指出的,连接组件a.k.a.区域标记a.k.a.轮廓检测是一种算法,用于查找连接像素的区域,通常在已二值化的图像中,以便所有像素都是黑色或白色

连接组件标签的Wikipedia条目包括单次传递算法的伪代码

另一种罪恶 本文提出了一种基于轮廓跟踪技术的零件标注算法。本文还介绍了一种边缘跟踪算法,如果您愿意,可以使用该算法仅查找轮廓。

那篇论文对边缘跟踪的描述很好,但我将在这里描述其基本思想

假设图形的外轮廓由像素a到f表示,背景像素由-:

- - - - -
- a b - -
- - - c -
- - - d -
- f e - -
- - - - -
如果我们从上到下扫描图像,沿着每行从左到右扫描,那么遇到的第一个像素是像素a。为了从像素a移动到像素b,然后从b移动到c,我们使用相对于当前像素p定义的8个方向跟踪每个移动的方向:

6 7 8
5 p 1
4 3 2
从背景到像素a的移动沿方向1。虽然我们知道b在哪里,但软件不知道,所以我们顺时针检查a的所有方向,以找到轮廓上的下一个像素。我们不需要检查方向5向左,因为我们只是从背景像素到a的左边。我们所做的是顺时针检查方向6、7、8、1、2等,寻找下一个轮廓像素。在相对于a的方向6、7和8上只找到背景像素之后,我们也沿着方向1找到b

如果我们看一下从c到d的转变,我们朝着方向3前进。为了找到下一个轮廓像素e,我们检查方向8、1、2、3、4,然后沿方向4移动找到轮廓像素e

一般的规则是,如果我们的最后一步是在方向d,我们检查下一步的第一个方向是方向d-3。如果最后一次移动是在方向5向左移动,那么我们将在方向2开始下一次顺时针搜索

在代码中,我们通常使用方向0-7,很明显,您将使用模运算或类似的数学,但我希望这个想法是明确的。Chang和Chen的论文合理地描述了基本轮廓跟踪算法,并提到了算法是否需要回溯某些像素的必要检查

边缘跟踪算法可能足以满足您的需要,但出于各种原因,您可能也想找到像素的连接区域

对于连接组件算法,要记住的一件事是要考虑像素的邻居。您可以只查看4个邻居:

-  n  -
n  p  n
-  n  -
其中p是中心像素,n标记四个邻居,并且-标记不被视为邻居的像素。你也可以考虑8个邻居,这只是一个给定像素周围的所有像素:

n  n  n
n  p  n
n  n  n
通常,在检查前景对象的连接时,4个邻居是更好的选择。如果选择8邻技术,则可以将以下棋盘格图案视为单个对象:

p - p - p - p - p
- p - p - p - p - 
p - p - p - p - p
- p - p - p - p - 
p - p - p - p - p
- p - p - p - p - 
假设您有一个类似于下面的blob,前景像素标记为p,背景像素标记为-:

- - - - - - - - - -
- - - - p p p - - -
- - p p p - p p - -
- - - p p - p p - -
- - - - p p p - - -
- p p p p p p - - -
- - - - - - - - - -

如果你只考虑外轮廓的像素,你会发现计算周长有点棘手。对于下面的像素1、2、3、4和5,可以使用像素1-5计算周长,从像素1到2,然后从像素2到3逐步移动,以此类推。通常,最好仅使用对角线上的像素1、3和5来计算此段的周长。对于底部的一行像素,必须注意算法不会对这些像素计数两次

- - - - - - - - - -
- - - - p p p - - -
- - 1 2 p - p p - -
- - - 3 4 - p p - -
- - - - 5 p p - - -
- p p p p p p - - -
- - - - - - - - - -
对于相对较大的连接区域(没有突出的半岛,只有一个像素宽),计算周长相对简单。对于非常小的对象,很难计算真实周长,部分原因是我们有有限数量的离散方形像素,这些像素代表一个真实世界的对象,其轮廓可能是平滑的,略微弯曲。对象的图像表示为块状

如果有从边缘跟踪算法中找到的有序像素列表,则可以通过检查轮廓像素列表中两个连续像素的X和Y变化来计算周长。通过计算沿轮廓像素的像素到像素距离之和来计算周长

对于像素N和像素N+1:如果X相同或Y相同,则从N到N+1的方向为左、右、上或下,距离为1

如果像素N和N+1的X和Y都不同,则从一个像素到下一个像素的移动方向与水平方向成45度角,并且像素中心之间的距离是2的平方根

无论你创建什么算法,都要考虑它对简单图形的精度:正方形、矩形、圆圈等。圆圈特别有助于检查周界计算,因为圆的轮廓,尤其是图像中的小圆圈会有锯齿状而不是平滑的边缘。

- - - - - - - - - -
- - - p p p p - - -
- - p p p p p p - -
- - p p p p p p - -
- - p p p p p p - -
- - - p p p p - - -
- - - - - - - - - -
有一些技术可以在灰度和灰度中找到形状并计算周长 彩色图像不依赖二值化使图像仅为黑白,但这些技术更为复杂。对于许多应用,简单的标准技术将起作用:

选择阈值以对图像进行二值化。 在二值化图像上运行连接的组件/区域标记算法 使用连接区域的轮廓像素计算周长 许多大学使用的图像处理教科书可以解答您关于图像处理的许多问题。如果你打算钻研图像处理,你应该至少有一本像这样的教科书在手边;这将节省你在网上寻找答案的时间

Gonzalez和Woods的数字图像处理第三版

图书网站:

你应该可以在网上找到一本35美元左右的国际版

如果您最终编写了大量代码来执行几何计算,另一本方便的参考书是Schneider和Eberly的《计算机图形几何工具》

这是昂贵的,但你可以找到使用过的副本便宜,有时在多站点搜索引擎,如

本书中的更正、理论PDF和代码可在此处找到:

正如JCooper所指出的,连接组件a.k.a.区域标记a.k.a.轮廓检测是一种查找连接像素区域的算法,通常在已二值化的图像中,以便所有像素都是黑色或白色

连接组件标签的Wikipedia条目包括单次传递算法的伪代码

另一种单程算法是Chang和Chen提出的基于轮廓跟踪技术的元件标记算法。本文还介绍了一种边缘跟踪算法,如果您愿意,可以使用该算法仅查找轮廓。

那篇论文对边缘跟踪的描述很好,但我将在这里描述其基本思想

假设图形的外轮廓由像素a到f表示,背景像素由-:

- - - - -
- a b - -
- - - c -
- - - d -
- f e - -
- - - - -
如果我们从上到下扫描图像,沿着每行从左到右扫描,那么遇到的第一个像素是像素a。为了从像素a移动到像素b,然后从b移动到c,我们使用相对于当前像素p定义的8个方向跟踪每个移动的方向:

6 7 8
5 p 1
4 3 2
从背景到像素a的移动沿方向1。虽然我们知道b在哪里,但软件不知道,所以我们顺时针检查a的所有方向,以找到轮廓上的下一个像素。我们不需要检查方向5向左,因为我们只是从背景像素到a的左边。我们所做的是顺时针检查方向6、7、8、1、2等,寻找下一个轮廓像素。在相对于a的方向6、7和8上只找到背景像素之后,我们也沿着方向1找到b

如果我们看一下从c到d的转变,我们朝着方向3前进。为了找到下一个轮廓像素e,我们检查方向8、1、2、3、4,然后沿方向4移动找到轮廓像素e

一般的规则是,如果我们的最后一步是在方向d,我们检查下一步的第一个方向是方向d-3。如果最后一次移动是在方向5向左移动,那么我们将在方向2开始下一次顺时针搜索

在代码中,我们通常使用方向0-7,很明显,您将使用模运算或类似的数学,但我希望这个想法是明确的。Chang和Chen的论文合理地描述了基本轮廓跟踪算法,并提到了算法是否需要回溯某些像素的必要检查

边缘跟踪算法可能足以满足您的需要,但出于各种原因,您可能也想找到像素的连接区域

对于连接组件算法,要记住的一件事是要考虑像素的邻居。您可以只查看4个邻居:

-  n  -
n  p  n
-  n  -
其中p是中心像素,n标记四个邻居,并且-标记不被视为邻居的像素。你也可以考虑8个邻居,这只是一个给定像素周围的所有像素:

n  n  n
n  p  n
n  n  n
通常,在检查前景对象的连接时,4个邻居是更好的选择。如果选择8邻技术,则可以将以下棋盘格图案视为单个对象:

p - p - p - p - p
- p - p - p - p - 
p - p - p - p - p
- p - p - p - p - 
p - p - p - p - p
- p - p - p - p - 
假设您有一个类似于下面的blob,前景像素标记为p,背景像素标记为-:

- - - - - - - - - -
- - - - p p p - - -
- - p p p - p p - -
- - - p p - p p - -
- - - - p p p - - -
- p p p p p p - - -
- - - - - - - - - -

如果你只考虑外轮廓的像素,你会发现计算周长有点棘手。对于下面的像素1、2、3、4和5,可以使用像素1-5计算周长,从像素1到2,然后从像素2到3逐步移动,以此类推。通常,最好仅使用对角线上的像素1、3和5来计算此段的周长。对于底部的一行像素,您必须注意算法不会将这些像素两次计数 行政长官

对于相对较大的连接区域(没有突出的半岛,只有一个像素宽),计算周长相对简单。对于非常小的对象,很难计算真实周长,部分原因是我们有有限数量的离散方形像素,这些像素代表一个真实世界的对象,其轮廓可能是平滑的,略微弯曲。对象的图像表示为块状

如果有从边缘跟踪算法中找到的有序像素列表,则可以通过检查轮廓像素列表中两个连续像素的X和Y变化来计算周长。通过计算沿轮廓像素的像素到像素距离之和来计算周长

对于像素N和像素N+1:如果X相同或Y相同,则从N到N+1的方向为左、右、上或下,距离为1

如果像素N和N+1的X和Y都不同,则从一个像素到下一个像素的移动方向与水平方向成45度角,并且像素中心之间的距离是2的平方根

无论你创建什么算法,都要考虑它对简单图形的精度:正方形、矩形、圆圈等。圆圈特别有助于检查周界计算,因为圆的轮廓,尤其是图像中的小圆圈会有锯齿状而不是平滑的边缘。

- - - - - - - - - -
- - - p p p p - - -
- - p p p p p p - -
- - p p p p p p - -
- - p p p p p p - -
- - - p p p p - - -
- - - - - - - - - -
有一些技术可以在灰度和彩色图像中找到形状并计算周长,这些技术不依赖于二值化使图像变成黑白,但这些技术比较复杂。对于许多应用,简单的标准技术将起作用:

选择阈值以对图像进行二值化。 在二值化图像上运行连接的组件/区域标记算法 使用连接区域的轮廓像素计算周长 许多大学使用的图像处理教科书可以解答您关于图像处理的许多问题。如果你打算钻研图像处理,你应该至少有一本像这样的教科书在手边;这将节省你在网上寻找答案的时间

Gonzalez和Woods的数字图像处理第三版

图书网站:

你应该可以在网上找到一本35美元左右的国际版

如果您最终编写了大量代码来执行几何计算,另一本方便的参考书是Schneider和Eberly的《计算机图形几何工具》

这是昂贵的,但你可以找到使用过的副本便宜,有时在多站点搜索引擎,如

本书中的更正、理论PDF和代码可在此处找到:

这里的周长是什么意思?是区域中相同颜色像素的数量吗?或者是由区域边界限定的区域?图像是二值的,所以是白色区域和黑色背景。周长定义为一个区域边界上的像素数。好的,那么最后一个问题-如何定义一个区域的边界特别是一个有孔的区域没有孔:所有属于该区域的点,至少有一个相邻像素不属于该区域有孔的区域:这里是困难的地方,因为我只能凭直觉区分边界/非边界像素,但找不到合适的数学定义……这里的周长是什么意思?是区域中相同颜色像素的数量吗?或者是由区域边界限定的区域?图像是二值的,所以是白色区域和黑色背景。周长定义为一个区域边界上的像素数。好的,那么最后一个问题-如何定义一个区域的边界特别是一个有孔的区域没有孔:所有属于该区域的点,至少有一个相邻像素不属于该区域有孔的区域:这里是困难的地方,因为我只能凭直觉区分边界/非边界像素,但找不到合适的数学定义……你能更详细地解释一下你的意思吗?我看不出在这种情况下,我怎么能只得到边界像素,你能更详细地解释一下你的意思吗?istrandjev说,我不明白如何才能在这种情况下只获得边界像素。谢谢你的详细评论。我现在正在尝试,看看它是否能正常工作。谢谢你的详细评论,istrandjev。我现在就在试,看看效果如何。