[ Python  OpenCV  ]

OpenCV-Python 笔记

图像处理

读取/修改像素

1. 通过数组下标[]

读取某个像素的 GRB 值:

>>> px = img[100,100]
>>> print( px )
[157 166 200]

读取某个像素的蓝色像素值:

>>> blue = img[100,100,0]
>>> print( blue )
157

修改像素值:

>>> img[100,100] = [255,255,255]
>>> print( img[100,100] )
[255 255 255]

通过 Numpy indexing 选取一个矩形区域的像素并且把它复制到另一个地方:

>>> ball = img[280:340, 330:390]
>>> img[273:333, 100:160] = ball

2. 通过np.array.item()

# accessing RED value
>>> img.item(10,10,2)
59
# modifying RED value
>>> img.itemset((10,10,2),100)
>>> img.item(10,10,2)
100

读取图像属性

图像属性包括

通过np.array.shape()获取图像行数列数通道数

>>> print( img.shape )
(342, 548, 3)

获取np.array.dtype图像数据类型:

>>> print( img.dtype )
uint8

将三通道分离

使用cv.split()将BRG三通道分离,使用cv.merge()将三通道合体:

>>> b,g,r = cv.split(img)
>>> img = cv.merge((b,g,r))

或者使用数组下标读取/修改单个通道:

>>> b = img[:,:,0]
>>> img[:,:,2] = 0

cv.split()很耗费时间!

为图像生成边框(Padding)

使用 cv.copyMakeBorder().

图像叠加

普通叠加

有两种方法,一种使用 OpenCV 的 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]

建议都使用 cv.add()

图像混合(加权叠加)

使用cv.addWeighted()

dst = cv.addWeighted(img1,0.7,img2,0.3,0)

位操作

用于提取图像中不规则的区域。具体API有:

图像变换

OpenCV 提供了两个函数, cv.warpAffine() (仿射变换)和 cv.warpPerspective()(透视变换),通过它们你能进行所有变换

cv.warpAffine() 将2x3的矩阵作为输入; cv.warpPerspective() 将3x3的矩阵作为输入。

缩放

可以直接使用cv.resize()。有不同的插值模式:压缩可以使用cv.INTER_AREA;放大可以使用cv.INTER_CUBIC(速度慢)和cv.INTER_LINEAR。默认使用cv.INTER_LINEAR

import numpy as np
import cv2 as cv
img = cv.imread('messi5.jpg')
res = cv.resize(img,None,fx=2, fy=2, interpolation = cv.INTER_CUBIC)

# 或
height, width = img.shape[:2]
res = cv.resize(img,(2*width, 2*height), interpolation = cv.INTER_CUBIC)

平移

使用cv.warpAffine()。平移$(t_x,t_y)$ 的变换矩阵为: 平移 $(100, 50)$ 的示例为:

import numpy as np
import cv2 as cv
img = cv.imread('messi5.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()

注意cv.warpAffine()的第三个参数是(width, height),所以应当是(cols, rows)

旋转

使用cv.getRotationMatrix2D()生成一个“旋转加缩放”的变换矩阵。API:

M = cv.getRotationMatrix2D(center, angle, scale)

示例:

img = cv.imread('messi5.jpg',0)
rows,cols = img.shape
# cols-1 and rows-1 are the coordinate limits.
M = cv.getRotationMatrix2D(((cols-1)/2.0,(rows-1)/2.0),90,1)
dst = cv.warpAffine(img,M,(cols,rows))

任意仿射变换

使用cv.getAffineTransform()生成任意变换矩阵:

img = cv.imread('drawing.png')
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))
plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
plt.show()

透视变换

使用cv.getPerspectiveTransformcv.warpPerspective

img = cv.imread('sudoku.png')
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.setMouseCallback()绑定一个事件对应的回调函数:

import numpy as np
import cv2 as cv
# mouse callback function
def draw_circle(event,x,y,flags,param):
    if event == cv.EVENT_LBUTTONDBLCLK:
        cv.circle(img,(x,y),100,(255,0,0),-1)
# Create a black image, a window and bind the function to window
img = np.zeros((512,512,3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image',draw_circle)
while(1):
    cv.imshow('image',img)
    if cv.waitKey(20) & 0xFF == 27:
        break
cv.destroyAllWindows()

数值计算

特征值计算

性能测量

记录运行时间

使用cv.getTickCount() cv.getTickFrequency()

img1 = cv.imread('messi5.jpg')
e1 = cv.getTickCount()
for i in xrange(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

或者使用 IPython 魔法命令%timeit

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

查看/修改优化模式

使用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

优化技巧

参考资料:Python 性能优化(官方建议)