三 opencv 中的图像颜色空间变换
3.1 转换颜色空间
说明:颜色空间变换,在进行有明显颜色差异图像的二值化时很有用。
import cv2
import os
import numpy as np
from matplotlib import pyplot as plt
"""
在OpenCV中有超过150中进行颜色空间转换的方法。但是你以后就会发现我们经常用到的也就两种:BGR<->GRAY 和 BGR<->HSV
我们要用到的函数是 cv2.cvtColor(input_image,flag) 其中flag就是转换类型。
对于 BGR<->GRAY 的转换,我们要使用的Flag就是cv2.COLOR_BGR2GRAY
对于 BGR<->HSV 的转换,我们要使用的Flag就是cv2.COLOR_BGR2HSV
"""
flags = [i for i in dir(cv2) if i.startswith("COLOR_")]
print(flags) # 打印所有可用的Flags
"""
在Opencv 的HSV格式中,H(色彩/色度)的取值范围是[0,179],S(饱和度)的取值范围[0,255],V(亮度)的取值范围[0,255]。但是不同的软件使用的值
可能不同。所以当你需要拿OpenCV的HSV值与其他软件的HSV值进行对比时,一定要记得归一化。
"""
imagePath1 = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath("."))),"OpenCVLogo.PNG")
image = cv2.imread(imagePath1)
imgHSV = cv2.cvtColor(image,cv2.COLOR_RGB2HSV)
lower_Orange = np.array([0, 43, 46])
upper_Orange = np.array([255, 255, 255])
lower_Blue = np.array([0, 0, 46])
upper_Blue = np.array([155, 255, 250])
# 根据阈值构建掩模
maskOrange = cv2.inRange(imgHSV, lower_Orange, upper_Orange)
# 根据阈值构建掩模
maskBlack = cv2.inRange(imgHSV, lower_Blue, upper_Blue)
# 对原图和掩模进行位运算
resOrange = cv2.bitwise_and(image, image, mask=maskOrange)
# 对原图和掩模进行位运算
resBlack = cv2.bitwise_and(image, image, mask=maskBlack)
plt.subplot(231),plt.imshow(image,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(imgHSV,'gray'),plt.title('imgHSV')
plt.subplot(233),plt.imshow(maskOrange, 'gray'),plt.title('maskOrange')
plt.subplot(234),plt.imshow(resOrange,'gray'),plt.title('resOrange')
plt.subplot(235),plt.imshow(maskBlack,'gray'),plt.title('maskBlack')
plt.subplot(236),plt.imshow(resBlack,'gray'),plt.title('resBlack')
plt.show()
输出结果如下:
3.2 如何在已知RGB值的情况下获取对应的HSV
import cv2
import os
import numpy as np
"""
# 一维会报错,必须是三维的才可以,注意在OPencv中像素排列是BRG
"""
# green = np.uint8([0,255,0])# 一维会报错,必须是三维的才可以
green = np.uint8([[[0,255,0]]])# 一维会报错,必须是三维的才可以
# green = cv2.COLOR_RGB2BGR
hsv_green = cv2.cvtColor(green,cv2.COLOR_BGR2HSV)
# hsv_green = cv2.cvtColor(hsv_green,cv2.COLOR_BGR2HSV)
blue = np.uint8([[[255,0,0]]])# 一维会报错,必须是三维的才可以
hsv_blue = cv2.cvtColor(blue,cv2.COLOR_BGR2HSV)
red = np.uint8([[[0,0,255]]])# 一维会报错,必须是三维的才可以
hsv_red = cv2.cvtColor(red,cv2.COLOR_BGR2HSV)
print(hsv_green,hsv_blue,hsv_red)
输出结果如下:
[[[ 60 255 255]]] [[[120 255 255]]] [[[ 0 255 255]]]
四 图像的几何变换
OpenCV提供了两个变换函数,cv2.warpAffine和cv2.warpPerspective,使用这两个函数你可以实现所有的类型的变换。
cv2.warpAffine 接受的是2*3 的变换矩阵,而cv2.warpPerspective 接收的是3*3 的变换矩阵。
4.1 cv2.warpAffine 函数使用:
说明:cv2.warpAffine 函数可以完成图像平移,旋转,仿射变换等功能,接下来会详细介绍
函数原型:cv2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) → dst
参考Geometric Image Transformations — OpenCV 2.3.2 documentation
参数:
src - 源图像。
dst - 与 src 大小为 dsize 且类型相同的目标图像。
M - 2×3 变换矩阵。
dsize - 目标图像的大小。
flags - 插值方法
插值方法:
INTER_NEAEST-最近邻插值
INTER_LINEAR-双线性插值(默认使用)
INTER_AREA-使用像素面积关系重新采样。这可能是图像抽取的首选方法,
因为它可以得到 无莫尔条纹的结果。但是当图像被缩放时,
它类似于INTER_NEAEST方法。
INTER_CUBIC-4x4像素邻域上的双三次插值
INTER_LANCZOS4-8x8像素邻域上的Lanczos插值
borderMode - 像素外推法
cv2.BORDER_CONSTANT 常数填充:|oooo|abcd|oooo|
cv2.BORDER_ISOLATED 使用黑色像素进行填充,
同:cv2.BORDER_CONSTANT类型且value=0
cv2.BORDER_REFLECT 从外向内取图像边缘的像素填充:|dcba|abcd|dcba|
cv2.BORDER_REFLECT101 反射填充的另一种情况,
跳过原图边上的一个像素值:|dcb|abcd|cba|
cv2.BORDER_REFLECT_101 同cv2.BORDER_REFLECT101
cv2.BORDER_DEFAULT 同cv2.BORDER_REFLECT101
cv2.BORDER_REPLICATE 复制图像最边上的像素进行填充:|aaaa|abcd|dddd|
cv2.BORDER_TRANSPARENT 这个类型在新的OpenCV4中已经被取消
cv2.BORDER_WRAP 在图像对侧从外向内取图像边缘的像素
填充:|dcba|abcd|abcd|
当 borderMode=BORDER_TRANSPARENT 时,意味着目标图像中与源图像中
"异常值 "相对应的像素不会被函数修改。
borderValue - 恒定边框时使用的值。默认值为 0。
4.1.1 图像的平移
函数warpAffine使用指定的矩阵变换源图像:
原始像素(x,y)和M矩阵相乘等到目标像素(dx,dy),由于矩阵式2*3 矩阵,所以原始像素需要补充一个"1",以便可以进行矩阵运算。
如果M 矩阵的值是如下值:
代入计算公式 dst(x,y) = (1*x +0*y +0*1,0*x+1*y+0*1),结果和原始像素值一样。
如果M矩阵的值是如下值:
代入计算公式 dst(x,y) = (1*x +0*y +100*1,0*x+1*y+100*1),得到结果d(x,y) = (x+100,y+100)。
可以看到M13 和 M23 决定了图像的平移。
我们构建如下代码平移图像
import cv2
import os
import numpy as np
from matplotlib import pyplot as plt
imagePath1 = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath("."))),"OpenCVLogo.PNG")
image1 = cv2.imread(imagePath1)
imgH,imgW=image1.shape[:2]
"""
# 定义平移矩阵,需要是numpy的float32类型
# x轴平移200,y轴平移100, 2*3矩阵
定义矩阵有三种方法:
方法一:M = np.float32([[1, 0, 200], [0, 1, 100]])
方法二:M = np.eye(3)[:2]
M[0][2] = 200
M[1][2] = 100
方法三:M = np.identity(3)[:2]
M[0][2] = 200
M[1][2] = 100
"""
M = np.float32([[1, 0, 200], [0, 1, 100]])
"""
# 用仿射变换实现平移
注意:函数cv2.wrapAffine()的第三个参数是输出图像的大小,它的格式应该是图像的(宽,高)。应该记住的是图像的宽对应的是列数,高对应的是行数。
"""
img_s = cv2.warpAffine(image1, M, (imgW, imgH), borderValue=(155, 150, 200))
plt.subplot(131),plt.imshow(image1,'gray'),plt.title('ORIGINAL')
plt.subplot(132),plt.imshow(img_s,'gray'),plt.title('img_s')
plt.show()
结果如下:
我们修改一下代码扩大图像的尺寸
img_s = cv2.warpAffine(image1, M, (2*cols, 2*rows), borderValue=(155, 150, 200))
得到如下结果:
4.1.2 图像的旋转
说明:特殊角度90,180,270的旋转可以使用
cv2.rotate(src, rotateCode, dst=None)
函数cv2.rotate以三种不同方式之一旋转阵列:
.*顺时针旋转90度(rotateCode=Rotate_90_clockwise)。
.*顺时针旋转180度(rotateCode=Rotate_180)。
.*顺时针旋转270度(rotateCode=Rotate_90_COUNTERCLOCKWISE)。
任意角度旋转需要配合cv2.getRotationMatrix2D 使用
cv2.getRotationMatrix2D 函数原型:
参考:Geometric Image Transformations — OpenCV 2.3.2 documentation
cv2.getRotationMatrix2D(center, angle, scale)
参数:
center–源图像中旋转的中心(x,y)。如果是以图像中心为中心旋转可以设置为(imgW/2,imgH/2)
angle–旋转角度,单位为度。正值表示逆时针旋转(坐标原点假定为左上角)。
scale–各向同性比例因子。
mapMatrix–输出仿射变换,2x3浮点矩阵。
该函数计算以下矩阵:
我们构建旋转45和90度的代码:
import cv2
import os
import numpy as np
from matplotlib import pyplot as plt
imagePath1 = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath("."))),"OpenCVLogo.PNG")
image1 = cv2.imread(imagePath1)
imgH,imgW=image1.shape[:2]
"""
# 这里的第一个参数为旋转中心,第二个为旋转角度,第三个为旋转后的缩放因子
# 可以通过设置旋转中心,缩放因子,以及窗口大小来防止旋转后超出边界的问题
"""
M=cv2.getRotationMatrix2D((imgW/2,imgH/2),90,0.9)
# 第三个参数是输出图像的尺寸中心
centerDst90=cv2.warpAffine(image1,M,(int(1.5*imgW),int(1.5*imgH)),borderMode=cv2.BORDER_CONSTANT,borderValue=(255,255,255))
M=cv2.getRotationMatrix2D((imgW/2,imgH/2),45,0.9)
# 第三个参数是输出图像的尺寸中心
centerDst45=cv2.warpAffine(image1,M,(int(1.5*imgW),int(1.5*imgH)),borderMode=cv2.BORDER_CONSTANT,borderValue=(255,255,255))
M=cv2.getRotationMatrix2D((imgW/2+200,imgH/2),90,0.9)
# 第三个参数是输出图像的尺寸中心
xoffDst90=cv2.warpAffine(image1,M,(int(1.5*imgW),int(1.5*imgH)),borderMode=cv2.BORDER_CONSTANT,borderValue=(255,255,255))
M=cv2.getRotationMatrix2D((imgW/2+200,imgH/2),45,0.9)
# 第三个参数是输出图像的尺寸中心
xoffDst45=cv2.warpAffine(image1,M,(int(1.5*imgW),int(1.5*imgH)),borderMode=cv2.BORDER_CONSTANT,borderValue=(255,255,255))
M=cv2.getRotationMatrix2D((imgW/2,imgH/2+200),45,0.9)
# 第三个参数是输出图像的尺寸中心
yoffDst45=cv2.warpAffine(image1,M,(int(1.5*imgW),int(1.5*imgH)),borderMode=cv2.BORDER_CONSTANT,borderValue=(255,255,255))
plt.subplot(231),plt.imshow(image1,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(centerDst90,'gray'),plt.title('centerDst90')
plt.subplot(233),plt.imshow(centerDst45,'gray'),plt.title('centerDst45')
plt.subplot(234),plt.imshow(xoffDst90,'gray'),plt.title('xoffDst90')
plt.subplot(235),plt.imshow(xoffDst45,'gray'),plt.title('xoffDst45')
plt.subplot(236),plt.imshow(yoffDst45,'gray'),plt.title('yoffDst45')
plt.show()
输出结果如下:
4.1.3 图像的仿射变换
注意:仿射变换可以将矩形映射为平行四边形,下一节透视变换可以将矩形图片映射为任意四边形
import cv2
import os
import numpy as np
from matplotlib import pyplot as plt
"""
在仿射变换中,原图中所有的平行线在结果图像中同样平行。为了创建这个矩阵我们需要从原图像中找到三个点以及他们在输出图像中的位置。
然后cv2.getAffineTransform会创建一个2*3的矩阵,最后这个矩阵会被传给函数cv2.warpAffine.
来看看下面的例子,以及我选择的点
"""
imagePath1 = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath("."))),"OpenCVLogo.PNG")
image1 = cv2.imread(imagePath1)
imgH,imgW=image1.shape[:2]
pst1 = np.float32([[50,50],[200,50],[50,200]])
pst2 = np.float32([[10,100],[200,50],[100,250]])
#第一步构建矩阵
M = cv2.getAffineTransform(pst1,pst2)
#第二步根据矩阵得到仿射变换后的图片
dst = cv2.warpAffine(image1,M,(int(imgW*1.5),int(imgH*1.5)),borderMode=cv2.BORDER_CONSTANT,borderValue=(255,255,255))
plt.subplot(121),plt.imshow(image1),plt.title("input")
plt.subplot(122),plt.imshow(dst),plt.title("output")
# plt.imshow(image1)
plt.show()
输出结果如下:
4.2 cv2.warpPerspective 函数使用
cv2.warpPerspective主要是完成图像的透视变换。
注意:上一节仿射变换可以将矩形映射为平行四边形,这一节透视变换可以将矩形图片映射为任意四边形
函数原型:
cv2.warpPerspective(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) → dst
参考:Geometric Image Transformations — OpenCV 2.3.2 documentation
参数:
src–源图像。
dst–大小为dsize、类型与src相同的目标图像。
M–3 *3变换矩阵。
dsize–目标映像的大小。
flags–插值方法,参见cv2.warpAffine 介绍
borderMode–像素外推法,参见cv2.warpAffine 介绍
borderValue–在边界为常量的情况下使用的值。默认情况下,它为0。
函数warpPerspective使用指定的矩阵变换源图像:
我们构建如下代码实现透视变换:
import cv2
import os
import numpy as np
from matplotlib import pyplot as plt
"""
对于视角变换,我们需要一个3*3变换矩阵。在变换前后直线还是直线。要构建这个变换矩阵,你需要在输入图像上找4个点,以及他们在输出图像
上对应的位置。这四个点中任意三个都不能共线。这个变换矩阵可以有函数cv2.getPerspectiveTransform()够建。然后把这个矩阵传给函数
cv2.warpPerspective
"""
imagePath1 = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath("."))),"OpenCVLogo.PNG")
image1 = cv2.imread(imagePath1)
imgH,imgW=image1.shape[:2]
pst1 = np.float32([[56,65],[368,52],[28,587],[389,590]])
pst2 = np.float32([[0,0],[300,0],[0,300],[300,300]])
M1 = cv2.getPerspectiveTransform(pst1,pst2)
dst = cv2.warpPerspective(image1,M1,(imgW,imgH),borderMode=cv2.BORDER_CONSTANT,borderValue=(255,255,255))
"""
由于变换后图片有一些超出边界,我们在X,Y方便分别平移100个像素
"""
M2 = np.identity(3)
M2[0][2] = 100
M2[1][2] = 100
ResultM =np.matmul(M1, M2)
img_s = cv2.warpPerspective(image1,ResultM,(int(imgW*1.2),int(imgH*1.2)),borderMode=cv2.BORDER_CONSTANT,borderValue=(255,255,255))
plt.subplot(131),plt.imshow(image1),plt.title("input")
plt.subplot(132),plt.imshow(dst),plt.title("output")
plt.subplot(133),plt.imshow(img_s),plt.title("img_s")
plt.show()
输出结果如下:
4.3 扩展缩放
import cv2
import os
import numpy as np
from matplotlib import pyplot as plt
"""
扩展缩放只是改变图像的尺寸大小。Opencv提供的函数cv2.resize() 可以实现这个功能。图像尺寸可以自己手动设置,你也可以指定缩放因子。
我们可以选择使用不同的插值方法。在缩放时我们推荐使用cv2.INTER_AREA, 扩展时我们推荐使用cv2.INTER_CUBIC(慢)和cv2.INTER_LINEAR
默认情况下所有改变图像尺寸大小的操作使用插值方法都是cv2.INTER_LINEAR. 你可以使用下面任意一种方法改变图像的尺寸:
"""
imagePath1 = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath("."))),"OpenCVLogo.PNG")
image1 = cv2.imread(imagePath1)
"""
方法一:直接设置缩放因子
# 下面的None 本应该是输出图像的尺寸,但是因为后边我们设置了缩放因子
# 因此这里为None
"""
res1=cv2.resize(image1,None,fx=2,fy=2,interpolation=cv2.INTER_CUBIC)
"""
方法二:直接设置输出图像尺寸
# 这里呢,我们直接设置输出图像的尺寸,所以不用设置缩放因子
"""
height,width=image1.shape[:2]
res2=cv2.resize(image1,(2*width,2*height),interpolation=cv2.INTER_CUBIC)
plt.subplot(131),plt.imshow(image1,'gray'),plt.title('ORIGINAL')
plt.subplot(132),plt.imshow(res1,'gray'),plt.title('res1')
plt.subplot(133),plt.imshow(res2, 'gray'),plt.title('res2')
plt.show()
输出结果如下:
可以看到扩展后的图像比原图在宽度和长度方面都大了1倍