[OpenCV-Python] OpenCV 核心操做 部分 III

部分 III
核心操做

OpenCV-Python 中文教程(搬运)目录html

 

9 图像的基础操做

 


目标
  • 获取像素值并修改
  • 获取图像的属性(信息)
  • 图像的 ROI()
  • 图像通道的拆分及合并
几乎全部这些操做与 Numpy 的关系都比与 OpenCV 的关系更加紧密,所以熟练 Numpy 能够帮助咱们写出性能更好的代码。
(示例将会在 Python 终端中展现,由于他们大部分都只有一行代码)python


9.1 获取并修改像素值
  首先咱们须要读入一幅图像:算法

import cv2
import numpy as np
img=cv2.imread('messi5.jpg')编程

 

你能够根据像素的行和列的坐标获取他的像素值。对 BGR 图像而言,返回值为 B,G,R 的值。对灰度图像而言,会返回他的灰度值(亮度?intensity)数组

import cv2
import numpy as np
img=cv2.imread('messi5.jpg')
px=img[100,100]
print(px)
blue=img[100,100,0]
print(blue)缓存

 

你能够以相似的方式修改像素值。app

import cv2
import numpy as np
img=cv2.imread('messi5.jpg')
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() 分割他们。oop

获取像素值及修改的更好方法。post

import cv2
import numpy as np
img=cv2.imread('messi5.jpg')
print(img.item(10,10,2))
img.itemset((10,10,2),100)
print(img.item(10,10,2))

# 59

# 100

 

9.2 获取图像属性
  图像的属性包括:行,列,通道,图像数据类型,像素数目等img.shape 能够获取图像的形状。他的返回值是一个包含行数,列数,通道数的元组。

import cv2
import numpy as np
img=cv2.imread('messi5.jpg')
print(img.shape)

##(342, 548, 3)

注意:若是图像是灰度图,返回值仅有行数和列数。因此经过检查这个返回值就能够知道加载的是灰度图仍是彩色图。
img.size 能够返回图像的像素数目:

import cv2
import numpy as np
img=cv2.imread('messi5.jpg')
print(img.size, img.dtype) # 返回的是图像的数据类型.

# 562248 uint8

# uint8

注意:在debug时 img.dtype 很是重要。由于在 OpenCV Python 代码中常常出现数据类型的不一致。


9.3 图像 ROI
有时你须要对一幅图像的特定区域进行操做。例如咱们要检测一副图像中眼睛的位置,咱们首先应该在图像中找到脸,再在脸的区域中找眼睛,而不是直接在一幅图像中搜索。这样会提升程序的准确性和性能。
ROI 也是使用 Numpy 索引来得到的。如今咱们选择球的部分并把他拷贝到图像的其余区域。

import cv2
import numpy as np
img=cv2.imread('messi5.jpg')
ball=img[280:340,330:390]
img[273:333,100:160]=ball
img=cv2.imshow('test', img)
cv2.waitKey(0)

看看结果吧:

 


9.4 拆分及合并图像通道
有时咱们须要对 BGR 三个通道分别进行操做。这是你就须要把 BGR 拆分红单个通道。有时你须要把独立通道的图片合并成一个 BGR 图像。你能够这样作:

import cv2
import numpy as np
img=cv2.imread('/home/duan/workspace/opencv/images/roi.jpg')
b,g,r=cv2.split(img)
img=cv2.merge(b,g,r)

或者:

import cv2
import numpy as np
img=cv2.imread('/home/duan/workspace/opencv/images/roi.jpg')
b=img[:,:,0]

 

假如你想使全部像素的红色通道值都为 0,你没必要先拆分再赋值。你能够直接使用 Numpy 索引,这会更快。

import cv2
import numpy as np
img=cv2.imread('/home/duan/workspace/opencv/images/roi.jpg')
img[:,:,2]=0

警告:cv2.split() 是一个比较耗时的操做。只有真正须要时才用它,能用Numpy 索引就尽可能用。


9.5 为图像扩边(填充)
  若是你想在图像周围建立一个边,就像相框同样,你可使用 cv2.copyMakeBorder()函数。这常常在卷积运算或 0 填充时被用到。这个函数包括以下参数:
  • src 输入图像
  • top, bottom, left, right 对应边界的像素数目。
  • borderType 要添加那种类型的边界,类型以下:
    – cv2.BORDER_CONSTANT 添加有颜色的常数值边界,还须要下一个参数(value)。
    – cv2.BORDER_REFLECT 边界元素的镜像。好比: fedcba|abcde-fgh|hgfedcb
    – cv2.BORDER_REFLECT_101 or cv2.BORDER_DEFAULT跟上面同样,但稍做改动。例如: gfedcb|abcdefgh|gfedcba
    – cv2.BORDER_REPLICATE 重复最后一个元素。例如: aaaaaa|abcdefgh|hhhhhhh
    – cv2.BORDER_WRAP 不知道怎么说了, 就像这样: cdefgh|abcdefgh|abcdefg
  • value 边界颜色,若是边界的类型是 cv2.BORDER_CONSTANT

