Подтвердить что ты не робот

Эффективное пороговое значение красного цвета с использованием 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?

4b9b3361

Ответ 1

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

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

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

Ответ 2

Это немного поздно, но я пытаюсь это сделать.

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

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

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

Ответ 3

Вы можете рассчитать 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);
}

Ответ 4

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

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

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

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

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

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

Ответ 5

Существует очень простой способ сделать это.

Сначала создайте два разных цветовых диапазона

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

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