我正在尝试使用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
似乎不支持模数。当然,我可以迭代每个像素,但我担心这会非常慢。
我已经阅读了很多教程和代码示例,但它们似乎只是方便地查看不包含色调范围的范围,或者使用甚至更加丑陋的解决方案(例如重新实现{{1}通过迭代每个像素并对颜色表进行手动比较。)
那么,解决这个问题的常用方法是什么?什么是最好的方式?每个人的权衡是什么?迭代像素比使用内置CV函数要慢得多吗?
答案 0 :(得分:6)
这有点晚了,但这就是我要尝试的。
进行转换:cvCvtColor(imageBgr,imageHsv,CV_RGB2HSV);
注意,RGB vs Bgr是故意交叉的。
这样,红色将在蓝色通道中处理,并以170左右为中心。方向上也会有一个翻转,但只要你知道它就可以了。
答案 1 :(得分:2)
你不会相信,但我有完全相同的问题,我通过Hue(不是整个HSV)图像的简单迭代解决了它。
迭代像素比使用内置CV函数慢得多吗?
我刚刚尝试理解cv::inRange函数,但根本没有得到它(似乎作者使用了一些特定的迭代)。
答案 2 :(得分:2)
您可以使用CV_BGR2HSV_FULL
计算0..255范围内的Hue通道。您{h} 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);
}
答案 3 :(得分:0)
cvAddS(...)
在元素级别上等同于:
out = static_cast<dest> ( in + shift );
这个static_cast是问题,因为剪辑/截断值。
解决方案是将数据从(0-180)转移到(x,255),然后应用带有溢出的非剪切添加:
out = uchar(in + (255-180) + rotation );
现在您应该可以使用单个InRange调用,只需根据上面的公式调整红色区间
答案 4 :(得分:0)
有一种非常简单的方法可以做到这一点。
首先制作两种不同的颜色范围
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找到了
的想法