为了更好的理解这几种类型请看下面的演示程序。

import cv2
import numpy as np
from matplotlib import pyplot as plt
BLUE=[255,0,0]
img1=cv2.imread('opencv_logo.png')
replicate = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_WRAP)
constant= cv2.copyMakeBorder(img1,10,10,10,10,cv2.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 绘制,因此交换 R 和 B 的位置,OpenCV 中是按 BGR,matplotlib 中是按 RGB 排列):

 

10 图像上的算术运算


目标
  • 学习图像上的算术运算,加法,减法,位运算等。
  • 咱们将要学习的函数与有:cv2.add(),cv2.addWeighted() 等。


10.1 图像加法
  你可使用函数 cv2.add() 将两幅图像进行加法运算,固然也能够直接使用 numpy,res=img1+img。两幅图像的大小,类型必须一致,或者第二个图像可使一个简单的标量值。
注意:OpenCV 中的加法与 Numpy 的加法是有所不一样的。OpenCV 的加法是一种饱和操做,而 Numpy 的加法是一种模操做。
例以下面的两个例子:

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

这种差异在你对两幅图像进行加法时会更加明显。OpenCV 的结果会更好一点。因此咱们尽可能使用 OpenCV 中的函数。


10.2 图像混合
  这其实也是加法,可是不一样的是两幅图像的权重不一样,这就会给人一种混合或者透明的感受。图像混合的计算公式以下:
    g (x) = (1 − α)f 0 (x) + αf 1 (x)
  经过修改 α 的值(0 → 1),能够实现很是酷的混合。
  如今咱们把两幅图混合在一块儿。第一幅图的权重是 0.7,第二幅图的权重是 0.3。函数 cv2.addWeighted() 能够按下面的公式对图片进行混合操做。
    dst = α · img1 + β · img2 + γ
  这里 γ 的取值为 0。

img1 = cv2.imread('ml.png')
img2 = cv2.imread('opencv_logo.jpg') dst = cv2.addWeighted(img1,0.7,img2,0.3,0) cv2.imshow('dst',dst) cv2.waitKey(0) cv2.destroyAllWindows()
dst = cv2.addWeighted(img1,0.7,img2,0.3,0)
error: C:\projects\opencv-python\opencv\modules\core\src\arithm.cpp:659:
error: (-209) The operation is neither 'array op array' (where arrays have
the same size and the same number of channels), nor 'array op scalar',
nor 'scalar op array' in function cv::arithm_op

下面就是结果:#  这个运行有问题

 

10.3 按位运算
  这里包括的按位操做有:AND,OR,NOT,XOR 等。当咱们提取图像的一部分,选择非矩形 ROI 时这些操做会颇有用(下一章你就会明白)。下面的例子就是教给咱们如何改变一幅图的特定区域。我想把 OpenCV 的标志放到另外一幅图像上。若是我使用加法,颜色会改变,若是使用混合,会获得透明效果,可是我不想要透明。若是他是矩形我能够象上一章那样使用 ROI。可是他不是矩形。可是咱们能够经过下面的按位运算实现:

import cv2
import numpy as np
# Load two images
img1 = cv2.imread('messi5.jpg')
img2 = cv2.imread('opencv-logo-white.png')

# I want to put logo on top-left corner, So I create a ROI
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols ]

# Now create a mask of logo 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)

# Now black-out the area of logo in ROI
img1_bg = cv2.bitwise_and(roi,roi,mask = mask_inv)

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

# Put logo 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()

结果以下。左面的图像是咱们建立的掩码。右边的是最终结果。为了帮助你们理解我把上面程序的中间结果也显示了出来,特别是 img1_bg 和 img2_fg。

    Otsu's Thresholding

    

 

练习
1. 建立一个幻灯片用来演示一幅图如何平滑的转换成另外一幅图(使用函数cv2.addWeighted)

 

11 程序性能检测及优化


目标
在图像处理中你每秒钟都要作大量的运算,因此你的程序不只要能给出正确的结果,同时还必需要快。因此这节咱们将要学习:
  • 检测程序的效率
  • 一些可以提升程序效率的技巧
  • 你要学到的函数有:cv2.getTickCount,cv2.getTickFrequency等
除了 OpenCV,Python 也提供了一个叫 time 的的模块,你能够用它来测量程序的运行时间。另一个叫作 profile 的模块会帮你获得一份关于你的程序的详细报告,其中包含了代码中每一个函数运行须要的时间,以及每一个函数被调用的次数。若是你正在使用 IPython 的话,全部这些特色都被以一种用户友好的方式整合在一块儿了。咱们会学习几个重要的,要想学到更加详细的知识就打开更多资源中的连接吧。


