题 在OpenCV中使用HSV有效地阈值红色


我正在尝试使用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 当它们低于0时,操作不会将色调值包装回180,因此您将丢失数据。我看着将图像转换为 CvMat 这样我就可以减去它,然后使用模数将负值包回到范围的顶部,但是 CvMat 似乎不支持模数。当然,我可以迭代每个像素,但我担心这会非常慢。


我已经阅读了很多教程和代码示例,但它们似乎只是方便地查看不包含色调范围的范围,或使用更加丑陋的解决方案(例如重新实现) cvInRangeS 通过迭代每个像素并对颜色表进行手动比较)。

那么,通常的解决方法是什么?什么是最好的方式?各自的权衡是什么?迭代像素比使用内置CV函数慢得多吗?


20
2017-08-30 19:58


起源


交换红色和绿色通道,阈值绿色,然后换回? - MSalters


答案:


你不会相信,但我有完全相同的问题,我通过Hue(不是整个HSV)图像的简单迭代解决了它。

迭代像素比使用内置CV函数慢得多吗?

我刚试过去理解 CV :: INRANGE 功能,但根本没有得到它(似乎作者使用了一些特定的迭代)。


2
2017-08-30 20:55



好的,我通过迭代来做到了。它允许我将一些不同的阵列操作组合到一个手动循环中,因此性能可以相当或更好。它似乎并不慢,但我还没有实际基准。你可以在这里看到我的解决方案 gist.github.com/3562715 - Ian


这有点晚了,但这是我试过的。

进行转换:cvCvtColor(imageBgr,imageHsv,CV_RGB2HSV);

注意,RGB与Bgr是有目的地交叉的。

这样,红色将在蓝色通道中处理,并将以170左右为中心。也会有一个方向翻转,但只要你知道它就可以了。


5
2018-06-27 03:36



非常聪明。如果我需要再次使用它,可能会使用它。 - Ian
我喜欢这种方法!感谢分享 - Alexdm


您可以使用在0..255范围内计算Hue通道 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
2018-04-24 07:00



当原始色调是时,这仍然有效 下 128? (比如,20) - efaj
是的,只是计算 rotation = 128 - yourColor - Oliv


cvAddS(...) 在元素级别上等同于:

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

这个static_cast是问题,因为剪辑/截断值。

解决方案是将数据从(0-180)转移到(x,255),然后应用带有溢出的非剪切添加:

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

现在您应该可以使用单个InRange调用,只需根据上面的公式移动红色间隔


0
2017-08-31 09:13



是的,但是如果你要循环以进行元素颜色转换,你也可以手动在同一个循环中执行阈值处理。 - Ian


有一种非常简单的方法可以做到这一点。

首先制作两种不同的颜色范围

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
2018-01-12 01:00