opencv学习笔记——5.0

第四章

图像算术运算

图像加法

cv.add对两副图像相加,要求两幅或多幅相加的图像的大小应该相同,处理时将两副图像相同位置的像素的灰度值(灰度图像)或彩色像素各通道值(彩色图像)分别相加;对一副图像和一个标量相加时,则将图像所有像素的各通道值分别与标量进行相加。
numpy加法是直接将数据相加取模。
参考以下代码:

x = np.uint8([250])
y = np.uint8([10])
# uint8是专门用于储存各种图像的,范围是0-255
# cv.add为饱和运算:[250] + [10] = [250+10](>255) = 255
print(cv.add(x, y))
# numpy加法是模运算:[250] + [10] = [250+10]%256 = 4
print(x+y)

运算结果:

[[255]]
[4]
图像融合

也是图像加法,但是对图像赋予不同的权重,达到融合或透明的效果。
这里使用的是cv.addWEighted()来实现对图像的加权。其中第一个参数是第一个图像;第二个参数是第一个图像的权值;第三个是第二个图像;第四个是第二个图像的参数;第五个是偏置,具体代码:

img1 = cv.imread('./picture/5_1.jpg')
img2 = cv.imread('./picture/5_2.jpg')
# print(img1.shape)
dst = cv.addWeighted(img1, 0.3, img2, 0.7, 0)
dst = cv.resize(dst, (108*3, 240*3), interpolation=cv.INTER_AREA)
cv.imshow('dst', dst)
cv.waitKey(0)
cv.destroyAllWindows()

实现的效果:
在这里插入图片描述

按位运算

opencv中按位运算是十分重要的运算方式,其中与、或、异或、取反分别为cv.bitwise_and()、or()、xor()、not(),四个函数的参数都由src1、src2两个图像数据,以及mask可选操作掩码

img1 = cv.imread('./picture/1_read.jpg')
img2 = cv.imread('./picture/5_3.jpg')
# 获取logo的图像数据并打算放在img1的右上角ROI处
rows, cols, channels = img2.shape
roi = img1[0:rows, 0:cols]
# 创建logo的掩码,并创建其相反掩码
img2gray = cv.cvtColor(img2, cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)
mask_inv = cv.bitwise_not(mask)
# 将ROI中的logo区域涂黑
img1_bg = cv.bitwise_and(roi, roi, mask = mask_inv)
# 仅从logo图像中提取logo区域
img2_fg = cv.bitwise_and(img2, img2, mask)
# 将logo放入ROI并修改图像
dst = cv.add(img1_bg, img2_fg)
img1[0:rows, 0:cols] = dst

cv.namedWindow('res', 0)
cv.resizeWindow('res', int(1920/2), int(1080/2))
cv.imshow('res', img1)
cv.waitKey(0)
cv.destroyAllWindows()

运行效果如下:
在这里插入图片描述

练习题

通过加权函数实现幻灯片切换效果,只需要通过循环动态改变权值的大小即可

img1 = cv.imread('./picture/5_1.jpg')
img2 = cv.imread('./picture/5_2.jpg')
dst = cv.addWeighted(img1, 1.0, img2, 0.0, 0)
dst = cv.resize(dst, (108*3, 240*3), interpolation=cv.INTER_AREA)
for i in range(11):
    dst = cv.addWeighted(img1, 1.0-0.1*i, img2, 0.1*i, 0)
    dst = cv.resize(dst, (108*3, 240*3), interpolation=cv.INTER_AREA)
    cv.imshow('dst', dst)
    cv.waitKey(100)

cv.waitKey(0)
cv.destroyAllWindows()

性能衡量和提升技术

使用opencv衡量性能

cv.getTickCount()返回从参考事件到调用此函数的时钟周期数
cv.getTickFrequency()返回时钟周期的频率或每秒的时钟周期数
所以可以通过下面代码来获取代码段执行的执行事件/s:

e1 = cv.getTickCount()
…………………………………………
e2 = cv.getTickCount()
time = (e2-e1)/cv.getTickFrequency
使用opencv中的默认优化

许多opencv函数都是使用SSE2、AVX等进行优化的,在编译时将默认启用优化。可以通过cv.useOptimized()检查是否启用/禁用,使用cv.setUseOptimized()来启用/禁用。

print(cv.useOptimized())
cv.setUseOptimized(False)
print(cv.useOptimized())

输出:

True
False
在IPython中衡量性能

conda中自带了ipython,只需要在命令行启动ipython即可
ipython中有很多魔法指令用于测试代码的运行效果,如%timeit、%%time、%time、%%time。其中time为代码运行一次花费的时间,timeit为运行若干次中的最佳结果,%为行模式,即在魔法指令后只能紧跟一行代码,而%%单元模式则可以输入多行代码
这里使用ipython时出现了无法使用numpy等库的问题……

改变颜色空间

改变颜色空间

可以通过以下代码查看所有的颜色空间转换方法

flags = [i for i in dir(cv) if i.startswith('COLOR_')]
print( flags )

可以通过cvtColor(img, flag)来转换,其中HSV色相范围[0, 179],饱和度范围[0, 255],值范围[0, 255]

对象追踪

在HSV中比在BGR颜色空间中更容易表示颜色,下面尝试在图片或视频的一帧中,转换BGR到HSV,设置蓝色范围的阈值并单独提取蓝色对象

