OpenCV基础操作(4)颜色空间转换、几何变换、图像二值化操作

news2024/11/25 12:01:22

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()


在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/552138.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《面试1v1》volatile

基本功 我是 javapub,一名 Markdown 程序员从👨‍💻,八股文种子选手。 面试官: 你能解释一下 volatile 关键字的作用吗? 候选人: 当我们在编写多线程程序时,经常会遇到线程安全的问…

iptables防火墙(一)

iptables防火墙 一、iptables概述1、netfilter 与 iptables 的关系1.netfilter2.iptables 2、四表五链1.四表2.五链3.表的匹配优先级4.规则链之间的匹配顺序5.规则链内的匹配顺序 二、iptables防火墙的安装及配置方法1、iptables防火墙安装2、iptables防火墙的配置方法1.iptabl…

nginx(七十八)日志的深入探究

一 日志 ① nginx与日志相关的指令 access_log log_format error_log rewrite_log log_subrequest debug_connection rewrite_log limit_conn_log_level limit_req_log_level log_not_found open_log_file_cache uninitialized_variable_warn log_not_found …

【重新定义matlab强大系列八】利用matlab求局部值(函数islocalmax求局部最大值+函数islocalmin求局部最小值)

🔗 运行环境:Matlab 🚩 撰写作者:左手の明天 🥇 精选专栏:《python》 🔥 推荐专栏:《算法研究》 #### 防伪水印——左手の明天 #### 💗 大家好🤗&#x1f91…

三控开关接线方式记录

参考原视频 三控开关与双控开关的多种接法,多控开关的工作原理_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Qh4y1J7gC/?spm_id_from333.337.search-card.all.click&vd_sourcee821a225c7ba4a7b85e5aa6d013ac92e原视频讲的不错,大家可以关…

国内好用的免费AI处理工具:Chat8(实现类似ChatGPT功能、TCP/IP通讯问题)

目前国外的ChatGPT比较火,朋友推荐了一个chat8,经过使用,觉得还不错,链接如下: https://ai.chat86.co/go/kl/775283,进去后可以直接用手机注册使用,以下是我问其关于TCP/IP的对话过程&#xff0…

(转载)从0开始学matlab(第11天)—关系运算符和逻辑运算符

选择结构的运算由一个表达式控制的,这个表达式的结果只有 true(1) 和 false(0)。有两种形式的运算符可以在 MATLAB 中关系得到 true/false:关系运算符和逻辑运算符。跟 C 语言一样, MATLAB 没有布尔型和逻辑数据类型。 MATLAB 把 0 …

模块一:k8s集群部署与安全配置

模块一:k8s集群部署与安全配置 目录 1、K8s安全运维概述 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PST6trat-1684674016197)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/1665822875941-73d822c8-7bdd-481c-acc1-df97b70c…

python爬虫实战——小说爬取

python爬虫实战——小说爬取 基于requests库和lxml库编写的爬虫,目标小说网站域名http://www.365kk.cc/,类似的小说网站殊途同归,均可采用本文方法爬取。 目标网站:传送门 本文的目标书籍:《我的师兄实在太稳健了》…

软件设计师第4题

首先,我是备考2023年上半年的考试。 一、历年考试题 历年的考题如下,从表中分析可以看出,动态规划法、排序算法、回溯法、分治法是很大概率考察的算法,尤其是动态规划法,本身其理解难度较高,且可以出的题型…

docker:容器的数据卷

1 数据卷概念及作用 1.1 什么是容器数据卷 先来看看Docker的理念: 将应用与运行的环境打包形成容器运行 ,运行可以伴随着容器,但是我们对数据的要求希望是持久化的容器之间希望有可能共享数据 Docker容器产生的数据,如果不通过…

什么是人工智能的知识图谱?知识图谱的组成、构建、应用有哪些?

人工智能(Artificial Intelligence,AI)是一种通过计算机模拟人类智能的技术,其应用范围越来越广泛。知识图谱(Knowledge Graph,KG)则是人工智能技术中的重要组成部分,它是一种结构化…

Redis集群安装之主从集群

1.主从集群 Redis有三种集群模式,分别是:主从模式、哨兵模式、Cluster模式。Rdis最开始使用主从模式做集群,若master宕机需要手动配置slave转为master;后来为了高可用提出来哨兵模式,该模式下有一个哨兵监视master和s…

TCP实现HTTP服务

在之前的篇章中我们已经讲过七层参考模型了,今天我们从传输层实现应用层http服务 使用nodejs原生net模块就可以打通TCP传输层并且提供一个端口号进行监听 创建一个TCP服务 import net from netconst server net.createServer((socket) > {socket.on(data, (da…

【路径规划】基于人工蜂群算法的栅格法路径规划 机器人路径规划【Matlab代码#23】

文章目录 【可更换其他算法,获取资源请见文章第6节:资源获取】1. 原始ABC算法2. 机器人路径规划环境创建3. 路径规划模型建立4. 部分代码展示5. 仿真结果展示6. 资源获取 【可更换其他算法,获取资源请见文章第6节:资源获取】 1. 原…

【实时性】实时性优化的一些参数设置和心得

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。 博客…

Deformable DETR 论文学习

1. 解决了什么问题? DETR 去除了目标检测算法中的人为设计,取得了不错的表现。但是其收敛速度很慢,对低分辨率特征识别效果差: 模型初始化时,注意力模块给特征图上所有的像素点分配的权重是均匀的,就需要…

chatgpt赋能Python-python5的阶乘

Python5的阶乘介绍 Python是一门广泛应用于编写脚本、自动化、爬虫、数据分析等方面的编程语言,也是很多科研领域使用的首选。Python的功能和灵活性可以帮助用户解决各种问题,而本文要介绍的是Python中计算阶乘的方法。 阶乘是数学中的一个概念&#x…

fullter 学习记录_01_插件整理

flutter学习记录第一节--搭建项目及路由的设置 1.轮播图: flutter_swiper1.1 用处1.2 导入flutter_swiper库1.3 导入库,运行后可能遇到的问题1.4 属性说明1.5 代码案例 2. flutter_screenutil2.1 用处2.2 引用2.3 使用说明2.4 代码实现按理2.5 ScreenUtl 的封装 1.轮…

UNIX环境高级编程——守护进程

13.1 引言 守护进程(daemon)是生存期长的一种进程。它们常常在系统引导装入时启动,仅在系统关闭时才终止。因为它们没有控制终端,所以说它们是在后台运行的。 13.2 守护进程的特征 系统进程依赖于操作系统实现。父进程ID为0的各…