赞
踩
基本操作:
注意:本节中几乎所有的操作都主要与Numpy而不是OpenCV有关。
>>> np.random.seed(123) # 设置随机数种子
>>> x = np.random.randint(9, size=(3, 3)) # 生成随机数矩阵
>>> x
array([[2, 2, 6],
[1, 3, 6],
[1, 0, 1]])
>>> x.item(3) # 参数为一个数字;展开数组并获得index为3的项,注意:index从0开始
1
>>> x.item(7) # 同上
0
>>> x.item((0, 1)) # 参数为一个元组;以n维的方式获得对应项 第一维index为0([2, 2, 6]),第二维index为1(即[2, 2, 6]中的第二个元素 2)
2
>>> x.item((2, 2)) # [1, 0, 1]中的第3个元素 1
1
注意:可分别用来根据位置获取numpy矩阵中的值和为给定位置赋新值,速度比用index快。
>>> x = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]], np.int32)
>>> x
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]], dtype=int32) # 三维数组 shape = (2,2,3)
# 把 index = 1 的 value 改成 999
>>> x.itemset(1, 999) # 参数1是数字1,那么,先展开数组后,将index=1的元素 改为999, 即[1,2,3....11,12]中的2改为999
>>> x
array([[[ 1, 999, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[ 10, 11, 12]]], dtype=int32)
# 把 index = (1, 1, 2) 的值改成 888
>>> x.itemset((1, 1, 2), 888)
# 参数1是元组(1,1,2);那么,按矩阵方式,获取一维数组index=1的元素(即:[[7,8,9],[10,11,12]]),再获得第二维index=1的元素(即:[10,11,12]),再获得第三维index=2的元素(即:12);将这元素12 改为 888
>>> x
array([[[ 1, 999, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[ 10, 11, 888]]], dtype=int32)
>>> b,g,r = cv.split(img) # 分割图像
'''
numpy的等效方法,推荐
numpy的方法比cv.split()更快
b == img[:,:,0]
g == img[:,:,1]
r == img[:,:,2]
'''
警告:cv.split()是一个耗时的操作。所以只有在必要时才使用它。
注意:b,g,r尺寸应该一致
>>> img = cv.merge((b,g,r)) # 合并通道
让我们先加载一个彩色图像。
>>> import numpy as np
>>> import cv2 as cv
>
>>> img = cv.imread('messi5.jpg') # cv.imread 读取图像 需要注意的是CV图像的像素是BGR排序的。一般彩色图像为RGB
你可以通过其行和列坐标来访问一个像素值。
# img 为numpy.ndarray数组,且img.shape为3维。
# 第一维表示图像的行数(即:图像的高),第二维表示图像的列数(即:图像的宽),第三维表示图像的深度(彩色为3,分别BGR,灰度为1,即强度)
>>> px = img[100,100] # 获取图像的一个像素;[100,100] 指定了第一维(行)和第二维(列)
>>> print( px )
# 输出结果:
[157 166 200] # 这证明了 彩色图像的深度为3,即:3个字节,分别是B G R
# accessing only blue pixel
>>> blue = img[100,100,0] # 只访问一个像素的blue分量
>>> print( blue )
# 输出结果
157
笔记:第一维表示图像的行数(即:图像的高),第二维表示图像的列数(即:图像的宽),第三维表示图像的深度(彩色为3,分别BGR,灰度为1,即强度)
你可以用同样的方法修改像素值。
>>> img[100,100] = [255,255,255]
>>> print( img[100,100] )
[255 255 255]
警告:Numpy是一个用于快速数组计算的优化库。因此,简单地访问每一个像素值并对其进行修改将是非常缓慢的,我们不鼓励这样做。
注释:上述方法通常用于选择一个数组的某个区域(即:数组切片),例如前5行和后3列。
对于单个像素的访问,Numpy数组方法,array.item()和array.itemset()被认为更好。然而,它们总是返回一个标量,所以如果你想访问所有的B、G、R值,你将需要为每个值分别调用array.item()。
更好的像素访问和编辑方法:
# accessing RED value
>>> img.item(10,10,2) # numpy.ndarray.item()方法
59
# modifying RED value
>>> img.itemset((10,10,2),100) # numpy.ndarray.itemset()方法
>>> img.item(10,10,2)
100
图像属性包括行、列和通道的数量;图像数据的类型;像素的数量等。
图像的形状是由img.shape访问的。它返回一个包含行数、列数和通道数(如果图像是彩色的)的元组。
>>> print( img.shape ) # 彩色图像 (342, 548, 3) 灰度图像 (342, 548) 也可以是(342, 548, 1)
(342, 548, 3)
如果一个图像是灰度的,返回的元组只包含行(高)和列(宽)的数量,所以这是一个很好的方法来检查加载的图像是灰度还是彩色。
总像素数由Img.size访问。
>> print( img.size ) # ndarray 展开后的元素数量
562248
图像数据类型由img.dtype 获得。
>> print( img.dtype ) # 数据类型: 如果是彩色B,G,R 其中一个分量的值由几位存储。例如B,需要一个8位的字节进行存储
uint8
注意: img.dtype在调试时非常重要,因为OpenCV-Python代码中大量的错误是由无效的数据类型引起的。
有时,你必须对图像的某些区域进行处理。对于图像中的眼睛检测,首先在整个图像上进行人脸检测。当得到一个人脸时,我们单独选择人脸区域并在其中搜索眼睛,而不是搜索整个图像。它提高了准确性(因为眼睛总是在脸上)和性能(因为我们在一个小区域内搜索)。
使用Numpy索引再次获得ROI。这里我选择了球,并将其复制到图像的另一个区域。
>> ball = img[280:340, 330:390] # 切片 注意:切片并不会创建新的内存,而是引用了img数组对应的部分;
>> img[273:333, 100:160] = ball # 切片赋值给另一个切片
笔记:切片并不会创建新的内存,而是引用了img数组对应的部分;
有时你需要分别处理图像的B、G、R通道。在这种情况下,你需要将BGR图像分割成单个通道。在其他情况下,你可能需要将这些单独的通道连接起来以创建一个BGR图像。你可以通过以下方式简单地做到这一点。
>>> b,g,r = cv.split(img) # 分割图像
>>> img = cv.merge((b,g,r)) # 合并通道
或者:
>>> b = img[:,:,0] # 切片的方式获得通道0 即:b
假设你想把所有的红色像素设置为零–你不需要先分割通道。Numpy索引的速度更快。
>>> img[:,:,2] = 0
警告:cv.split()是一个耗时的操作。所以只有在必要时才使用它。否则,请使用Numpy索引。
如果你想在图像周围创建一个边框,类似于一个相框,你可以使用cv.copyMakeBorder()。但它在卷积操作、零填充等方面有更多应用。这个函数需要以下参数:
下面是一个演示所有这些边框类型的示例代码,以便更好地理解:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
BLUE = [255,0,0] # 一个纯蓝像素
img1 = cv.imread('opencv-logo.png') # 读取一幅图像
# 画边框 后会获得一幅新图像(即:为replicate数组新建内存)
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()
学习图像上的几种算术运算,如加法、减法、位运算等。
cv.add()
作用:图像相加
参数:
cv.addWeighted()
作用:图像加权相加 即:图像混合
cv.bitwise_and()
作用:位与
cv.bitwise_or()
作用:位或
cv.bitwise_not()
作用:位反
cv.bitwise_xor()
作用:位异或
mask 掩膜参数
下为脸部面具图,背景为白色,利用按位操作及掩膜技术清晰抠出面具轮廓。
// C++
// 转换目标(一个面具)为灰度图像
cvtColor(faceMaskSmall, grayMaskSmall, CV_BGR2GRAY);
// 隔离图像上像素的边缘,仅与面具有关(即面具的白色区域剔除),下面函数将大于230像素的值置为0,小于的置为255
threshold(grayMaskSmall, grayMaskSmallThresh, 230, 255, CV_THRESH_BINARY_INV);
// 通过反转上面的图像创建掩码(因为不希望背景影响叠加)
bitwise_not(grayMaskSmallThresh, grayMaskSmallThreshInv);
//使用位“与”运算来提取面具精确的边界
bitwise_and(faceMaskSmall, faceMaskSmall, maskedFace, grayMaskSmallThresh);
// 使用位“与”运算来叠加面具
bitwise_and(frameROI, frameROI, maskedFrame, grayMaskSmallThreshInv);
利用掩膜(mask)进行“与”操作,即掩膜图像白色区域是对需要处理图像像素的保留,黑色区域是对需要处理图像像素的剔除,其余按位操作原理类似只是效果不同而已。
算术操作全部函数
# 注意 以下为C++版本
/*
两幅图像可以相加、相减、相乘、相除、位运算、平方根、对数、绝对值等;
图像也可以放大、缩小、旋转,还可以截取其中的一部分作为ROI(感兴趣区域)进行操作,各个颜色通道还可以分别提取及对各个颜色通道进行各种运算操作
*/
// 相加
void add(InputArray src1, InputArray src2, OutputArray dst,InputArray mask=noArray(), int dtype=-1);//dst = src1 + src2
// 相减
void subtract(InputArray src1, InputArray src2, OutputArray dst,InputArray mask=noArray(), int dtype=-1);//dst = src1 - src2
// 相乘
void multiply(InputArray src1, InputArray src2,OutputArray dst, double scale=1, int dtype=-1);//dst = scale*src1*src2
// 除法
void divide(InputArray src1, InputArray src2, OutputArray dst,double scale=1, int dtype=-1);//dst = scale*src1/src2
void divide(double scale, InputArray src2,OutputArray dst, int dtype=-1);//dst = scale/src2
void scaleAdd(InputArray src1, double alpha, InputArray src2, OutputArray dst);//dst = alpha*src1 + src2
// 加权相加 即:图像混合
void addWeighted(InputArray src1, double alpha, InputArray src2,double beta, double gamma, OutputArray dst, int dtype=-1);//dst = alpha*src1 + beta*src2 + gamma
// 平方根
void sqrt(InputArray src, OutputArray dst);//计算每个矩阵元素的平方根
// 幂运算
void pow(InputArray src, double power, OutputArray dst);//src的power次幂
// 指数运算
void exp(InputArray src, OutputArray dst);//dst = e**src(**表示指数的意思)
// 对数运算(幂运算的逆运算)
void log(InputArray src, OutputArray dst);//dst = log(abs(src))
/*位运算*/
//bitwise_and、bitwise_or、bitwise_xor、bitwise_not这四个按位操作函数。
// 位与
void bitwise_and(InputArray src1, InputArray src2,OutputArray dst, InputArray mask=noArray());//dst = src1 & src2
// 位或
void bitwise_or(InputArray src1, InputArray src2,OutputArray dst, InputArray mask=noArray());//dst = src1 | src2
// 位异或
void bitwise_xor(InputArray src1, InputArray src2,OutputArray dst, InputArray mask=noArray());//dst = src1 ^ src2
// 位反
void bitwise_not(InputArray src, OutputArray dst,InputArray mask=noArray());//dst = ~src
你可以用OpenCV函数cv.add()将两幅图像相加,或者简单地用numpy操作res = img1 + img2。
图像加法运算的要求:
注意:
OpenCV的加法和Numpy的加法是有区别的。OpenCV加法是一个饱和操作,而Numpy加法是一个模数操作。
笔记:opencv中加法是饱和操作,也就是有上限值,numpy会对结果取模。
饱和操作 opencv
举例说明:
模数操作 numpy
取模运算是求两个数相除的余数。
其运算法则:目标图像 = 图像1 + 图像2,运算结果进行取模计算
推荐使用:cv.add()
例如,考虑下面的例子:
>>> x = np.uint8([250])
>>> y = np.uint8([10])
>>> print( cv.add(x,y) ) # 250+10 = 260 => 255
[[255]]
>>> print( x+y ) # 250+10 = 260 % 256 = 4
[4]
当你添加两张图片时,这将更加明显。
请使用OpenCV函数,因为它们会提供一个更好的结果。
笔记:numpy.+的图像运算取模显然是不合理的(用在图像上不合理),因此,图像相加用opencv.add()
这也是图像添加,但对图像给予不同的权重,以便给人以混合或透明的感觉。图像的添加是按照下面的公式进行的:
通过改变α从0→1,你可以在一个图像和另一个图像之间进行很酷的过渡。
这里我取了两张图片来混合。第一张图片的权重为0.7,第二张图片的权重为0.3。cv.addWeighted()对图片应用了以下公式。
这里的γ取0。
img1 = cv.imread('ml.png')
img2 = cv.imread('opencv-logo.png')
dst = cv.addWeighted(img1,0.7,img2,0.3,0)
cv.imshow('dst',dst)
cv.waitKey(0)
cv.destroyAllWindows()
请看下面的结果:
这包括按位的AND、OR、NOT和XOR操作。
笔记:XOR 异或,英文为exclusive OR,缩写成xor。
它们在提取图像的任何部分(正如我们将在接下来的章节中看到的那样)、定义和处理非矩形的ROI等方面将非常有用。下面我们将看到一个如何改变图像中某一区域的例子。
我想把OpenCV的标志放在一张图片上面。
但我希望它是不透明的。如果它是一个矩形区域,我可以像我们在上一章做的那样使用ROI。但是OpenCV的标志不是一个矩形的形状(比如:抠图)。所以你可以用位操作来做,如下图所示。
# Load two images
img1 = cv.imread('messi5.jpg')
img2 = cv.imread('opencv-logo-white.png')
# I want to put logo on top-left corner, So I create a ROI
rows,cols,channels = img2.shape
# 笔记:图像的形状,返回值=行,列,通道;那么反推 创建时形状参数[512,256,3],第一维512是行数,第二维256是列数,第三维3维是通道
roi = img1[0:rows, 0:cols] # roi切片, 整幅图像
# Now create a mask of logo and create its inverse mask also
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY) # 获得灰度图像
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY) # 阈值化图像 获得二值图像
mask_inv = cv.bitwise_not(mask) # 掩膜 笔记:用二值化图计算掩膜
# Now black-out the area of logo in ROI
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv) # 现在将ROI中的logo区域涂黑
# Take only region of logo from logo image.
img2_fg = cv.bitwise_and(img2,img2,mask = mask) # 从标志图像中只取标志的区域。
# Put logo in ROI and modify the main image
dst = cv.add(img1_bg,img2_fg) # 在ROI中放置logo,并修改主图像
img1[0:rows, 0:cols ] = dst
cv.imshow('res',img1)
cv.waitKey(0)
cv.destroyAllWindows()
笔记:图像的形状,返回值 行,列,通道;那么反推 创建时形状参数[512,256,3],第一维长度512 = 行数,第二维长度256 = 列数,第三维长度3=通道
请看下面的结果。左图是我们创建的遮罩。右图是最终的结果。为了更好地理解,显示上述代码中的所有中间图像,特别是img1_bg和img2_fg。
在图像处理中,由于你要每秒处理大量操作,你的代码不仅要提供正确的解决方案,而且要以最快的方式提供,这是必须的。因此,在本章中,你将学习:测试代码的性能;一些提高代码性能的技巧。
cv.getTickCount
与C++中getTickCount()函数作用相同(感觉是同一个)
作用:
返回一个参考事件(比如机器被打开的那一刻)到这个函数被调用的那一刻之后的时钟周期的数量
cv.getTickFrequency
作用:
返回时钟周期的频率,或每秒的时钟周期数。
time模块
profile模块
作用:Python代码性能分析
用法:
需要查看哪个函数的性能时,可在 profile.run() 中输入要分析的函数名。
import profile # 调用profile模块
def a():
sum = 0
for i in range(1,10001):
sum += i
return sum
def b():
sum = 0
for i in range(1,100):
sum += a()
return sum
if __name__ == "__main__":
profile.run("b()") # profile模块使用方法
import time
# 注意正式运行时,需要删除@profile
@profile # 在需要做性能分析的函数前面加装饰器 @profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
time.sleep(10)
del b
del a
print("+++++++++")
if __name__ == '__main__':
my_func()
然后在执行文件所在目录下,打开命令行窗口,输入 python -m memory_profiler xxx.py
即可返回该 xxx.py 函数的性能分析结果
得到的结果中:
Mem usage :内存占用情况
Increment :执行该行代码后新增的内存
Occurences :执行次数
Line Contents :行内容
- 使用方法二:引入模块,在调试和运行时使用
from memory_profiler import profile
@profile(precision=4,stream=open('memory_profiler.log','w+'))
# precision:精确到小数后几位
# stream:此模块分析结果会保存到 'memory_profiler'日志文件,若无此参数,则结果会在控制台展示
# @profile 当不需要分析该函数性能时,将该行注释掉即可
def test1():
c=0
for item in xrange(100000):
c+=1
print c
if __name__=='__main__':
test1()
注意:性能分析过程本身的耗时不低于代码运行时间,因为性能分析的过程其实也相当于跑了遍程序。
IPython
IPython 是一个 python 的交互式 shell,比默认的python shell 好用得多,支持变量自动补全,自动缩进,支持 bash shell 命令,内置了许多很有用的功能和函数。
安装:
在ubuntu 下只要 sudo apt-get install ipython 就装好了,通过 ipython 启动。
Cython库
Cython语言使得Python语言的C扩展与Python本身一样简单。Cython是基于Pyrx的源代码转换器,但支持更多的边缘功能和优化。Cython语言是Python语言的一个超集(几乎所有的Python代码是有效的,但Cython Cython代码)还支持可选的静态类型来调用C函数,使用C++类和声明块C类型变量和类的属性。这允许编译器从Cython代码生成非常高效的C代码。
这使得Cython编写外部C / C++库代码的理想语言,和快速的C模块,提高Python代码的执行速度。
CPU都有一个基本的指令集,比如说目前英特尔和AMD的绝大部分处理器都使用的是X86指令集,因为它们都源自于X86架构。但无论CPU有多快,X86指令也只能一次处理一个数据,这样效率就很低下,毕竟在很多应用中,数据都是成组出现的,比如一个点的坐标(XYZ)和颜色(RGB)、多声道音频等。为了提高CPU在某些方面的性能,就必须增加一些特殊的指令满足时代进步的需求,这些新增的指令就构成了扩展指令集。该指令集采用单指令多数据(single instruction multiple data,简称 SIMD)扩展技术。
演变历史:
MMX
英特尔在1996年率先引入了MMX(Multi Media eXtensions)多媒体扩展指令集,也开创了SIMD(Single Instruction Multiple Data,单指令多数据)指令集之先河
SSE
SSE(Streaming SIMD Extensions,流式单指令多数据扩展)指令集是1999年英特尔在Pentium III处理器中率先推出的,并将矢量处理能力从64位扩展到了128位。
AVX
2007年8月,AMD抢先宣布了SSE5指令集(SSE到SSE4均为英特尔出品),英特尔当即黑脸表示不支持SSE5,转而在2008年3月宣布Sandy Bridge微架构将引入全新的AVX指令集,同年4月英特尔公布AVX指令集规范,随后开始不断进行更新,业界普遍认为支持AVX指令集是Sandy Bridge最重要的进步,没有之一。
AVX(Advanced Vector Extensions,高级矢量扩展)指令集借鉴了一些AMD SSE5的设计思路,进行扩展和加强,形成一套新一代的完整SIMD指令集规范。
check if optimization is enabled
检测cv函数优化功能是否开启。
设置cv函数优化功能是否启用/禁用
除了OpenCV之外,Python还提供了一个模块time,这对测量执行时间很有帮助。另一个模块profile有助于获得代码的详细报告,比如代码中每个函数花了多少时间,函数被调用了多少次,等等。但是,如果你使用的是IPython,所有这些功能都以一种用户友好的方式整合在一起。我们将看到一些重要的功能,更多的细节,请查看附加资源部分的链接。
cv.getTickCount函数返回一个参考事件(比如机器被打开的那一刻)到这个函数被调用的那一刻之后的时钟周期的数量。因此,如果你在函数执行之前和之后调用它,你可以得到执行一个函数所使用的时钟周期数。
cv.getTickFrequency函数返回时钟周期的频率,或每秒的时钟周期数。
所以要找到以秒为单位的执行时间,你可以做以下工作。
e1 = cv.getTickCount() # 获得时钟周期的数量
# your code execution
e2 = cv.getTickCount()
time = (e2 - e1)/ cv.getTickFrequency() # 时钟周期的数量 / 频率(周期/秒) = 时间
我们将用下面的例子来证明。下面的例子应用中值滤波,其内核大小从5到49不等。 不要担心结果会是什么样子–那不是我们的目标:
img1 = cv.imread('messi5.jpg')
e1 = cv.getTickCount()
for i in range(5,49,2):
img1 = cv.medianBlur(img1,i)
e2 = cv.getTickCount()
t = (e2 - e1)/cv.getTickFrequency()
print( t )
# Result I got is 0.521107655 seconds
你可以用时间模块做同样的事情。不使用cv.getTickCount,而使用time.time()函数。然后取这两个时间的差值。
OpenCV的许多函数都使用SSE2,AVX等进行了优化。它也包含未经优化的代码。因此,如果我们的系统支持这些功能,我们应该利用它们(几乎所有的现代处理器都支持它们)。在编译的时候,它是默认启用的。所以,如果OpenCV启用了优化代码,它就会运行优化的代码,否则就会运行未优化的代码。你可以使用**cv.useOptimized()**来检查它是否被启用/禁用,**cv.setUseOptimized()**来启用/禁用它。让我们看一个简单的例子。
# check if optimization is enabled
In [5]: cv.useOptimized()
Out[5]: True
In [6]: %timeit res = cv.medianBlur(img,49)
10 loops, best of 3: 34.9 ms per loop
# Disable it
In [7]: cv.setUseOptimized(False)
In [8]: cv.useOptimized()
Out[8]: False
In [9]: %timeit res = cv.medianBlur(img,49)
10 loops, best of 3: 64.1 ms per loop
正如你所看到的,优化的中值滤波比未优化的版本快2倍。如果你检查它的源代码,你可以看到中值滤波是SIMD优化的。因此,你可以用它来在你的代码顶部启用优化(记住它是默认启用的)。
有时你可能需要比较两个类似操作的性能。IPython给了你一个神奇的命令timeit来执行这个任务。它将代码运行数次,以获得更准确的结果。但是,它适合于测量单行的代码。
例如,你知道下面的运算哪个更快,x=5;y=x**2,x=5;y=xx,x=np.uint8([5]);y=xx,或者y=np.square(x)?我们将通过IPython shell中的timeit来找出答案。
In [10]: x = 5
In [11]: %timeit y=x**2
10000000 loops, best of 3: 73 ns per loop
In [12]: %timeit y=x*x
10000000 loops, best of 3: 58.3 ns per loop
In [15]: z = np.uint8([5])
In [17]: %timeit y=z*z
1000000 loops, best of 3: 1.25 us per loop
In [19]: %timeit y=np.square(z)
1000000 loops, best of 3: 1.16 us per loop
你可以看到,x = 5 ; y = x*x是最快的,与Numpy相比,它大约快20倍。如果你也考虑到数组的创建,它可能达到100倍的速度。(Numpy的开发者们正在解决这个问题)。
注意:Python的标量操作要比Numpy的标量操作快。所以对于包括一个或两个元素的操作,Python标量比Numpy数组更好。当数组的大小稍微大一点时,Numpy有优势。
我们将再试一个例子。这一次,我们将比较cv.countNonZero()和np.count_nonzero()对同一图像的性能:
In [35]: %timeit z = cv.countNonZero(img)
100000 loops, best of 3: 15.8 us per loop
In [36]: %timeit z = np.count_nonzero(img)
1000 loops, best of 3: 370 us per loop
看,OpenCV函数比Numpy函数快了近25倍。
注意:通常情况下,OpenCV函数比Numpy函数快。所以对于同样的操作,OpenCV函数是首选。但是,也可能有例外**,特别是当Numpy使用视图而不是拷贝时**。
还有其他一些神奇的命令来测量性能、剖析、行剖析、内存测量等等。它们都有很好的文档。所以这里只提供这些文档的链接。建议有兴趣的读者可以尝试一下。
有几种技术和编码方法可以发挥Python和Numpy的最大性能。这里只指出了相关的技术和方法,并给出了重要来源的链接。这里需要注意的是,首先尝试以一种简单的方式实现算法。一旦它开始工作,对它进行剖析,找到瓶颈,并对其进行优化。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。