Most dominant color in RGB image - OpenCV / NumPy / Python

后端 未结 3 1380
广开言路
广开言路 2020-12-14 04:05

I have a python image processing function, that uses tries to get the dominant color of an image. I make use of a function I found here https://github.com/tarikd/python-kmea

相关标签:
3条回答
  • 2020-12-14 04:25

    Two approaches using np.unique and np.bincount to get the most dominant color could be suggested. Also, in the linked page, it talks about bincount as a faster alternative, so that could be the way to go.

    Approach #1

    def unique_count_app(a):
        colors, count = np.unique(a.reshape(-1,a.shape[-1]), axis=0, return_counts=True)
        return colors[count.argmax()]
    

    Approach #2

    def bincount_app(a):
        a2D = a.reshape(-1,a.shape[-1])
        col_range = (256, 256, 256) # generically : a2D.max(0)+1
        a1D = np.ravel_multi_index(a2D.T, col_range)
        return np.unravel_index(np.bincount(a1D).argmax(), col_range)
    

    Verification and timings on 1000 x 1000 color image in a dense range [0,9) for reproducible results -

    In [28]: np.random.seed(0)
        ...: a = np.random.randint(0,9,(1000,1000,3))
        ...: 
        ...: print unique_count_app(a)
        ...: print bincount_app(a)
    [4 7 2]
    (4, 7, 2)
    
    In [29]: %timeit unique_count_app(a)
    1 loop, best of 3: 820 ms per loop
    
    In [30]: %timeit bincount_app(a)
    100 loops, best of 3: 11.7 ms per loop
    

    Further boost

    Further boost upon leveraging multi-core with numexpr module for large data -

    import numexpr as ne
    
    def bincount_numexpr_app(a):
        a2D = a.reshape(-1,a.shape[-1])
        col_range = (256, 256, 256) # generically : a2D.max(0)+1
        eval_params = {'a0':a2D[:,0],'a1':a2D[:,1],'a2':a2D[:,2],
                       's0':col_range[0],'s1':col_range[1]}
        a1D = ne.evaluate('a0*s0*s1+a1*s0+a2',eval_params)
        return np.unravel_index(np.bincount(a1D).argmax(), col_range)
    

    Timings -

    In [90]: np.random.seed(0)
        ...: a = np.random.randint(0,9,(1000,1000,3))
    
    In [91]: %timeit unique_count_app(a)
        ...: %timeit bincount_app(a)
        ...: %timeit bincount_numexpr_app(a)
    1 loop, best of 3: 843 ms per loop
    100 loops, best of 3: 12 ms per loop
    100 loops, best of 3: 8.94 ms per loop
    
    0 讨论(0)
  • 2020-12-14 04:39

    @Divakar has given a great answer. But if you want to port your own code to OpenCV, then:

        img = cv2.imread('image.jpg',cv2.IMREAD_UNCHANGED)
    
        data = np.reshape(img, (-1,3))
        print(data.shape)
        data = np.float32(data)
    
        criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
        flags = cv2.KMEANS_RANDOM_CENTERS
        compactness,labels,centers = cv2.kmeans(data,1,None,criteria,10,flags)
    
        print('Dominant color is: bgr({})'.format(centers[0].astype(np.int32)))
    

    Result for your image:

    Dominant color is: bgr([41 31 23])

    Time it took: 0.10798478126525879 secs

    0 讨论(0)
  • 2020-12-14 04:40

    The equivalent code for cv2.calcHist() is to replace:

    (hist, _) = np.histogram(clt.labels_, bins=num_labels)  
    

    with

    dmin, dmax, _, _ = cv2.minMaxLoc(clt.labels_)
    
    if np.issubdtype(data.dtype, 'float'): dmax += np.finfo(data.dtype).eps
    else: dmax += 1
    
    hist = cv2.calcHist([clt.labels_], [0], None, [num_labels], [dmin, dmax]).flatten()
    

    Note that cv2.calcHist only accepts uint8 and float32 as element type.

    Update

    It seems like opencv's and numpy's binning differs from each other as the histograms differ if the number of bins doesn't map the value range:

    import numpy as np
    from matplotlib import pyplot as plt
    import cv2
    
    #data = np.random.normal(128, 1, (100, 100)).astype('float32')
    data = np.random.randint(0, 256, (100, 100), 'uint8')
    BINS = 20
    
    np_hist, _ = np.histogram(data, bins=BINS)
    
    dmin, dmax, _, _ = cv2.minMaxLoc(data)
    if np.issubdtype(data.dtype, 'float'): dmax += np.finfo(data.dtype).eps
    else: dmax += 1
    
    cv_hist = cv2.calcHist([data], [0], None, [BINS], [dmin, dmax]).flatten()
    
    plt.plot(np_hist, '-', label='numpy')
    plt.plot(cv_hist, '-', label='opencv')
    plt.gcf().set_size_inches(15, 7)
    plt.legend()
    plt.show()
    
    0 讨论(0)
提交回复
热议问题