图像数据类型及其含义

In skimage, images are simply numpy arrays, which support a variety of data types 1, i.e. "dtypes". To avoid distorting image intensities (see Rescaling intensity values), we assume that images use the following dtype ranges:

数据类型

射程

uint8

0到255

Uint16

0至65535

Uint32

0到232 -1

浮动

-1到1或0到1

Int8

-128至127

Int16

-32768至32767

int32

-2f2f2 31 至231 -1

请注意,浮点图像应该限制在-1到1的范围内,即使数据类型本身可能超过这个范围;另一方面,所有整型数据类型的像素强度都可以跨越整个数据类型范围。除了几个例外, 64-bit (u)int images are not supported

中的函数 skimage 被设计成可以接受这些数据类型中的任何一种,但是为了提高效率, 可能返回不同数据类型的图像 (请参阅 Output types )。如果您需要特定的数据类型, skimage 提供用于转换数据类型和正确调整图像强度的实用程序函数(请参见 Input types )。你应该 切勿使用 astype ,因为它违反了以下关于数据类型范围的假设:

>>> from skimage.util import img_as_float
>>> image = np.arange(0, 50, 10, dtype=np.uint8)
>>> print(image.astype(float)) # These float values are out of range.
[  0.  10.  20.  30.  40.]
>>> print(img_as_float(image))
[ 0.          0.03921569  0.07843137  0.11764706  0.15686275]

输入类型

虽然我们的目标是保留输入图像的数据范围和类型,但函数可能只支持这些数据类型的子集。在这种情况下,输入将被转换为所需的类型(如果可能),如果需要内存副本,则会在日志中打印一条警告消息。应在文档字符串中注明类型要求。

开发人员和用户可以使用主包中的以下实用程序函数:

函数名称

描述

img_as_float

转换为64位浮点。

img_as_ubyte

转换为8位单位。

img_as_uint

转换为16位uint。

img_as_int

转换为16位整型。

这些函数将图像转换为所需的数据类型并 适当地重新调整其值 ::

>>> from skimage.util import img_as_ubyte
>>> image = np.array([0, 0.5, 1], dtype=float)
>>> img_as_ubyte(image)
array([  0, 128, 255], dtype=uint8)

注意!这些转换可能会导致精度损失,因为8位不能保存与64位相同的信息量::

>>> image = np.array([0, 0.5, 0.503, 1], dtype=float)
>>> image_as_ubyte(image)
array([  0, 128, 128, 255], dtype=uint8)

此外,有些函数需要 preserve_range 参数,其中范围转换很方便,但不是必需的。例如,内插在 transform.warp 需要类型为Float的图像,其范围应为 [0, 1] 。因此,默认情况下,输入图像将重新缩放到此范围。但是,在某些情况下,图像值表示用户不希望重新缩放的物理测量值,例如温度或雨量值。使用 preserve_range=True ,则数据的原始范围将被保留,即使输出为浮点图像。然后,用户必须确保下游函数正确处理此非标准图像,这些函数可能会在 [0, 1] 。

>>> from skimage import data
>>> from skimage.transform import rescale
>>> image = data.coins()
>>> image.dtype, image.min(), image.max(), image.shape
(dtype('uint8'), 1, 252, (303, 384))
>>> rescaled = rescale(image, 0.5)
>>> (rescaled.dtype, np.round(rescaled.min(), 4),
...  np.round(rescaled.max(), 4), rescaled.shape)
(dtype('float64'), 0.0147, 0.9456, (152, 192))
>>> rescaled = rescale(image, 0.5, preserve_range=True)
>>> (rescaled.dtype, np.round(rescaled.min()),
...  np.round(rescaled.max()), rescaled.shape
(dtype('float64'), 4.0, 241.0, (152, 192))

输出类型

函数的输出类型由函数作者决定,并为用户的利益进行了文档记录。虽然这需要用户显式地将输出转换为所需的任何格式,但它确保不会发生不必要的数据复制。

需要特定类型输出(例如,用于显示目的)的用户可以写::

>>> from skimage.util import img_as_uint
>>> out = img_as_uint(sobel(image))
>>> plt.imshow(out)

使用OpenCV

您可能需要使用使用 skimage 使用 OpenCV 或者反之亦然。OpenCV图像数据可以在NumPy中访问(无需复制)(因此,也可以在SCRICKIT-IMAGE中访问)。OpenCV对彩色图像使用BGR(而不是SCRKIT-IMAGE的RGB),默认情况下其dtype为uint8(参见 Image data types and what they mean )。BGR代表蓝、绿、红。

将BGR转换为RGB或反之亦然

中的彩色图像 skimage OpenCV有3个维度:宽度、高度和颜色。RGB和BGR使用相同的颜色空间,只是颜色顺序相反。

请注意,在 scikit-image 我们通常指的是 rowscolumns 而不是宽度和高度(请参见 坐标约定 )。

下面的指令有效地颠倒了颜色的顺序,使行和列不受影响。

>>> image = image[:, :, ::-1]

通过使用OpenCV中的图像 skimage

如果CV_IMAGE是无符号字节的数组, skimage 默认情况下会理解它。如果您更喜欢使用浮点图像, img_as_float() 可用于转换图像::

>>> from skimage.util import img_as_float
>>> image = img_as_float(any_opencv_image)

使用来自 skimage 使用OpenCV

相反的情况可以通过以下方式实现 img_as_ubyte() ::

>>> from skimage.util import img_as_ubyte
>>> cv_image = img_as_ubyte(any_skimage_image)

图像处理流水线

此dtype行为允许您将任何 skimage 函数,而不用担心图像数据类型。另一方面,如果要使用需要特定数据类型的定制函数,则应该调用其中一个数据类型转换函数(在这里, func1func2skimage 函数):

>>> from skimage.util import img_as_float
>>> image = img_as_float(func1(func2(image)))
>>> processed_image = custom_func(image)

更好的是,您可以在内部转换图像并使用简化的处理管道:

>>> def custom_func(image):
...     image = img_as_float(image)
...     # do something
...
>>> processed_image = custom_func(func1(func2(image)))

重新调整强度值

如果可能,函数应避免盲目拉伸图像强度(例如,重新缩放浮点图像,使最小和最大强度为0和1),因为这可能会严重扭曲图像。例如,如果要在暗图像中寻找明亮的标记,则可能存在没有标记的图像;拉伸其输入强度以跨越整个范围会使背景噪波看起来像标记。

然而,有时您的图像应该覆盖整个强度范围,但却没有。例如,某些相机以每像素10位、12位或14位深度存储图像。如果这些图像存储在具有dtype uint16的数组中,则图像不会扩展到整个亮度范围,因此会显得比应有的更暗。若要更正此错误,可以使用 rescale_intensity 函数来重新调整图像的比例,使其使用完整的数据类型范围::

>>> from skimage import exposure
>>> image = exposure.rescale_intensity(img10bit, in_range=(0, 2**10 - 1))

在这里, in_range 参数设置为10位图像的最大范围。默认情况下, rescale_intensity 扩展了 in_range 与数据类型的范围相匹配。 rescale_intensity 还接受字符串作为输入 in_rangeout_range ,所以上面的例子也可以写成::

>>> image = exposure.rescale_intensity(img10bit, in_range='uint10')

关于负值的注记

人们经常用带符号的dtype表示图像,即使他们只操作图像的正值(例如,在int8图像中仅使用0-127)。因此,转换函数 只传播积极的价值观 在无符号数据类型的整个范围内的有符号数据类型。换句话说,当从有符号数据类型转换为无符号数据类型时,负值将被剪裁为0。(在带符号的数据类型之间转换时保留负值。)为了防止这种剪切行为,您应该事先重新调整图像的比例::

>>> image = exposure.rescale_intensity(img_int32, out_range=(0, 2**31 - 1))
>>> img_uint8 = img_as_ubyte(image)

这种行为是对称的:无符号数据类型中的值仅分布在有符号数据类型的正范围内。

参考文献

1

https://docs.scipy.org/doc/numpy/user/basics.types.html