【问题标题】:Thresholding a range of colors from an image对图像中的一系列颜色进行阈值处理
【发布时间】:2013-05-19 12:19:33
【问题描述】:

计划

我的项目能够捕获目标窗口的位图并将其转换为IplImage,然后将该图像显示在cvNamedWindow 中,可以进行进一步处理。
为了测试,我将图像加载到 MSPaint 中,如下所示:



然后允许用户在图像内任意数量的像素上单击并拖动鼠标,以创建包含这些 RGB 颜色值的vector<cv::Scalar_<BYTE>>
然后,在ColorRGBToHLS() 的帮助下,这个数组再按色调从左到右排序,如下所示:

// PixelColor is just a cv::Scalar_<BYTE>
bool comparePixelColors( PixelColor& pc1, PixelColor& pc2 ) {
    WORD h1 = 0, h2 = 0;
    WORD s1 = 0, s2 = 0;
    WORD l1 = 0, l2 = 0;
    ColorRGBToHLS(RGB(pc1.val[2], pc1.val[1], pc1.val[0]), &h1, &l1, &s1);
    ColorRGBToHLS(RGB(pc2.val[2], pc2.val[1], pc2.val[0]), &h2, &l2, &s2);
    return ( h1 < h2 );
}

//..(elsewhere in code)
std::sort(m_colorRange.begin(), m_colorRange.end(), comparePixelColors);


...然后显示在一个新的cvNamedWindow 中,类似于:



问题

现在,这里的想法是创建一个二进制阈值图像(或“蒙版”),其中所选的颜色范围变为白色,而源图像的其余部分变为黑色......类似“按颜色选择” 工具在 GIMP 中的操作方式,或 “魔术棒” 工具在 Photoshop 中的操作方式...选择,我们实际上是对整个图像进行操作。

我已经阅读了cvInRangeS,听起来这正是我所需要的。
然而,无论出于何种原因,经过阈值处理的图像最终总是全黑...

VOID ShowThreshedImage(const IplImage* src, const PixelColor& min, const PixelColor& max)
{
    IplImage* imgHSV = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3);
    cvCvtColor(src, imgHSV, CV_RGB2HLS);
    cvNamedWindow("T1");
    cvShowImage("T1", imgHSV); // <-- Shows up like the image below

    IplImage* imgThreshed = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
    cvInRangeS(imgHSV, min, max, imgThreshed);
    cvNamedWindow("T2");
    cvShowImage("T2", imgThreshed); // <-- SHOWS UP PITCH BLACK!
}


这就是"T1" 窗口最终的样子(我想这是正确的?)



记住因为颜色范围向量被存储为 RGB(并且 OpenCV 在内部将此顺序反转为 BGR),我已经转换了最小/最大值在将它们传递给 ShowThreshedImage() 之前进入 HLS,如下所示:

CvScalar rgbPixelToHSV(const PixelColor& pixelColor)
{
    WORD h = 0, s = 0, l = 0;
    ColorRGBToHLS(RGB(pixelColor.val[2], pixelColor.val[1], pixelColor.val[0]), &h, &l, &s);
    return PixelColor(h, s, l);
}

//...(elsewhere in code)
if(m_colorRange.size() > 0)
    m_minHSV = rgbPixelToHSV(m_colorRange[0]);
if(m_colorRange.size() > 1)
    m_maxHSV = rgbPixelToHSV(m_colorRange[m_colorRange.size() - 1]);

ShowThreshedImage(m_imgSrc,  m_minHSV, m_maxHSV);


...但是即使没有这种转换并且只是简单地传递 RGB 值,结果仍然是一个全黑的图像。我什至尝试过手动插入某些最小/最大值,我得到的最好结果是一些点亮的像素(尽管是不正确的像素)。

问题:

我在这里做错了什么?
cvInRangeS 方法有什么我不明白的地方吗? 我是否需要逐步检查每一种颜色才能正确地将所选范围从源图像中设置为阈值?
有没有其他方法可以做到这一点?

感谢您的宝贵时间。

更新:

