我有一个显示一些物体的图像,其中一个物体总是在另一个物体内。 背景总是黑色。
我想知道两个物体的周长并找到解决方案。 我使用滤波器来获得每个像素的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个灰色像素和一个白色像素组成。 因此,外圆周为3 + 3 + 3 + 3 = 12,内部为4。 通过直方图进行的像素计数将为外部提供8,为内部提供1。
答案 0 :(得分:3)
我使用OpenCV
进行图片处理,this question
对OpenCV
来说并不难。
- 阅读图片并转换为灰色
- 阈值灰色
- 在脱粒二进制图像上查找轮廓。
- 计算每个轮廓的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)
答案 1 :(得分:1)
您可以通过使用形态侵蚀来解决这个问题,这会改变具有较低邻居的像素的颜色。然后比较两个图像的相等性,得到下面的结果。
这些操作应该在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
现在的瓶颈是读取图像(来自文件)。
答案 2 :(得分:0)
如果使用contour detection,则可以同时检测给定图像的两个对象的周长。如果图像是自然的,这会有点棘手,但对于其他方法,例如基于区域和形态的方法,情况就是如此。