最近在OpenCV中文論壇上解答了個問題,大概問題是這樣的,把下圖中綠色的八卦部分摳出來:
可以看出問題解決方案很直接:周遊圖檔的每個像素,然後如果像素的顔色接近于綠色,保留此像素;反之遺棄,設值為0。解決思路跟photoshop的顔色選擇功能類似。
問題主要的難點在于如何去比較顔色。輸入圖像的RGB色彩并不适用于顔色的比較;這裡就要引入HSV色彩空間,把RGB顔色轉化成H色相,S飽和度,V色調(亮度)。根據餅狀圖,我們發現綠色範圍(轉化成0~255區間)大概在35到90之間。是以,周遊過程中,我們留下H通道值在35~90範圍内,并且飽和度和色調足夠高(保證像素足夠明亮)的像素;反之,剩下的我們認為是背景和噪點。
代碼如下:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
void getGreenMask(Mat& src, Mat& dst)
{
cvtColor(src, dst, cv::COLOR_BGR2HSV);
int hi = 90, lo = 35;
int lo_sat_v = 50;
for(int i = 0; i < src.rows; i ++)
{
Vec3b * row_ptr = dst.ptr<Vec3b>(i);
for(int j = 0; j < src.cols; j ++)
{
Vec3b hsv = row_ptr[j];
if((hsv[1] < lo_sat_v || hsv[2] < lo_sat_v) // 飽和度和亮度過低
|| (hsv[0] > hi || hsv[0] < lo)) // 色相不在範圍内
{
row_ptr[j] = Vec3b::all(0); // 剔除不在範圍内的像素
}
}
}
cvtColor(dst, dst, cv::COLOR_HSV2BGR);
}
int main(int argc, char* argv[])
{
Mat in = imread("F:/test.JPG");
namedWindow("green channel");
Mat out(in.size(), CV_8UC3, Scalar::all(0));
getGreenMask(in, out);
imshow("green channel", out);
waitKey();
return 0;
}
效果如下圖: