【问题标题】:efficiently threshold red using HSV in OpenCV在 OpenCV 中使用 HSV 有效地阈值红色
【发布时间】:2012-08-25 15:11:10
【问题描述】:

我正在尝试使用 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);

这让我可以对所有颜色进行类似处理。

但是,当色调值低于 0 时,cvAddS 操作不会将色调值返回到 180,因此您会丢失数据。我考虑将图像转换为CvMat,以便可以从中减去,然后使用模数将负值包装回范围的顶部,但CvMat 似乎不支持模数。当然,我可以遍历每个像素,但我担心这会很慢。


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

那么,解决这个问题的常用方法是什么?最好的方法是什么?各自的取舍是什么?迭代像素是否比使用内置 CV 函数慢得多?

【问题讨论】:

  • 交换红色和绿色通道,阈值绿色,然后交换回来?

标签: c++ image-processing opencv


【解决方案1】:

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

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

我刚刚尝试理解cv::inRange函数,但完全没有理解(似乎作者使用了一些特定的迭代)。

【讨论】:

  • 好的,我通过迭代做到了。它允许我将几个不同的数组操作组合到一个手动循环中,因此性能可能相当或更好。它似乎并不慢,但我也没有真正进行基准测试。你可以在这里看到我的解决方案:gist.github.com/3562715
【解决方案2】:

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

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

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

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

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

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

【讨论】:

  • 是的,但是如果您要循环以进行元素颜色偏移,您也可以在同一循环中手动执行阈值处理。
【解决方案3】:

您可以使用CV_BGR2HSV_FULL 计算 0..255 范围内的色调通道。 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);
}

【讨论】:

  • 当原始色调 128 时,这仍然有效吗? (比如,20)
  • 是的,只计算rotation = 128 - yourColor
【解决方案4】:

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

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

注意,RGB 与 Bgr 是故意交叉的。

这样,红色将在蓝色通道中处理,并以 170 为中心。还会有方向翻转,但只要你知道预期它就可以。

【讨论】:

  • 非常聪明。如果我需要再做一次,可能会使用它。
【解决方案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);

我从a blog post得到这个想法,我发现了

【讨论】:

    猜你喜欢
    • 2019-12-03
    • 2016-11-15
    • 2019-12-19
    • 2011-12-09
    • 1970-01-01
    • 1970-01-01
    • 2016-02-03
    • 2014-12-27
    • 1970-01-01
    相关资源
    最近更新 更多