
在本教程中,我們將學習計算機視覺中常用的顏色空間,并使用它進行基于顏色的分割,我們還將共享c++和Python的演示代碼,
我們在許多計算機視覺應用中都遇到了這個問題,包括膚色檢測、交通燈識別等基于顏色的分割,讓我們看看如何幫助他為他的機器人建立一個強大的顏色檢測系統,
文章組織如下:
- 首先,我們將看到如何讀取OpenCV中的影像,并將其轉換為不同的顏色空間,并看看每個顏色空間的不同通道為我們提供了什么新資訊,
- 我們將采用一種簡單的顏色分割演算法,并仔細考慮其缺點,
- 然后我們將進入一些分析,并使用系統的方法進行選擇:(1)正確的顏色空間,(2)正確的分割閾值,
1.不同的顏色空間
在本節中,我們將介紹計算機視覺中使用的一些重要的顏色空間,我們不會描述它們背后的理論,因為它可以在維基百科上找到,相反,我們將建立一個基本的直覺,并學習一些重要的性質,這將對以后的決策有用,
讓我們加載兩個相同立方體的影像,默認情況下以BGR格式加載,我們可以使用OpenCV函式cvtColor()在不同的顏色空間之間進行轉換,稍后會展示,
第一幅影像是在陽光明媚的室外條件下拍攝的,第二幅是在室內正常光照條件下拍攝的,


(1)RGB顏色空間
RGB顏色空間具有以下屬性:
- 它是一個加性色彩空間,其中顏色由紅色、綠色和藍色值的線性組合獲得,
- 這三個通道是由照射到表面的光線量關聯起來的,
讓我們將這兩幅影像分割成R、G和B分量,并觀察它們,以更深入地了解顏色空間,

觀測
如果你看藍色通道,可以看到,在室內光照條件下,第二幅影像中的藍色和白色片段看起來很相似,但在第一幅影像中有明顯的區別,這種不均勻性使得基于顏色的分割在這個顏色空間中非常困難,此外,兩幅影像的值之間存在整體差異,下面我們總結了RGB顏色空間的內在問題: - 重要的知覺不均勻
- 混合色度(顏色相關資訊)和亮度(強度相關資訊)資料,
(2)LAB色彩空間
LAB顏色空間有三個組成部分,
- L:亮度(強度),
- a:顏色成分從綠色到品紅不等,
- b:顏色成分范圍從藍色到黃色,
LAB顏色空間與RGB顏色空間有很大的不同,在RGB顏色空間中,顏色資訊被分為三個通道,但同樣的三個通道也編碼亮度資訊,另一方面,在Lab顏色空間中,L通道與顏色資訊無關,只對亮度進行編碼,另外兩個通道編碼顏色,
它具有以下屬性,
- 感知上一致的顏色空間,近似于我們感知顏色的方式,
- 獨立于設備(捕獲或顯示),
- 在Adobe Photoshop中廣泛使用,
- 與RGB顏色空間有關的是一個復變換方程,

觀測
- 從圖中可以很清楚的看出,光照的變化對L分量的影響最大,
- 包含顏色資訊的A和B分量沒有發生大的變化,
- 室內的顏色資訊與室外的顏色資訊相似
(3)YCrCb彩色空間
YCrCb顏色空間是由RGB顏色空間派生而來的,它有以下三個組成部分,
- Y -經過伽瑪校正后由RGB得到的亮度或亮度分量,
- Cr = R - Y(紅色分量離Luma有多遠),
- Cb = B - Y(藍色分量到Luma的距離),
這個顏色空間具有以下屬性,
- 將亮度和色度組件分離到不同的通道中,
- 主要用于電視傳輸壓縮(Cr和Cb元件),
- 設備相關

觀測 - 亮度和顏色成分的光照變化,與LAB類似的觀測結果,
- 與LAB相比,在室外影像中,紅色和橙色的感知差異更小,
- 白色在所有三種成分中都發生了變化,
(4)HSV彩色空間
HSV顏色空間有以下三個組成部分:
- H -色相(主波長),
- S -飽和度(純度/顏色的深淺),
- V -值(強度),
讓我們列舉它的一些屬性,
- 最好的是,它只使用一個通道來描述顏色(H),使它非常直觀地指定顏色,
- 設備相關

觀測
- 兩幅影像的H分量非常相似,這表明即使在光照變化的情況下,顏色資訊也是完整的,
- 在兩幅影像中S分量也非常相似,
- V分量捕捉落在它上面的光量,因此它會隨著照明的變化而變化,
- 室外影像和室內影像的紅色塊值有很大的差異,這是因為Hue被表示為一個圓,紅色是在起始角度,因此,它可以取[300,360]和[0,60]之間的值,
2.如何使用這些顏色空間進行分割
(1)最簡單地方式
現在我們已經對不同的顏色空間有了一些了解,讓我們首先嘗試使用它們來檢測立方體中的綠色,
步驟1:獲取特定顏色的顏色值
找出每個顏色空間的綠色值的近似范圍,為此,使用一個互動式GUI,你可以通過將滑鼠懸停在影像上來檢查每個像素的所有顏色空間的值,如下所示:

