python - 常规与使用 For 循环时的 Numpy 数组乘法差异
问题描述
我想在具有完全黄色图像的图像上应用多重混合。图片为: 巴黎埃菲尔铁塔
黄色图像是使用以下方法创建的:
img_paris = img1 = cv2.imread("/content/drive/My Drive/Datasets/Images/paris.jpg")
yellow_image = np.ones(img_paris.shape) * 255
yellow_image[:,:,0] *= 0
我尝试了两种技术:首先我使用了正则乘法
imgc = img_paris.copy()
imgc = (imgc * yellow_image)
这导致: 埃菲尔倍增
然后我使用 for 循环将单个元素相乘
for x in range(yellow_image.shape[0]):
for y in range(yellow_image.shape[1]):
imgc[x,y] = (imgc[x,y] * yellow_image[x,y])
这导致了 埃菲尔乘以 2
second 的结果看起来图像正在以某种方式反转。
两种技术都应该产生相似的结果。为什么在 for 循环技术中图像会倒置?我想使用 For 循环进行更多控制。有人能告诉我为什么会这样吗?为什么两种技术会导致不同的结果?
解决方案
两种技术都应该产生相似的结果。为什么在 for 循环技术中图像会倒置?
因为您应该这样做,所以转换数据类型:
imgc = np.uint64(img_paris.copy()) # <-- convert datatype
for x in range(yellow_image.shape[0]):
for y in range(yellow_image.shape[1]):
imgc[x,y] = (imgc[x,y] * yellow_image[x,y])
解释第 1 部分(dtype)
这是因为dtype
重新分配整个矩阵时会发生变化,而重新分配切片不会发生变化:
a = np.array([[1]], np.uint8)
b = np.array([[1]], np.float64)
a[0] = a[0] * b[0] # assigning slices dtype of a does not change
print(a.dtype) #=> uint8
a = a * b # while assigning the full matrix it does
print(a.dtype) #=> float64
如果您dtype
一路打印,您会看到:
yellow_image_1 = np.ones(img_paris.shape) * 255
print(yellow_image_1.dtype) #=> float64
yellow_image_1[:,:,0] *= 0
print(yellow_image_1.dtype) #=> float64
imgc_1 = img_paris.copy()
print(imgc_1.dtype) #=> uint8
imgc_1 = (imgc_1 * yellow_image_1)
print(imgc_1.dtype) #=> float64
和这个:
yellow_image_2 = np.ones(img_paris.shape) * 255
print(yellow_image_2.dtype) #=> float64
yellow_image_2[:,:,0] *= 0
print(yellow_image_2.dtype) #=> float64
imgc_2 = img_paris.copy()
print(imgc_2.dtype) #=> uint8
for x in range(yellow_image_2.shape[0]):
for y in range(yellow_image_2.shape[1]):
imgc_2[x,y] = (imgc_2[x,y] * yellow_image_2[x,y])
print(imgc_2.dtype) #=> uint8
所以你最终得到不同的dtype
矩阵。
解释第 2 部分(OpenCV BGR)
如前所述,请记住 OpenCv 使用BGR 格式,并且每个像素值从0
到255
表示np.uint8
。
因此,如果您使用例如 matplotlib,为了显示图像,您必须交换 B 和 R 通道:
img_paris = cv2.imread('3ClnT.jpg')
plt.imshow(img_paris[:,:,::-1])
如果您使用cv2.imwrite()或cv2imshow()保存,则不需要这样做,例如:
cv2.imwrite('paris.jpg', img_paris)
也就是说,您可以使用以下命令生成纯黄色图像:
yellow_image = np.ones_like(img_paris) * (0, 255, 255)
并显示或保存:
plt.imshow(yellow_image[:,:,::-1])
cv2.imwrite('solid_yellow.jpg', yellow_image)
现在,您的乘法paris_yellow = img_paris * yellow_image
结果会产生一个大于 的值255
:
[0..1]
使用 RGB 数据(对于浮点数或[0..255]
整数)将输入数据剪切到 imshow 的有效范围。
因此,当您相乘时,您最终会得到一个最大像素值,它可以是255 * 255 = 65025
.
然后你需要:
- 将乘法项转换为支持整数的数据类型
65025
- 乘法后,归一化然后转换回
uint8
这是一个例子:
paris_yellow_2 = np.int64(img_paris) * np.int64(yellow_image) # <- use int64 terms
max_px_val = np.amax(paris_yellow_2) # <-- Max pixel alue
paris_yellow_2 = np.uint8((paris_yellow_2/max_px_val) * 255) # <- normalize and convert back to uint8
plt.imshow(paris_yellow_2[:,:,::-1])
这是结果:
其他选项,它给出了不同的结果是乘以一个系数大于G
然后裁剪值的通道。在这种情况下,您需要使用 float :R
1
>255
dtype
paris_yellow_3 = np.float64(img_paris) * (1, 3, 3)
paris_yellow_3[paris_yellow_3 > 255] = 255 # <- crops to 255 pixels values > 255
paris_yellow_3 = paris_yellow_3.astype(np.uint8) # <- back to uint8
在这种情况下B
,乘以1
(不变),G
再R
乘以3
,得到以下结果: