棘手的部分是,如果对象有孔洞,则轮廓中不应该看到它们。因此,如果源图像是甜甜圈,则相应的轮廓图像将是一个带有没有同心较小线条的锯齿形圆形线。
这是一个示例图像,源图像及其轮廓:
![enter image description here](https://istack.dev59.com/GUJPE.webp)
我同意amgaera的观点。 如果你想要找到轮廓,那么在Python中使用OpenCV是最好的工具之一。 如同他/她的帖子所述,使用findContours
方法,并使用RETR_EXTERNAL
标志来获取形状的最外层轮廓。 这是一些可重复使用的代码,以说明这一点。 您首先需要安装OpenCV和NumPy
才能开始。
我不确定您正在使用哪个平台,但是:
libopencv-dev
和python-numpy
上执行apt-get
(即sudo apt-get install libopencv-dev python-numpy
)。brew install opencv
和brew install numpy
来安装。http://www.lfd.uci.edu/~gohlke/pythonlibs/
- 检查OpenCV软件包并安装它正在请求的所有依赖项,包括您可以在此页面上找到的NumPy
。无论如何,我拿了你的甜甜圈图像,并提取了仅带有甜甜圈图像的部分。 换句话说,我创建了这张图片:
至于您的图像是PNG格式且具有alpha通道,实际上并不重要。 只要您在此图像中仅包含单个对象,我们实际上根本不需要访问alpha通道。 下载此图像后,将其保存为donut.png
,然后继续运行此代码:
import cv2 # Import OpenCV
import numpy as np # Import NumPy
# Read in the image as grayscale - Note the 0 flag
im = cv2.imread('donut.png', 0)
# Run findContours - Note the RETR_EXTERNAL flag
# Also, we want to find the best contour possible with CHAIN_APPROX_NONE
contours, hierarchy = cv2.findContours(im.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# Create an output of all zeroes that has the same shape as the input
# image
out = np.zeros_like(im)
# On this output, draw all of the contours that we have detected
# in white, and set the thickness to be 3 pixels
cv2.drawContours(out, contours, -1, 255, 3)
# Spawn new windows that shows us the donut
# (in grayscale) and the detected contour
cv2.imshow('Donut', im)
cv2.imshow('Output Contour', out)
# Wait indefinitely until you push a key. Once you do, close the windows
cv2.waitKey(0)
cv2.destroyAllWindows()
让我们慢慢地看一下代码。首先,我们导入了 OpenCV 和 NumPy 包。我将 NumPy 导入为 np
,如果你查看 numpy
的文档和教程,他们都这样做以减少输入量。OpenCV 和 NumPy 相互配合使用,因此需要安装这两个包。然后,我们使用 imread
读取图像。我将标志设置为 0
,以将图像变成灰度图像,以使事情更加简单。加载图像后,运行 findContours
函数,该函数的输出会输出两个元组:
contours
- 这是一个数组结构,它给出了图像中每个轮廓的 (x,y)
坐标。hierarchy
- 这包含有关检测到的轮廓的其他信息,例如拓扑结构,但出于本文的考虑,让我们跳过这些内容。请注意,我指定了 RETR_EXTERNAL
来检测对象的最外层轮廓。我还指定了 CHAIN_APPROX_NONE
标志,以确保获得完整的轮廓而不进行任何近似。一旦检测到轮廓,我们创建一个全黑的新输出图像。这将包含我们检测到的甜甜圈的外轮廓。一旦创建了这个图像,我们就运行 drawContours
方法。您需要指定要在其中显示轮廓的图像、先前创建的轮廓结构以及 -1
标志,表示在图像中绘制所有轮廓。如果一切顺利,你应该只能检测到一个轮廓。然后指定你想让轮廓看起来像什么颜色。在我们的情况下,我们希望它是白色的。最后,您需要指定要绘制轮廓的粗细。我选择了 3 个像素的厚度。
最后一件事是展示结果。我调用 imshow
来展示原始甜甜圈图像(灰度)和输出轮廓图像。但这并不是全部。除非你调用 cv2.waitKey(0)
,否则你看不到任何输出。现在,这意味着你可以无限期地显示图像,直到你按下一个键。一旦按下键,cv2.destroyAllWindows()
调用将关闭生成的所有窗口。
这是我得到的结果(一旦你将窗口重新排列成并排状态):
作为额外的奖励,如果你想保存这个图像,只需运行 imwrite
来保存图像。您需要指定要写入的图像的名称和要访问的变量。因此,你可以做如下的操作:
cv2.imwrite('contour.png', out)
您将把这个轮廓图像保存到名为contour.png
的文件中。
这应该足以让您开始了。
祝你好运!
convert donut.png -channel A -morphology EdgeOut Diamond +channel -fx 'a' -negate output.jpg
-channel A
选择了alpha(透明度),并应用形态学来提取不透明区域的轮廓。然后,+channel
告诉ImageMagick我现在再次处理所有通道。 -fx
是一个自定义函数(运算符),其中我将输出图像的每个像素设置为修改后阿尔法通道中的Alpha值a
。
编辑后
以下方法可能比使用上述的 fx
操作符更快:convert donut.png -channel RGBA -separate -delete 0-2 -morphology EdgeOut Diamond -negate output.png
结果:
如果您需要描绘许多数百(或上千)图像,我建议使用 GNU Parallel,可以从这里下载。然后它将利用所有 CPU 核心快速完成作业。您的命令将类似于这样 - 但请务必先备份并在副本上操作,直到您掌握了它的使用方法!
parallel convert {} -channel A -morphology EdgeOut Diamond +channel -fx 'a' -negate {.}.jpg ::: *.png
这段内容的意思是使用:::
后面的所有内容作为需要处理的文件。然后并行地利用所有可用的核心,将每个PNG文件转换成相应的JPEG文件,并将其名称更改为输出文件名。
我找到了一个非常有用的轮廓追踪REST API,比使用其他程序更简单。 我在Ruby项目中使用了API,现在也通过使用CURL;它运行得非常好!
这里有一个演示和API文档。 所以我使用了你的甜甜圈图片作为测试的源图像。唯一的问题是你的图片没有透明背景颜色。所以我使用Gimp制作了这个带有透明背景的图片,其中你的图片是黑色的部分。所以当你调用API时,如果matching_not_color=0,你引用的是不透明颜色之外的任何图像部分。
现在我多次使用API进行一些测试。正如你所看到的,你可以获取JSON数据(轮廓折线)或者一个带有轮廓绘制的示例图像。所以我使用CURL命令调用API:
curl -v -H "Accept: application/json" -X POST -F "png_image=@rRBal.png" -F "match_not_color=0" -F
"options[compress][linear]=true" -F
"options[compress][visvalingam]=true" -o output.png
http://tracecontour.com/outlines_image
我使用了match_not_color=0。这样我要求考虑任何像素都不是背景透明颜色。我得到了这个png图像作为结果(如curl命令所述的output.png)。
正如你在这里看到的匹配区域是蓝色的。每个外部折线是红色的,每个内部折线是绿色的。我尝试了visvalingam选项以获得一个不太精确的轮廓,但你可以要求最精确的轮廓。
如果你这样调用,你将得到带有坐标(x,y)的JSON数据。
curl -v -H "Accept: application/json" -X POST -F
"png_image=@rRBal.png" -F "match_not_color=0" -F
"options[compress][linear]=true" -F
"options[compress][visvalingam]=true" http://tracecontour.com/outlines
&> /dev/stdout
以下是最精确的结果。这里是没有visvalingam选项的命令:
curl -v -H "Accept: application/json" -X POST -F
"png_image=@rRBal.png" -F "match_not_color=0" -F
"options[compress][linear]=true" -o output.png
http://tracecontour.com/outlines_image
结果在这里,非常精确且获取速度非常快
非常有用的API!