首页 > 代码库 > OpenCV——基于颜色的物体检测系统

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<cv::Mat> 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<const cv::Vec3b>(r);        const uchar* curr_r_hue = channels[0].ptr<const uchar>(r);        const uchar* curr_r_satur = channels[1].ptr<const uchar>(r);        const uchar* curr_r_value = http://www.mamicode.com/channels[2].ptr<const uchar>(r);        uchar* curr_r_result = result.ptr<uchar>(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<uchar>(r);        cv::Vec3b* curr_r_image = image.ptr<cv::Vec3b>(r);        cv::Vec3b* curr_r_result = result.ptr<cv::Vec3b>(r);        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);}

结果如下:

技术分享

 

 可以看到被子的倒影对于边缘检测产生了很大的影响,若杯体本身跟后边背景的颜色差异不大的话,也很难被检测到。

 

OpenCV——基于颜色的物体检测系统