在本教程中,了解计算机视觉中使用的流行色彩空间,并将其用于基于颜色的分割。
使用OpenCV色彩空间
- 不同的颜色空间
- RGB颜色空间
- LAB颜色空间
- YCrCB 颜色空间
- HSV颜色空间
- 如何使用这些颜色空间进行分割
- 简单方法
文章内容如下:
- 首先,我们将了解如何在OpenCV中读取图像并将其转换为不同的颜色空间,并了解每个颜色空间的不同通道为我们提供了哪些新信息。
- 我们将应用掩码,来做一个简单的颜色分割算法,在算法中使用系统的方法来选择指定的颜色:1.正确的颜色空间。2.分割的正确阈值。
不同的颜色空间
在本节中,将介绍计算机视觉中使用的一些重要颜色空间。
通过加载一个立方体的图像。将讲解不同的颜色空间。
首先是,Opencv默认情况下,它将以BGR格式加载。我们可以使用OpenCV函数cvtColor()在不同的颜色空间之间进行转换,稍后将显示。
Python
#python
img = cv2.imread('cube.jpg')
C++
img = cv::imread('cube.jpg')
RGB颜色空间
RGB颜色空间具有以下属性
- 它是一个加法颜色空间,其中颜色是通过红色、绿色和蓝色值的线性组合获得的。
- 这三个通道与撞击表面的光量相关。
让我们将这两个图像分成R、G和B分量,并观察它们,以获得对颜色空间的更多了解。
LAB颜色空间
Lab颜色空间有三个通道
- L–亮度(强度)
- a–颜色成分范围从绿色到红色
- b–从蓝色到黄色的颜色成分
Lab颜色空间与RGB颜色空间非常不同。在RGB颜色空间中,颜色信息被分成三个通道,但相同的三个通道也编码亮度信息。但在Lab颜色空间中,L通道独立于颜色信息,仅对亮度进行编码。其他两个通道编码颜色。
它具有以下属性。
- 感知均匀的颜色空间,近似于我们感知颜色的方式。
- 独立于设备(捕获或显示)。
- 在Adobe Photoshop中广泛使用。
- 与RGB颜色空间相关的是一个复杂的变换方程。
让我们看看Lab颜色空间中的两幅图像,它们被分成三个通道。
Python
#python
LAB = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
C++
//C++
cv::cvtColor(img, LAB, cv::COLOR_BGR2LAB);
YCrCB 颜色空间
YCrCb颜色空间源自RGB颜色空间,具有以下三种通道。
- 从RGB获得的亮度经过Y–伽马校正后的分量。
- Cr=R–Y(红色分量距离Luminance距离)。
- Cb=B–Y(蓝色分量距离Luminance距离)。
此颜色空间具有以下属性。
- 将亮度和色度分量分离到不同的通道中。
- 主要用于电视传输的压缩(Cr和Cb成分)。
Python
#python
YCB = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
C++
//C++
cv::cvtColor(img, YCB, cv::COLOR_BGR2YCrCb);
HSV颜色空间
HSV颜色空间包含以下三个通道
- H–色调(颜色波长)。
- S–饱和度(颜色的纯度/色调)。
- V–值(强度)。
其中H通道可以用它来描述颜色最为直观
魔方图像的H、S和V分量如下所示。
Python
#python
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
C++
//C++
cv::cvtColor(img,hsv, cv::COLOR_BGR2HSV);
如何使用这些颜色空间进行分割
简单方法
现在我们已经对不同的颜色空间有了一些了解,让我们首先尝试使用它们来检测立方体中的蓝色。
应用阈值进行分割
从图像中所有像素,提取值接近蓝色的像素。我们可以设定一个蓝色颜色范围,来更可能获取到所有的蓝色像素
再使用opencv.inRange()函数查找这个范围内所有的像素,再使用bitwise_and运算从图像中获取这部分像素。(也就是除蓝色范围内的所有像素,其他所有内容去掉)
还要注意,为了将一个像素转换为另一个颜色空间,我们首先需要将1D矩阵转换为3D矩阵。
Python
import cv2
import numpy as np
bright = cv2.imread('data/cube.jpg')
brightLAB = cv2.cvtColor(bright, cv2.COLOR_BGR2LAB)
brightYCB = cv2.cvtColor(bright, cv2.COLOR_BGR2YCrCb)
brightHSV = cv2.cvtColor(bright, cv2.COLOR_BGR2HSV)
bgr = [255, 50, 50]
thresh = 50
minBGR = np.array([bgr[0] - thresh, bgr[1] - thresh, bgr[2] - thresh])
maxBGR = np.array([bgr[0] + thresh, bgr[1] + thresh, bgr[2] + thresh])
maskBGR = cv2.inRange(bright,minBGR,maxBGR)
resultBGR = cv2.bitwise_and(bright, bright, mask = maskBGR)
#convert 1D array to 3D, then convert it to HSV and take the first element
# this will be same as shown in the above figure [65, 229, 158]
hsv = cv2.cvtColor( np.uint8([[bgr]] ), cv2.COLOR_BGR2HSV)[0][0]
minHSV = np.array([hsv[0] - thresh, hsv[1] - thresh, hsv[2] - thresh])
maxHSV = np.array([hsv[0] + thresh, hsv[1] + thresh, hsv[2] + thresh])
maskHSV = cv2.inRange(brightHSV, minHSV, maxHSV)
resultHSV = cv2.bitwise_and(brightHSV, brightHSV, mask = maskHSV)
#convert 1D array to 3D, then convert it to YCrCb and take the first element
ycb = cv2.cvtColor( np.uint8([[bgr]] ), cv2.COLOR_BGR2YCrCb)[0][0]
minYCB = np.array([ycb[0] - thresh, ycb[1] - thresh, ycb[2] - thresh])
maxYCB = np.array([ycb[0] + thresh, ycb[1] + thresh, ycb[2] + thresh])
maskYCB = cv2.inRange(brightYCB, minYCB, maxYCB)
resultYCB = cv2.bitwise_and(brightYCB, brightYCB, mask = maskYCB)
#convert 1D array to 3D, then convert it to LAB and take the first element
lab = cv2.cvtColor( np.uint8([[bgr]] ), cv2.COLOR_BGR2LAB)[0][0]
minLAB = np.array([lab[0] - thresh, lab[1] - thresh, lab[2] - thresh])
maxLAB = np.array([lab[0] + thresh, lab[1] + thresh, lab[2] + thresh])
maskLAB = cv2.inRange(brightLAB, minLAB, maxLAB)
resultLAB = cv2.bitwise_and(brightLAB, brightLAB, mask = maskLAB)
cv2.imshow("Result BGR", resultBGR)
cv2.imshow("Result HSV", resultHSV)
cv2.imshow("Result YCB", resultYCB)
cv2.imshow("Output LAB", resultLAB)
cv2.waitKey(0)
cv2.destroyAllWindows()
C++
//C++ code
cv::Vec3b bgrPixel(255, 50, 50);
// Create Mat object from vector since cvtColor accepts a Mat object
Mat3b bgr (bgrPixel);
//Convert pixel values to other color spaces.
Mat3b hsv,ycb,lab;
cvtColor(bgr, ycb, COLOR_BGR2YCrCb);
cvtColor(bgr, hsv, COLOR_BGR2HSV);
cvtColor(bgr, lab, COLOR_BGR2Lab);
//Get back the vector from Mat
Vec3b hsvPixel(hsv.at<Vec3b>(0,0));
Vec3b ycbPixel(ycb.at<Vec3b>(0,0));
Vec3b labPixel(lab.at<Vec3b>(0,0));
int thresh = 50;
cv::Scalar minBGR = cv::Scalar(bgrPixel.val[0] - thresh, bgrPixel.val[1] - thresh, bgrPixel.val[2] - thresh)
cv::Scalar maxBGR = cv::Scalar(bgrPixel.val[0] + thresh, bgrPixel.val[1] + thresh, bgrPixel.val[2] + thresh)
cv::Mat maskBGR, resultBGR;
cv::inRange(bright, minBGR, maxBGR, maskBGR);
cv::bitwise_and(bright, bright, resultBGR, maskBGR);
cv::Scalar minHSV = cv::Scalar(hsvPixel.val[0] - thresh, hsvPixel.val[1] - thresh, hsvPixel.val[2] - thresh)
cv::Scalar maxHSV = cv::Scalar(hsvPixel.val[0] + thresh, hsvPixel.val[1] + thresh, hsvPixel.val[2] + thresh)
cv::Mat maskHSV, resultHSV;
cv::inRange(brightHSV, minHSV, maxHSV, maskHSV);
cv::bitwise_and(brightHSV, brightHSV, resultHSV, maskHSV);
cv::Scalar minYCB = cv::Scalar(ycbPixel.val[0] - thresh, ycbPixel.val[1] - thresh, ycbPixel.val[2] - thresh)
cv::Scalar maxYCB = cv::Scalar(ycbPixel.val[0] + thresh, ycbPixel.val[1] + thresh, ycbPixel.val[2] + thresh)
cv::Mat maskYCB, resultYCB;
cv::inRange(brightYCB, minYCB, maxYCB, maskYCB);
cv::bitwise_and(brightYCB, brightYCB, resultYCB, maskYCB);
cv::Scalar minLAB = cv::Scalar(labPixel.val[0] - thresh, labPixel.val[1] - thresh, labPixel.val[2] - thresh)
cv::Scalar maxLAB = cv::Scalar(labPixel.val[0] + thresh, labPixel.val[1] + thresh, labPixel.val[2] + thresh)
cv::Mat maskLAB, resultLAB;
cv::inRange(brightLAB, minLAB, maxLAB, maskLAB);
cv::bitwise_and(brightLAB, brightLAB, resultLAB, maskLAB);
cv2::imshow("Result BGR", resultBGR)
cv2::imshow("Result HSV", resultHSV)
cv2::imshow("Result YCB", resultYCB)
cv2::imshow("Output LAB", resultLAB)
我们也可以设计一个GUI,通过人工调节颜色的范围来观察图像中的变化,获取想要的 颜色
python代码如下:
import cv2
import numpy as np
import os
#定义HSV滑块的值
def empty(a):
h_min = cv2.getTrackbarPos("Hue Min","TrackBars")
h_max = cv2.getTrackbarPos("Hue Max", "TrackBars")
s_min = cv2.getTrackbarPos("Sat Min", "TrackBars")
s_max = cv2.getTrackbarPos("Sat Max", "TrackBars")
v_min = cv2.getTrackbarPos("Val Min", "TrackBars")
v_max = cv2.getTrackbarPos("Val Max", "TrackBars")
print(h_min, h_max, s_min, s_max, v_min, v_max)
return h_min, h_max, s_min, s_max, v_min, v_max
#图片拼接,将4张图片拼接到一起
def stackImages(scale,imgArray):
rows = len(imgArray)
cols = len(imgArray[0])
rowsAvailable = isinstance(imgArray[0], list)
width = imgArray[0][0].shape[1]
height = imgArray[0][0].shape[0]
if rowsAvailable:
for x in range ( 0, rows):
for y in range(0, cols):
if imgArray[x][y].shape[:2] == imgArray[0][0].shape [:2]:
imgArray[x][y] = cv2.resize(imgArray[x][y], (0, 0), None, scale, scale)
else:
imgArray[x][y] = cv2.resize(imgArray[x][y], (imgArray[0][0].shape[1], imgArray[0][0].shape[0]), None, scale, scale)
if len(imgArray[x][y].shape) == 2: imgArray[x][y]= cv2.cvtColor( imgArray[x][y], cv2.COLOR_GRAY2BGR)
imageBlank = np.zeros((height, width, 3), np.uint8)
hor = [imageBlank]*rows
hor_con = [imageBlank]*rows
for x in range(0, rows):
hor[x] = np.hstack(imgArray[x])
ver = np.vstack(hor)
else:
for x in range(0, rows):
if imgArray[x].shape[:2] == imgArray[0].shape[:2]:
imgArray[x] = cv2.resize(imgArray[x], (0, 0), None, scale, scale)
else:
imgArray[x] = cv2.resize(imgArray[x], (imgArray[0].shape[1], imgArray[0].shape[0]), None,scale, scale)
if len(imgArray[x].shape) == 2: imgArray[x] = cv2.cvtColor(imgArray[x], cv2.COLOR_GRAY2BGR)
hor= np.hstack(imgArray)
ver = hor
return ver
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
path = os.path.join(root,'data/cube.jpg')
cv2.namedWindow("TrackBars")
# 创建一个窗口,放置6个滑动条rackBars")
cv2.resizeWindow("TrackBars",640,240)
cv2.createTrackbar("Hue Min","TrackBars",0,179,empty)
cv2.createTrackbar("Hue Max","TrackBars",19,179,empty)
cv2.createTrackbar("Sat Min","TrackBars",110,255,empty)
cv2.createTrackbar("Sat Max","TrackBars",240,255,empty)
cv2.createTrackbar("Val Min","TrackBars",153,255,empty)
cv2.createTrackbar("Val Max","TrackBars",255,255,empty)
while True:
img = cv2.imread(path)
imgHSV = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
# 调用回调函数,获取滑动条的值
h_min = cv2.getTrackbarPos("Hue Min","TrackBars")
h_max = cv2.getTrackbarPos("Hue Max", "TrackBars")
s_min = cv2.getTrackbarPos("Sat Min", "TrackBars")
s_max = cv2.getTrackbarPos("Sat Max", "TrackBars")
v_min = cv2.getTrackbarPos("Val Min", "TrackBars")
v_max = cv2.getTrackbarPos("Val Max", "TrackBars")
h_min, h_max, s_min, s_max, v_min, v_max = empty(0)
lower = np.array([h_min,s_min,v_min])
upper = np.array([h_max,s_max,v_max])
# 获得指定颜色范围内的掩码
mask = cv2.inRange(imgHSV,lower,upper)
# 对原图图像进行按位与的操作,掩码区域保留
imgResult = cv2.bitwise_and(img,img,mask=mask)
# cv2.imshow("Original",img)
# cv2.imshow("HSV",imgHSV)
# cv2.imshow("Mask", mask)
# cv2.imshow("Result", imgResult)
imgStack = stackImages(0.6,([img,imgHSV],[mask,imgResult]))
cv2.imshow("Stacked Images", imgStack)
cv2.waitKey(1)