互動式GUI代碼如下:
(1)Python
interactiveColorDetect.py
import cv2,argparse,glob
import numpy as np
# 滑鼠回呼函式
def showPixelValue(event,x,y,flags,param):
global img, combinedResult, placeholder
if event == cv2.EVENT_MOUSEMOVE:
# 從滑鼠在(x,y)中的位置獲取像素值
bgr = img[y,x]
# 將BGR像素轉換為其他顏色格式
ycb = cv2.cvtColor(np.uint8([[bgr]]),cv2.COLOR_BGR2YCrCb)[0][0]
lab = cv2.cvtColor(np.uint8([[bgr]]),cv2.COLOR_BGR2Lab)[0][0]
hsv = cv2.cvtColor(np.uint8([[bgr]]),cv2.COLOR_BGR2HSV)[0][0]
# 創建一個空占位符來顯示值
placeholder = np.zeros((img.shape[0],400,3),dtype=np.uint8)
# 用顏色空間的值填充占位符
cv2.putText(placeholder, "BGR {}".format(bgr), (20, 70), cv2.FONT_HERSHEY_COMPLEX, .9, (255,255,255), 1, cv2.LINE_AA)
cv2.putText(placeholder, "HSV {}".format(hsv), (20, 140), cv2.FONT_HERSHEY_COMPLEX, .9, (255,255,255), 1, cv2.LINE_AA)
cv2.putText(placeholder, "YCrCb {}".format(ycb), (20, 210), cv2.FONT_HERSHEY_COMPLEX, .9, (255,255,255), 1, cv2.LINE_AA)
cv2.putText(placeholder, "LAB {}".format(lab), (20, 280), cv2.FONT_HERSHEY_COMPLEX, .9, (255,255,255), 1, cv2.LINE_AA)
# 將兩個結果合并在一個影像中并排顯示
combinedResult = np.hstack([img,placeholder])
cv2.imshow('PRESS P for Previous, N for Next Image',combinedResult)
if __name__ == '__main__' :
# 加載影像并設定滑鼠回呼函式
global img
files = glob.glob('images/rub*.jpg')
files.sort()
img = cv2.imread(files[0])
img = cv2.resize(img,(400,400))
cv2.imshow('PRESS P for Previous, N for Next Image',img)
# 創建一個空視窗
cv2.namedWindow('PRESS P for Previous, N for Next Image')
# 為滑鼠上的任何事件創建一個回呼函式
cv2.setMouseCallback('PRESS P for Previous, N for Next Image',showPixelValue)
i = 0
while(1):
k = cv2.waitKey(1) & 0xFF
# 檢查檔案夾中的下一個影像
if k == ord('n'):
i += 1
img = cv2.imread(files[i%len(files)])
img = cv2.resize(img,(400,400))
cv2.imshow('PRESS P for Previous, N for Next Image',img)
# 檢查檔案夾中的前一個影像
elif k == ord('p'):
i -= 1
img = cv2.imread(files[i%len(files)])
img = cv2.resize(img,(400,400))
cv2.imshow('PRESS P for Previous, N for Next Image',img)
elif k == 27:
cv2.destroyAllWindows()
break
(2) C++
interactiveColorDetect.cpp
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace cv;
using namespace std;
//全域變數
Mat img, placeholder;
// 滑鼠上的任何事件的回呼函式
void onMouse( int event, int x, int y, int flags, void* userdata )
{
if( event == EVENT_MOUSEMOVE )
{
Vec3b bgrPixel(img.at<Vec3b>(y, x));
Mat3b hsv,ycb,lab;
// 從向量創建 Mat 物件,因為 cvtColor 接受一個 Mat 物件
Mat3b bgr (bgrPixel);
//將單個像素的BGR Mat轉換為其他格式
cvtColor(bgr, ycb, COLOR_BGR2YCrCb);
cvtColor(bgr, hsv, COLOR_BGR2HSV);
cvtColor(bgr, lab, COLOR_BGR2Lab);
//從 Mat 取回向量
Vec3b hsvPixel(hsv.at<Vec3b>(0,0));
Vec3b ycbPixel(ycb.at<Vec3b>(0,0));
Vec3b labPixel(lab.at<Vec3b>(0,0));
// 創建一個空的占位符來顯示值
placeholder = Mat::zeros(img.rows,400,CV_8UC3);
//用顏色空間的值填充占位符
putText(placeholder, format("BGR [%d, %d, %d]",bgrPixel[0],bgrPixel[1],bgrPixel[2]), Point(20, 70), FONT_HERSHEY_COMPLEX, .9, Scalar(255,255,255), 1);
putText(placeholder, format("HSV [%d, %d, %d]",hsvPixel[0],hsvPixel[1],hsvPixel[2]), Point(20, 140), FONT_HERSHEY_COMPLEX, .9, Scalar(255,255,255), 1);
putText(placeholder, format("YCrCb [%d, %d, %d]",ycbPixel[0],ycbPixel[1],ycbPixel[2]), Point(20, 210), FONT_HERSHEY_COMPLEX, .9, Scalar(255,255,255), 1);
putText(placeholder, format("LAB [%d, %d, %d]",labPixel[0],labPixel[1],labPixel[2]), Point(20, 280), FONT_HERSHEY_COMPLEX, .9, Scalar(255,255,255), 1);
Size sz1 = img.size();
Size sz2 = placeholder.size();
//將兩個結果合并在一個影像中并排顯示
Mat combinedResult(sz1.height, sz1.width+sz2.width, CV_8UC3);
Mat left(combinedResult, Rect(0, 0, sz1.width, sz1.height));
img.copyTo(left);
Mat right(combinedResult, Rect(sz1.width, 0, sz2.width, sz2.height));
placeholder.copyTo(right);
imshow("PRESS P for Previous, N for Next Image", combinedResult);
}
}
int main( int argc, const char** argv )
{
// 讀取輸入影像
int image_number = 0;
int nImages = 10;
if(argc > 1)
nImages = atoi(argv[1]);
char filename[20];
sprintf(filename,"images/rub%02d.jpg",image_number%nImages);
img = imread(filename);
// 將影像大小調整為 400x400
Size rsize(400,400);
resize(img,img,rsize);
if(img.empty())
{
return -1;
}
// 創建一個空視窗
namedWindow("PRESS P for Previous, N for Next Image", WINDOW_AUTOSIZE);
// 為滑鼠上的任何事件創建一個回呼函式
setMouseCallback( "PRESS P for Previous, N for Next Image", onm ouse );
imshow( "PRESS P for Previous, N for Next Image", img );
while(1)
{
char k = waitKey(1) & 0xFF;
if (k == 27)
break;
//檢查檔案夾中的下一個影像
if (k =='n')
{
image_number++;
sprintf(filename,"images/rub%02d.jpg",image_number%nImages);
img = imread(filename);
resize(img,img,rsize);
}
//檢查檔案夾中先前的影像
else if (k =='p')
{
image_number--;
sprintf(filename,"images/rub%02d.jpg",image_number%nImages);
img = imread(filename);
resize(img,img,rsize);
}
}
return 0;
}
步驟2:應用閾值進行分割
從影像中提取出與綠色像素值相近的所有像素,我們可以為每個顏色空間取一個+/- 40的范圍,并檢查結果看起來如何,我們將使用opencv函式inRange()來查找綠色像素的mask,然后使用bitwise_and()操作使用mask從影像中獲取綠色像素,
interactiveColorSegment.py
(1)Python
import cv2,time,argparse,glob
import numpy as np
# 要跟蹤的全域變數
show = False
def onTrackbarActivity(x):
global show
show = True
pass
if __name__ == '__main__' :
# 獲取檔案名
files = glob.glob('images/rub*.jpg')
files.sort()
# 加載圖片
original = cv2.imread(files[0])
# 調整影像大小
rsize = 250
original = cv2.resize(original,(rsize,rsize))
# 螢屏上視窗開始的位置
initialX = 50
initialY = 50
# 創建顯示影像的視窗
cv2.namedWindow('P-> Previous, N-> Next',cv2.WINDOW_AUTOSIZE)
cv2.namedWindow('SelectBGR',cv2.WINDOW_AUTOSIZE)
cv2.namedWindow('SelectHSV',cv2.WINDOW_AUTOSIZE)
cv2.namedWindow('SelectYCB',cv2.WINDOW_AUTOSIZE)
cv2.namedWindow('SelectLAB',cv2.WINDOW_AUTOSIZE)
# 移動視窗,將它們水平堆疊
cv2.moveWindow('P-> Previous, N-> Next',initialX,initialY)
cv2.moveWindow('SelectBGR',initialX + (rsize + 5),initialY)
cv2.moveWindow('SelectHSV',initialX + 2*(rsize + 5),initialY)
cv2.moveWindow('SelectYCB',initialX + 3*(rsize + 5),initialY)
cv2.moveWindow('SelectLAB',initialX + 4*(rsize + 5),initialY)
#創建跟蹤條以獲取YCrCb的值
cv2.createTrackbar('CrMin','SelectYCB',0,255,onTrackbarActivity)
cv2.createTrackbar('CrMax','SelectYCB',0,255,onTrackbarActivity)
cv2.createTrackbar('CbMin','SelectYCB',0,255,onTrackbarActivity)
cv2.createTrackbar('CbMax','SelectYCB',0,255,onTrackbarActivity)
cv2.createTrackbar('YMin','SelectYCB',0,255,onTrackbarActivity)
cv2.createTrackbar('YMax','SelectYCB',0,255,onTrackbarActivity)
#創建跟蹤條來獲取HSV的值
cv2.createTrackbar('HMin','SelectHSV',0,180,onTrackbarActivity)
cv2.createTrackbar('HMax','SelectHSV',0,180,onTrackbarActivity)
cv2.createTrackbar('SMin','SelectHSV',0,255,onTrackbarActivity)
cv2.createTrackbar('SMax','SelectHSV',0,255,onTrackbarActivity)
cv2.createTrackbar('VMin','SelectHSV',0,255,onTrackbarActivity)
cv2.createTrackbar('VMax','SelectHSV',0,255,onTrackbarActivity)
#創建跟蹤條來獲取BGR的值
cv2.createTrackbar('BGRBMin','SelectBGR',0,255,onTrackbarActivity)
cv2.createTrackbar('BGRBMax','SelectBGR',0,255,onTrackbarActivity)
cv2.createTrackbar('BGRGMin','SelectBGR',0,255,onTrackbarActivity)
cv2.createTrackbar('BGRGMax','SelectBGR',0,255,onTrackbarActivity)
cv2.createTrackbar('BGRRMin','SelectBGR',0,255,onTrackbarActivity)
cv2.createTrackbar('BGRRMax','SelectBGR',0,255,onTrackbarActivity)
#創建跟蹤條來獲取LAB的值
cv2.createTrackbar('LABLMin','SelectLAB',0,255,onTrackbarActivity)
cv2.createTrackbar('LABLMax','SelectLAB',0,255,onTrackbarActivity)
cv2.createTrackbar('LABAMin','SelectLAB',0,255,onTrackbarActivity)
cv2.createTrackbar('LABAMax','SelectLAB',0,255,onTrackbarActivity)
cv2.createTrackbar('LABBMin','SelectLAB',0,255,onTrackbarActivity)
cv2.createTrackbar('LABBMax','SelectLAB',0,255,onTrackbarActivity)
# 初始顯示所有影像
cv2.imshow('SelectHSV',original)
cv2.imshow('SelectYCB',original)
cv2.imshow('SelectLAB',original)
cv2.imshow('SelectBGR',original)
i = 0
while(1):
cv2.imshow('P-> Previous, N-> Next',original)
k = cv2.waitKey(1) & 0xFF
# 檢查檔案夾中的下一個影像
if k == ord('n'):
i += 1
original = cv2.imread(files[i%len(files)])
original = cv2.resize(original,(rsize,rsize))
show = True
# 檢查檔案夾中的先前影像
elif k == ord('p'):
i -= 1
original = cv2.imread(files[i%len(files)])
original = cv2.resize(original,(rsize,rsize))
show = True
# 按下esc鍵后關閉所有視窗
elif k == 27:
break
if show: # 如果在軌跡欄上有任何事件
show = False
# 從BGR跟蹤器獲取值
BMin = cv2.getTrackbarPos('BGRBMin','SelectBGR')
GMin = cv2.getTrackbarPos('BGRGMin','SelectBGR')
RMin = cv2.getTrackbarPos('BGRRMin','SelectBGR')
BMax = cv2.getTrackbarPos('BGRBMax','SelectBGR')
GMax = cv2.getTrackbarPos('BGRGMax','SelectBGR')
RMax = cv2.getTrackbarPos('BGRRMax','SelectBGR')
minBGR = np.array([BMin, GMin, RMin])
maxBGR = np.array([BMax, GMax, RMax])
# 從HSV跟蹤器獲取值
HMin = cv2.getTrackbarPos('HMin','SelectHSV')
SMin = cv2.getTrackbarPos('SMin','SelectHSV')
VMin = cv2.getTrackbarPos('VMin','SelectHSV')
HMax = cv2.getTrackbarPos('HMax','SelectHSV')
SMax = cv2.getTrackbarPos('SMax','SelectHSV')
VMax = cv2.getTrackbarPos('VMax','SelectHSV')
minHSV = np.array([HMin, SMin, VMin])
maxHSV = np.array([HMax, SMax, VMax])
# 從LAB跟蹤器獲取值
LMin = cv2.getTrackbarPos('LABLMin','SelectLAB')
AMin = cv2.getTrackbarPos('LABAMin','SelectLAB')
BMin = cv2.getTrackbarPos('LABBMin','SelectLAB')
LMax = cv2.getTrackbarPos('LABLMax','SelectLAB')
AMax = cv2.getTrackbarPos('LABAMax','SelectLAB')
BMax = cv2.getTrackbarPos('LABBMax','SelectLAB')
minLAB = np.array([LMin, AMin, BMin])
maxLAB = np.array([LMax, AMax, BMax])
# 從YCrCb跟蹤器獲取值
YMin = cv2.getTrackbarPos('YMin','SelectYCB')
CrMin = cv2.getTrackbarPos('CrMin','SelectYCB')
CbMin = cv2.getTrackbarPos('CbMin','SelectYCB')
YMax = cv2.getTrackbarPos('YMax','SelectYCB')
CrMax = cv2.getTrackbarPos('CrMax','SelectYCB')
CbMax = cv2.getTrackbarPos('CbMax','SelectYCB')
minYCB = np.array([YMin, CrMin, CbMin])
maxYCB = np.array([YMax, CrMax, CbMax])
# 將BGR影像轉換為其他顏色空間
imageBGR = np.copy(original)
imageHSV = cv2.cvtColor(original,cv2.COLOR_BGR2HSV)
imageYCB = cv2.cvtColor(original,cv2.COLOR_BGR2YCrCb)
imageLAB = cv2.cvtColor(original,cv2.COLOR_BGR2LAB)
# 使用從軌跡欄獲得的最小值和最大值創建掩碼,并應用bitewise_and操作來獲得結果
maskBGR = cv2.inRange(imageBGR,minBGR,maxBGR)
resultBGR = cv2.bitwise_and(original, original, mask = maskBGR)
maskHSV = cv2.inRange(imageHSV,minHSV,maxHSV)
resultHSV = cv2.bitwise_and(original, original, mask = maskHSV)
maskYCB = cv2.inRange(imageYCB,minYCB,maxYCB)
resultYCB = cv2.bitwise_and(original, original, mask = maskYCB)
maskLAB = cv2.inRange(imageLAB,minLAB,maxLAB)
resultLAB = cv2.bitwise_and(original, original, mask = maskLAB)
# 顯示結果
cv2.imshow('SelectBGR',resultBGR)
cv2.imshow('SelectYCB',resultYCB)
cv2.imshow('SelectLAB',resultLAB)
cv2.imshow('SelectHSV',resultHSV)
cv2.destroyAllWindows()
(2) C++
interactiveColorSegment.cpp
#include "opencv2/opencv.hpp"
#include <iostream>
#include <cstring>
using namespace cv;
using namespace std;
// 要跟蹤的全域變數
bool show = false;
// 為跟蹤欄上的事件創建回呼
void onTrackbarActivity(int pos, void* userdata){
// 只需要在全域變數中指定有一個事件
show = true;
return;
}
int main(int argc, char **argv)
{
int image_number = 0;
int nImages = 10;
if(argc > 1)
nImages = atoi(argv[1]);
char filename[20];
sprintf(filename,"images/rub%02d.jpg",image_number%nImages);
Mat original = imread(filename);
// 影像調整寬度和高度
int resizeHeight = 250;
int resizeWidth = 250;
Size rsize(resizeHeight,resizeWidth);
resize(original, original, rsize);
// 螢屏上視窗開始的位置
int initialX = 50;
int initialY = 50;
// 創建顯示影像的視窗
namedWindow("P-> Previous, N-> Next", WINDOW_AUTOSIZE);
namedWindow("SelectBGR", WINDOW_AUTOSIZE);
namedWindow("SelectHSV", WINDOW_AUTOSIZE);
namedWindow("SelectYCB", WINDOW_AUTOSIZE);
namedWindow("SelectLAB", WINDOW_AUTOSIZE);
// 移動視窗,將它們水平堆疊
moveWindow("P-> Previous, N-> Next", initialX, initialY);
moveWindow("SelectBGR", initialX + 1 * (resizeWidth + 5), initialY);
moveWindow("SelectHSV", initialX + 2 * (resizeWidth + 5), initialY);
moveWindow("SelectYCB", initialX + 3 * (resizeWidth + 5), initialY);
moveWindow("SelectLAB", initialX + 4 * (resizeWidth + 5), initialY);
// 創建跟蹤條以獲取YCrCb的值
createTrackbar("CrMin", "SelectYCB", 0, 255, onTrackbarActivity);
createTrackbar("CrMax", "SelectYCB", 0, 255, onTrackbarActivity);
createTrackbar("CbMin", "SelectYCB", 0, 255, onTrackbarActivity);
createTrackbar("CbMax", "SelectYCB", 0, 255, onTrackbarActivity);
createTrackbar("YMin", "SelectYCB", 0, 255, onTrackbarActivity);
createTrackbar("YMax", "SelectYCB", 0, 255, onTrackbarActivity);
// 創建跟蹤條來獲取HSV的值
createTrackbar("HMin", "SelectHSV", 0, 180, onTrackbarActivity);
createTrackbar("HMax", "SelectHSV", 0, 180, onTrackbarActivity);
createTrackbar("SMin", "SelectHSV", 0, 255, onTrackbarActivity);
createTrackbar("SMax", "SelectHSV", 0, 255, onTrackbarActivity);
createTrackbar("VMin", "SelectHSV", 0, 255, onTrackbarActivity);
createTrackbar("VMax", "SelectHSV", 0, 255, onTrackbarActivity);
// 創建跟蹤條來獲取BGR的值
createTrackbar("BMin", "SelectBGR", 0, 255, onTrackbarActivity);
createTrackbar("BMax", "SelectBGR", 0, 255, onTrackbarActivity);
createTrackbar("GMin", "SelectBGR", 0, 255, onTrackbarActivity);
createTrackbar("GMax", "SelectBGR", 0, 255, onTrackbarActivity);
createTrackbar("RMin", "SelectBGR", 0, 255, onTrackbarActivity);
createTrackbar("RMax", "SelectBGR", 0, 255, onTrackbarActivity);
// 創建跟蹤條來獲取LAB的值
createTrackbar("LMin", "SelectLAB", 0, 255, onTrackbarActivity);
createTrackbar("LMax", "SelectLAB", 0, 255, onTrackbarActivity);
createTrackbar("AMin", "SelectLAB", 0, 255, onTrackbarActivity);
createTrackbar("AMax", "SelectLAB", 0, 255, onTrackbarActivity);
createTrackbar("BMin", "SelectLAB", 0, 255, onTrackbarActivity);
createTrackbar("BMax", "SelectLAB", 0, 255, onTrackbarActivity);
// 初始顯示所有影像
imshow("SelectHSV", original);
imshow("SelectYCB", original);
imshow("SelectLAB", original);
imshow("SelectBGR", original);
//宣告區域變數
int BMin, GMin, RMin;
int BMax, GMax, RMax;
Scalar minBGR, maxBGR;
int HMin, SMin, VMin;
int HMax, SMax, VMax;
Scalar minHSV, maxHSV;
int LMin, aMin, bMin;
int LMax, aMax, bMax;
Scalar minLab, maxLab;
int YMin, CrMin, CbMin;
int YMax, CrMax, CbMax;
Scalar minYCrCb, maxYCrCb;
Mat imageBGR, imageHSV, imageLab, imageYCrCb;
Mat maskBGR, maskHSV, maskLab, maskYCrCb;
Mat resultBGR, resultHSV, resultLab, resultYCrCb;
char k;
while (1){
imshow("P-> Previous, N-> Next", original);
k = waitKey(1) & 0xFF;
//檢查檔案夾中的下一個影像
if (k =='n')
{
image_number++;
sprintf(filename,"images/rub%02d.jpg",image_number%nImages);
original = imread(filename);
resize(original,original,rsize);
show = true;
}
//檢查檔案夾中先前的影像
else if (k =='p')
{
image_number--;
sprintf(filename,"images/rub%02d.jpg",image_number%nImages);
original = imread(filename);
resize(original,original,rsize);
show = true;
}
// 按下esc鍵后關閉所有視窗
if (k == 27){
break;
}
if (show) { //如果在軌跡欄上有任何事件
show = false;
// 從BGR trackbar獲取值
BMin = getTrackbarPos("BMin", "SelectBGR");
GMin = getTrackbarPos("GMin", "SelectBGR");
RMin = getTrackbarPos("RMin", "SelectBGR");
BMax = getTrackbarPos("BMax", "SelectBGR");
GMax = getTrackbarPos("GMax", "SelectBGR");
RMax = getTrackbarPos("RMax", "SelectBGR");
minBGR = Scalar(BMin, GMin, RMin);
maxBGR = Scalar(BMax, GMax, RMax);
// 從HSV軌跡欄獲取值
HMin = getTrackbarPos("HMin", "SelectHSV");
SMin = getTrackbarPos("SMin", "SelectHSV");
VMin = getTrackbarPos("VMin", "SelectHSV");
HMax = getTrackbarPos("HMax", "SelectHSV");
SMax = getTrackbarPos("SMax", "SelectHSV");
VMax = getTrackbarPos("VMax", "SelectHSV");
minHSV = Scalar(HMin, SMin, VMin);
maxHSV = Scalar(HMax, SMax, VMax);
// 從LAB跟蹤欄獲取值
LMin = getTrackbarPos("LMin", "SelectLAB");
aMin = getTrackbarPos("AMin", "SelectLAB");
bMin = getTrackbarPos("BMin", "SelectLAB");
LMax = getTrackbarPos("LMax", "SelectLAB");
aMax = getTrackbarPos("AMax", "SelectLAB");
bMax = getTrackbarPos("BMax", "SelectLAB");
minLab = Scalar(LMin, aMin, bMin);
maxLab = Scalar(LMax, aMax, bMax);
// 從YCrCb軌跡欄獲取值
YMin = getTrackbarPos("YMin", "SelectYCB");
CrMin = getTrackbarPos("CrMin", "SelectYCB");
CbMin = getTrackbarPos("CbMin", "SelectYCB");
YMax = getTrackbarPos("YMax", "SelectYCB");
CrMax = getTrackbarPos("CrMax", "SelectYCB");
CbMax = getTrackbarPos("CbMax", "SelectYCB");
minYCrCb = Scalar(YMin, CrMin, CbMin);
maxYCrCb = Scalar(YMax, CrMax, CbMax);
// 將BGR影像轉換為其他顏色空間
original.copyTo(imageBGR);
cvtColor(original, imageHSV, COLOR_BGR2HSV);
cvtColor(original, imageYCrCb, COLOR_BGR2YCrCb);
cvtColor(original, imageLab, COLOR_BGR2Lab);
// 使用從軌跡欄獲得的最小值和最大值創建掩碼,并應用bitewise_and操作來獲得結果
inRange(imageBGR, minBGR, maxBGR, maskBGR);
resultBGR = Mat::zeros(original.rows, original.cols, CV_8UC3);
bitwise_and(original, original, resultBGR, maskBGR);
inRange(imageHSV, minHSV, maxHSV, maskHSV);
resultHSV = Mat::zeros(original.rows, original.cols, CV_8UC3);
bitwise_and(original, original, resultHSV, maskHSV);
inRange(imageYCrCb, minYCrCb, maxYCrCb, maskYCrCb);
resultYCrCb = Mat::zeros(original.rows, original.cols, CV_8UC3);
bitwise_and(original, original, resultYCrCb, maskYCrCb);
inRange(imageLab, minLab, maxLab, maskLab);
resultLab = Mat::zeros(original.rows, original.cols, CV_8UC3);
bitwise_and(original, original, resultLab, maskLab);
// 結果顯示
imshow("SelectBGR", resultBGR);
imshow("SelectYCB", resultYCrCb);
imshow("SelectLAB", resultLab);
imshow("SelectHSV", resultHSV);
}
}
destroyAllWindows();
return 0;
}

