将 float64 类型的 np.array 转换为 uint8 类型的缩放值
Posted
技术标签:
【中文标题】将 float64 类型的 np.array 转换为 uint8 类型的缩放值【英文标题】:Convert np.array of type float64 to type uint8 scaling values 【发布时间】:2018-03-23 04:48:11 【问题描述】:我有一个特定的 np.array data 代表一个特定的灰度图像。 我需要使用 SimpleBlobDetector() 不幸的是只接受 8 位图像,所以我需要转换这个图像,显然有质量损失。
我已经试过了:
import numpy as np
import cv2
[...]
data = data / data.max() #normalizes data in range 0 - 255
data = 255 * data
img = data.astype(np.uint8)
cv2.imshow("Window", img)
但 cv2.imshow 并没有按预期给出图像,而是出现奇怪的失真......
最后,我只需要将 np.float64 转换为 np.uint8 缩放所有值并截断其余值,例如。 65535 变成 255,65534 变成 254 等等....有什么帮助吗?
谢谢。
【问题讨论】:
发生了什么奇怪的失真?对我来说,规范化代码似乎很好。是data
np.float64
的类型吗?此外,在我看来,将 65535 转换为 255 似乎您的预期输入类型是 np.uint16
,而不是 np.float64
。
图像似乎具有某种粒度,例如黑色背景变为灰色且不稳定(???)。你有什么替代建议吗?
我建议将图像除以该类型所经历的最大值,而不是图像中的最大值。我已经写了一个答案。让我知道它是否有效。
我除以 data.max() 也是为了标准化 0-1 范围内的值
【参考方案1】:
标准化图像的更好方法是获取每个值并除以数据类型所经历的最大值。这可确保图像中动态范围较小的图像保持较小,并且不会无意中归一化以使其变为灰色。例如,如果您的图像具有[0-2]
的动态范围,那么现在的代码会将其缩放为具有[0, 128, 255]
的强度。您希望这些在转换为 np.uint8
后保持较小。
因此,将每个值除以图像类型可能的最大值,而不是实际图像本身。然后,您将其缩放 255 以生成标准化结果。使用numpy.iinfo
并为其提供图像的类型(dtype
),您将获得该类型的信息结构。然后,您将从此结构中访问 max
字段以确定最大值。
因此,根据上述情况,对您的代码进行以下修改:
import numpy as np
import cv2
[...]
info = np.iinfo(data.dtype) # Get the information of the incoming image type
data = data.astype(np.float64) / info.max # normalize the data to 0 - 1
data = 255 * data # Now scale by 255
img = data.astype(np.uint8)
cv2.imshow("Window", img)
请注意,我已将图像另外转换为np.float64
,以防传入的数据类型不是这样,并在进行除法时保持浮点精度。
【讨论】:
啊,对不起。我现在看到了。我认为@decadenza 和我都在图像本身上调用 .max() ,这是一个函数。另外值得注意的是有一个cv2函数cv2.normalize。 我看到 astype 随处使用的一个小评论。 “你永远不应该在图像上使用 astype,因为它违反了这些关于 dtype 范围的假设”。来自scikit-image.org/docs/dev/user_guide/data_types.html @rayryeng 对我来说np.iinfo(data.dtype)
给出了这个错误:ValueError: Invalid integer data type.
。 data
只是一个 float64 numpy 数组,对吗?
@holastello data
在转换之前需要是整数类型。如果已经是浮点数组,则此方法无效。
@FilipBártek 谢谢你的现场。我不知道有人编辑了这个。我已经把它改回来了,我要给编辑它的人留言,告诉他我的想法。【参考方案2】:
考虑到您使用的是OpenCV,数据类型之间转换的最佳方式是使用normalize
函数。
img_n = cv2.normalize(src=img, dst=None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
但是,如果你不想使用 OpenCV,你可以在 numpy 中做到这一点
def convert(img, target_type_min, target_type_max, target_type):
imin = img.min()
imax = img.max()
a = (target_type_max - target_type_min) / (imax - imin)
b = target_type_max - a * imax
new_img = (a * img + b).astype(target_type)
return new_img
然后像这样使用它
imgu8 = convert(img16u, 0, 255, np.uint8)
这是基于我在此解决方案下的 cmets 中的交叉验证板上找到的答案https://stats.stackexchange.com/a/70808/277040
【讨论】:
我可以确认这就像 Sobel/Canny/Laplacian 生成的 OpenCV 图像数据的魅力一样,其中包含 float64 值,因此与我用来显示结果的QImage
不兼容(它只接受整数值)。【参考方案3】:
你可以使用skimage.img_as_ubyte(yourdata)
它会让你的numpy数组范围从0->255
from skimage import img_as_ubyte
img = img_as_ubyte(data)
cv2.imshow("Window", img)
【讨论】:
收益images of type float must be between -1 and 1
在哪一行??以上是关于将 float64 类型的 np.array 转换为 uint8 类型的缩放值的主要内容,如果未能解决你的问题,请参考以下文章
[Python For Data Analysis] Numpy Basics