OpenCV基础操作(4)颜色空间转换、几何变换、图像二值化操作
import cv2 as cv
import numpy as np
一、颜色空间转换
1、转换颜色空间
在 OpenCV 中有超过 150 中进行颜色空间转换的方法。但是你以后就会发现我们经常用到的也就两种:BGR↔Gray 和 BGR↔HSV。
我们要用到的函数是:cv2.cvtColor(input_image,flag),其中 flag就是转换类型。
对于 BGR↔Gray 的转换,我们要使用的 flag 就是 cv2.COLOR_BGR2GRAY。
同样对于 BGR↔HSV 的转换,我们用的 flag 就是 cv2.COLOR_BGR2HSV。
'''
在 OpenCV 的 HSV 格式中,H(色彩/色度)的取值范围是 [0,179],S(饱和度)的取值范围 [0,255],V(亮度)的取值范围 [0,255]。
但是不同的软件使用的值可能不同。所以当你需要拿 OpenCV 的 HSV 值与其他软件的 HSV 值进行对比时,一定要记得归一化。
颜色空间人们听得最多的是RGB,它也是计算机色彩的自然表示,但对于人类来说,HSV颜色空间更符合人类的感知。HSV(色相饱和度值):
H(Hue):色调,取值范围是 [0,179] ,它用来限制某一个颜色的彩色光谱范围;
S(Saturation):饱和度,取值范围是 [0,255] ,它用来限制颜色的深度,值越大颜色越深;
V(Value):色值,取值范围是 [0,255] ,它用来限制像素的亮度,值越大像素越亮。
'''
# 得到所有可用的 flag
flags = [i for i in dir(cv) if i.startswith('COLOR_')]
print(flags)
['COLOR_BAYER_BG2BGR', 'COLOR_BAYER_BG2BGRA', 'COLOR_BAYER_BG2BGR_EA', 'COLOR_BAYER_BG2BGR_VNG', 'COLOR_BAYER_BG2GRAY', 'COLOR_BAYER_BG2RGB', 'COLOR_BAYER_BG2RGBA', 'COLOR_BAYER_BG2RGB_EA', 'COLOR_BAYER_BG2RGB_VNG', 'COLOR_BAYER_BGGR2BGR', 'COLOR_BAYER_BGGR2BGRA', 'COLOR_BAYER_BGGR2BGR_EA', 'COLOR_BAYER_BGGR2BGR_VNG', 'COLOR_BAYER_BGGR2GRAY', 'COLOR_BAYER_BGGR2RGB', 'COLOR_BAYER_BGGR2RGBA', 'COLOR_BAYER_BGGR2RGB_EA', 'COLOR_BAYER_BGGR2RGB_VNG', 'COLOR_BAYER_GB2BGR', 'COLOR_BAYER_GB2BGRA', 'COLOR_BAYER_GB2BGR_EA', 'COLOR_BAYER_GB2BGR_VNG', 'COLOR_BAYER_GB2GRAY', 'COLOR_BAYER_GB2RGB', 'COLOR_BAYER_GB2RGBA', 'COLOR_BAYER_GB2RGB_EA', 'COLOR_BAYER_GB2RGB_VNG', 'COLOR_BAYER_GBRG2BGR', 'COLOR_BAYER_GBRG2BGRA', 'COLOR_BAYER_GBRG2BGR_EA', 'COLOR_BAYER_GBRG2BGR_VNG', 'COLOR_BAYER_GBRG2GRAY', 'COLOR_BAYER_GBRG2RGB', 'COLOR_BAYER_GBRG2RGBA', 'COLOR_BAYER_GBRG2RGB_EA', 'COLOR_BAYER_GBRG2RGB_VNG', 'COLOR_BAYER_GR2BGR', 'COLOR_BAYER_GR2BGRA', 'COLOR_BAYER_GR2BGR_EA', 'COLOR_BAYER_GR2BGR_VNG', 'COLOR_BAYER_GR2GRAY', 'COLOR_BAYER_GR2RGB', 'COLOR_BAYER_GR2RGBA', 'COLOR_BAYER_GR2RGB_EA', 'COLOR_BAYER_GR2RGB_VNG', 'COLOR_BAYER_GRBG2BGR', 'COLOR_BAYER_GRBG2BGRA', 'COLOR_BAYER_GRBG2BGR_EA', 'COLOR_BAYER_GRBG2BGR_VNG', 'COLOR_BAYER_GRBG2GRAY', 'COLOR_BAYER_GRBG2RGB', 'COLOR_BAYER_GRBG2RGBA', 'COLOR_BAYER_GRBG2RGB_EA', 'COLOR_BAYER_GRBG2RGB_VNG', 'COLOR_BAYER_RG2BGR', 'COLOR_BAYER_RG2BGRA', 'COLOR_BAYER_RG2BGR_EA', 'COLOR_BAYER_RG2BGR_VNG', 'COLOR_BAYER_RG2GRAY', 'COLOR_BAYER_RG2RGB', 'COLOR_BAYER_RG2RGBA', 'COLOR_BAYER_RG2RGB_EA', 'COLOR_BAYER_RG2RGB_VNG', 'COLOR_BAYER_RGGB2BGR', 'COLOR_BAYER_RGGB2BGRA', 'COLOR_BAYER_RGGB2BGR_EA', 'COLOR_BAYER_RGGB2BGR_VNG', 'COLOR_BAYER_RGGB2GRAY', 'COLOR_BAYER_RGGB2RGB', 'COLOR_BAYER_RGGB2RGBA', 'COLOR_BAYER_RGGB2RGB_EA', 'COLOR_BAYER_RGGB2RGB_VNG', 'COLOR_BGR2BGR555', 'COLOR_BGR2BGR565', 'COLOR_BGR2BGRA', 'COLOR_BGR2GRAY', 'COLOR_BGR2HLS', 'COLOR_BGR2HLS_FULL', 'COLOR_BGR2HSV', 'COLOR_BGR2HSV_FULL', 'COLOR_BGR2LAB', 'COLOR_BGR2LUV', 'COLOR_BGR2Lab', 'COLOR_BGR2Luv', 'COLOR_BGR2RGB', 'COLOR_BGR2RGBA', 'COLOR_BGR2XYZ', 'COLOR_BGR2YCR_CB', 'COLOR_BGR2YCrCb', 'COLOR_BGR2YUV', 'COLOR_BGR2YUV_I420', 'COLOR_BGR2YUV_IYUV', 'COLOR_BGR2YUV_YV12', 'COLOR_BGR5552BGR', 'COLOR_BGR5552BGRA', 'COLOR_BGR5552GRAY', 'COLOR_BGR5552RGB', 'COLOR_BGR5552RGBA', 'COLOR_BGR5652BGR', 'COLOR_BGR5652BGRA', 'COLOR_BGR5652GRAY', 'COLOR_BGR5652RGB', 'COLOR_BGR5652RGBA', 'COLOR_BGRA2BGR', 'COLOR_BGRA2BGR555', 'COLOR_BGRA2BGR565', 'COLOR_BGRA2GRAY', 'COLOR_BGRA2RGB', 'COLOR_BGRA2RGBA', 'COLOR_BGRA2YUV_I420', 'COLOR_BGRA2YUV_IYUV', 'COLOR_BGRA2YUV_YV12', 'COLOR_BayerBG2BGR', 'COLOR_BayerBG2BGRA', 'COLOR_BayerBG2BGR_EA', 'COLOR_BayerBG2BGR_VNG', 'COLOR_BayerBG2GRAY', 'COLOR_BayerBG2RGB', 'COLOR_BayerBG2RGBA', 'COLOR_BayerBG2RGB_EA', 'COLOR_BayerBG2RGB_VNG', 'COLOR_BayerBGGR2BGR', 'COLOR_BayerBGGR2BGRA', 'COLOR_BayerBGGR2BGR_EA', 'COLOR_BayerBGGR2BGR_VNG', 'COLOR_BayerBGGR2GRAY', 'COLOR_BayerBGGR2RGB', 'COLOR_BayerBGGR2RGBA', 'COLOR_BayerBGGR2RGB_EA', 'COLOR_BayerBGGR2RGB_VNG', 'COLOR_BayerGB2BGR', 'COLOR_BayerGB2BGRA', 'COLOR_BayerGB2BGR_EA', 'COLOR_BayerGB2BGR_VNG', 'COLOR_BayerGB2GRAY', 'COLOR_BayerGB2RGB', 'COLOR_BayerGB2RGBA', 'COLOR_BayerGB2RGB_EA', 'COLOR_BayerGB2RGB_VNG', 'COLOR_BayerGBRG2BGR', 'COLOR_BayerGBRG2BGRA', 'COLOR_BayerGBRG2BGR_EA', 'COLOR_BayerGBRG2BGR_VNG', 'COLOR_BayerGBRG2GRAY', 'COLOR_BayerGBRG2RGB', 'COLOR_BayerGBRG2RGBA', 'COLOR_BayerGBRG2RGB_EA', 'COLOR_BayerGBRG2RGB_VNG', 'COLOR_BayerGR2BGR', 'COLOR_BayerGR2BGRA', 'COLOR_BayerGR2BGR_EA', 'COLOR_BayerGR2BGR_VNG', 'COLOR_BayerGR2GRAY', 'COLOR_BayerGR2RGB', 'COLOR_BayerGR2RGBA', 'COLOR_BayerGR2RGB_EA', 'COLOR_BayerGR2RGB_VNG', 'COLOR_BayerGRBG2BGR', 'COLOR_BayerGRBG2BGRA', 'COLOR_BayerGRBG2BGR_EA', 'COLOR_BayerGRBG2BGR_VNG', 'COLOR_BayerGRBG2GRAY', 'COLOR_BayerGRBG2RGB', 'COLOR_BayerGRBG2RGBA', 'COLOR_BayerGRBG2RGB_EA', 'COLOR_BayerGRBG2RGB_VNG', 'COLOR_BayerRG2BGR', 'COLOR_BayerRG2BGRA', 'COLOR_BayerRG2BGR_EA', 'COLOR_BayerRG2BGR_VNG', 'COLOR_BayerRG2GRAY', 'COLOR_BayerRG2RGB', 'COLOR_BayerRG2RGBA', 'COLOR_BayerRG2RGB_EA', 'COLOR_BayerRG2RGB_VNG', 'COLOR_BayerRGGB2BGR', 'COLOR_BayerRGGB2BGRA', 'COLOR_BayerRGGB2BGR_EA', 'COLOR_BayerRGGB2BGR_VNG', 'COLOR_BayerRGGB2GRAY', 'COLOR_BayerRGGB2RGB', 'COLOR_BayerRGGB2RGBA', 'COLOR_BayerRGGB2RGB_EA', 'COLOR_BayerRGGB2RGB_VNG', 'COLOR_COLORCVT_MAX', 'COLOR_GRAY2BGR', 'COLOR_GRAY2BGR555', 'COLOR_GRAY2BGR565', 'COLOR_GRAY2BGRA', 'COLOR_GRAY2RGB', 'COLOR_GRAY2RGBA', 'COLOR_HLS2BGR', 'COLOR_HLS2BGR_FULL', 'COLOR_HLS2RGB', 'COLOR_HLS2RGB_FULL', 'COLOR_HSV2BGR', 'COLOR_HSV2BGR_FULL', 'COLOR_HSV2RGB', 'COLOR_HSV2RGB_FULL', 'COLOR_LAB2BGR', 'COLOR_LAB2LBGR', 'COLOR_LAB2LRGB', 'COLOR_LAB2RGB', 'COLOR_LBGR2LAB', 'COLOR_LBGR2LUV', 'COLOR_LBGR2Lab', 'COLOR_LBGR2Luv', 'COLOR_LRGB2LAB', 'COLOR_LRGB2LUV', 'COLOR_LRGB2Lab', 'COLOR_LRGB2Luv', 'COLOR_LUV2BGR', 'COLOR_LUV2LBGR', 'COLOR_LUV2LRGB', 'COLOR_LUV2RGB', 'COLOR_Lab2BGR', 'COLOR_Lab2LBGR', 'COLOR_Lab2LRGB', 'COLOR_Lab2RGB', 'COLOR_Luv2BGR', 'COLOR_Luv2LBGR', 'COLOR_Luv2LRGB', 'COLOR_Luv2RGB', 'COLOR_M_RGBA2RGBA', 'COLOR_RGB2BGR', 'COLOR_RGB2BGR555', 'COLOR_RGB2BGR565', 'COLOR_RGB2BGRA', 'COLOR_RGB2GRAY', 'COLOR_RGB2HLS', 'COLOR_RGB2HLS_FULL', 'COLOR_RGB2HSV', 'COLOR_RGB2HSV_FULL', 'COLOR_RGB2LAB', 'COLOR_RGB2LUV', 'COLOR_RGB2Lab', 'COLOR_RGB2Luv', 'COLOR_RGB2RGBA', 'COLOR_RGB2XYZ', 'COLOR_RGB2YCR_CB', 'COLOR_RGB2YCrCb', 'COLOR_RGB2YUV', 'COLOR_RGB2YUV_I420', 'COLOR_RGB2YUV_IYUV', 'COLOR_RGB2YUV_YV12', 'COLOR_RGBA2BGR', 'COLOR_RGBA2BGR555', 'COLOR_RGBA2BGR565', 'COLOR_RGBA2BGRA', 'COLOR_RGBA2GRAY', 'COLOR_RGBA2M_RGBA', 'COLOR_RGBA2RGB', 'COLOR_RGBA2YUV_I420', 'COLOR_RGBA2YUV_IYUV', 'COLOR_RGBA2YUV_YV12', 'COLOR_RGBA2mRGBA', 'COLOR_XYZ2BGR', 'COLOR_XYZ2RGB', 'COLOR_YCR_CB2BGR', 'COLOR_YCR_CB2RGB', 'COLOR_YCrCb2BGR', 'COLOR_YCrCb2RGB', 'COLOR_YUV2BGR', 'COLOR_YUV2BGRA_I420', 'COLOR_YUV2BGRA_IYUV', 'COLOR_YUV2BGRA_NV12', 'COLOR_YUV2BGRA_NV21', 'COLOR_YUV2BGRA_UYNV', 'COLOR_YUV2BGRA_UYVY', 'COLOR_YUV2BGRA_Y422', 'COLOR_YUV2BGRA_YUNV', 'COLOR_YUV2BGRA_YUY2', 'COLOR_YUV2BGRA_YUYV', 'COLOR_YUV2BGRA_YV12', 'COLOR_YUV2BGRA_YVYU', 'COLOR_YUV2BGR_I420', 'COLOR_YUV2BGR_IYUV', 'COLOR_YUV2BGR_NV12', 'COLOR_YUV2BGR_NV21', 'COLOR_YUV2BGR_UYNV', 'COLOR_YUV2BGR_UYVY', 'COLOR_YUV2BGR_Y422', 'COLOR_YUV2BGR_YUNV', 'COLOR_YUV2BGR_YUY2', 'COLOR_YUV2BGR_YUYV', 'COLOR_YUV2BGR_YV12', 'COLOR_YUV2BGR_YVYU', 'COLOR_YUV2GRAY_420', 'COLOR_YUV2GRAY_I420', 'COLOR_YUV2GRAY_IYUV', 'COLOR_YUV2GRAY_NV12', 'COLOR_YUV2GRAY_NV21', 'COLOR_YUV2GRAY_UYNV', 'COLOR_YUV2GRAY_UYVY', 'COLOR_YUV2GRAY_Y422', 'COLOR_YUV2GRAY_YUNV', 'COLOR_YUV2GRAY_YUY2', 'COLOR_YUV2GRAY_YUYV', 'COLOR_YUV2GRAY_YV12', 'COLOR_YUV2GRAY_YVYU', 'COLOR_YUV2RGB', 'COLOR_YUV2RGBA_I420', 'COLOR_YUV2RGBA_IYUV', 'COLOR_YUV2RGBA_NV12', 'COLOR_YUV2RGBA_NV21', 'COLOR_YUV2RGBA_UYNV', 'COLOR_YUV2RGBA_UYVY', 'COLOR_YUV2RGBA_Y422', 'COLOR_YUV2RGBA_YUNV', 'COLOR_YUV2RGBA_YUY2', 'COLOR_YUV2RGBA_YUYV', 'COLOR_YUV2RGBA_YV12', 'COLOR_YUV2RGBA_YVYU', 'COLOR_YUV2RGB_I420', 'COLOR_YUV2RGB_IYUV', 'COLOR_YUV2RGB_NV12', 'COLOR_YUV2RGB_NV21', 'COLOR_YUV2RGB_UYNV', 'COLOR_YUV2RGB_UYVY', 'COLOR_YUV2RGB_Y422', 'COLOR_YUV2RGB_YUNV', 'COLOR_YUV2RGB_YUY2', 'COLOR_YUV2RGB_YUYV', 'COLOR_YUV2RGB_YV12', 'COLOR_YUV2RGB_YVYU', 'COLOR_YUV420P2BGR', 'COLOR_YUV420P2BGRA', 'COLOR_YUV420P2GRAY', 'COLOR_YUV420P2RGB', 'COLOR_YUV420P2RGBA', 'COLOR_YUV420SP2BGR', 'COLOR_YUV420SP2BGRA', 'COLOR_YUV420SP2GRAY', 'COLOR_YUV420SP2RGB', 'COLOR_YUV420SP2RGBA', 'COLOR_YUV420p2BGR', 'COLOR_YUV420p2BGRA', 'COLOR_YUV420p2GRAY', 'COLOR_YUV420p2RGB', 'COLOR_YUV420p2RGBA', 'COLOR_YUV420sp2BGR', 'COLOR_YUV420sp2BGRA', 'COLOR_YUV420sp2GRAY', 'COLOR_YUV420sp2RGB', 'COLOR_YUV420sp2RGBA', 'COLOR_mRGBA2RGBA']
2、物体跟踪
'''
我们知道怎样将一幅图像从 BGR 转换到 HSV 了,我们可以利用这一点来提取带有某个特定颜色的物体。
在 HSV 颜色空间中要比在 BGR 空间中更容易表示一个特定颜色。
我们要提取的是摄像头视频中的蓝色的物体。下面就是就是我们要做的几步:
• 从视频中获取每一帧图像
• 将图像转换到 HSV 空间
• 设置 HSV 阈值到蓝色范围。
• 获取蓝色物体,当然我们还可以做其他任何我们想做的事,比如:在蓝色物体周围画一个圈。
注意:图像中仍然有一些噪音,后面介绍如何消减噪音。
'''
cap = cv.VideoCapture(0)
while(1):
# 获取每一帧
ret,frame = cap.read()
# 转换到 HSV
hsv = cv.cvtColor(frame,cv.COLOR_BGR2HSV)
# 设定蓝色的阈值
lower_blue = np.array([110,50,50])
upper_blue = np.array([130,255,255])
# 红色的范围
# lower_red = np.array([150,50,50])
# upper_red = np.array([200,255,255])
# 根据阈值构建掩模
# 在这个范围内的图像像素,设置为255,不在这个范围内的设置为0
mask = cv.inRange(hsv,lower_blue,upper_blue)
# 对原图像和掩模进行位运算
# 在所有图像基本运算的操作函数中,凡是带有掩膜(mask)的处理函数,其掩膜都参与运算(输入图像运算完之后再与掩膜图像做矩阵运算)
# 前两个图像与完之后,再跟mask按位与,mask中是黑色的地方,不管和谁与都是0了,而白色部分1不管和谁与,都是它本身了,所以能得到掩膜后数据的样子。
res = cv.bitwise_and(frame,frame,mask=mask)
# 显示图像
cv.imshow('frame',frame)
cv.imshow('mask',mask)
cv.imshow('res',res)
k = cv.waitKey(5) & 0xFF
if k == 27:
break
# 关闭窗口
cv.destroyAllWindows()
3、怎样找到要跟踪对象的 HSV 值?
'''
函数 cv2.cvtColor() 也可以用到这里,但是现在你要传入的参数是(你想要的)BGR 值而不是一副图。
例如,我们要找到绿色的 HSV 值
可以分别用 [H-100,100,100] 和 [H+100,255,255] 做上下阈值。
也可根据实际情况调整,背景干扰少时范围可放宽一些,背景干扰多时范围限制严格一点,但要确保得到的HSV在上下阈值内
比如[H-50 50 50] 和 [H+50 255 255]
'''
green = np.uint8([[[0,255,0]]])
hsv_green = cv.cvtColor(green,cv.COLOR_BGR2HSV)
print(hsv_green)
[[[ 60 255 255]]]
''''
同时提取红,蓝,绿三个不同颜色的物体
'''
cap = cv.VideoCapture(0)
while (1):
# 读取帧
_, frame = cap.read()
# 转换颜色空间 BGR 到 HSV
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
# 定义HSV中蓝色的范围
lower_blue = np.array([106, 43, 46])
upper_blue = np.array([130, 255, 255])
# 设置HSV的阈值使得只取蓝色
mask_B = cv.inRange(hsv, lower_blue, upper_blue)
lower_green = np.array([35, 43, 46])
upper_green = np.array([77, 255, 255])
mask_G = cv.inRange(hsv, lower_green, upper_green)
lower_red1 = np.array([0, 43, 46])
upper_red1 = np.array([20, 255, 255])
mask_R1 = cv.inRange(hsv, lower_red1, upper_red1)
lower_red2 = np.array([150, 43, 46])
upper_red2 = np.array([180, 255, 255])
mask_R2 = cv.inRange(hsv, lower_red2, upper_red2)
mask = mask_B + mask_G + mask_R1 + mask_R2
# 将掩膜和图像逐像素相加
res = cv.bitwise_and(frame, frame, mask=mask)
cv.imshow('frame', frame)
cv.imshow('mask', mask)
cv.imshow('res', res)
k = cv.waitKey(5) & 0xFF
if k == 27:
break
cv.destroyAllWindows()
二、几何变换
OpenCV 提供了两个变换函数,cv2.warpAffine 和 cv2.warpPerspective,使用这两个函数你可以实现所有类型的变换。
cv2.warpAffine 接收的参数是2 × 3 的变换矩阵
cv2.warpPerspective 接收的参数是 3 × 3 的变换矩阵。
1、扩展缩放
cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) -> dst
src - 原图
dst - 目标图像。当参数dsize不为0时,dst的大小为size;否则,它的大小需要根据src的大小,参数fx和fy决定。dst的类型(type)和src图像相同
dsize - 目标图像大小。当dsize为0时,它可以通过以下公式计算得出:
dsize = Size(round(fxsrc.cols,round(fysrc.rows)))
所以,参数dsize和参数(fx, fy)不能够同时为0
fx - 水平轴上的比例因子。当它为0时,计算公式如下:(double)dsize.width/src.cols
fy - 垂直轴上的比例因子。当它为0时,计算公式如下:(double)dsize.height/src.cols
interpolation - 插值方法。共有5种:
1)INTER_NEAREST - 最近邻插值法
2)INTER_LINEAR - 双线性插值法(默认)
3)INTER_AREA - 基于局部像素的重采样(resampling using pixel area relation)。对于图像抽取(image decimation)来说,这可能是一个更好的方法。但如果是放大图像时,它和最近邻法的效果类似。
4)INTER_CUBIC - 基于4x4像素邻域的3次插值法
5)INTER_LANCZOS4 - 基于8x8像素邻域的Lanczos插值
'''
扩展缩放只是改变图像的尺寸大小。
OpenCV 提供的函数 cv2.resize()可以实现这个功能。图像的尺寸可以自己手动设置,你也可以指定缩放因子。
我们可以选择使用不同的插值方法。
在缩放时我们推荐使用 cv2.INTER_AREA,
在扩展时我们推荐使用 cv2.INTER_CUBIC(慢) 和 cv2.INTER_LINEAR。
默认情况下所有改变图像尺寸大小的操作使用的插值方法都是 cv2.INTER_LINEAR
'''
img = cv.imread('open_cv.png')
height, width = img.shape[:2]
# 缩小图像
size = (int(width*0.5), int(height*0.5))
shrink = cv.resize(img, size, interpolation=cv.INTER_AREA)
# 放大图像
fx = 1.6
fy = 1.2
enlarge = cv.resize(img, (0, 0), fx=fx, fy=fy, interpolation=cv.INTER_LINEAR)
# 显示
cv.imshow("src", img)
cv.imshow("shrink", shrink)
cv.imshow("enlarge", enlarge)
cv.waitKey(0)
cv.destroyAllWindows()
2、平移
'''
平移就是将对象换一个位置。如果你要沿(x,y)方向移动,移动的距离是(t_x,t_y),
你可以以下面的方式构建移动矩阵:
[
[1 0 t_x],
[0 1 t_y]
]
你可以使用 Numpy 数组构建这个矩阵(数据类型是 np.float32),然后把它传给函数 cv2.warpAffine()。
def warpAffine(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)
src:输入图像
M:运算矩阵,2行3列的,数据类型要求是float32位及以上
dsize:运算后矩阵的大小,也就是输出图片的尺寸
dst:输出图像
flags:插值方法的组合,与resize函数中的插值一样,可以查看cv2.resize
borderMode:像素外推方法,详情参考官网
borderValue:在恒定边框的情况下使用的borderValue值;默认情况下,它是 0
'''
img = cv.imread('open_cv.png')
h, w, channel = img.shape
'''
函数 cv2.warpAffine() 的第三个参数的是输出图像的大小,它的格式应该是图像的(宽,高)。
应该记住的是图像的宽对应的是列数,高对应的是行数。
'''
# 相当于是对图像进行平移
M = np.float32([[1, 0, 50], [0, 1, 50]])
new_img1 = cv.warpAffine(img, M, (w, h))
# 对图片进行了3维的旋转
M = np.float32([[1, 0.2, 0], [0.2, 1, 0]])
new_img2 = cv.warpAffine(img, M, (w, h))
# 对图片进行了放大
M = np.float32([[1.3, 0, 0], [0, 1.3, 0]])
new_img3 = cv.warpAffine(img, M, (w, h))
cv.imshow("1_new_img", new_img1)
cv.imshow("2_new_img", new_img2)
cv.imshow("3_new_img", new_img3)
cv.waitKey(0)
cv.destroyAllWindows()
3、旋转
img = cv.imread('open_cv.png',0)
rows,cols = img.shape
# 这里的第一个参数为旋转中心center,第二个为旋转角度angle,第三个为旋转后的缩放因子scale
# 可以通过设置旋转中心,缩放因子,以及窗口大小来防止旋转后超出边界的问题
M = cv.getRotationMatrix2D(center=(cols/2,rows/2), angle = 45, scale = 0.6)
# 第三个参数是输出图像的尺寸中心
# dst = cv.warpAffine(img,M,(2*cols,2*rows))
dst = cv.warpAffine(img,M,(cols,rows))
while(1):
cv.imshow('simg',img)
cv.imshow('img',dst)
if cv.waitKey(1) & 0xFF==27:
break
cv.destroyAllWindows()
# 90、180、270度旋转
# 顺时针旋转90度
# dst = cv.rotate(img,rotateCode=cv.ROTATE_90_CLOCKWISE)
# 旋转180度
# dst = cv.rotate(img,rotateCode=cv.ROTATE_180)
# 逆时针旋转90
dst = cv.rotate(img,rotateCode=cv.ROTATE_90_COUNTERCLOCKWISE)
cv.imshow('dst', dst)
cv.waitKey(0)
cv.destroyAllWindows()
# 翻转
dst = cv.flip(img,0) # 垂直翻转
dst1 = cv.flip(img,1) # 水平翻转
cv.imshow('dst', dst)
cv.imshow('dst1', dst1)
cv.waitKey(0)
cv.destroyAllWindows()
4、仿摄变换
'''
在仿射变换中,原图中所有的平行线在结果图像中同样平行。
为了创建这个矩阵我们需要从原图像中找到三个点以及他们在输出图像中的位置。
然后cv2.getAffineTransform 会创建一个 2x3 的矩阵
最后这个矩阵会被传给函数 cv2.warpAffine
'''
from matplotlib import pyplot as plt
img = cv.imread('retangel.png')
rows,cols,ch = img.shape
pts1 = np.float32([[40,40],[180,40],[40,180]])
pts2 = np.float32([[10,100],[200,50],[100,250]])
M = cv.getAffineTransform(pts1,pts2)
dst = cv.warpAffine(img,M,(cols,rows))
plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
plt.show()
5、投射变换
'''
对于视角变换,我们需要一个 3x3 变换矩阵。
在变换前后直线还是直线。
要构建这个变换矩阵,你需要在输入图像上找 4 个点,以及他们在输出图像上对应的位置。这四个点中的任意三个都不能共线。
这个变换矩阵可以有函数 cv2.getPerspectiveTransform() 构建。
然后把这个矩阵传给函数cv2.warpPerspective
'''
img = cv.imread("car3_plat.jpg")
h,w,_ = img.shape
M = cv.getRotationMatrix2D(center=(0, 0), angle=20, scale=1)
dst1 = cv.warpAffine(img, M, (w+30, w // 2), borderValue=[0,0,0])
h,w,_ = dst1.shape
# 分布在原始图像中选择三个点以及这三个点在新图像中的位置
pts1 = np.float32([[15,0], [245,0], [250,115], [34,100]])
pts2 = np.float32([[0,0], [230,0], [230,115], [0,115]])
# 构建对应的M
M = cv.getPerspectiveTransform(pts1,pts2)
print(M)
dst = cv.warpPerspective(dst1,M,(w,h))
# 可视化画图
plt.subplot(131)
plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.title('Input')
plt.subplot(132)
plt.imshow(cv.cvtColor(dst1, cv.COLOR_BGR2RGB))
plt.title('dst1')
plt.subplot(133)
plt.imshow(cv.cvtColor(dst, cv.COLOR_BGR2RGB))
plt.title('dst')
plt.show()
[[ 1.17425320e+00 -2.23108108e-01 -1.76137980e+01]
[ 7.81597009e-17 1.08822639e+00 -7.79277133e-17]
[ 7.11237553e-04 -7.78982594e-04 1.00000000e+00]]
三、图像阈值
1、简单阈值
'''
当像素值高于阈值时,我们给这个像素赋予一个新值(可能是白色),否则我们给它赋予另外一种颜色(也许是黑色)。
这个函数就是 cv2.threshhold()。
这个函数的第一个参数就是原图像,原图像应该是灰度图。
第二个参数就是用来对像素值进行分类的阈值。
第三个参数就是当像素值高于(有时是小于)阈值时应该被赋予的新的像素值。
第三个参数是阈值类型
'''
# 产生一个图像(从白色到黑色的递增的形式)
img = np.arange(255, -1, -1).reshape((1, -1))
for i in range(255):
img = np.append(img, np.arange(255, -1, -1).reshape((1, -1)), axis=0)
img = img.astype(np.uint8)
# 进行普通二值化操作(第一个参数是返回的阈值,第二个参数返回的是二值化之后的图像)
# 普通二值化操作, 将小于等于阈值thresh的设置为0,大于该值的设置为maxval
ret, thresh1 = cv.threshold(src=img, thresh=127, maxval=255, type=cv.THRESH_BINARY)
# 反转的二值化操作, 将小于等于阈值thresh的设置为maxval,大于该值的设置为0
ret, thresh2 = cv.threshold(src=img, thresh=127, maxval=255, type=cv.THRESH_BINARY_INV)
# 截断二值化操作,将小于等于阈值thresh的设置为原始值,大于该值的设置为maxval
ret,thresh3 = cv.threshold(img,127,255,cv.THRESH_TRUNC)
# 0二值化操作,将小于等于阈值的设置为0,大于该值的设置为原始值
ret,thresh4 = cv.threshold(img,127,255,cv.THRESH_TOZERO)
# 反转0二值化操作,将小于等于阈值的设置为原始值,大于阈值的设置为0
ret,thresh5 = cv.threshold(img,127,255,cv.THRESH_TOZERO_INV)
titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plt.subplot(2,3,i+1)
plt.imshow(images[i]/255.0,'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
2、自适应阈值
'''
在前面的部分我们使用是全局阈值,整幅图像采用同一个数作为阈值。
当然这种方法并不适应与所有情况,尤其是当同一幅图像上的不同部分的具有不同亮度时。
这种情况下我们需要采用自适应阈值。此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。
因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。
• adaptiveMethod- 指定计算阈值的方法。
– cv2.ADPTIVE_THRESH_MEAN_C :阈值取自相邻区域的平均值
– cv2.ADPTIVE_THRESH_GAUSSIAN_C :阈值取值相邻区域的加权和,权重为一个高斯窗口。
• blockSize - 邻域大小(用来计算阈值的区域大小)。
• C - 这就是是一个常数,阈值就等于的平均值或者加权平均值减去这个常数。
'''
img = cv.imread('open_cv.png',0)
# 中值滤波
img = cv.medianBlur(img,5)
ret,th1 = cv.threshold(img,175,255,cv.THRESH_BINARY)
#11为blockSize,2为C值
th2 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 11, 2)
th3 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 11, 2)
titles = ['Original Image', 'Global Thresholding (v = 175)','Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
for i in range(4):
plt.subplot(2,2,i + 1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
3、Otsu’s 二值化
'''
在使用全局阈值时,我们就是随便给了一个数来做阈值,那我们怎么知道我们选取的这个数的好坏呢?答案就是不停的尝试。
如果是一副双峰图像(简单来说双峰图像是指图像直方图中存在两个峰)呢?
我们岂不是应该在两个峰之间的峰谷选一个值作为阈值?这就是 Otsu 二值化要做的。
简单来说就是对一副双峰图像自动根据其直方图计算出一个阈值。(对于非双峰图像,这种方法得到的结果可能会不理想)。
用到的函数还是 cv2.threshold(),但是需要多传入一个参数(flag):cv2.THRESH_OTSU。
这时要把阈值设为 0。然后算法会找到最优阈值,这个最优阈值就是返回值 retVal。
如果不使用 Otsu 二值化,返回的retVal 值与设定的阈值相等。
二值化的原理是:
其实就是在两个峰之间找到一个阈值 t,将这两个峰分开,并且使每一个峰内的方差最小。
'''
# 下面的例子中,输入图像是一副带有噪声的图像。
# 第一种方法,我们设127 为全局阈值。
# 第二种方法,我们直接使用 Otsu 二值化。
# 第三种方法,我们首先使用一个 5x5 的高斯核除去噪音,然后再使用 Otsu 二值化。
# 看看噪音去除对结果的影响
img = cv.imread('noise.png',0)
# 1、global thresholding
ret1,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
# 2、Otsu's thresholding
ret2,th2 = cv.threshold(img,0,255,cv.THRESH_BINARY + cv.THRESH_OTSU)
# 3、Otsu's thresholding after Gaussian filtering
#(5,5)为高斯核的大小,0 为标准差
blur = cv.GaussianBlur(img,(5,5), 0)
# 阈值一定要设为 0!
ret3,th3 = cv.threshold(blur,0,255,cv.THRESH_BINARY + cv.THRESH_OTSU)
# plot all the images and their histograms
images = [img, 0, th1,
img, 0, th2,
blur, 0, th3]
titles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)',
'Original Noisy Image','Histogram',"Otsu's Thresholding",
'Gaussian filtered Image','Histogram',"Otsu's Thresholding"]
# 这里使用了 pyplot 中画直方图的方法,plt.hist, 要注意的是它的参数是一维数组
# 所以这里使用了(numpy)ravel 方法,将多维数组转换成一维,也可以使用 flatten 方法
#ndarray.flat 1-D iterator over an array.
#ndarray.flatten 1-D array copy of the elements of an array in row-major order.
for i in range(3):
plt.subplot(3,3,i*3+1),plt.imshow(images[i*3],'gray')
plt.title(titles[i*3]), plt.xticks([]), plt.yticks([])
plt.subplot(3,3,i*3+2),plt.hist(images[i*3].ravel(),256)
plt.title(titles[i*3+1]), plt.xticks([]), plt.yticks([])
plt.subplot(3,3,i*3+3),plt.imshow(images[i*3+2],'gray')
plt.title(titles[i*3+2]), plt.xticks([]), plt.yticks([])
plt.show()