2019-7-9 opencv核心操作(基础操作/数学运算/性能测量和改进)

64 篇文章 3 订阅

官网参见https://docs.opencv.org/3.4.1/d7/d16/tutorial_py_table_of_contents_core.html

  • 图像基础操作
    • 获取像素值和修改像素值
    • 获取图像属性
    • 图像ROI(region of interest,感兴趣的区域)
    • 拆分和合并图像通道
    • 为图像制作边框(填充)。含:RGB和BGR互换。
  • 图像上的数学运算
    • 图像相加
    • 图像混合
    • 按位运算
  • 性能测量和改进技术
    • 利用opencv测量性能
    • opencv默认优化
    • 性能优化技术

1.图像基础操作

1-1. 获取像素值和修改像素值

例1,读取图像,根据像素的行和列的坐标获取它的像素值。imread()函数

import numpy as np
import cv2

img = cv2.imread('test.jpg')

px = img[100,100]
print px

100行和100列的像素值是:[236 236 236]

对BGR图像而言,返回值为B,G,R的值。对灰度图像而言,会返回像素的强度(intensity)。

例2,修改指定坐标的像素值

import numpy as np
import cv2

img = cv2.imread('test.jpg')

img[100,100]=[255,255,255]

例3,获取指定坐标,指定通道的像素值。item()函数

import numpy as np
import cv2

img = cv2.imread('test.jpg')

print img.item(100,100,2) #结果236

100行和100列红色通道的像素值是236

例4,修改指定坐标,指定通道的像素值。itemset()函数。

import numpy as np
import cv2

img = cv2.imread('test.jpg')

img.itemset((100,100,2),100) #100行100列红色像素值修改为100

利用矩阵可以很方便修改像素,不再需要使用循环操作。

例5,修改指定区域的像素颜色

import numpy as np
import cv2

img = cv2.imread('test.jpg')

img[0:100, 0:100]=0 #从(0,0)到(100,100)区域修改为黑色

cv2.imshow("Image", img)
cv2.waitKey(0)

1-2.获取图像属性

图像的属性包括:行数,列数,通道数,图像数据类型,像素数目等
例1,获取图像数,列数,通道数。shape属性。

import numpy as np
import cv2

img = cv2.imread('test.jpg')

print img.shape

返回的元组值为:(440L, 650L, 3L)。该图片包含440行,650列和3个通道。

注意:灰度图返回值只有行数和列数。根据返回值就可以判断是彩色图还是灰度图。

例2,获得像素数目。size属性

import numpy as np
import cv2

img = cv2.imread('test.jpg')

print img.size

该图的像素数目是858000

例3,获得图像数据类型。dtype属性

import numpy as np
import cv2

img = cv2.imread('test.jpg')

print img.dtype

该图像数据类型为unit8

img.dtype 非常重要。因为图像经常需要进行转换,所以代码中经常出现数据类型的不一致的情况。在debug时候,会用到dtype属性。

1-3.图像ROI(region of interest,感兴趣的区域)

对一幅图像的特定区域进行操作。
例1,复制一个球

# -*- coding: cp936 -*-
import numpy as np
import cv2

img = cv2.imread('test.jpg')

ball=img[0:120, 185:305] #复制1个球
img[0:120,0:120]=ball #在图像指定区域粘贴

cv2.imshow("Image", img)
cv2.waitKey(0)

在这里插入图片描述
上图中,把最上面的哪个粉色球复制在图像的左上角。

1-4.拆分和合并图像通道

有时我们需要对B,G,R 3个通道分别进行操作。这时候你就需要把BGR图像拆分成单个通道。有时我们需要把独立通道的图片合并成一个BGR图像。
例1,拆分通道。split()函数

 b,g,r = cv.split(img) #方法1
 b = img[:,:,0] #方法2,单独获得蓝色通道

假如你想使所有像素的红色通道值都为0,你不必先拆分再赋值。你可以直接使用Numpy,这会更快。
例2,修改红色通道值为黑色

img[:,:,2] = 0

注意:cv2.split() 是一个比较耗时的操作,只有真正需要时才用它。请尽量使用Numpy。

1-5.为图像制作边框(填充)

如果你想为图像创建一个边框,就像相框一样,你可以使用cv2.copyMakeBorder()函数。该函数常常应用在卷积运算或0填充时。

