OpenCV学习笔记[2]——核心操作

核心操作

图像基本操作

前言

要使用OpenCV编写更好的优化代码,需要Numpy的丰富知识。

目标

学会:

  • 访问像素值并修改它们
  • 访问图像属性
  • 设置感兴趣区域(ROI)
  • 分割和合并图像

访问和修改像素值

import numpy as np
import cv2 as cv
img = cv.imread('myPicture.jpg') # 这里指定一个图片地址就可以了
 # 返回坐标(128,64)的像素值
print(img[128,64])# 对于 BGR 图像,它返回一个由蓝色、绿色和红色值组成的数组。对于灰度图像,只返回相应的灰度。
# 仅访问蓝色像素
print(img[100,100,0]);
img[100,100] = [255,255,255] # 修改指定像素点像素值

# 更好的像素访问和编辑方法:
img.item(10,10,2) # 访问 RED 值
img.itemset((10,10,2),100) # 修改 RED 值
img.item(10,10,2) # 再次访问

访问图像的属性

图像属性包括行数列数通道数图像数据类型像素数等。

import numpy as np
import cv2 as cv
img = cv.imread('myPicture.jpg') # 这里指定一个图片地址就可以了
# 访问图像的形状
print(img.shape) # 返回行,列和通道数的元组(如果图像是彩色的)
# 访问图像数据类型
print( img.dtype)  # 大部分情况是返回 uint8
# 访问图像的总像素数
print(img.size)

注意 img.dtype在调试时非常重要,因为OpenCV-Python代码中的大量错误是由无效的数据类型引起的。

图像感兴趣区域 ROI

有时候不得不处理一些特定区域的图像。对于图像中的眼睛检测,首先对整个图像进行人脸检测。在获取人脸图像时,我们只选择人脸区域,搜索其中的眼睛,而不是搜索整个图像。它提高了准确性(因为眼睛总是在面部上:D )和性能(因为我们搜索的区域很小)。

# 使用numpy索引获得ROI
ball = img[280:340, 330:390] # 获取280<=x<=340, 330<=y<=390 区域间的图像,赋值给ball
img[273:333, 100:160] = ball # 把ball图像粘贴到新的区域

拆分和合并图像通道

# 分别取出B,G,R的图像
b = img [:, :, 0]
g = img [:, :, 1]
r = img [:, :, 2]

# 可以对单个图像进行整体赋值
b = 0 # 把蓝色通道置零
# 也可以不拆分通道直接对原图处理
img[:, :, 1] = 0 # 把原图的绿色通道置0

设置图像边框

像是加个相框?常应用在卷积运算,零填充等方面

方法:

  • cv2.copyMakeBorder()

参数:

  • src - 输入图像

  • topbottomleftright 边界宽度(以相应方向上的像素数为单位)

  • borderType - 定义要添加哪种边框的标志。它可以是以下类型:

    • cv.BORDER_CONSTANT - 添加恒定的彩色边框。该值应作为下一个参数给出。

    • cv.BORDER_REFLECT - 边框将是边框元素的镜像,如下所示: fedcba | abcdefgh | hgfedcb

    • cv.BORDER_REFLECT_101cv.BORDER_DEFAULT与上述相同,但略有变化,例如: gfedcb | abcdefgh | gfedcba

    • cv.BORDER_REPLICATE最后一个元素被复制,像这样: aaaaaa | abcdefgh | hhhhhhh

    • cv.BORDER_WRAP难以解释,它看起来像这样: cdefgh | abcdefgh | abcdefg

  • value 边框的颜色,如果边框类型为cv.BORDER_CONSTANT才需要传入此参数

# 这是示例代码,可以更好地理解上面的参数
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
BLUE = [255,0,0]
img1 = cv.imread('mypicture.jpg') # 图片路径需正确
replicate = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REPLICATE)
reflect = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REFLECT)
reflect101 = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REFLECT_101)
wrap = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_WRAP)
constant= cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_CONSTANT,value=BLUE)
plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')
plt.show()

(图像与matplotlib一起显示。因此红色和蓝色通道将互换)

图像算数运算

加法

可以通过OpenCV的 cv.add() 函数或者 numpy 操作 res = img1 + img2 添加两个图像。两个图像应具有相同的深度和类型,或者第二个图像可以只是一个标量值

需要注意的是:OpenCV加法是饱和运算,而Numpy加法是模运算。即前者有防溢出,上限255;后者会溢出,超出255会归零

# 示例参考
x = np.uint8([250])
y = np.uint8([10])
print( cv.add(x,y) ) # 250+10 = 260 => 255
print( x+y )          # 250+10 = 260 % 256 = 4

图像融合

这也是图像加法,但是对图像赋予不同的权重,以使其具有融合或透明的感觉。

根据以下等式添加图像:

G(x)=(1−α)f0(x)+αf1(x)

具体函数 : cv.addWeighted() 。 **注:**融合的图片

# 示例参考
img1 = cv.imread('l.jpg')
img2 = cv.imread('2.jpg')
dst = cv.addWeighted(img1,0.7,img2,0.3,0) # dst = img1*0.7+img2*0.3+0
cv.imshow('dst',dst) # 将结果与显示出
cv.waitKey(0) 
cv.destroyAllWindows()

按位运算

包括了 and or not xor 操作。 它们在提取图像的任何部分、定义和处理非矩形 ROI 等方面非常有用。

下面举一个例子,如何改变一个图像的特定区域。
我想把 OpenCV 的LOGO放在一个图像上面。
如果我添加两个图像,它会改变颜色。
如果我混合它,我得到一个透明的效果。但我希望它是不透明的。
如果是一个矩形区域,我可以使用 ROI,就像我们在上一章中所做的那样。但是 OpenCV 的 logo 不是长方形的。
所以可以使用如下的按位操作来实现:

知识补充:

  • 颜色空间:指图像中每个像素的颜色信息的表示方式。例如,RGB颜色空间;灰度颜色空间,可以表示灰度图像;BGR颜色空间。
  • 掩码:掩码是一种用来控制图像处理的区域或过程的方法。它可以用一个小于等于源图像的单通道道矩阵来表示,其中非零的元素表示要保留或修改的像素值,零的元素表示要忽略或删除的像素值。就像是一张挖好形状的膜。掩码即膜上挖好的形状。
  • cv2.cvtColor() :将图像从一种颜色空间转换为另一种颜色空间。
  • cv2.threshold():将图像进行二值化处理(常被用于获取掩码)
  • cv2.bitwise_not():对图像进行按位取反操作,将图像中的每个像素的二进制值进行取反操作,常用于图像增强、边缘检测等。
  • cv2.bitwise_and(src1, src2, dst=None, mask=None):对图像1和2进行按位与操作,
	# 加载两张图片
img1 = cv.imread('messi5.jpg')
img2 = cv.imread('opencv-logo-white.png')
# 我想把logo放在左上角,所以我创建了ROI
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols ]
# 现在创建logo的掩码,并同时创建其相反掩码
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY) # 获取logo在灰度空间上的图片
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) # 按位与,将ROI中logo的区域涂黑
# 仅从logo图像中提取logo区域
img2_fg = cv.bitwise_and(img2,img2,mask = mask) # 按位与,通过纯黑背景获取去背景的 logo
# 将logo放入ROI并修改主图像
dst = cv.add(img1_bg,img2_fg) # 将logo放入已经将LOGO区置零的ROI
img1[0:rows, 0:cols] = dst # 修改主图像
cv.imshow('res',img1)
cv.waitKey(0)
cv.destroyAllWindows()

性能衡量和提升

在图像处理中,由于每秒要处理大量操作,因此必须使代码不仅提供正确的解决方案,而且还必须以最快的方式提供。

使用OpenCV衡量性能

  • cv.getTickCount 函数返回从参考事件(如打开机器的那一刻)到调用此函数那一刻之间的时钟周期数。因此,如果在函数执行之前和之后调用它,则会获得用于执行函数的时钟周期数。
  • cv.getTickFrequency 函数返回时钟周期的频率或每秒的时钟周期数。

OpenCV中的默认优化

知识补充:

  • SSE2:SSE2是一种SIMD(单指令多数据流)的指令集,它可以让CPU同时执行多个数据操作,提高计算效率。它比之前的MMX指令集增加了浮点和整数运算的指令,以及整数和浮点数据之间的转换。

  • AVX:AVX是在SSE2的基础上发展出来的,它的主要特点是将寄存器的位宽从128位扩展到256位,从而可以同时处理更多的数据。AVX还增加了一些新的指令和功能,比如三操作数指令、数据收集指令、FMA指令等。

许多 OpenCV 函数都是使用 SSE2、 AVX 等进行优化的。 它还包含未优化的代码。因此,如果我们的系统支持这些特性,我们就应该利用它们(几乎所有现代的处理器都支持它们)。

可以使用 cvUseoptimized 检查是否启用 / 禁用和 cvSetuseoptimized 以启用 / 禁用它。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值