此次區別於證件照,我試着編寫了一下在複雜背景下分離純色物體的系統,由於只是簡單的編程,因此結果有待優化,先分析一下實驗環境: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); }
結果以下:
能夠看到被子的倒影對於邊緣檢測產生了很大的影響,若杯體自己跟後邊背景的顏色差別不大的話,也很難被檢測到。