dst = cv.copyMakeBorder(src, top, bottom, left, right, borderType[, dst[, value]])
  • top, bottom, left, right 对应边界的像素数目。例如:top=1表示顶部边框宽度为1个像素。
  • borderType 边框类型
    • BORDER_REFLICATE    # 重复最后一个元素填充, 例如:aaaaaa | abcdefg | gggg
    • BORDER_REFLECT     # 边界元素镜像,abcdefg | gfedcbamn | nmabcd
    • BORDER_REFLECT_101   # 边界元素镜像,和上面类似。但在镜像时,会把边界空开,abcdefg | egfedcbamne | nmabcd
    • BORDER_WRAP      # 类似于这种方式abcdf | mmabcdf | mmabcd
    • BORDER_CONSTANT    # 常量,创建颜色常量边框,需要和参数value配合
  • dst 和源图像相同类型的图像。尺寸为行:SRC.cols+Left+Right,列:SRC.Rows+Top+Bottom

例1,各种边框

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

BLUE = [255,0,0]
img1 = cv.imread('test.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,它采用的是rgb方式,但是opencv采用的是bgr方式,所以代码中设置的蓝色[255,0,0],在图中显示为红色。也就是b和r交换了

BRG和RGB互相转换参考如下

  • BGR to RGB,OpenCV image to Matplotlib
rgb = bgr[...,::-1]
  • RGB to BGR,Matplotlib image to OpenCV
bgr = rgb[...,::-1]

2.图像上的数学运算

2-1图像相加

可以使用cv2.add() 函数将两幅图像进行相加,也可以直接使用numpy,例如:res=img1+img2。前提条件是两幅图像的大小,类型必须一致,或者第二个
图像是一个标量值。

注意:OpenCV 中的加法与Numpy 的加法是不同的。OpenCV 的加法是一种饱和运算,而Numpy 的加法是一种模运算。

例1,对比opencv和numpy的加法运算

# -*- coding: cp936 -*-
import cv2 
import numpy as np

x = np.uint8([250])
y = np.uint8([10])
print cv2.add(x,y) # [[255]]。250+10 = 260 => 255

print x+y # [4]。250+10 = 260 % 256 = 4

对比以上2个加法运算的结果,在进行2个图像相加时候,显然opencv的add()函数更有优势。所以请尽量使用add()函数。

2-2.图像混合

其实这也是一种加法,只是不同图像采用了不同权重。这会给人一种混合或者透明的感觉。
图像混合加法公式如下:

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

通过修改α的值(0→1),可以实现从一幅图像到另外一幅图像之间的转换。
例1,图像混合。addWeighted函数。

# -*- coding: cp936 -*-
import cv2 
import numpy as np
from matplotlib import pyplot as plt

img1 = cv2.imread('test1.jpg') #(313L, 432L, 3L)
img2 = cv2.imread('test2.jpg') #(354L, 500L, 3L)

img3=img2[0:313, 0:432] 
dst = cv2.addWeighted(img1,0.7,img3,0.3,0)


plt.subplot(221),plt.imshow(img1,'gray'),plt.title('test1'+str(img1.shape))
plt.subplot(222),plt.imshow(img2,'gray'),plt.title('test2'+str(img2.shape))
plt.subplot(223),plt.imshow(dst,'gray'),plt.title('Image Blending'+str(dst.shape))
plt.tight_layout() #设置默认的间距,不然上下图会有点重叠
plt.show()

在这里插入图片描述
说明:

  1. 2个图像尺寸不一样,(313L, 432L, 3L)和(354L, 500L, 3L)。而图像混合尺寸必须一样,所以对第2个图做了尺寸调整。尺寸调整方法在上面图像ROI时候介绍过。
  2. 2个图像分别设置了权重,第1幅图的权重是0.7,第2幅图的权重是0.3

2-3.按位运算

按位运算包含::AND,OR,NOT,XOR 等。
提取图像的一部分,或者操作非矩形的ROI时候,有时就需要按位运算。

下面有2福图片,我们打算把第一个图上的方块叠加到第二个图像上。
如果只是把图1和图2相加,那会改变图1的白色底色也叠加过去,改变了颜色。
如果采用图像混合,那又会有透明效果。
现在我们需要的不透明效果。
如果图1的方块不是立体的,那就是1个矩形,显然可以用上面介绍的图像ROI把这个区域单独获取,可以很容易叠加到第2图上了。
但是现在立方体是一个不规则图形,那就得使用按位运算了。
在这里插入图片描述

在这里插入图片描述

在按位运算之前,先需要对图1做图像分割处理。具体参见https://blog.csdn.net/weixin_42555985/article/details/93713477。

在这里插入图片描述
效果如上,背景底色变为黑色。立方体下部的阴影也别识别为前景。
现在可以图片按位运算了

# -*- coding: cp936 -*-
import cv2 
import numpy as np
from matplotlib import pyplot as plt

img1 = cv2.imread('test2.jpg')
img2 = cv2.imread('test1.jpg')

#图像分割处理
# Step2. 创建掩模、背景图和前景图
mask = np.zeros(img2.shape[:2], np.uint8)# 创建大小相同的掩模


bgdModel = np.zeros((1,65), np.float64)# 创建背景图像 
fgdModel = np.zeros((1,65), np.float64)# 创建前景图像


# Step3. 初始化矩形区域 # 这个矩形必须完全包含前景
rect = (50,0,432,313) #格式为(x, y, w, h)


# Step4. GrubCut算法,迭代5次 # mask的取值为0,1,2,3
cv2.grabCut(img2, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)


# Step5. mask中,值为2和0的统一转化为0, 1和3转化为1
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')

img2 = img2 * mask2[:,:,np.newaxis] # np.newaxis 插入一个新维度,相当于将二维矩阵扩充为三维

cv2.imshow('img2',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()

#开始按位运算
#打算把img2的立方体放入img1, So I create a ROI
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols ]

# Now create a mask of cube and create its inverse mask also
img2gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)
mask_inv = cv2.bitwise_not(mask)
print mask_inv.dtype

