首页 文章

从图像中对一系列颜色进行阈值处理

提问于
浏览
5

计划

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

MSPaint Test Window

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

// 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 中显示,看起来像:

ColorRange Window

问题

现在,这里的想法是创建一个二进制阈值图像(或"mask"),其中所选颜色范围变为白色,源图像的其余部分变为黑色...类似于"Select By Color"工具在GIMP中运行的方式,或者"magic wand"工具在Photoshop中工作...除了不限于特定的轮廓选择,我们实际上是在整个图像上操作 .

我已经阅读了 cvInRangeS ,听起来这正是我所需要的 .
然而,无论出于何种原因, the thresholded image always ends up being totally black ......

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" 窗口最终看起来像(我认为这是正确的?):

HSV Result Window

Bearing in mind 因为颜色范围向量存储为RGB(并且OpenCV内部将此顺序反转为BGR),我已将最小/最大值转换为HLS,然后将其传递到 ShowThreshedImage() ,如下所示:

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 的值 . 但是,当选择一系列颜色时,似乎不能保证会出现这种情况,通常会导致黑色阈值图像 . 并且交换值以强制执行此规则可能会在新范围内导致不需要的颜色(在某些情况下,这可能包括 all 颜色而不仅仅是所需的颜色) .

所以我想这里真正的问题是:
"How do you segment an array of RGB colors, and use them to threshold an image?"

2 回答

  • 1

    您的问题可能是由OpenCV维持不同的值范围而不是instanc MSpaint的简单事实引起的 . 例如,油漆中的HSV颜色空间为360,100,100,而在OpenCV中为180,255,255 . 在openCV bu中检查输入值,在单击某个像素时输出像素值 . 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后,将这些值用作范围内方法的下限和上限 . 这应该可以让你获得理想的结果 .

  • 0

    迭代每个像素并不会太低效 . 这正是 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 <min.r)||,结果为黑色(pixel.r> max.r)|| (pixel.g <min.g)|| (pixel.g> max.g)|| (pixel.b <min.b)|| (pixel.b> max.b),否则结果是像素值 .

    这一切都应该很容易,只要它实际上是你想要的 .

相关问题