我正在尝试将图像上的颜色聚类为预定义的类(黑色,白色,蓝色,绿色,红色)。我正在使用以下代码:
import numpy as np
import cv2
src = cv2.imread('objects.png')
colors = np.array([[0x00, 0x00, 0x00],
[0xff, 0xff, 0xff],
[0xff, 0x00, 0x00],
[0x00, 0xff, 0x00],
[0x00, 0x00, 0xff]], dtype=np.float32)
classes = np.array([[0], [1], [2], [3], [4]], np.float32)
dst = np.zeros(src.shape, np.float32)
knn = cv2.KNearest()
knn.train(colors, classes)
# This loop is very inefficient!
for i in range(0, src.shape[0]):
for j in range(0, src.shape[1]):
sample = np.reshape(src[i,j], (-1,3)).astype(np.float32)
retval, result, neighbors, dist = knn.find_nearest(sample, 1)
dst[i,j] = colors[result[0,0]]
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
代码效果很好,结果如下所示。左边的图像是输入,右边的图像是输出。
然而,上面的循环非常低效并且使转换变慢。取代上面的循环最有效的Numpy操作是什么?
答案 0 :(得分:2)
如果你想要一个简单的平方差测量(“这是欧几里得最接近的数字),这将有效。
计算差异
diff = ((src[:,:,:,None] - colors.T)**2).sum(axis=2)
(假设src
为y,x,形状为3)
选择最接近的颜色索引:
index = diff.argmin(axis=2)
新图片:
out = colors[index]
如果你的颜色真的是组件值为0或0xff,你可以使用像
这样的东西out = np.where(src>0x88, 0xff, 0)
答案 1 :(得分:0)
您可以构建一个查找表。这样您就可以了解每种颜色的相应类别。它不必是256x256x256,你可以减少垃圾箱数量。
答案 2 :(得分:0)
我设法使用下面的代码删除循环。代码运行速度非常快,与C ++版本几乎相似。
import numpy as np
import cv2
src = cv2.imread('objects.png')
src_flatten = np.reshape(np.ravel(src, 'C'), (-1, 3))
dst = np.zeros(src.shape, np.float32)
colors = np.array([[0x00, 0x00, 0x00],
[0xff, 0xff, 0xff],
[0xff, 0x00, 0x00],
[0x00, 0xff, 0x00],
[0x00, 0x00, 0xff]], dtype=np.float32)
classes = np.array([[0], [1], [2], [3], [4]], np.float32)
knn = cv2.KNearest()
knn.train(colors, classes)
retval, result, neighbors, dist = knn.find_nearest(src_flatten.astype(np.float32), 1)
dst = colors[np.ravel(result, 'C').astype(np.uint8)]
dst = dst.reshape(src.shape).astype(np.uint8)
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
代码生成的结果与以前一样正确,执行时间更短。