色彩空间变换
\qquad
色彩空间也称颜色模型、颜色空间、色彩模型等,它是图像在计算机内部的一种存储方式。常见的色彩空间包括 RGB、GRAY、XYZ、YCrCb、HSV 等。色彩空间在物体追踪、特征检测等(简单来说就是识别一个东西)扮演着很重要的角色。毕竟每个物体都有特定的颜色和特征,我们可以应用其物体与环境颜色上的差异,从图像中提取特定范围的颜色并加上一些逻辑性的判断,获取待检测物体的位置。每种色彩空间都有其擅长的问题解决领域,所以在解决具体色彩问题时往往需要进行色彩空间类型转换。 OpenCV 的 cv2.cvtColor()函数用于转换色彩空间类型,其基本格式如下。
dst = cv2.cvtcolor(src,code[,dstcn]])
其参数说明如下:
参数 | 说明 |
---|
dst | 表示转换后的图像 | src | 表示转换前的原图像 | code | 表示色彩空间类型转换码 | dstCn | 表示目标图像的通道数 |
常见的色彩空间类型转换码如下:
参数 | 说明 |
---|
cv2.COLOR_BGR2RGB | 将BGR 色彩空间转换为 RGB 色彩空间 | cv2.COLOR_BGR2GRAY | 将 BGR 色彩空间转换为 GRAY 色彩空间 | cv2.COLOR_BGR2HSV | 将 BGR 色彩空间转换为 HSV 色彩空间 | cv2.COLOR_BGR2YCrCb | 将 BGR 色彩空间转换为 YCrCb 色彩空间 | cv2.COLOR_RGB2BGR | 将 RGB 色彩空间转换为 BGR 色彩空间 | cv2.COLOR_RGB2GRAY | 将 RGB 色彩空间转换为 GRAY 色彩空间 | c2.COLOR_RGB2YCrCb | 将 RGB 色彩空间转换为 YCrCb 色彩空间 | cv2.COLOR_RGB2HSV | 将RGB 色彩空间转换为 HSV 色彩空间 |
RGB色彩空间
\qquad
RGB 色彩空间使用R(Red,红)、G(Green,绿)和B(Blue,蓝)3种基本颜色表示图像像素。RGB 色彩空间中,图像的每个像素用一个三元组表示,三元组中的3个值依次表示红色、绿色和蓝色,依次对应 R、G和B通道。OpenCV默认采用 BGR 色彩空间,它按 B、G和R通道顺序表示图像。 在cv2.cvtColor()函数中使用cv2.COLOR_BGR2RGB转换码可将图像从BGR色彩空间转换RGB色彩空间。
import cv2
img = cv2.imread('bee.jpg')
cv2.imshow('bee', img)
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
cv2.imshow('RGB', img2)
cv2.waitKey(0)
GRAY色彩空间
\qquad
GRAY 色彩空间通常指 8 位灰度图像,其颜色取值范围为[0,255],共 256个灰度级。从 RGB 色彩空间转换为 GRAY 色彩空间的计算公式如下。
G
r
a
y
=
0.299
R
+
0.587
G
+
0.114
B
Gray =0.299R+0.587G+0.114B
Gray=0.299R+0.587G+0.114B 其中,R、G和 B为 RGB 色彩空间中 R、G 和 B 通道的图像。在 cv2.cvtColor()函数中使用 cv2.COLOR_BGR2GRAY 转换码可将图像从BGR 色彩空间转换为 GRAY 色彩空间。
import cv2
img = cv2.imread('bee.jpg')
cv2.imshow('bee', img)
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('GRAY', img2)
cv2.waitKey(0)
YCrCb色彩空间
\qquad
YCrCb 色彩空间用亮度 Y、红色 Cr 和蓝色 Cb 表示图像。从 RGB 色彩空间转换为 YCrCb 色彩空间的计算公式如下。
Y
=
0.299
R
+
0.587
G
+
0.114
B
C
r
=
0.713
(
R
?
Y
)
+
d
e
l
t
a
C
b
=
0.564
(
B
?
Y
)
+
d
e
l
t
a
Y=0.299R+ 0.587G+ 0.114B\\ Cr= 0.713(R- Y)+ delta\\ Cb= 0.564(B- Y)+ delta\\
Y=0.299R+0.587G+0.114BCr=0.713(R?Y)+deltaCb=0.564(B?Y)+delta 其中
d
e
l
t
a
=
{
128
8
位
图
像
32767
16
位
图
像
0.5
单
精
度
图
像
delta= \begin{cases} 128 & 8位图像\\ 32767 & 16位图像 \\ 0.5 & 单精度图像 \\ \end{cases}
delta=??????128327670.5?8位图像16位图像单精度图像? 在cv2.cvtColor()函数中使用 cv2.COLOR_BGR2YCrCb 转换码可将图像从 BGR 色彩空间转换为 YCrCb 色彩空间。
import cv2
img = cv2.imread('bee.jpg')
cv2.imshow('bee', img)
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
cv2.imshow('YCrCb', img2)
cv2.waitKey(0)
HSV色彩空间
\qquad
HSV 色彩空间使用色调(Hue,也称色相)、饱和度(Saturation)和亮度(Value)表示图像。 色调 H表示颜色,用角度表示,取值范围为[0°,360°],从红色开始按逆时针方向计算。例如,红色为 0°、黄色为 60°、绿色为 120°、青色为 180°、蓝色为 240°、紫色为 300°等。饱和度 S 表示颜色接近光谱色的程度,或者表示光谱色中混入白光的比例。光谱色中白光的比例越低,饱和度越高,颜色越深、艳。光谱色中白光比例为 0 时,饱和度达到最高。饱和度的取值范围为[0,1]。亮度V表示颜色明亮的程度,是人眼可感受到的明暗程度,其取值范围为[0,1]。 从 RGB 色彩空间转换为 HSV 色彩空间的计算公式如下:
V
=
m
a
x
(
R
,
G
,
B
)
V = max(R,G,B)
V=max(R,G,B)
S
=
{
V
?
m
i
n
(
R
,
G
,
B
)
V
V
≠
0
0
V
=
0
S= \begin{cases} \frac{V-min(R,G,B)}{V} & V\neq0\\ 0 & V=0 \\ \end{cases}
S={VV?min(R,G,B)?0?V?=0V=0?
H
=
{
60
?
(
G
?
B
)
V
?
m
i
n
(
R
,
G
,
B
)
V
=
R
120
+
60
?
(
B
?
R
)
V
?
m
i
n
(
R
,
G
,
B
)
V
=
G
240
+
60
?
(
R
?
G
)
V
?
m
i
n
(
R
,
G
,
B
)
V
=
B
H= \begin{cases} \frac{60-(G-B)}{V-min(R,G,B)} & V=R\\ 120+\frac{60-(B-R)}{V-min(R,G,B)} & V=G \\ 240+\frac{60-(R-G)}{V-min(R,G,B)} & V=B \\ \end{cases}
H=??????V?min(R,G,B)60?(G?B)?120+V?min(R,G,B)60?(B?R)?240+V?min(R,G,B)60?(R?G)??V=RV=GV=B? 计算结果中如果H<0,则令H= H+360. 在cv2.cvtColor()函数中使用 cv2.COLOR_BGR2HSV转换码可将图像从 BGR 色彩空间转换为 HSV 色彩空间。
import cv2
img = cv2.imread('bee.jpg')
cv2.imshow('BGR', img)
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
cv2.imshow('HSV', img2)
cv2.waitKey(0)
几何变换
缩放
OpenCV的cv2.resize()函数用于缩放图像,其基本格式如下。
dst=cv2.resize(src,dsize[,dst[,fx[,fy[,interpolation]]]])
其参数说明如下:
参数 | 说明 |
---|
dst | 表示转换后的图像 | src | 表示用于缩放的原图像 | dsize | 表示转换后的图像大小 | fx | 表示水平方向的缩放比例 | fy | 表示垂直方向的缩放比例 | interpolation | 表示插值方式。在转换过程中,可能存在一些不能通过转换算法确定值的像素,插值方式决定了如何获得这些像素的值。可用的插值方式如下表 |
参数 | 说明 |
---|
cv2.INTER_NEAREST | 最近邻插值 | cv2.INTER_LINEAR | 双线性插值,默认方式 | cv2.INTER_CUBIC | 3 次样条插值 | cv2.INTER_AREA | 区域插值 | cv2.INTER_LANCZOS4 | Lanczos 插值 | cv2.INTER_LINEAR_EXACT | 位精确双线性插值 | cv2.INTER_MAX | 插值编码掩码 | cv2.WARP_FILL_OUTLIERS | 标志,填充目标图像中的所有像素 | cv2.WARP_INVERSE_MAP | 标志,逆变换 |
cv2.resize()函数在转换图像时,目标图像的类型和大小与转换之前 dst表示的图像无关。目标图像的类型与 src 表示的原图像一致,其大小可通过参数dsize、fx和 fy 来确定。当dsize 参数不为 None时,不管是否设置参数fx和fy,都由dsize 来确定目标图像的大小。dsize是一个二元组,其格式为“(width,height)”,width 表示目标图像的宽度,height表示目标图像的高度。当dsize 参数为 None时,参数fx和fy不能设置为 0。此时,目标图像的宽度为“round(原图像的宽度×fx)”,目标图像的高度为“round(原图像的高度×fy)”。
import cv2
img = cv2.imread('bee.jpg')
sc = [1, 0.2, 0.5, 1.5, 2]
cv2.imshow('showimg', img)
while True:
key = cv2.waitKey()
if 48 <= key <= 52:
x = y = sc[key-48]
img2 = cv2.resize(img, None, fx=x, fy=y)
cv2.imshow('showimg', img2)
翻转
OpenCV 的 cv2.flip()函数用于翻转图像,其基本格式如下。
dst=cv2.flip(src,flipCode)
其参数说明如下:
参数 | 说明 |
---|
dst | 表示转换后的图像 | src | 表示原图像 | fipCode | 表示翻转类型。flip 为 0 时绕 X轴翻转(垂直翻转),fip 为大于 0 的整数时绕y轴翻转(水平翻转),flip 为小于 0 的整数时同时绕 ×轴和 y轴翻转(水平和垂直翻转) |
import cv2
img = cv2.imread('bee.jpg')
cv2.imshow('showimg', img)
while True:
key = cv2.waitKey()
if key == 48:
img2 = img
elif key == 49:
img2 = cv2.flip(img, 0)
elif key == 50:
img2 = cv2.flip(img, 1)
elif key == 51:
img2 = cv2.flip(img, -1)
cv2.imshow('showimg', img2)
仿射
\qquad
仿射变换包含了平移、旋转、缩放等操作,其主要特点是:原图像中的所有平行线在转换后的图像中仍然平行。OpenCV 的 cv2.warpAfine()函数用于实现图像的仿射变换,其基本格式如下。
dst=cv2.warpAffine(src,M,dsize[,dst[,flags[,bonderMode[, borderValue]]]])
其参数说明如下:
参数 | 说明 |
---|
dst | 表示转换后的图像,图像类型和原图像一致,大小由 dsize 决定 | src | 表示原图像 | M | 是一个大小为 2×3的转换矩阵,使用不同的转换矩阵可实现平移、旋转等多种操作 | dsize | 为转换后的图像大小 | flags | 为插值方式,默认值为 cV2.INTER_LINEAR | borderMode | 为边类型,默认值为 cv2.BORDER_CONSTANT | borderValue | 为边界值,默认为 0 |
在 cv2.warpAffine()函数省略可选参数时,图像转换的矩阵运算公式如下:
d
s
t
(
x
,
y
)
=
s
r
c
(
M
11
x
+
M
12
y
+
M
13
,
M
21
x
+
M
22
y
+
M
23
)
dst(x,y)=src(M_{11}x+M_{12y}+M_{13},M_{21}x+M_{22}y+M_{23})
dst(x,y)=src(M11?x+M12y?+M13?,M21?x+M22?y+M23?)
平移
\qquad
平移是指图像沿水平或者垂直方向移动一定的像素。假设
(
x
0
,
y
0
)
(x_0, y_0)
(x0?,y0?)为原图像上的一个点,图像水平平移量为
t
x
t_x
tx?,垂直平移为
t
y
t_y
ty?,则平移后点
(
x
0
,
y
0
)
(x_0,y_0)
(x0?,y0?)坐标将变成
(
x
1
,
y
1
)
(x_1,y_1)
(x1?,y1?)。
(
x
0
,
y
0
)
(x_0, y_0)
(x0?,y0?)和
(
x
1
,
y
1
)
(x_1,y_1)
(x1?,y1?)的关系如下:
{
x
1
=
x
0
+
t
x
y
1
=
y
0
+
t
y
\left\{ \begin{aligned} x_1= x_0+t_x \\ y_1 = y_0+t_y\\ \end{aligned} \right.
{x1?=x0?+tx?y1?=y0?+ty??
其
中
d
s
t
(
x
0
,
y
0
)
=
s
r
c
(
x
0
+
t
x
,
y
0
+
t
y
)
其中dst(x_0,y_0) = src(x_0+t_x,y_0+t_y)
其中dst(x0?,y0?)=src(x0?+tx?,y0?+ty?)
用矩阵表示如下:
[
x
1
y
1
1
]
=
[
1
0
t
x
0
1
t
y
0
0
1
]
[
x
0
y
0
1
]
\left[ \begin{matrix} x_1 \\ y_1 \\ 1\\ \end{matrix} \right] = \left[ \begin{matrix} 1 & 0& t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1\\ \end{matrix} \right] \left[ \begin{matrix} x_0 \\ y_0\\ 1\\ \end{matrix} \right]
???x1?y1?1????=???100?010?tx?ty?1???????x0?y0?1????
其逆运算如下:
[
x
0
y
0
1
]
=
[
1
0
?
t
x
0
1
?
t
y
0
0
1
]
[
x
1
y
1
1
]
\left[ \begin{matrix} x_0\\ y_0 \\ 1\\ \end{matrix} \right] = \left[ \begin{matrix} 1 & 0& -t_x \\ 0 &1& -t_y \\ 0 & 0 & 1\\ \end{matrix} \right] \left[ \begin{matrix} x_1 \\ y_1\\ 1\\ \end{matrix} \right]
???x0?y0?1????=???100?010??tx??ty?1???????x1?y1?1???? 转换矩阵
M
=
[
1
0
t
x
0
1
t
y
]
M=\left[ \begin{matrix} 1 & 0& t_x \\ 0 & 1 & t_y \\ \end{matrix} \right]
M=[10?01?tx?ty??] 这样,平移后的图像上的每一点都可以在原图像中找到对应的点。例如,对于新图中的(0.0)像素,对应原图中的像素
(
?
t
x
,
?
t
y
)
(-t_x,-t_y)
(?tx?,?ty?)。如果
t
x
t_x
tx?或
t
y
t_y
ty?大于 0,则
(
t
x
,
t
y
)
(t_x,t_y)
(tx?,ty?)不在原图中。对于不在原图中的点,可以将它的像素值设置为 0 或则 255(对于灰度图就是黑色或白色)。若有点不在新图中,说明原图中有点被移出显示区域。如果不想丢失被移出的部分图像,可以将新生成的图像宽度扩大
∣
t
x
∣
|t_x|
∣tx?∣,高度扩大
∣
t
y
∣
|t_y|
∣ty?∣。
import cv2
import numpy as np
img = cv2.imread('bee.jpg')
cv2.imshow('img', img)
height = img.shape[0]
width = img.shape[1]
dsize = (width, height)
m = np.float32([[1, 0, 100], [0, 1, 50]])
img2 = cv2.warpAffine(img, m, dsize)
cv2.imshow('imgx+100y+50', img2)
cv2.waitKey(0)
缩放
假设图像的宽度缩放比例为h,高度缩放比例为v, 根据图像转换的矩阵运算公式可得出执行缩放的转换矩阵
M
=
[
h
0
0
0
v
0
]
M=\left[ \begin{matrix} h & 0&0 \\ 0 & v & 0 \\ \end{matrix} \right]
M=[h0?0v?00?]
import cv2
import numpy as np
img = cv2.imread('bee.jpg')
cv2.imshow('img', img)
height = img.shape[0]
width = img.shape[1]
dsize = (width, height)
m = np.float32([[0.5, 0, 0], [0, 0.5, 0]])
img2 = cv2.warpAffine(img, m, dsize)
cv2.imshow('img0.5x+0.5y', img2)
cv2.waitKey(0)
旋转
OpenCV 的cv2.getRotationMatrix2D()函数可用于计算执行旋转操作的转换矩阵,其基本格式如下。
m = cv2.getRotationMatrix2D(center, angle, scale)
其参数说明如下:
参数 | 说明 |
---|
center | 表示原图像中作为旋转中心的坐标 | angle | 表示旋转角度,正数表示按逆时针方向旋转,负数表示按顺时针方向旋转 | scale | 表示目标图像与原图像的大小比例 |
假设原图像宽度为 width,高度为 height,将图像中心作为旋转中心顺时针旋转 60°,并将图像缩小 50%,则用于计算转换矩阵的语句如下,过程就不再推导了,和平移差不多,有兴趣的可以尝试下推导:
m = cv2.getRotationMatrix2D((width/2, height/2 ), -60, 0.5)
import cv2
img = cv2.imread('bee.jpg')
cv2.imshow('img', img)
height = img.shape[0]
width = img.shape[1]
dsize = (width, height)
m = cv2.getRotationMatrix2D((width/2, height/2), -60, 0.5)
img2 = cv2.warpAffine(img, m, dsize)
cv2.imshow('imgRotation', img2)
cv2.waitKey(0)
三点映射变换
三点映射变换会将图像转换为任意的平行四边形,cv2.getAffineTransform()函数用于计算其转换矩阵,基本格式如下。
m= cv2.getAffineTransform(src, dst)
其参数说明如下:
参数 | 说明 |
---|
src | 为原图像中 3 个点的坐标 | dst | 为原图像中 3 个点在目标图像中的对应坐标 |
cv2.getAffineTransform()函数将 src 和 dst 中的 3 个点作为平行四边形左上角、右上角和左下角的 3 个点,按原图和目标图像与 3 个点之间的坐标关系计算所有像素的转换矩阵。
import cv2
import numpy as np
img = cv2.imread('bee.jpg')
cv2.imshow('img', img)
height = img.shape[0]
width = img.shape[1]
dsize = (width, height)
src = np.float32([[0, 0], [width-10, 0], [0, height-1]])
dst = np.float32([[50, 50], [width-100, 80], [100, height-100]])
m = cv2.getAffineTransform(src, dst)
img2 = cv2.warpAffine(img, m, dsize)
cv2.imshow('imgThreePoint', img2)
cv2.waitKey(0)
透视
透视变换会将图像转换为任意的四边形,其主要特点是:原始图像中的所有直线在转换后的图像中仍然是直线。OpenCV 的 cv2.warpPerspective()函数用于执行透视变换操作,其基本格式如下。
dst=cv2.warpPerspective(src,M,dsize[,flags[,borderMode[,borderValue]]])
其中,M 是大小为 3×3 的转换矩阵,其他参数含义与 cv2.warpAffine()函数中的一致。 OpenCV 的 cv2.getPerspectiveTransform()函数用于计算透视变换使用的转换矩阵,其基本格式如下。
M = cv2.getPerspectiveTransform(src,dst)
其参数说明如下:
参数 | 说明 |
---|
src | 为原图像中 4 个点的坐标 | dst | 为原图像中 4 个点在转换后的目标图像中的对应坐标 |
import cv2
import numpy as np
img = cv2.imread('bee.jpg')
cv2.imshow('img', img)
height = img.shape[0]
width = img.shape[1]
dsize = (width, height)
src = np.float32([[0, 0], [width-10, 0], [0, height-10], [width-1, height-1]])
dst = np.float32([[50, 50], [width-50, 80], [50, height-100], [width-100, height-10]])
m = cv2.getPerspectiveTransform(src, dst)
img2 = cv2.warpPerspective(img, m, dsize)
cv2.imshow('imgFourPoint', img2)
cv2.waitKey(0)
实验素材
|