OpenCV顏色轉換和面板檢測
本筆記重點記錄OpenCV中的顏色轉換和利用色彩空間的特性進行面板檢測
顏色轉換
實現原理
之所以要引入色調/飽和度/亮度的色彩空間概念,是因為人們喜歡憑直覺分辨各種顏色,而它與這種方式吻合。實際上,人類更喜歡用色彩、彩度、亮度等直觀的屬性來描述顏色,而大多 數直覺色彩空間正是基於這三個屬性。
- 色調(hue)表示主色,我們使用的顏色名稱(例如綠色、 黃色和紅色)就對應了不同的色調值;
- 飽和度(saturation)表示顏色的鮮豔程度,柔和的顏色飽 和度較低,而彩虹的顏色飽和度就很高
- 亮度(brightness)是一個主觀的屬性,表示某種 顏色的光亮程度。
其他直覺色彩空間使用顏色明度(value)或顏色亮度(lightness)的概念描述 有關顏色的強度。
利用這些顏色概念,能儘可能地模擬人類對顏色的直觀感知。因此,它們沒有標準的定義。 根據文獻資料,色調、飽和度和亮度都有多種不同的定義和計算公式。OpenCV 建議的兩種直覺 色彩空間的實現是 HSV 和 HLS 色彩空間,它們的轉換公式略有不同,但是結果非常相似。
亮度成分可能是最容易解釋的。在 OpenCV 對 HSV 的實現中,它被定義為三個 BGR 成分中 的最大值,以非常簡化的方式實現了亮度的概念。為了讓定義更符合人類視覺系統,應該使用均 勻感知的色彩空間 Lab和 Luv的 L 通道。舉個例子,L 通道已經考慮到了,在強度相同的 情況下,人們會覺得綠色比藍色等顏色的亮度更高。
OpenCV 用一個公式來計算飽和度,該公式基於 BGR 元件的最小值和最大值:
其原理是:灰度顏色包含的 R、G、B 的成分是相等的,相當於一種極不飽和的顏色,因此 它的飽和度是 0(飽和度是一個 0~1.0 的值)。對於 8 點陣圖像,飽和度被調節成一個 0~255 的值, 並且作為灰度影象顯示的時候,較亮區域對應的顏色具有較高的飽和度。
利用opencv把RGB圖片像HSV顏色空間轉變(CV_BGR2HSV)的時候,
H通道的值範圍為: 0-180
S: 0-255
V:0-255
實現程式碼
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <iostream> #include <vector> int main() { // 讀入影象 cv::Mat image= cv::imread("boldt.jpg"); if (!image.data) return 0; // 原始影象 cv::namedWindow("Original image"); cv::imshow("Original image",image); // 轉換成HSV色彩空間 cv::Mat hsv; cv::cvtColor(image, hsv, CV_BGR2HSV); // 把三個通道分割進三幅影象中 std::vector<cv::Mat> channels; cv::split(hsv,channels); // channels[0] 色調 // channels[1] 飽和度 // channels[2] 亮度 // 亮度 cv::namedWindow("Value"); cv::imshow("Value",channels[2]); // display 飽和度 cv::namedWindow("Saturation"); cv::imshow("Saturation",channels[1]); // display 色調 cv::namedWindow("Hue"); cv::imshow("Hue",channels[0]); cv::waitKey(); }
實現效果
面板檢測
8 位版本的色調在 0~180,飽和度在 0~255
實現原理
在對特定物體做初步檢測時,顏色資訊非常有用。例如輔助駕駛程式中的路標檢測功能,就要憑藉標準路標的顏色快速識別可能是路標的資訊。另一個例子是膚色檢測,檢測到的面板區域 可作為影象中有人存在的標誌。手勢識別就經常使用膚色檢測確定手的位置。
膚色檢測領域的大量研究已經表明,來自不同人種的人群的面板顏色,可以在色調、飽和度、色彩空間中很好地歸類。
實現程式碼
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <vector>
void detectHScolor(const cv::Mat& image, // input image
double minHue, double maxHue, // Hue interval
double minSat, double maxSat, // saturation interval
cv::Mat& mask) { // output mask
// convert into HSV space
cv::Mat hsv;
cv::cvtColor(image, hsv, CV_BGR2HSV);
// split the 3 channels into 3 images
std::vector<cv::Mat> channels;
cv::split(hsv, channels);
// channels[0] is the Hue
// channels[1] is the Saturation
// channels[2] is the Value
// Hue masking
cv::Mat mask1; // below maxHue
cv::threshold(channels[0], mask1, maxHue, 255, cv::THRESH_BINARY_INV);
cv::Mat mask2; // over minHue
cv::threshold(channels[0], mask2, minHue, 255, cv::THRESH_BINARY);
cv::Mat hueMask; // hue mask
if (minHue < maxHue)
hueMask = mask1 & mask2;
else // if interval crosses the zero-degree axis
hueMask = mask1 | mask2;
// Saturation masking
// below maxSat
cv::threshold(channels[1], mask1, maxSat, 255, cv::THRESH_BINARY_INV);
// over minSat
cv::threshold(channels[1], mask2, minSat, 255, cv::THRESH_BINARY);
cv::Mat satMask; // saturation mask
satMask = mask1 & mask2;
// combined mask
mask = hueMask&satMask;
}
int main()
{
// 讀入影象
cv::Mat image= cv::imread("girl.jpg");
if (!image.data)
return 0;
// show original image
cv::namedWindow("Original image");
cv::imshow("Original image",image);
// detect skin tone
cv::Mat mask;
detectHScolor(image,
160, 10, // hue from 320 degrees to 20 degrees
25, 166, // saturation from ~0.1 to 0.65
mask);
// show masked image
cv::Mat detected(image.size(), CV_8UC3, cv::Scalar(0, 0, 0));
image.copyTo(detected, mask);
cv::imshow("Detection result",detected);
cv::waitKey();
}
實現效果
可改進的地方
在檢測時沒有考慮顏色的亮度。在實際應用中,排除較高亮度的顏色可以降低把明亮的淡紅色誤認為面板的可能性。所以要想對面板顏色進行可靠和準確的檢測, 還需要更加精確的分析。對不同的影象進行檢測,也很難保證效果都好,因為攝影時影響彩色再 現的因素有很多,如白平衡和光照條件等。