# 获取相机
cap = cv.VideoCapture(0)
while True:
    _, frame = cap.read()   # 读取帧
    hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
    # 定义HSV中蓝色的范围,第一个为色相范围,第二个为饱和度,第三个为值
    lower_blue = np.array([110, 50, 50])
    upper_blue = np.array([130, 255, 255])
    # 设置HSV的蓝色阈值
    mask = cv.inRange(hsv, lower_blue, upper_blue)
    # 将掩膜和图像逐像素相加
    res = cv.bitwise_and(frame, frame, mask=mask)
    cv.imshow('frame', frame)
    cv.imshow('mask', mask)
    cv.imshow('res', res)
    if cv.waitKey(5) & 0xFF == 27:
        break
cv.destroyAllWindows()

运行效果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fhZOj9NO-1688652064212)(./src/5_3.PNG)]
注意:图像中有一些噪点。我们将在后面的章节中看到如何删除它们。 这是对象跟踪中最简单的方法。一旦学习了轮廓的功能,你就可以做很多事情,例如找到该对象的质心并使用它来跟踪对象,仅通过将手移到相机前面以及其他许多有趣的东西就可以绘制图表。

如何获取要追踪色域的HSV值

可以同样通过使用函数**cv.cvtColor()**返回值来获取,例如以下代码,先构建一个用BGR表示的绿色图,将其传入cvCOlor中获取返回值即为HSV值

green = np.uint8([[[0, 255, 0]]])
hsv_green = cv.cvtColor(green, cv.COLOR_BGR2HSV)
print(hsv_green)

图像的几何变换

缩放

OpenCV带有一个函数cv.resize()。图像的大小可以手动指定,也可以指定缩放比例。也可使用不同的插值方法。首选的插值方法是cv.INTER_AREA用于缩小,cvINTER_CUBIC(慢)和cv.INTER_LINEAR用于缩放。默认情况下,出于所有调
整大小的目的,使用的插值方法为cv.INTER_LINEAR。

平移

使用矩阵来表示位移的方向和长度,其中matrix的格式为[1, 0, t_x], [0, 1, t_y],t_x、t_y是位移后的坐标

img = cv.imread('./picture/1_read.jpg', 0)
rows, cols = img.shape
M = np.float32([[1, 0, 100], [0, 1, 50]])
dst = cv.warpAffine(img, M, (cols, rows))
cv.imshow('img', dst)
cv.waitKey(0)
cv.destroyAllWindows()
旋转

图像旋转也可以用矩阵变换来表示,但是一般使用opencv提供的getRotationMatrix2D()来实现会比较方便,第一个参数是图片的旋转中心,第二个是旋转角度,第三个是缩放比例或旋转方向

img = cv.imread('./picture/1_read.jpg', 0)
rows, cols = img.shape
M = cv.getRotationMatrix2D(((cols-1)/2.0, (rows-1)/2.0), 90, 1)
dst = cv.warpAffine(img, M, (cols, rows))
cv.imshow('img', dst)
cv.waitKey(0)
cv.destroyAllWindows()
仿射变换

在仿射变换中,原始图像中的所有平行线在输出图像中仍将平行。为了找到变换矩阵,我们需要输入图像中的三个点及其在输出图像中的对应位置。然后cv.getAffineTransform将创建一个2x3矩阵

img = cv.imread('./picture/1_read.jpg')
rows, cols, ch = img.shape
pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
pts2 = np.float32([[10, 100], [200, 50], [100, 250]])
M = cv.getAffineTransform(pts1, pts2)
dst = cv.warpAffine(img, M, (cols, rows))
cv.imshow('img', dst)
cv.waitKey(0)
cv.destroyAllWindows()
透视变换

透视变换的作用是:选定一个图片中心,以及四个点,在变换后图片中心不变,选定的四个点将会是输出图片的四个顶点

img = cv.imread('./picture/1_read.jpg')
rows, cols, ch = img.shape
pts1 = np.float32([[56, 65], [368, 52], [28, 387], [389, 390]])
pts2 = np.float32([[0, 0], [300, 0], [0, 300], [300, 300]])
M = cv.getPerspectiveTransform(pts1, pts2)
dst = cv.warpPerspective(img, M, (300,300))
plt.subplot(121), plt.imshow(img), plt.title('Input')
plt.subplot(122), plt.imshow(dst), plt.title('Output')
plt.show()
图像阈值

可以通过函数cv.threshold()来对图像进行阈值设置,第一个参数是图像,第二个参数是阈值(需要使用灰度图像),第三个参数是分配给超过阈值的像素值的最大值,第四个参数是opencv提供的不同参数:

  • cv.THRESH_BINARY
  • cv.THRESH_BINARY_INV
  • cv.THRESH_TRUNC
  • cv.THRESH_TOZERO
  • cv.THRESH_TOZERO_INV
    函数返回值为阈值和输出图像
img = cv.imread('./picture/gradient.jpg', 0)
ret, thresh1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
ret, thresh2 = cv.threshold(img, 127, 255, cv.THRESH_BINARY_INV)
ret, thresh3 = cv.threshold(img, 127, 255, cv.THRESH_TRUNC)
ret, thresh4 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO)
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], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

自适应阈值

自适应阈值化可以处理不同区域有光照等不同条件影响的情况,使用cv.adaptiveThreshold(),算法基于像素周围的小区域确定像素的阈值,第一个参数是图像;第二个参数是超过区域部分的取值;第三个参数是算法类型:
cv.ADAPTIVE_THRESH_MEAN_C::阈值是邻近区域的平均值减去常数C
cv.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是邻域值的高斯加权总和减去常数C
第四个参数是阈值类型:THRESH_BINARY 和THRESH_BINARY_INV;第五个参数是局部邻域大小;第六个参数是偏移值调整量

img = cv.imread('./picture/1_read.jpg', 0)
img = cv.medianBlur(img, 5)
ret, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
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 = 127)',
          '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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值