OpenCV——基于颜色的物体检测系统
这次区别于证件照,我试着编写了一下在复杂背景下分离纯色物体的系统,因为只是简单的编程,所以结果有待优化,先分析一下实验环境:
这次的背景杂乱,虽然主体是粉色主导,但是因为光照不统一,色域跨度较大,倒影中也有粉色痕迹,杯壁上有花纹,这种情况下边缘检测误差很大。
为了让计算机更好的识别主体颜色,要先将RGB色域转换为HSV色域,在HSV色域中,红色的H值在(0,3)U(156,180)中。粉色的S值饱和度不高,但是比白色要高很多,区间在(50,150)以内。
V代表Value,只有黑色或偏黑的颜色V值会偏低,这里我们只要设定一个稍高的阈值就可以了。
第一步是大体分离出主体部分,满足条件的颜色区域会被标记为白色(255)其余为黑色(0):
左边这张图是Hue单通道的检测,因为在Opencv中,Hue通道的取值范围是0-180,红色在180左右的位置,所以发白,而杯体其他部分的红色较深,集中在0-5内,所以显示为黑色。
将两个区域相并,并加上对value与Saturation的限制,右图既是HSVmask的结果。
复制代码
void ToHSV(cv::Mat image, cv::Mat result) //产出是一个mask
{
cv::Mat hsv_image; //转HSV
hsv_image.create(image.size(), image.type());
cv::cvtColor(image, hsv_image, CV_BGR2HSV);
vector channels;
cv::split(hsv_image, channels);
int num_row = image.rows;
int num_col = image.cols;
for (int r = 0; r < num_row; r++)
{
const cv::Vec3b* curr_r_image = image.ptr(r);
const uchar* curr_r_hue = channels[0].ptr(r);
const uchar* curr_r_satur = channels[1].ptr(r);
const uchar* curr_r_value = channels[2].ptr(r);
uchar* curr_r_result = result.ptr(r);
for (int c = 0; c < num_col; c++)
{
if (((curr_r_hue[c] <= 2 && curr_r_hue[c] >= 0) || (curr_r_hue[c] <= 180 && curr_r_hue[c] >= 150)) && curr_r_value[c]>130 && curr_r_satur[c]>35 && curr_r_satur[c]<150) //找颜色
{
curr_r_result[c] = 255;
}
else
{
curr_r_result[c] = 0;
}
}
}
}
复制代码
这个方法中,参数第一个为3通道RGB图像,第二个参数为单通道的灰度二值图像。
因为这个mask还有一些瑕疵,为了去除这部分瑕疵我们需要使用形态学滤波器:
void Homography(cv::Mat image, cv::Mat Opened) //mask
{
cv::Mat element_9(9, 9, CV_8U, cv::Scalar(1));
cv::morphologyEx(image, Opened, cv::MORPH_OPEN, element_9);
}
形态学滤波只针对二值图像,因此输入输出都是二值图像,structure element为9X9,意味着长宽不足9的像素块会被抹去,结果如下:
将mask运用到原图像上:
复制代码
void copymask(cv::Mat image, cv::Mat openmask, cv::Mat result)
{
int num_row = image.rows;
int num_col = image.cols;
for (int r = 0; r < num_row; r++)
{
uchar* curr_r_open = openmask.ptr®;
cv::Vec3b* curr_r_image = image.ptrcv::Vec3b®;
cv::Vec3b* curr_r_result = result.ptrcv::Vec3b®;
for (int c = 0; c < num_col; c++)
{
if (curr_r_open[c] ==255)
{
curr_r_result[c] = curr_r_image[c];
}
}
}
}
复制代码
结果如下:
可以看到损失了一部分,损失的这部分就是原图中的高光区域,这些区域的颜色因为光的照射变为白色,不好从颜色上区分,也是这种方法的一个盲点。
还有一种区分的办法为边缘检测,在OpenCV中,边缘检测极易实现,但是图片背景过于复杂的话则会产生许多干扰:
复制代码
void edgedetection(cv::Mat image, cv::Mat edge)
{
cv::morphologyEx(image, edge, cv::MORPH_GRADIENT, cv::Mat());
int threshold =240;
cv::threshold(edge, edge, threshold, 255, cv::THRESH_BINARY);
}
复制代码
结果如下:
可以看到被子的倒影对于边缘检测产生了很大的影响,若杯体本身跟后边背景的颜色差异不大的话,也很难被检测到。