OpenCV——基於顏色的物體檢測系統

此次區別於證件照,我試着編寫了一下在複雜背景下分離純色物體的系統,由於只是簡單的編程,因此結果有待優化,先分析一下實驗環境:c++

此次的背景雜亂,雖然主體是粉色主導,可是由於光照不統一,色域跨度較大,倒影中也有粉色痕跡,杯壁上有花紋,這種狀況下邊緣檢測偏差很大。編程

爲了讓計算機更好的識別主體顏色,要先將RGB色域轉換爲HSV色域,在HSV色域中,紅色的H值在(0,3)U(156,180)中。粉色的S值飽和度不高,可是比白色要高不少,區間在(50,150)之內。優化

V表明Value,只有黑色或偏黑的顏色V值會偏低,這裏咱們只要設定一個稍高的閾值就能夠了。spa

第一步是大致分離出主體部分,知足條件的顏色區域會被標記爲白色(255)其他爲黑色(0):code

左邊這張圖是Hue單通道的檢測,由於在Opencv中,Hue通道的取值範圍是0-180,紅色在180左右的位置,因此發白,而杯體其餘部分的紅色較深,集中在0-5內,因此顯示爲黑色。blog

將兩個區域相併,並加上對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 = 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圖像,第二個參數爲單通道的灰度二值圖像。element

由於這個mask還有一些瑕疵,爲了去除這部分瑕疵咱們須要使用形態學濾波器:it

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的像素塊會被抹去,結果以下:io

將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);
}

結果以下:

 

 能夠看到被子的倒影對於邊緣檢測產生了很大的影響,若杯體自己跟後邊背景的顏色差別不大的話,也很難被檢測到。

相關文章
相關標籤/搜索