首页 > 解决方案 > 常规与使用 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 循环进行更多控制。有人能告诉我为什么会这样吗?为什么两种技术会导致不同的结果?

标签: pythonnumpyopencvcv2

解决方案


两种技术都应该产生相似的结果。为什么在 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 格式,并且每个像素值0255表示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 :R1>255dtype

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(不变),GR乘以3,得到以下结果:

在此处输入图像描述


推荐阅读