绘制任何透明度图像周围轮廓或描边的算法

9

我正在开发一种算法,不确定是否有任何名称 - "生长邻域算法"听起来是一个适当的名称。那么我的问题是什么呢?

我想在alpha透明图像周围绘制一个描边。描边的大小应该由用户定义。

我有一个由零和一填充的数组,将数组的每个项视为Game of Life中的单元格。值为0的项为空(透明像素),值为1的项是第一代细胞(非透明像素),周围描边的大小由世代数量定义。

这个例子描述了一个被透明值包围的矩形:

0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0
0 0 0 1 1 1 1 0 0 0
0 0 0 1 1 1 1 0 0 0
0 0 0 1 1 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0

然后我希望通过将每个0代Moore邻居包围来让它们生长出新一代。这是第二代(线条为1像素)- 因此在生长后,数组如下所示:

0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 2 2 2 2 2 2 0 0
0 0 2 1 1 1 1 2 0 0
0 0 2 1 1 1 1 2 0 0
0 0 2 1 1 1 1 2 0 0
0 0 2 1 1 1 1 2 0 0
0 0 2 2 2 2 2 2 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0

第三代和第四代(带有3像素的笔画):

4 4 4 4 4 4 4 4 4 4
4 3 3 3 3 3 3 3 3 4
4 3 2 2 2 2 2 2 3 4
4 3 2 1 1 1 1 2 3 4
4 3 2 1 1 1 1 2 3 4
4 3 2 1 1 1 1 2 3 4
4 3 2 1 1 1 1 2 3 4
4 3 2 2 2 2 2 2 3 4
4 3 3 3 3 3 3 3 3 4
4 4 4 4 4 4 4 4 4 4

到目前为止还不错。我通过以下代码片段完成了这个简单的任务:

for (int gen = 1; gen <= 4; gen++)
{
    for (int x = 1; x < arrayWidth - 1; x++)
    {
        for (int y = 1; y < arrayHeight - 1; y++)
        {
            // See if this cell is in the current generation.
            if (_generation[x + arrayWidth * y] == gen)
            {
                // Generate next generation.
                for (int i = x - 1; i <= x + 1; i++)
                {
                    for (int j = y - 1; j <= y + 1; j++)
                    {
                        if (_generation[i + arrayWidth * j] == 0 || _generation[i + arrayWidth * j] > gen)
                        {
                            _generation[i + arrayWidth * j] = gen + 1;
                        }
                    }
                }
            }
        }
    }
}

这种方法对于简单形状(例如矩形)非常有效。但是如何应用到椭圆形呢?一旦在单元格中出现了类似楼梯的图案,结果就会变得混乱:
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 0
0 0 0 0 1 1 1 1 1 1 0 0 0 0
0 0 0 1 1 1 1 1 1 1 1 0 0 0
0 0 1 1 1 1 1 1 1 1 1 1 0 0
0 0 1 1 1 1 1 1 1 1 1 1 0 0
0 0 1 1 1 1 1 1 1 1 1 1 0 0
0 0 1 1 1 1 1 1 1 1 1 1 0 0
0 0 1 1 1 1 1 1 1 1 1 1 0 0
0 0 0 1 1 1 1 1 1 1 1 0 0 0
0 0 0 0 1 1 1 1 1 1 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 2 2 2 2 2 2 0 0 0 0
0 0 0 2 2 1 1 1 1 2 2 0 0 0
0 0 2 2 1 1 1 1 1 1 2 2 0 0
0 2 2 1 1 1 1 1 1 1 1 2 2 0
0 2 1 1 1 1 1 1 1 1 1 1 2 0
0 2 1 1 1 1 1 1 1 1 1 1 2 0
0 2 1 1 1 1 1 1 1 1 1 1 2 0
0 2 1 1 1 1 1 1 1 1 1 1 2 0
0 2 1 1 1 1 1 1 1 1 1 1 2 0
0 2 2 1 1 1 1 1 1 1 1 2 0 0
0 0 2 2 1 1 1 1 1 1 2 2 0 0
0 0 0 2 2 1 1 1 1 2 2 0 0 0
0 0 0 0 2 2 2 2 2 2 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0

0 0 0 3 3 3 3 3 3 3 3 0 0 0
0 0 3 3 2 2 2 2 2 2 3 3 0 0
0 3 3 2 2 1 1 1 1 2 2 3 3 0
3 3 2 2 1 1 1 1 1 1 2 2 3 3
3 2 2 1 1 1 1 1 1 1 1 2 2 3
3 2 1 1 1 1 1 1 1 1 1 1 2 3
3 2 1 1 1 1 1 1 1 1 1 1 2 3
3 2 1 1 1 1 1 1 1 1 1 1 2 3
3 2 1 1 1 1 1 1 1 1 1 1 2 3
3 2 1 1 1 1 1 1 1 1 1 1 2 3
3 2 2 1 1 1 1 1 1 1 1 2 2 3
3 3 2 2 1 1 1 1 1 1 2 2 3 3
0 3 3 2 2 1 1 1 1 2 2 3 3 0
0 0 3 3 2 2 2 2 2 2 3 3 0 0
0 0 0 3 3 3 3 3 3 3 3 0 0 0

将此算法应用于椭圆时,由于存在问题,轮廓看起来有点奇怪(左:算法结果,右:请求的结果):
![算法结果(左),请求的结果(右)](https://istack.dev59.com/UpItV.webp)
这里的问题是,我不想要那些每次出现这种“楼梯”图案时都会出现的2 2和3 3重复块:
1 0 0 0 0 0 0 1
0 1 0 0 0 0 1 0
0 0 1 0 0 1 0 0
0 0 0 1 1 0 0 0

我希望第二代和第三代的计算结果看起来像这样:
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 2 2 2 2 0 0 0 0 0
0 0 0 0 2 1 1 1 1 2 0 0 0 0
0 0 0 2 1 1 1 1 1 1 2 0 0 0
0 0 2 1 1 1 1 1 1 1 1 2 0 0
0 2 1 1 1 1 1 1 1 1 1 1 2 0
0 2 1 1 1 1 1 1 1 1 1 1 2 0
0 2 1 1 1 1 1 1 1 1 1 1 2 0
0 2 1 1 1 1 1 1 1 1 1 1 2 0
0 2 1 1 1 1 1 1 1 1 1 1 2 0
0 0 2 1 1 1 1 1 1 1 1 2 0 0
0 0 0 2 1 1 1 1 1 1 2 0 0 0
0 0 0 0 2 1 1 1 1 2 0 0 0 0
0 0 0 0 0 2 2 2 2 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 3 3 3 3 0 0 0 0 0
0 0 0 0 3 2 2 2 2 2 3 0 0 0
0 0 0 3 2 1 1 1 1 2 3 0 0 0
0 0 3 2 1 1 1 1 1 1 2 3 0 0
0 3 2 1 1 1 1 1 1 1 1 2 3 0
3 2 1 1 1 1 1 1 1 1 1 1 2 3
3 2 1 1 1 1 1 1 1 1 1 1 2 3
3 2 1 1 1 1 1 1 1 1 1 1 2 3
3 2 1 1 1 1 1 1 1 1 1 1 2 3
3 2 1 1 1 1 1 1 1 1 1 1 2 3
0 3 2 1 1 1 1 1 1 1 1 2 3 0
0 0 3 2 1 1 1 1 1 1 2 3 0 0
0 0 0 3 2 1 1 1 1 2 3 0 0 0
0 0 0 3 2 2 2 2 2 2 3 0 0 0
0 0 0 0 3 3 3 3 3 3 0 0 0 0

我尝试了许多方法来过滤那些重复的单元格块,但是我找不到一个简单通用的解决方案来解决这个问题。

有没有什么想法可以获得类似于Photoshop或Paint.NET中获得的描边/轮廓?

谢谢!

祝好


2
我认为你可以通过使用适当的“结构元素”进行“形态膨胀”来取得进展。我没有时间详细解释,但在等待答案的同时,可以开始阅读http://en.wikipedia.org/wiki/Mathematical_morphology。 - High Performance Mark
2
你的问题应该成为每个人的榜样 - “如何提出好问题”。 - Andrey Rubshtein
1个回答

7
适当的名称是“膨胀”,请查看形态学操作。您应该尝试使用圆元素进行膨胀,这将为您提供所需的结果。
以下是演示如何完成此操作的Matlab代码:
im = imcircle(70);
im = padarray(im,[20,20]);
figure;imshow(im);
im2 = imdilate(im,strel('disk',8));
figure;imshow(im2);

enter image description here


2
这正是我正在寻找的!谢谢!知道算法的名称让研究变得更容易... ;)一些非常有用的链接:http://homepages.inf.ed.ac.uk/rbf/HIPR2/dilate.htm 还有Matlab参考选择适当的核心:http://www.mathworks.de/de/help/images/ref/strel.html - barnacleboy
1
这个概念帮助我将一个160多行的复杂脚本减少到约20行易于阅读的代码。谷歌搜索引擎带我来到了这个不错的网页:https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html。 - itnAAnti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接