为了账号安全,请及时绑定邮箱和手机立即绑定

常规与使用 For 循环时的 Numpy 数组乘法差异

常规与使用 For 循环时的 Numpy 数组乘法差异

慕盖茨4494581 2023-02-22 16:30:55
我想在一个完全黄色的图像上应用乘法混合。图片是: 巴黎埃菲尔铁塔黄色图像是使用以下方法创建的: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])结果是 Eiffel Multiplied 2第二个结果看起来图像以某种方式倒置了。这两种技术应该会产生相似的结果。为什么图像在 for 循环技术中会倒转?我想使用 For 循环进行更多控制。有人能告诉我为什么会这样吗?为什么这两种技术会导致不同的结果?
查看完整描述

3 回答

?
阿晨1998

TA贡献2037条经验 获得超6个赞

图像不同,因为前一个图像包含浮点数据,而后一个图像的数据类型是uint8.

imgc = (imgc * yellow_image)由于 的数据类型是浮点数,该指令生成一个带有浮点数数据的新数组yellow_image

创建一个yellow_imagewith 数据类型uint8来解决问题:

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)


查看完整回答
反对 回复 2023-02-22
?
噜噜哒

TA贡献1784条经验 获得超7个赞

这两种技术应该会产生相似的结果。为什么图像在 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])

这是结果:

//img1.sycdn.imooc.com//63f5d3060001104f04690352.jpg

给出不同结果的其他选项G是将和通道相乘以获得大于crop 值的R系数。在这种情况下,您需要使用 float :1>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(不变),G并R乘以3,得到以下结果:

//img1.sycdn.imooc.com//63f5d3100001260f04710351.jpg

查看完整回答
反对 回复 2023-02-22
?
烙印99

TA贡献1829条经验 获得超13个赞

问题出现在一个完全出乎意料的地方:在创建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

问题解决了。


注意- 回答关于为什么图像倒置的问题:


当乘法结果为浮点类型时,它会将大数字(24225、12750 等)作为像素颜色值。当您使用此数组写入图像时,所有这些数字都被裁剪为最大可能的像素颜色值:255。因此,您看到的大部分图像都是黄色的,因为所有“溢出”值都被裁剪为 255,导致最亮的黄色阴影。


另一方面,当乘法完成强制uint8类型时,任何大于 255 的值都会“回滚”到无符号 8 位整数的最小可能值:0。因此,如果值为 487,dtype 限制将强迫它成为0 + (287 - 255) - 1 = 31。这是溢出。所以很大的数字最终会变得很小——数学很简单,查一下。因此,您会得到一种倒置的图像(意外的暗像素)。


查看完整回答
反对 回复 2023-02-22
  • 3 回答
  • 0 关注
  • 83 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信