Rec*_*als 5 c c++ opencv pixel colors
我的项目能够捕获目标窗口的位图并将其转换为a IplImage,然后在a中显示该图像cvNamedWindow,进行进一步处理.
为了测试,我将图像加载到MSPaint中,如下所示:
然后允许用户单击并将鼠标拖动到图像内的任意数量的像素上以创建vector<cv::Scalar_<BYTE>>包含这些RGB颜色值的像素.
然后,在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);
Run Code Online (Sandbox Code Playgroud)
...然后以新的方式显示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!
}
Run Code Online (Sandbox Code Playgroud)
这就是"T1"窗口最终看起来像(我认为是正确的?):
请记住,因为颜色范围向量存储为RGB (并且OpenCV内部将此顺序反转为BGR),所以我将最小值/最大值转换为HLS,然后将它们传递到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);
Run Code Online (Sandbox Code Playgroud)
...但即使没有这种转换,只是简单地传递RGB值,结果仍然是一个完全黑色的图像.我甚至尝试手动插入某些最小/最大值,我得到的最好结果是几个像素(尽管是不正确的).
我在这做错了什么?
有什么我不明白的cvInRangeS方法吗?我是否需要逐步浏览每种颜色,以便正确地将所选范围限制在源图像之外?
有没有其他方法来实现这一目标?
感谢您的时间.
我发现,cvInRangeS希望所有数值为min为比低max.但是,当选择一系列颜色时,似乎不能保证会出现这种情况,通常会导致黑色阈值图像.并且交换值以强制执行此规则可能会在新范围内导致不需要的颜色(在某些情况下,这可能包括所有颜色而不仅仅是所需的颜色).
所以我想这里真正的问题是:
"你如何分割RGB颜色数组,并使用它们来阈值图像?"
您的问题可能是由一个简单的事实引起的:OpenCV 维护的值范围与实例 MSpaint 不同。例如,Paint 中的 HSV 颜色空间为 360,100,100,而 OpenCV 中的 HSV 颜色空间为 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;
}
}
Run Code Online (Sandbox Code Playgroud)
当您通过使用此值了解 HSV 值时,请在使用 将图像转换为 HSV 后,将这些值用作 in range 方法的下限和上限cvtColor(image, imageHSV, CV_BGR2HSV)。这应该能让你得到想要的结果。