2017-10-09 5 views
0

Disons que je l'image suivante:Déterminer la couleur la plus fréquente dans une image (OpenCV & C++)

enter image description here

Ce que je cherche est un moyen de déterminer programatically que le rouge est le plus couleur commune dans l'image. Jusqu'ici, j'ai essayé quelques approches, avec divers résultats de pauvres à abysmaux pour accomplir ceci. Mon approche actuelle consiste à réduire d'abord les couleurs de l'image.

enter image description here

Cela se fait avec le code suivant:

Mat samples(src.rows * src.cols, 3, CV_32F); 
for(int y = 0; y < src.rows; y++) 
for(int x = 0; x < src.cols; x++) 
for(int z = 0; z < 3; z++) 
samples.at<float>(y + x * src.rows, z) = src.at<Vec3b>(y,x)[z]; 

int clusterCount = 16; 
Mat labels; 
int attempts = 2; 
Mat centers; 
kmeans(samples, clusterCount, labels, TermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 10000, 0.0001), attempts, KMEANS_PP_CENTERS, centers); 

Mat reduced(src.size(), src.type()); 
for(int y = 0; y < src.rows; y++) 
for(int x = 0; x < src.cols; x++) 
{ 
    int cluster_idx = labels.at<int>(y + x * src.rows,0); 
    reduced.at<Vec3b>(y,x)[0] = centers.at<float>(cluster_idx, 0); 
    reduced.at<Vec3b>(y,x)[1] = centers.at<float>(cluster_idx, 1); 
    reduced.at<Vec3b>(y,x)[2] = centers.at<float>(cluster_idx, 2); 
} 

Il y a un problème ennuyeux avec elle où il a un problème avec mise à l'échelle qui laisse une partie du droit, mais je peux vivre avec ça pour le moment.

Ensuite, j'ai essayé quelques approches où je veux obtenir les couleurs mappées, comme avec les histogrammes.

Mat image_hsv; 

cvtColor(src, image_hsv, CV_BGR2HSV); 

// Quanta Ratio 
int scale = 10; 

int hbins = 36, sbins = 25, vbins = 25; 
int histSize[] = {hbins, sbins, vbins}; 

float hranges[] = { 0, 360 }; 
float sranges[] = { 0, 256 }; 
float vranges[] = { 0, 256 }; 

const float* ranges[] = { hranges, sranges, vranges }; 
MatND hist; 

int channels[] = {0, 1, 2}; 

calcHist(&image_hsv, 1, channels, Mat(), // do not use mask 
     hist, 3, histSize, ranges, 
     true, // the histogram is uniform 
     false); 

int maxVal = 0; 

int hue = 0; 
int saturation = 0; 
int value = 0; 

for(int h = 0; h < hbins; h++) 
for(int s = 0; s < sbins; s++) 
for(int v = 0; v < vbins; v++) 
{ 
    int binVal = hist.at<int>(h, s, v); 
    if(binVal > maxVal) 
    { 
     maxVal = binVal; 

     hue = h; 
     saturation = s; 
     value = v; 
    } 
} 

hue = hue * scale * scale; // angle 0 - 360 
saturation = saturation * scale; // 0 - 255 
value = value * scale; // 0 - 255 

Le problème est, pour cette image que je reçois les valeurs suivantes:

  • Hue: 240
  • Saturation: 0
  • Valeur: 0

Cependant, je J'attends des valeurs HSV plus proches de ceci:

  • Hue: 356
  • Saturation: 94
  • Valeur: 58

Espérons que quelqu'un peut signaler où je suis allé mal.

+0

trop paresseux pour analyser votre code mais juste un indice pour tester est-ce que vous gérez correctement votre format de pixel d'image? Mon pari est que vous avez RVB et le gérer comme BGR ou vice versa quelque part comme la teinte 240 est bleue au lieu de rouge (je suis habitué à ce GDI/Canvas a généralement l'ordre inverse des canaux que les données d'image brutes pour certains formats de pixels). , V mis à zéro est en effet étrange. Jetez également un coup d'oeil à ceci: [histogramme HSV] (https://stackoverflow.com/a/29286584/2521214) – Spektre

+0

Juste curieux, que diriez-vous de calculer l'histogramme de couleur en utilisant [calcHist] (https://docs.opencv.org /2.4/modules/imgproc/doc/histograms.html#calchist) et trouver le pic? Bien sûr, pas la réponse à votre question avec les valeurs HSV. calcHist sera beaucoup plus rapide que kmeans. – dhanushka

+0

@dhanushka la vitesse dépend de l'implémentation de l'histogramme des couleurs, du nombre de bins etc ... donc il ne peut pas être nécessairement plus rapide que k-means avec la même précision. Mais oui j'utiliserais aussi l'histogramme, c'est pourquoi je suggère le lien dans le commentaire précédent car il y a mon implémentation C++ qui fait la conversion RGB -> HSV, calculer et rendre l'histogramme HSV (pas openCV) – Spektre

Répondre

0

C'est en effet un problème classique en infographie. Trouver simplement la couleur la plus fréquente n'est pas une bonne idée, étonnamment vous pouvez découvrir que c'est une nuance de jaune/vert plutôt que le rouge, parce que les valeurs de "rouge" contiennent des variations et ne tombent presque jamais dans le même histogramme .

Un algorithme approprié est basé sur les moindres carrés. Vous devez trouver une couleur dont la somme des "distances" au carré est minime. Vous pouvez définir votre distance car dr^2 + dg^2 + db^2 (r/b/g représente les composantes rouge/vert/bleu), ou vous pouvez jouer avec des poids pour refléter la sensibilité différente à ces composants. Vous pouvez également le représenter dans différentes bases (par exemple yuv ou etc.)

Veuillez écrire dans les commentaires si vous avez besoin d'aide pour implémenter les moindres carrés.

Mise à jour:

La solution des moindres carrés dans votre cas est que le niveau de la valeur moyenne avec des poids donnés par l'histogramme.

résultat = somme (n * hist [n])/somme (hist [n]).Notez que cette formule s'applique à chaque composante de couleur, c'est-à-dire qu'elle est indépendante.