【问题标题】:Efficient way to cluster colors using K-Nearest使用 K-Nearest 聚类颜色的有效方法
【发布时间】:2014-09-01 08:28:51
【问题描述】:

我正在尝试将图像上的颜色聚集到预定义的类(黑色、白色、蓝色、绿色、红色)。我正在使用以下代码:

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 操作是什么?

【问题讨论】:

    标签: python opencv numpy machine-learning


    【解决方案1】:

    我设法使用下面的代码删除了循环。代码运行速度非常快,几乎和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()
    

    代码像以前一样生成正确的结果,执行时间更快。

    【讨论】:

      【解决方案2】:

      您可以建立一个查找表。这样你就知道每种颜色对应的类。它不一定是 256x256x256,您可以减少 bin 的数量。

      【讨论】:

        【解决方案3】:

        如果您想要一个简单的平方差度量(“这是欧几里得最接近的数字),这将起作用。

        计算差异

        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)
        

        【讨论】:

        • 后一个建议还会为您提供 0xff 和 0 的所有 8 种颜色组合,例如 (0xff, 0xff, 0),而不仅仅是您指定的 5 种。
        猜你喜欢
        • 2017-03-22
        • 2019-06-22
        • 2018-01-12
        • 1970-01-01
        • 2016-06-01
        • 2020-04-22
        • 1970-01-01
        • 2015-11-09
        • 2018-01-09
        相关资源
        最近更新 更多