這次區別於證件照,我試着編寫了一下在復雜背景下分離純色物體的系統,因為只是簡單的編程,所以結果有待優化,先分析一下實驗環境:
這次的背景雜亂,雖然主體是粉色主導,但是因為光照不統一,色域跨度較大,倒影中也有粉色痕跡,杯壁上有花紋,這種情況下邊緣檢測誤差很大。
為了讓計算機更好的識別主體顏色,要先將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 = 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); }
結果如下:
可以看到被子的倒影對於邊緣檢測產生了很大的影響,若杯體本身跟后邊背景的顏色差異不大的話,也很難被檢測到。