11.1 使用 OpenCV 检测程序效率

  cv2.getTickCount 函数返回从参考点到这个函数被执行的时钟数。因此当你在一个函数执行先后都调用它的话,你就会获得这个函数的执行时间(时钟数)。
  cv2.getTickFrequency 返回时钟频率,或者说每秒钟的时钟数。因此你能够按照下面的方式获得一个函数运行了多少秒:

e1 = cv2.getTickCount()
# your code execution
e2 = cv2.getTickCount() time = (e2 - e1)/ cv2.getTickFrequency()

 

咱们将会用下面的例子演示。下面的例子是用窗口大小不一样(5,7,9)的核函数来作中值滤波:

img1 = cv2.imread('messi5.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 # Result I got is 0.521107655 seconds

注 意: 你 也 可 以 中 time 模 块 实 现 上 面 的 功 能。 但 是 要 用 的 函 数 是time.time() 而不是 cv2.getTickCount。比较一下这两个结果的差异吧。


11.2 OpenCV 中的默认优化

  OpenCV 中的不少函数都被优化过(使用 SSE2,AVX 等)。也包含一些没有被优化的代码。若是咱们的系统支持优化的话要尽可能利用只一点。在编译时优化是被默认开启的。所以 OpenCV 运行的就是优化后的代码,若是你把优化关闭的话就只能执行低效的代码了。你可使用函数 cv2.useOptimized()来查看优化是否被开启了,使用函数 cv2.setUseOptimized() 来开启优化。
让咱们来看一个简单的例子吧。

# check if optimization is enabled
In [5]: cv2.useOptimized()
Out[5]: True In [6]: %timeit res = cv2.medianBlur(img,49) 10 loops, best of 3: 34.9 ms per loop # Disable it In [7]: cv2.setUseOptimized(False) In [8]: cv2.useOptimized() Out[8]: False In [9]: %timeit res = cv2.medianBlur(img,49) 10 loops, best of 3: 64.1 ms per loop

看见了吗,优化后中值滤波的速度是原来的两倍。若是你查看源代码的话,你会发现中值滤波是被 SIMD 优化的。因此你能够在代码的开始处开启优化(你要记住优化是默认开启的)。


11.3 在 IPython 中检测程序效率

  有时你须要比较两个类似操做的效率,这时你可使用 IPython 为你提供的魔法命令%time。他会让代码运行好几回从而获得一个准确的(运行)时间。它也能够被用来测试单行代码的。
例如,你知道下面这同一个数学运算用哪一种行式的代码会执行的更快吗?
x = 5; y = x ∗ ∗2
x = 5; y = x ∗ x
x = np.uint([5]); y = x ∗ x
y = np.squre(x)
咱们能够在 IPython 的 Shell 中使用魔法命令找到答案。

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

居然是第一种写法,它竟然比 Nump 快了 20 倍。若是考虑到数组构建的话,能达到 100 倍的差。
注意:Python 的标量计算比 Nump 的标量计算要快。对于仅包含一两个元素的操做 Python 标量比 Numpy 的数组要快。可是当数组稍微大一点时Numpy 就会胜出了。
咱们来再看几个例子。咱们来比较一下 cv2.countNonZero() 和
np.count_nonzero()。

In [35]: %timeit z = cv2.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 对视图(而非复制)进行操做时。


11.4 更多 IPython 的魔法命令
还有几个魔法命令能够用来检测程序的效率,profiling,line profiling,内存使用等。他们都有完善的文档。因此这里只提供了超连接。感兴趣的能够本身学习一下。


11.5 效率优化技术
  有些技术和编程方法可让咱们最大的发挥 Python 和 Numpy 的威力。

  咱们这里仅仅提一下相关的,你能够经过超连接查找更多详细信息。咱们要说的最重要的一点是:首先用简单的方式实现你的算法(结果正确最重要),当结果正确后,再使用上面的提到的方法找到程序的瓶颈来优化它。
  1. 尽可能避免使用循环,尤为双层三层循环,它们天生就是很是慢的。
  2. 算法中尽可能使用向量操做,由于 Numpy 和 OpenCV 都对向量操做进行了优化。
  3. 利用高速缓存一致性。
  4. 没有必要的话就不要复制数组。使用视图来代替复制。数组复制是很是浪费资源的。
就算进行了上述优化,若是你的程序仍是很慢,或者说大的训话不可避免的话,你你应该尝试使用其余的包,好比说 Cython,来加速你的程序。

更多资源1. Python Optimization Techniques2. Scipy Lecture Notes - Advanced Numpy3. Timing and Profiling in IPython

相关文章
相关标签/搜索