常规与使用 For 循环时的 Numpy 数组乘法差异
Numpy Array Multiplication Differnce in Regular vs when using For loops
我想在一张完全黄色的图像上应用乘法混合。
图片是:
Eiffel Tower Paris
黄色图像是使用以下方法创建的:
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)
这导致:
Eiffel Multiplied
然后我使用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])
这导致
Eiffel Multiplied 2
第二个结果看起来图像以某种方式反转了。
这两种技术应该会产生相似的结果。
为什么图像在 for 循环技术中会倒转?
我想使用 For 循环进行更多控制。有人能告诉我为什么会这样吗?为什么两种技术会产生不同的结果?
图片不一样,因为前一张图片是浮点数据,而后一张图片的数据类型是uint8
.
由于yellow_image
的数据类型是浮点数,指令imgc = (imgc * yellow_image)
生成了一个新的浮点数数组。
创建数据类型为 uint8
的 yellow_image
来解决问题:
yellow_image = np.ones(img_paris.shape, dtype=np.uint8) * 255
yellow_image[:,:,0] *= 0
imgc *= yellow_image
或者使用 numpy.ndarray.astyp
创建数组的副本并转换为 uint8
:
imgc = (imgc * yellow_image)
imgc = (imgc * yellow_image).astype(np.uint8)
或使用 numpy.multiply
,通过指定转换规则和类型:
imgc = np.multiply(imgc, yellow_image, casting='unsafe', dtype=np.uint8)
问题出现在一个完全出乎意料的地方:在 yellow_image
的创建中。 np.ones
默认创建一个浮点型数组:因此 yellow_image
是用浮点型元素创建的。检查:
>>> yellow_image.dtype
dtype('float64')
因此,当您执行以下操作时:imgc * yellow_image
,生成的数组数据类型被提升为精度更高的数据类型(当然是浮点类型),因此 imgc
具有元素如:
array([[[ 0., 24225., 12750.],
[ 0., 23715., 12240.],
[ 0., 23460., 11985.],
...,
是浮点型
为避免此问题,并且避免显式编写数据类型的麻烦,请使用:
yellow_image = np.ones_like(img_paris) * 255
np.ones_like
创建一个数组,其中包含传递给它的数组的形状,以及完全相同的 dtype - 消除您的后顾之忧。
现在检查:
>>> yellow_image.dtype
dtype('uint8')
现在考虑 imgc1 = imgc * yellow_image
并且 imgc2
是循环的输出。检查:
>>> np.allclose(imgc1, imgc2)
True
问题已解决。
N.B. - 回答你的问题为什么图像是倒置的:
当乘法结果为浮点类型时,它会将大数字(24225、12750 等)作为像素颜色值。当您使用此数组写入图像时,所有这些数字都被裁剪为最大可能的像素颜色值:255。因此,您看到的大部分图像都是黄色的,因为所有 'overflowing' 值都被裁剪为 255,导致最亮的黄色阴影。
另一方面,当乘法完成并强制 uint8
类型时,任何大于 255 'roll back' 的值都会变成 无符号的最小可能值,8- bit integer: 0。所以如果值为 487,dtype 限制将强制它为 0 + (287 - 255) - 1 = 31
。这是溢出。所以很大的数字最终会变得很小——数学很简单,查一下。因此,您会得到倒置的图像(意外的暗像素)。
Both techniques should result in similar results. Why is the image
getting inverted in the for loop technique?
因为你应该这样做,转换数据类型:
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 format 并且每个像素值 从 0
到 255
,说 np.uint8
.
因此,如果您使用的是 matplotlib,为了显示图像,您必须交换 B 和 R 通道:
img_paris = cv2.imread('3ClnT.jpg')
plt.imshow(img_paris[:,:,::-1])
虽然如果您使用 cv2.imwrite() or 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
:
Clipping input data to the valid range for imshow with RGB data ([0..1]
for floats or [0..255]
for integers).
所以,当你相乘时,你最终得到的最大像素值可以是 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
和 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
,得到以下结果:
我想在一张完全黄色的图像上应用乘法混合。 图片是: Eiffel Tower Paris
黄色图像是使用以下方法创建的:
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)
这导致: Eiffel Multiplied
然后我使用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])
这导致 Eiffel Multiplied 2
第二个结果看起来图像以某种方式反转了。
这两种技术应该会产生相似的结果。 为什么图像在 for 循环技术中会倒转? 我想使用 For 循环进行更多控制。有人能告诉我为什么会这样吗?为什么两种技术会产生不同的结果?
图片不一样,因为前一张图片是浮点数据,而后一张图片的数据类型是uint8
.
由于yellow_image
的数据类型是浮点数,指令imgc = (imgc * yellow_image)
生成了一个新的浮点数数组。
创建数据类型为 uint8
的 yellow_image
来解决问题:
yellow_image = np.ones(img_paris.shape, dtype=np.uint8) * 255
yellow_image[:,:,0] *= 0
imgc *= yellow_image
或者使用 numpy.ndarray.astyp
创建数组的副本并转换为 uint8
:
imgc = (imgc * yellow_image)
imgc = (imgc * yellow_image).astype(np.uint8)
或使用 numpy.multiply
,通过指定转换规则和类型:
imgc = np.multiply(imgc, yellow_image, casting='unsafe', dtype=np.uint8)
问题出现在一个完全出乎意料的地方:在 yellow_image
的创建中。 np.ones
默认创建一个浮点型数组:因此 yellow_image
是用浮点型元素创建的。检查:
>>> yellow_image.dtype
dtype('float64')
因此,当您执行以下操作时:imgc * yellow_image
,生成的数组数据类型被提升为精度更高的数据类型(当然是浮点类型),因此 imgc
具有元素如:
array([[[ 0., 24225., 12750.],
[ 0., 23715., 12240.],
[ 0., 23460., 11985.],
...,
是浮点型
为避免此问题,并且避免显式编写数据类型的麻烦,请使用:
yellow_image = np.ones_like(img_paris) * 255
np.ones_like
创建一个数组,其中包含传递给它的数组的形状,以及完全相同的 dtype - 消除您的后顾之忧。
现在检查:
>>> yellow_image.dtype
dtype('uint8')
现在考虑 imgc1 = imgc * yellow_image
并且 imgc2
是循环的输出。检查:
>>> np.allclose(imgc1, imgc2)
True
问题已解决。
N.B. - 回答你的问题为什么图像是倒置的:
当乘法结果为浮点类型时,它会将大数字(24225、12750 等)作为像素颜色值。当您使用此数组写入图像时,所有这些数字都被裁剪为最大可能的像素颜色值:255。因此,您看到的大部分图像都是黄色的,因为所有 'overflowing' 值都被裁剪为 255,导致最亮的黄色阴影。
另一方面,当乘法完成并强制 uint8
类型时,任何大于 255 'roll back' 的值都会变成 无符号的最小可能值,8- bit integer: 0。所以如果值为 487,dtype 限制将强制它为 0 + (287 - 255) - 1 = 31
。这是溢出。所以很大的数字最终会变得很小——数学很简单,查一下。因此,您会得到倒置的图像(意外的暗像素)。
Both techniques should result in similar results. Why is the image getting inverted in the for loop technique?
因为你应该这样做,转换数据类型:
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 format 并且每个像素值 从 0
到 255
,说 np.uint8
.
因此,如果您使用的是 matplotlib,为了显示图像,您必须交换 B 和 R 通道:
img_paris = cv2.imread('3ClnT.jpg')
plt.imshow(img_paris[:,:,::-1])
虽然如果您使用 cv2.imwrite() or 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
:
Clipping input data to the valid range for imshow with RGB data (
[0..1]
for floats or[0..255]
for integers).
所以,当你相乘时,你最终得到的最大像素值可以是 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
和 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
,得到以下结果: