1.背景
计算机视觉/computer vision是一个火了N年的topic。持续化升温的原因也非常简单:在搜索/影像内容理解/医学应用/地图识别等等领域应用太多,大家都有一个愿景『让计算机能够像人一样去”看”一张图片,甚至”读懂”一张图片』。
有几个比较重要的计算机视觉任务,比如图片的分类,物体识别,物体定位于检测等等。而近年来的神经网络/深度学习使得上述任务的准确度有了非常大的提升。加之最近做了几个不大不小的计算机视觉上的项目,爱凑热闹的博主自然不打算放过此领域,也边学边做点笔记总结,写点东西,写的不正确的地方,欢迎大家提出和指正。
2.基础知识
为了简单易读易懂,这个课程笔记系列中绝大多数的代码都使用Python完成。这里稍微介绍一下python和Numpy/Scipy(python中的科学计算包)的一些基础。
2.1 python基础
python是一种长得像伪代码,具备高可读性的编程语言。
优点挺多:可读性相当好,写起来也简单,所想立马可以转为实现代码,且社区即为活跃,可用的package相当多;缺点:效率一般。
2.1.1 基本数据类型
最常用的有数值型(Numbers),布尔型(Booleans)和字符串(String)三种。
可进行简单的运算,如下:
x = 5
print type(x)
print x
print x + 1
print x - 1
print x * 2
print x ** 2
x += 1
print x
x *= 2
print x
y = 2.5
print type(y)
print y, y + 1, y * 2, y ** 2
PS:python中没有x++ 和 x– 操作
包含True False和常见的与或非操作
t = True
f = False
print type(t)
print t and f
print t or f
print not t
print t != f
字符串可以用单引号/双引号/三引号声明
hello = 'hello'
world = "world"
print hello
print len(hello)
hw = hello + ' ' + world
print hw
hw2015 = '%s %s %d' % (hello, world, 2015)
print hw2015
字符串对象有很有有用的函数:
s = "hello"
print s.capitalize()
print s.upper()
print s.rjust(7)
print s.center(7)
print s.replace('l', '(ell)')
print ' world '.strip()
2.1.2 基本容器
和数组类似的一个东东,不过可以包含不同类型的元素,同时大小也是可以调整的。
xs = [3, 1, 2]
print xs, xs[2]
print xs[-1]
xs[2] = 'foo'
print xs
xs.append('bar')
print xs
x = xs.pop()
print x, xs
列表最常用的操作有:
切片/slicing
即取子序列/一部分元素,如下:
nums = range(5)
print nums
print nums[2:4]
print nums[2:]
print nums[:2]
print nums[:]
print nums[:-1]
nums[2:4] = [8, 9]
print nums
循环/loops
即遍历整个list,做一些操作,如下:
animals = ['cat', 'dog', 'monkey']
for animal in animals:
print animal
可以用enumerate取出元素的同时带出下标
animals = ['cat', 'dog', 'monkey']
for idx, animal in enumerate(animals):
print '#%d: %s' % (idx + 1, animal)
List comprehension
这个相当相当相当有用,在很长的list生成过程中,效率完胜for循环:
nums = [0, 1, 2, 3, 4]
squares = []
for x in nums:
squares.append(x ** 2)
print squares
nums = [0, 1, 2, 3, 4]
squares = [x ** 2 for x in nums]
print squares
你猜怎么着,list comprehension也是可以加多重条件的:
nums = [0, 1, 2, 3, 4]
even_squares = [x ** 2 for x in nums if x % 2 == 0]
print even_squares
- 字典/Dict
和Java中的Map一样的东东,用于存储key-value对:
d = {'cat': 'cute', 'dog': 'furry'}
print d['cat']
print 'cat' in d
d['fish'] = 'wet'
print d['fish']
print d.get('monkey', 'N/A')
print d.get('fish', 'N/A')
del d['fish']
print d.get('fish', 'N/A')
对应list的那些操作,你在dict里面也能找得到:
循环/loops
d = {'person': 2, 'cat': 4, 'spider': 8}
for animal in d:
legs = d[animal]
print 'A %s has %d legs' % (animal, legs)
d = {'person': 2, 'cat': 4, 'spider': 8}
for animal, legs in d.iteritems():
print 'A %s has %d legs' % (animal, legs)
nums = [0, 1, 2, 3, 4]
even_num_to_square = {x: x ** 2 for x in nums if x % 2 == 0}
print even_num_to_square
- 元组/turple
本质上说,还是一个list,只不过里面的每个元素都是一个两元组对。
d = {(x, x + 1): x for x in range(10)}
t = (5, 6)
print type(t)
print d[t]
print d[(1, 2)]
2.1.3 函数
用def可以定义一个函数:
def sign(x):
if x > 0:
return 'positive'
elif x < 0:
return 'negative'
else:
return 'zero'
for x in [-1, 0, 1]:
print sign(x)
def hello(name, loud=False):
if loud:
print 'HELLO, %s' % name.upper()
else:
print 'Hello, %s!' % name
hello('Bob')
hello('Fred', loud=True)
python里面的类定义非常的直接和简洁:
class Greeter:
def __init__(self, name):
self.name = name
def greet(self, loud=False):
if loud:
print 'HELLO, %s!' % self.name.upper()
else:
print 'Hello, %s' % self.name
g = Greeter('Fred')
g.greet()
g.greet(loud=True)
2.2.NumPy基础
NumPy是Python的科学计算的一个核心库。它提供了一个高性能的多维数组(矩阵)对象,可以完成在其之上的很多操作。很多机器学习中的计算问题,把数据vectorize之后可以进行非常高效的运算。
2.2.1 数组
一个NumPy数组是一些类型相同的元素组成的类矩阵数据。用list或者层叠的list可以初始化:
import numpy as np
a = np.array([1, 2, 3])
print type(a)
print a.shape
print a[0], a[1], a[2]
a[0] = 5
print a
b = np.array([[1,2,3],[4,5,6]])
print b.shape
print b[0, 0], b[0, 1], b[1, 0]
生成一些特殊的Numpy数组(矩阵)时,我们有特定的函数可以调用:
import numpy as np
a = np.zeros((2,2))
print a
b = np.ones((1,2))
print b
c = np.full((2,2), 7)
print c
d = np.eye(2)
print d
e = np.random.random((2,2))
print e
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
2.2.2 Numpy数组索引与取值
可以通过像list一样的分片/slicing操作取出需要的数值部分。
import numpy as np
a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
b = a[:2, 1:3]
print a[0, 1]
b[0, 0] = 77
print a[0, 1]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
import numpy as np
a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
row_r1 = a[1, :]
row_r2 = a[1:2, :]
print row_r1, row_r1.shape
print row_r2, row_r2.shape
col_r1 = a[:, 1]
col_r2 = a[:, 1:2]
print col_r1, col_r1.shape
print col_r2, col_r2.shape
还可以这么着取:
import numpy as np
a = np.array([[1,2], [3, 4], [5, 6]])
print a[[0, 1, 2], [0, 1, 0]]
print np.array([a[0, 0], a[1, 1], a[2, 0]])
print a[[0, 0], [1, 1]]
print np.array([a[0, 1], a[0, 1]])
我们还可以通过条件得到bool型的Numpy数组结果,再通过这个数组取出符合条件的值,如下:
import numpy as np
a = np.array([[1,2], [3, 4], [5, 6]])
bool_idx = (a > 2)
print bool_idx
print a[bool_idx]
print a[a > 2]
Numpy数组的类型
import numpy as np
x = np.array([1, 2])
print x.dtype
x = np.array([1.0, 2.0])
print x.dtype
x = np.array([1, 2], dtype=np.int64)
print x.dtype
2.2.3 Numpy数组的运算
矩阵的加减开方和(元素对元素)乘除如下:
import numpy as np
x = np.array([[1,2],[3,4]], dtype=np.float64)
y = np.array([[5,6],[7,8]], dtype=np.float64)
print x + y
print np.add(x, y)
print x - y
print np.subtract(x, y)
print x * y
print np.multiply(x, y)
print x / y
print np.divide(x, y)
print np.sqrt(x)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
矩阵的内积是通过下列方法计算的:
import numpy as np
x = np.array([[1,2],[3,4]])
y = np.array([[5,6],[7,8]])
v = np.array([9,10])
w = np.array([11, 12])
print v.dot(w)
print np.dot(v, w)
print x.dot(v)
print np.dot(x, v)
print x.dot(y)
print np.dot(x, y)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
特别特别有用的一个操作是,sum/求和(对某个维度):
import numpy as np
x = np.array([[1,2],[3,4]])
print np.sum(x)
print np.sum(x, axis=0)
print np.sum(x, axis=1)
还有一个经常会用到操作是矩阵的转置,在Numpy数组里用.T实现:
import numpy as np
x = np.array([[1,2], [3,4]])
print x
print x.T
v = np.array([1,2,3])
print v
print v.T
2.2.4 Broadcasting
Numpy还有一个非常牛逼的机制,你想想,如果你现在有一大一小俩矩阵,你想使用小矩阵在大矩阵上做多次操作。额,举个例子好了,假如你想将一个1*n的矩阵,加到m*n的矩阵的每一行上:
import numpy as np
x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
v = np.array([1, 0, 1])
y = np.empty_like(x)
for i in range(4):
y[i, :] = x[i, :] + v
print y
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
import numpy as np
x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
v = np.array([1, 0, 1])
vv = np.tile(v, (4, 1))
print vv
y = x + vv
print y
import numpy as np
x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
v = np.array([1, 0, 1])
y = x + v
print y
更多Broadcasting的例子请看下面:
import numpy as np
v = np.array([1,2,3])
w = np.array([4,5])
print np.reshape(v, (3, 1)) * w
x = np.array([[1,2,3], [4,5,6]])
print x + v
print (x.T + w).T
print x + np.reshape(w, (2, 1))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
2.3 SciPy
Numpy提供了一个非常方便操作和计算的高维向量对象,并提供基本的操作方法,而Scipy是在Numpy的基础上,提供很多很多的函数和方法去直接完成你需要的矩阵操作。有兴趣可以浏览Scipy方法索引查看具体的方法,函数略多,要都记下来有点困难,随用随查吧。
向量距离计算
需要特别拎出来说一下的是,向量之间的距离计算,这个Scipy提供了很好的接口scipy.spatial.distance.pdist:
import numpy as np
from scipy.spatial.distance import pdist, squareform
x = np.array([[0, 1], [1, 0], [2, 0]])
print x
d = squareform(pdist(x, 'euclidean'))
print d
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
2.4 Matplotlib
这是python中的一个作图工具包。如果你熟悉matlab的语法的话,应该会用得挺顺手。可以通过matplotlib.pyplot.plot了解更多绘图相关的设置和参数。
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 3 * np.pi, 0.1)
y = np.sin(x)
plt.plot(x, y)
plt.show()
结果如下:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 3 * np.pi, 0.1)
y_sin = np.sin(x)
y_cos = np.cos(x)
plt.plot(x, y_sin)
plt.plot(x, y_cos)
plt.xlabel('x axis label')
plt.ylabel('y axis label')
plt.title('Sine and Cosine')
plt.legend(['Sine', 'Cosine'])
plt.show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 3 * np.pi, 0.1)
y_sin = np.sin(x)
y_cos = np.cos(x)
plt.subplot(2, 1, 1)
plt.plot(x, y_sin)
plt.title('Sine')
plt.subplot(2, 1, 2)
plt.plot(x, y_cos)
plt.title('Cosine')
plt.show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
2.5 简单图片读写
可以使用imshow
来显示图片。
import numpy as np
from scipy.misc import imread, imresize
import matplotlib.pyplot as plt
img = imread('/Users/HanXiaoyang/Comuter_vision/computer_vision.jpg')
img_tinted = img * [1, 0.95, 0.9]
plt.subplot(1, 2, 1)
plt.imshow(img)
plt.subplot(1, 2, 2)
plt.imshow(np.uint8(img_tinted))
plt.show()
参考资料与原文
cs231n python/Numpy指南