# Now black-out the area of cube in ROI
# 取roi 中与mask 中不为零的值对应的像素的值,其他值为0
# 注意这里必须有mask=mask 或者mask=mask_inv, 其中的mask= 不能忽略
img1_bg = cv2.bitwise_and(roi,roi,mask = mask_inv)

# Take only region of cube from cube image.
img2_fg = cv2.bitwise_and(img2,img2,mask = mask)

# Put cube in ROI and modify the main image
dst = cv2.add(img1_bg,img2_fg)
img1[0:rows, 0:cols ] = dst
cv2.imshow('res',img1)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

效果如上。通过按位运算,把图像分割处理后的立方体图中黑色底色全部替换为图2中的图像。

3.性能测量和改进技术

3-1.利用opencv测量性能

cv2.getTickCount() 函数返回从参考点到这个函数被执行的时钟数。所以在一个函数执行前后都调用getTickCount() ,就能得到这个函数的执行时间(时钟数)。
cv2.getTickFrequency 返回时钟频率,或者说每秒的时钟数。

例1,利用getTickCount()测试代码性能

# -*- coding: cp936 -*-
import cv2 
import numpy as np
from matplotlib import pyplot as plt

img1 = cv2.imread('roi.jpg')
e1 = cv2.getTickCount()

for i in xrange(5,49,2):
    img1 = cv2.medianBlur(img1,i)
    
e2 = cv2.getTickCount()
t = (e2 - e1)/cv2.getTickFrequency()
print t

执行结果:0.019151

我们也可以用time 模块实现上面的功能,要用的函数是time.time()

3-2.opencv默认优化

opencv的很多函数都使用SSE2, AVX优化过,但是也包含了一些没有优化过的代码。所以如果我们的系统支持SSE2, AVX,就应该使用这些函数。几乎所有现代的处理器都支持它们。
opencv编译时候,优化是默认开启的。也就是说如果开启优化,opencv运行的是优化的代码;反之,则是未优化的。
使用函数cv2.useOptimized()可以查看优化是否被开启。
使用函数cv2.setUseOptimized()可以开启优化。

例1,测试优化是否开启

import cv2 
import numpy as np
from matplotlib import pyplot as plt

print cv2.useOptimized() #true

3-3.性能优化技术

有很多技术和编码方法可以来最大程度利用python和numpy的性能。
其中最重要的一点就是要简化你的算法。一旦代码可以正常运行,就开始分析它,找到瓶颈并优化它。具体方法参照如下:

  • 尽量避免使用循环,特别是2层或者3层循环
  • 算法和代码应该最大可能的使用向量,因为numpy和opencv对向量操作进行了优化
  • 利用缓存一致性
  • 没有必要就不要拷贝数组,尽量使用视图。拷贝数组非常花费资源

如果使用了以上方法代码依然很慢,或者不可避免的要使用循环,那么可以尝试一些其他包(例如:Cython)来提高运行速度。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值