(2)為更好的解決方案進行一些資料分析
第一步:資料收集
我收集了立方體在不同光照條件下的10張影像,分別裁剪每一種顏色,得到6個不同顏色的資料集,你可以從視覺上看到顏色發生了多大的變化,

步驟2:計算密度圖
檢查特定顏色的分布,比如藍色或黃色在不同顏色空間中的分布,密度圖或2D直方圖給出了關于給定顏色值變化的想法,例如,理想情況下,藍色影像的藍色通道的值應該始終為255,但實際上,它分布在0到255之間,
# 匯入所需的包
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
import cv2, glob
import numpy as np
# 指定要繪制的直方圖的顏色
color = 'pieces/yellow'
#圖是否應該是完整的還是縮小的
zoom = 1
# 加載檔案夾中的所有檔案
files = glob.glob(color + '*.jpg')
files.sort()
# 空陣列用于分離的通道
B = np.array([])
G = np.array([])
R = np.array([])
H = np.array([])
S = np.array([])
V = np.array([])
Y = np.array([])
Cr = np.array([])
Cb = np.array([])
LL = np.array([])
LA = np.array([])
LB = np.array([])
# 資料創建
# 將每個檔案中的值附加到各自的通道中
for fi in files[:]:
# BGR
im = cv2.imread(fi)
b = im[:, :, 0]
b = b.reshape(b.shape[0] * b.shape[1])
g = im[:, :, 1]
g = g.reshape(g.shape[0] * g.shape[1])
r = im[:, :, 2]
r = r.reshape(r.shape[0] * r.shape[1])
B = np.append(B, b)
G = np.append(G, g)
R = np.append(R, r)
# HSV
hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
h = hsv[:, :, 0]
h = h.reshape(h.shape[0] * h.shape[1])
s = hsv[:, :, 1]
s = s.reshape(s.shape[0] * s.shape[1])
v = hsv[:, :, 2]
v = v.reshape(v.shape[0] * v.shape[1])
H = np.append(H, h)
S = np.append(S, s)
V = np.append(V, v)
# YCrCb
ycb = cv2.cvtColor(im, cv2.COLOR_BGR2YCrCb)
y = ycb[:, :, 0]
y = y.reshape(y.shape[0] * y.shape[1])
cr = ycb[:, :, 1]
cr = cr.reshape(cr.shape[0] * cr.shape[1])
cb = ycb[:, :, 2]
cb = cb.reshape(cb.shape[0] * cb.shape[1])
Y = np.append(Y, y)
Cr = np.append(Cr, cr)
Cb = np.append(Cb, cb)
# Lab
lab = cv2.cvtColor(im, cv2.COLOR_BGR2LAB)
ll = lab[:, :, 0]
ll = ll.reshape(ll.shape[0] * ll.shape[1])
la = lab[:, :, 1]
la = la.reshape(la.shape[0] * la.shape[1])
lb = lab[:, :, 2]
lb = lb.reshape(lb.shape[0] * lb.shape[1])
LL = np.append(LL, ll)
LA = np.append(LA, la)
LB = np.append(LB, lb)
# 繪制柱狀圖
nbins = 10
plt.figure(figsize=[20, 10])
plt.subplot(2, 3, 1)
plt.hist2d(B, G, bins=nbins, norm=LogNorm())
plt.xlabel('B')
plt.ylabel('G')
plt.title('RGB')
if not zoom:
plt.xlim([0, 255])
plt.ylim([0, 255])
plt.colorbar()
plt.subplot(2, 3, 2)
plt.hist2d(B, R, bins=nbins, norm=LogNorm())
plt.colorbar()
plt.xlabel('B')
plt.ylabel('R')
plt.title('RGB')
if not zoom:
plt.xlim([0, 255])
plt.ylim([0, 255])
plt.subplot(2, 3, 3)
plt.hist2d(R, G, bins=nbins, norm=LogNorm())
plt.colorbar()
plt.xlabel('R')
plt.ylabel('G')
plt.title('RGB')
if not zoom:
plt.xlim([0, 255])
plt.ylim([0, 255])
plt.subplot(2, 3, 4)
plt.hist2d(H, S, bins=nbins, norm=LogNorm())
plt.colorbar()
plt.xlabel('H')
plt.ylabel('S')
plt.title('HSV')
if not zoom:
plt.xlim([0, 180])
plt.ylim([0, 255])
plt.subplot(2, 3, 5)
plt.hist2d(Cr, Cb, bins=nbins, norm=LogNorm())
plt.colorbar()
plt.xlabel('Cr')
plt.ylabel('Cb')
plt.title('YCrCb')
if not zoom:
plt.xlim([0, 255])
plt.ylim([0, 255])
plt.subplot(2, 3, 6)
plt.hist2d(LA, LB, bins=nbins, norm=LogNorm())
plt.colorbar()
plt.xlabel('A')
plt.ylabel('B')
plt.title('LAB')
if not zoom:
plt.xlim([0, 255])
plt.ylim([0, 255])
plt.savefig(color + '.png', bbox_inches='tight')
else:
plt.savefig(color + '-zoom.png', bbox_inches='tight')
plt.show()
當分析blue圖片時候:

當分析green圖片時候:

當分析orange圖片時候:

當分析red圖片時候:

當分析yellow圖片時候:

(3)結論
當光照變化很大時,我們可以看到:
- 理想情況下,我們希望使用顏色通道最緊湊/最集中的密度圖的顏色空間,
- RGB的密度圖急劇膨脹,這意味著通道值的變化非常大,固定一個閾值是一個大問題,固定較高的范圍將檢測到與所需顏色相似的顏色(假陽性)和較低的范圍將不會檢測到不同照明下所需的顏色(假陰性),
- 在HSV中,由于只有H分量包含關于絕對顏色的資訊,因此,它成為我的顏色空間的第一選擇,因為我可以調整一個旋鈕(H)來指定顏色,而不是YCrCb (Cr和Cb)和LAB (A和B)中的兩個旋鈕,
- 比較YCrCb和LAB的圖,發現在LAB的情況下,緊湊程度更高,所以,我的下一個最佳選擇是LAB顏色空間,
(4)最終的效果
在最后一節中,我將展示檢測藍色和黃色塊的結果,方法是從密度圖中提取閾值,并將其應用到各自的顏色空間中,方法與第二節相同,當我們在HSV, YCrCb和LAB顏色空間中作業時,我們不必擔心強度分量,我們只需要為顏色分量指定閾值,圖中顯示了我用來生成結果的值,

在上面的結果中,我直接從密度圖中取了值,我們也可以選擇取密度圖中最密集區域的值,這樣可以更嚴格的控制顏色范圍,這將留下一些洞和散亂的像素,可以用侵蝕和膨脹,然后過濾,
3.顏色空間的其他有用應用
- 直方圖均衡化通常是在灰度影像上進行的,但是,您可以通過將RGB影像轉換為YCbCr并僅對Y通道進行直方圖均衡化來執行彩色影像的均衡化,
- 兩張影像之間通過將影像轉換到Lab彩色空間實作彩色變換
- 許多智能手機的相機應用程式(如谷歌相機或Instagram)的濾鏡都利用這些顏色空間轉換來創建很酷的效果!
參考目錄
https://learnopencv.com/color-spaces-in-opencv-cpp-python/
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/302877.html
標籤:其他
上一篇:人工智能必備數學基礎(二)
下一篇:萌新影像處理(一)
