今天在对着http://cs231n.github.io/python-numpy-tutorial练习matplotlib显示图片的时候,
img_tinted = img * [1, 0.95, 0.9]
这条语句就出错了,ValueError: operands could not be broadcast together with shapes (200,200,4) (3,)
这是因为我的图片和例子的图片shape不一样,违反了ufunc的广播机制
广播机制如下:
当我们使用ufunc函数对两个数组进行计算时,ufunc函数会对这两个数组的对应元素进行计算,因此它要求这两个数组有相同的大小(shape相同)。如果两个数组的shape不同的话,会进行如下的广播(broadcasting)处理:
import numpy as np
import matplotlib.pyplot as plt
from scipy.misc import imread, imresize
img = imread(u'images/猫扑.png')
print(img.dtype)
print(img.shape)
#print(img)
img_tinted = img * [1, 0.95, 0.9, 0.8]
plt.subplot(1,2,1)
plt.imshow(img)
plt.subplot(1,2,2)
plt.imshow(np.uint8(img_tinted))
plt.show()
上述4条规则理解起来可能比较费劲,让我们来看一个广播的实际例子。
先创建一个二维数组a,其shape为(6,1):
>>> a = np.arange(0, 60, 10).reshape(-1, 1) >>> a array([[ 0], [10], [20], [30], [40], [50]]) >>> a.shape (6, 1)
再创建一维数组b,其shape为(5,):
>>> b = np.arange(0, 5) >>> b array([0, 1, 2, 3, 4]) >>> b.shape (5,)
计算a和b的和,得到一个加法表,它相当于计算a,b中所有元素组的和,得到一个shape为(6,5)的数组:
>>> c = a + b >>> c array([[ 0, 1, 2, 3, 4], [10, 11, 12, 13, 14], [20, 21, 22, 23, 24], [30, 31, 32, 33, 34], [40, 41, 42, 43, 44], [50, 51, 52, 53, 54]]) >>> c.shape (6, 5)
由于a和b的shape长度(也就是ndim属性)不同,根据规则1,需要让b的shape向a对齐,于是将b的shape前面加1,补齐为(1,5)。相当于做了如下计算:
>>> b.shape=1,5 >>> b array([[0, 1, 2, 3, 4]])
这样加法运算的两个输入数组的shape分别为(6,1)和(1,5),根据规则2,输出数组的各个轴的长度为输入数组各个轴上的长度的最大值,可知输出数组的shape为(6,5)。
由于b的第0轴上的长度为1,而a的第0轴上的长度为6,因此为了让它们在第0轴上能够相加,需要将b在第0轴上的长度扩展为6,这相当于:
>>> b = b.repeat(6,axis=0) >>> b array([[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]])
由于a的第1轴的长度为1,而b的第一轴长度为5,因此为了让它们在第1轴上能够相加,需要将a在第1轴上的长度扩展为5,这相当于:
>>> a = a.repeat(5, axis=1) >>> a array([[ 0, 0, 0, 0, 0], [10, 10, 10, 10, 10], [20, 20, 20, 20, 20], [30, 30, 30, 30, 30], [40, 40, 40, 40, 40], [50, 50, 50, 50, 50]])
经过上述处理之后,a和b就可以按对应元素进行相加运算了。
当然,numpy在执行a+b运算时,其内部并不会真正将长度为1的轴用repeat函数进行扩展,如果这样做的话就太浪费空间了