efficacité du rouge de seuil en utilisant HSV dans OpenCV

j'essaie de limiter les pixels rouges dans un flux vidéo en utilisant OpenCV. J'ai d'autres couleurs qui fonctionnent assez bien, mais le rouge pose un problème parce qu'il enroule autour de l'axe de teinte (i.e.. HSV(0, 255, 255) et HSV (179, 255, 255) sont tous deux rouges). La technique que j'utilise maintenant est loin d'être idéale. Fondamentalement:

cvInRangeS(src, cvScalar(0, 135, 135), cvScalar(20, 255, 255), dstA);
cvInRangeS(src, cvScalar(159, 135, 135), cvScalar(179, 255, 255), dstB);
cvOr(dstA, dstB, dst);

ceci est sous-optimal parce qu'il nécessite une branche dans le code pour le rouge (bugs potentiels), l'attribution de deux images supplémentaires, et deux opérations supplémentaires par rapport au Facile bleu:

cvInRangeS(src, cvScalar(100, 135, 135), cvScalar(140, 255, 255), dst);

la meilleure alternative qui m'est venue à l'esprit était de "faire tourner" les couleurs de l'image, de sorte que la teinte de la cible soit à 90 degrés. Par exemple.

int rotation = 90 - 179; // 179 = red
cvAddS(src, cvScalar(rotation, 0, 0), dst1);
cvInRangeS(dst1, cvScalar(70, 135, 135), cvScalar(110, 255, 255), dst);

Cela me permet de traiter toutes les couleurs de la même façon.

cependant, le cvAddS l'opération n'enroule pas les valeurs de teinte à 180 quand elles vont en dessous de 0, donc vous perdez des données. J'ai regardé la conversion de l'image CvMat pour que je puisse soustraire, puis utiliser le module pour envelopper le les valeurs négatives vers le haut de la gamme, mais CvMat ne semble pas supporter le module. Bien sûr, je pourrais itérer sur chaque pixel, mais je crains que ce ne soit très lent.


j'ai lu beaucoup de tutoriels et d'exemples de code, mais ils semblent tous commodément seulement regarder les gammes qui ne s'enroulent pas autour du spectre de teinte, ou utiliser des solutions qui sont encore plus laid (par exemple. re-mise en œuvre cvInRangeS en itérant sur chaque pixel et en faisant des comparaisons manuelles une table des couleurs).

alors, quelle est la façon habituelle de résoudre ce problème? Quelle est la meilleure façon? Quels sont les compromis de chacun? L'itération sur pixels est-elle beaucoup plus lente que l'utilisation de fonctions CV intégrées?

20
demandé sur Ian 2012-08-30 23:58:03

5 réponses

vous ne le croirez pas mais j'ai eu exactement le même problème et je l'ai résolu en utilisant une itération simple à travers L'image Hue (pas toute HSV).

l'itération sur pixels est-elle beaucoup plus lente que l'utilisation des fonctions CV intégrées?

j'ai juste essayé de comprendre cv:: inRange fonction mais ne l'a pas obtenu du tout (il semble que l'auteur a utilisé une itération spécifique).

2
répondu ArtemStorozhuk 2012-08-30 20:55:54

C'est un peu tard, mais c'est ce que j'essaierais.

Make the conversion: cvCvtColor (imageBgr, imageHsv, CV_RGB2HSV);

Remarque, RVB vs Bgr sont délibérément traversé.

de Cette façon, la couleur rouge sera traitée dans un canal bleu et sera centrée autour de 170. Il y aurait aussi un retournement dans la direction, mais C'est OK tant que vous savez l'attendre.

5
répondu GaryK 2015-06-27 03:36:09

vous pouvez calculer le canal de teinte dans la gamme 0..255 avec CV_BGR2HSV_FULL. Votre différence de teinte originale de 10 deviendra 14 (10/180*256), c'est à dire la teinte doit être dans la gamme 128-14..128+14:

public void inColorRange(CvMat imageBgr, CvMat dst, int color, int threshold) {
    cvCvtColor(imageBgr, imageHsv, CV_BGR2HSV_FULL);
    int rotation = 128 - color;
    cvAddS(imageHsv, cvScalar(rotation, 0, 0), imageHsv);
    cvInRangeS(imageHsv, cvScalar(128-threshold, 135, 135), 
         cvScalar(128+threshold, 255, 255), dst);
}
2
répondu Oliv 2016-09-17 12:49:30

cvAddS(...) est équivalent, au niveau d'élément:

 out = static_cast<dest> ( in + shift );

C'static_cast est le problème, parce que c'est des clips/tronque les valeurs.

une solution serait de déplacer les données de (0-180) à (x, 255), puis d'appliquer un add non-clipping avec débordement:

 out = uchar(in + (255-180) + rotation );

Maintenant vous devriez être en mesure d'utiliser un seul appel InRange, il suffit de décaler votre intervalle rouge selon la formule ci-dessus

0
répondu Sam 2012-08-31 09:13:45

il y a une façon très simple de faire cela.

D'abord faire deux gammes de couleurs différentes

cv::Mat lower_red_hue_range;
cv::Mat upper_red_hue_range;
cv::inRange(hsv_image, cv::Scalar(0, 100, 100), cv::Scalar(10, 255, 255), lower_red_hue_range);
cv::inRange(hsv_image, cv::Scalar(160, 100, 100), cv::Scalar(179, 255, 255), upper_red_hue_range);

puis combiner les deux masques en utilisant addWeighted

cv::Mat red_hue_mask;
cv::addWeighted(lower_red_hue_range, 1.0, upper_red_hue_range, 1.0, 0.0, red_hue_mask);

Maintenant, vous pouvez simplement appliquer le masque sur l'image

cv::Mat result;
inputImageMat.copyTo(result, red_hue_mask);

j'ai eu l'idée de blog j'ai trouvé

0
répondu Ethan 2017-01-12 01:00:19