web-dev-qa-db-fra.com

Couleur la plus dominante dans l'image RVB - OpenCV / NumPy / Python

J'ai une fonction de traitement d'image python, qui utilise tente d'obtenir la couleur dominante d'une image. J'utilise une fonction que j'ai trouvée ici https://github.com/ tarikd/python-kmeans-dominant-colours/blob/master/utils.py

Cela fonctionne, mais malheureusement je ne comprends pas très bien ce qu'il fait et j'ai appris que np.histogram Est plutôt lent et je devrais utiliser cv2.calcHist Car il est 40 fois plus rapide selon ceci: https : //docs.opencv.org/trunk/d1/db7/tutorial_py_histogram_begins.html

J'aimerais comprendre comment je dois mettre à jour le code pour utiliser cv2.calcHist, Ou mieux, quelles valeurs je dois saisir.

Ma fonction

def centroid_histogram(clt):
    # grab the number of different clusters and create a histogram
    # based on the number of pixels assigned to each cluster
    num_labels = np.arange(0, len(np.unique(clt.labels_)) + 1)
    (hist, _) = np.histogram(clt.labels_, bins=num_labels)

    # normalize the histogram, such that it sums to one
    hist = hist.astype("float")
    hist /= hist.sum()

    # return the histogram
    return hist

Le pprint de clt est le suivant, je ne sais pas si cela aide

KMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=300,
    n_clusters=1, n_init=10, n_jobs=1, precompute_distances='auto',
    random_state=None, tol=0.0001, verbose=0)

Mon code peut être trouvé ici: https://github.com/primus852/python-movie-barcode

Je suis très débutant, donc toute aide est très appréciée.

Tel que demandé:

Exemple d'image

Sample

Couleur la plus dominante:

rgb(22,28,37)

Temps de calcul pour l'histogramme:

0.021515369415283203s

9
PrimuS

Deux approches utilisant np.unique et np.bincount pour obtenir la couleur la plus dominante pourrait être suggérée. De plus, dans la page liée, il parle de bincount comme une alternative plus rapide, ce qui pourrait être la voie à suivre.

Approche n ° 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()]

Approche n ° 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)

Vérification et horaires le 1000 x 1000 image couleur dans une gamme dense [0,9) pour des résultats reproductibles -

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

Boost supplémentaire

Poussée supplémentaire en tirant parti multi-core avec numexpr module pour les données volumineuses -

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)

Horaires -

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
7
Divakar

@Divakar a donné une excellente réponse. Mais si vous voulez porter votre propre code sur OpenCV, alors:

    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)))

Résultat pour votre image:

La couleur dominante est: bgr ([41 31 23])

Temps nécessaire: 0,10798478126525879 s

2
zindarod

Le code équivalent pour cv2.calcHist() est à remplacer:

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

avec

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()

Notez que cv2.calcHist accepte uniquement uint8 et float32 comme type d'élément.

Mettre à jour

Il semble que le regroupement d'opencv et de numpy diffère l'un de l'autre car les histogrammes diffèrent si le nombre de bacs ne correspond pas à la plage de valeurs:

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()
2
Timo