it-roy-ru.com

эффективно пороговое значение красного с использованием HSV в OpenCV

Я пытаюсь портировать красные пиксели в видеопотоке, используя OpenCV. У меня есть другие цвета, работающие довольно хорошо, но красный представляет проблему, потому что он оборачивается вокруг оси оттенка (то есть HSV (0, 255, 255) и HSV (179, 255, 255) оба красные). Техника, которую я использую сейчас, не идеальна. В принципе:

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

Это неоптимально, так как требует красной ветки в коде (потенциальные ошибки), выделения двух дополнительных изображений и двух дополнительных операций по сравнению с простым случаем синего:

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

Лучшая альтернатива, которая пришла мне в голову, заключалась в том, чтобы «повернуть» цвета изображения так, чтобы целевой оттенок находился под углом 90 градусов. Например.

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

Это позволяет мне относиться ко всем цветам одинаково.

Однако операция cvAddS не оборачивает значения оттенка обратно до 180, когда они опускаются ниже 0, поэтому вы теряете данные. Я посмотрел на преобразование изображения в CvMat так, чтобы я мог вычесть из него и затем использовать модуль для переноса отрицательных значений обратно в верхнюю часть диапазона, но CvMat, похоже, не поддерживает модуль. Конечно, я мог бы перебирать каждый пиксель, но я обеспокоен тем, что это будет очень медленно.


Я прочитал много уроков и примеров кода, но все они, кажется, удобно смотреть только на диапазоны, которые не охватывают спектр оттенков, или используют решения, которые даже более уродливы (например, повторно реализуем cvInRangeS, перебирая каждый пиксель и делать ручные сравнения с таблицей цветов).

Итак, как обычно это решить? Какой лучший способ? Каковы компромиссы каждого? Итерация по пикселям намного медленнее, чем использование встроенных функций CV?

21
Ian

Вы не поверите, но у меня была точно такая же проблема, и я решил ее, используя простую итерацию изображения Hue (не весь HSV).

Итерация по пикселям намного медленнее, чем использование встроенных функций CV?

Я только что попытался понять функцию cv :: inRange , но не получил ее вообще (кажется, что автор использовал какую-то конкретную итерацию).

2
ArtemStorozhuk

Это немного поздно, но это то, что я попробую.

Сделайте преобразование: cvCvtColor (imageBgr, imageHsv, CV_RGB2HSV);

Обратите внимание, что RGB и Bgr целенаправленно пересекаются.

Таким образом, красный цвет будет обрабатываться в синем канале и будет центрироваться около 170. Также будет изменение направления, но это нормально, если вы ожидаете этого.

6
GaryK

Вы можете рассчитать канал Hue в диапазоне 0..255 с помощью CV_BGR2HSV_FULL. Ваша исходная разница оттенков 10 станет 14 (10/180*256), т. Е. Оттенок должен находиться в диапазоне 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
Oliv

cvAddS(...) на уровне элемента эквивалентно:

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

Эта static_cast является проблемой, потому что это обрезает/усекает значения.

Решение состоит в том, чтобы переместить данные с (0-180) на (x, 255), а затем применить необрезанное добавление с переполнением:

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

Теперь вы сможете использовать один вызов InRange, просто сдвиньте красный интервал в соответствии с приведенной выше формулой

0
Sam

Есть действительно простой способ сделать это.

Сначала сделайте две разные цветовые гаммы

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

Затем объедините две маски, используя 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);

Теперь вы можете просто применить маску к изображению

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

Я получил идею от сообщение в блоге я нашел

0
Ethan