我发现cvInRangeS 期望min所有值 低于max 的值。 But when a range of colors are selected, there doesn't appear to be any guarantee that this will be the case, often resulting in a black thresholded image. 并且交换值以强制执行此规则可能会导致新范围内出现不需要的颜色(在某些情况下,这可能包括所有种颜色,而不仅仅是所需的颜色)。

所以我认为这里真正的问题是:
“如何分割 RGB 颜色数组,并使用它们对图像进行阈值处理?”

【问题讨论】:

  • 你混淆了最小值和最大值吗?只是一个猜测 - 尝试交换它们。
  • No... 并且如 更新 部分所述,您需要在 min.val &gt; max.val 处交换 min 和 max 的每个元素,这通常会导致不同的或不受欢迎的颜色范围。
  • 是的,你更新了你的帖子,而我正在考虑它。鉴于 cvInRangeS 似乎没有做你想做的事,我会扫描图像。
  • 如果“扫描”是指遍历图像中的每个像素以检查特定颜色,那么它的效率将变得太低(同时,您需要遍历图像中的每种颜色)数组来检查匹配,导致指数时间增加)。如果只使用几种颜色,它可以工作,但我的项目使用实时更新,它需要每秒至少显示一次更新的窗口图像,以便可以进行对象跟踪和预测。这就是我首先需要对图像进行阈值处理的原因。但我很欣赏你的建议,所以谢谢你。
  • 您的输出 HLS 图像至少是正确的。 OpenCV 无法显示 HLS,所以将其转换为 RGB,看起来很奇怪。

标签: c++ c opencv colors pixel


【解决方案1】:

您的问题可能是由 OpenCV 维护的值范围与实例 MSpaint 不同的简单事实引起的。例如,绘画中的 HSV 颜色空间为 360,100,100,而在 OpenCV 中为 180,255,255。检查您在 openCV 中的输入值,但在单击某个像素时输出像素值。 inRangeS 应该是完成这项工作的正确工具。也就是说,在 RGB 中它应该也能正常工作,因为范围与绘画中的相同。

cvSetMouseCallback("MyWindow", mouseEvent, (void*) &myImage);

void mouseEvent(int evt, int x, int y, int flags, void *param) {
    if (evt == CV_EVENT_LBUTTONDOWN) {
        printf("%d %d\n", x, y);
        IplImage* imageSource = (IplImage*) param;
        Mat image(imageSource);
        cout << "Image cols " << image.cols << " rows " << image.rows << endl;
        Mat imageHSV;
        cvtColor(image, imageHSV, CV_BGR2HSV);
        Vec3b p = imageHSV.at<Vec3b > (y, x);
        char text[20];
        sprintf(text, "H=%d, S=%d, V=%d", p[0], p[1], p[2]);
        cout << text << endl;
    }
}

当您通过使用此值了解 HSV 值时,在使用 cvtColor(image, imageHSV, CV_BGR2HSV) 将图像转换为 HSV 后,将这些值用作 in range 方法的下限和上限。这应该使您能够获得所需的结果。

【讨论】:

    【解决方案2】:

    遍历每个像素不会太低效。这正是cvInRangeS 会做的事情 - 请参阅:http://docs.opencv.org/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html#the-efficient-way(我一直这样做,对于合理尺寸的图像来说它是即时的)。

    我会将数组中的颜色视为 3D RGB 空间中的点。找到指定包含所有其他色点的棱镜的两个色点。这只是找到所有 r、g 和 b 值的最小值和最大值。如果这个想法不正确,那么您可能必须对照向量中的每个像素检查每个图像像素。

    那么对于图像中的每个像素:如果 (pixel.r max.r) || (pixel.g max.g) || (pixel.b max.b),否则结果为像素值。

    这一切都应该很容易,只要它真的是你想要的。

    【讨论】:

      猜你喜欢
      • 2017-08-11
      • 2021-12-23
      • 2016-03-03
      • 1970-01-01
      • 2015-10-19
      • 2014-11-30
      • 1970-01-01
      • 2021-02-10
      • 1970-01-01
      相关资源
      最近更新 更多