【问题标题】:Is there a faster way for finding the circumference of a shape?有没有更快的方法来找到形状的周长?
【发布时间】:2018-06-24 09:45:18
【问题描述】:

我有一张显示一些对象的图像,其中一个对象总是在另一个对象之内。 背景总是黑色的。

我想知道两个物体的周长并找到一个解决方案。 我使用过滤器内核来获取每个像素的 4 邻域。然后我计算像素周围的零像素数。这给了我形状的像素单位的长度。如果中心像素是所需的颜色,我只计算这种情况。

from scipy import misc, ndimage
import numpy as np
import time

def get_circumference(arr, only=50, repl=50):
    def c_length(values):
        # 3rd value is the pixel
        if values[2] == only:
            return sum([x == 0 or (repl == 0 and x != only) for x in values[[0,1,3,4]]])
        return 0

    fp = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]])
    res = ndimage.generic_filter(arr, c_length, footprint=fp)

    return np.sum(res, axis=None)

x = ndimage.imread("image.png", mode='L')

tic = time.time()
inner = get_circumference(x, only=255, repl=0)
outer = get_circumference(x, only=128, repl=128)
print("Inner object: {}, outer object: {}, took: {}".format(inner, outer, time.time() - tic))

给我:

Inner object: 510, outer object: 1054, took: 0.8387038707733154

它工作得很好。但这很慢。 单张图像大约需要 500 到 1000 毫秒。 由于我需要对数千张图像执行此操作,因此会花费太多时间。

有什么办法可以加快速度吗?我知道,图像将始终包含三种颜色,并且内部部分将始终被外部完全包围。此外,永远不会出现形状跨越图像边界的情况。

当然,实际计算周长有不同的方法。 给定一个像素,有两种可能的解决方案:1 或 4。 第一种方法只计算像素的数量,即单个像素计为单个周长单位。在后一种情况下,计算像素的实际边缘。 在我的解决方案中,我使用后一种方法来计算周长。 如果你只通过边缘检测和直方图来计算,你会得到第一个解决方案。

这是另一个示例,实际上也可以手动计算这种差异:

它由 8 个灰色像素和 1 个白色像素组成。 因此外圆周为 3+3+3+3 = 12,内圆周为 4。 按直方图计算的像素将改为 8 表示外部,1 表示内部。

【问题讨论】:

  • 很酷的问题!请问您是否有给定图像的“正确答案”以供验证?图像在大小和格式方面是否具有代表性?您能否提供一两张其他示例图片 - 请同时附上您的正确答案?
  • @MarkSetchell 我更新了计数的方法,还添加了另一个例子(非常极端的情况)
  • 请问大图的正确答案是什么?

标签: python numpy opencv image-processing scipy


【解决方案1】:

我使用OpenCV 进行图像处理,而this question 对于OpenCV 来说并不难。

  1. 读取图像并转换为灰色
  2. 灰色阈值
  3. 在谷类二进制图像上找到轮廓。
  4. 计算每个轮廓的 arclen(周长)和面积(如果需要)。

阈值为 120:

Th: 120.0
Length: 869.578
Area: 53932.000
Time: 0.002293109893798828s

阈值为 200:

Th: 200.0
Length: 423.990
Area: 11892.000
Time: 0.0015425682067871094s

## -------------------------------------
#!/usr/bin/python3
# 2018.01.16 10:23:47 CST
# 2018.01.16 12:30:20 CST
"""
Env: Python 3.5 + OpenCV 3.3
"""
import numpy as np
import cv2
import time

def findArc(img, th):
    res = img.copy()
    print("Th: {}".format(th))

    ts = time.time()
    ## convert to gray 
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    ## threshold the gray 
    th, threshed = cv2.threshold(gray, th, 255,  cv2.THRESH_BINARY)

    ## Find contours on the binary threshed image 
    cnts = cv2.findContours(threshed, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[-2]

    ## calcualte 
    for cnt in cnts:
        arclen = cv2.arcLength(cnt, True)
        area = cv2.contourArea(cnt)
        cv2.drawContours(res, [cnt], -1, (0,255,0), 3, cv2.LINE_AA)
        print("Length: {:.3f}\nArea: {:.3f}".format(arclen, area))

    print("Time: {}s".format(time.time()-ts))

    cv2.imshow("res", res)
    cv2.waitKey();cv2.destroyAllWindows()
    cv2.imwrite("res_{}.png".format(th), res)


img = cv2.imread("img03.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

findArc(img, 120)
findArc(img, 200)

【讨论】:

  • 您的解决方案看起来很好而且明显更快,但是周长的计算方式不同。但我知道有几种方法可以计算像素化对象的腰部。 span>
  • 人们无法真正计算出图像中圆的精确周长值。就像在那些椭圆中一样。必须存在累计错误。在对轮廓进行近似之后,这些值可能是接近的。
【解决方案2】:

您可以通过使用形态腐蚀来解决这个问题,这将改变具有较低邻居的像素的颜色。然后比较两个图像是否相等,您会得到以下结果。

这些操作应该在 OpenCV 中可用并且运行速度比 Python 代码快得多。

还要注意,计算给定颜色的像素只是计算直方图。


更新:

我们更容易:

  • 获取直方图,
  • 侵蚀,
  • 再次获取直方图。

计数之间的差异是轮廓像素的数量。一次就能计算出灰色和白色。

使用我的超级优化功能

Histogram: 0.250 ms
Erosion:   0.050 ms
Histogram: 0.250 ms
Total:     0.550 ms

更好:

取图像及其腐蚀的平均值,然后是直方图。轮廓像素出现中间灰度值。

Erosion:   0.060 ms
Average:   0.020 ms
Histogram: 0.230 ms
Total:     0.310 ms

现在的瓶颈是(从文件中)读取图像。

【讨论】:

    【解决方案3】:

    如果您使用contour detection,则可以针对给定图像同时检测两个对象的周长。如果图像是自然的,这会有点棘手,但对于其他方法,例如基于区域和形态学的方法,情况就是如此。

    【讨论】:

      猜你喜欢
      • 2022-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-12
      • 2016-02-06
      • 2011-03-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多