Image data types and what they mean

图像数据类型及其含义

skimage,图像是简单的numpy数组,支持各种数据类型[1], “dtypes”。为了避免扭曲图像强度(请参阅调整强度值),我们假设图像使用以下dtype范围:

数据类型范围
UINT80到255
UINT160至65535
UINT320至232
浮动-1到1或0到1
INT8-128至127
INT16-32768至32767
INT32-231至231-1

请注意,即使数据类型本身可能超过此范围,浮动图像也应限制在-1到1的范围内; 另一方面,所有的整型数据都具有可以跨越整个数据类型范围的像素强度。除少数例外,不支持64位(u)int图像

函数skimage的设计使它们可以接受任何这些dtype,但为了提高效率,可能会返回不同 dtype 的图像(请参见输出类型)。如果您需要特定的dtype,则skimage提供实用程序函数,用于转换dtypes并适当重新调整图像强度(请参阅输入类型)。你不应该在图像上使用 astype,因为它违反了有关dtype范围的这些假设:

>>> from skimage import img_as_float >>> image = np.arange(0, 50, 10, dtype=np.uint8) >>> print(image.astype(np.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位uint。
img_as_uint转换为16位uint。
img_as_int转换为16位整数。

这些功能将图像转换为所需的dtype并正确重新调整其值。如果转换降低图像的精度,则会发出警告:

>>> from skimage import img_as_ubyte >>> image = np.array([0, 0.5, 1], dtype=float) >>> img_as_ubyte(image) WARNING:dtype_converter:Possible precision loss when converting from float64 to uint8 array([ 0, 128, 255], dtype=uint8)

使用上下文管理器可以在本地忽略警告:

>>> import warnings >>> image = np.array([0, 0.5, 1], dtype=float) >>> with warnings.catch_warnings(): ... warnings.simplefilter("ignore") ... img_as_ubyte(image) array([ 0, 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 import img_as_uint >>> out = img_as_uint(sobel(image)) >>> plt.imshow(out)

使用OpenCV

您可能需要使用使用OpenCV创建的图像skimage,反之亦然。OpenCV图像数据可以在NumPy中(并且因此在scikit-image中)被访问(不复制)。OpenCV对彩色图像使用BGR(而不是scikit-image的RGB),默认情况下它的dtype为uint8(请参阅图像数据类型及其含义)。BGR代表蓝绿红。

将BGR转换为RGB,反之亦然

skimageOpenCV中的彩色图像有三个维度:宽度,高度和颜色。RGB和BGR使用相同的颜色空间,但颜色顺序相反。

请注意,scikit-image我们通常引用宽度和高度rowscolumns而不是宽度和高度(请参阅坐标约定)。

以下指令有效地反转颜色的顺序,使行和列不受影响。

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

使用来自OpenCV的图像 skimage

如果cv_image是一个无符号字节数组,skimage默认情况下会理解它。如果你喜欢使用浮点图像,img_as_float()可以用来转换图像:

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

使用skimageOpenCV中的图像

反过来可以通过以下方式实现img_as_ubyte()

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

图像处理管道

这种dtype行为允许你将任何skimage函数串起来,而不必担心图像dtype。在另一方面,如果你想使用需要特定的D型自定义函数,你应该叫的D型转换函数(在这里,func1func2skimage功能):

>>> from skimage 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位深度的图像。如果这些图像以dint uint16的形式存储在一个数组中,则图像不会在整个强度范围内延伸,因此会显得比它应该更暗淡。要更正此问题,可以使用该rescale_intensity功能重新缩放图像,以便使用完整的dtype范围:

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

这里,in_range参数被设置为10位图像的最大范围。默认情况下,rescale_intensity拉伸值in_range以匹配dtype的范围。rescale_intensity还接受字符串作为输入in_rangeout_range,所以上面的例子也可以被写为:

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

请注意负值

人们经常使用带符号的dtypes表示图像,尽管它们只是操纵图像的正值(例如,在int8图像中仅使用0-127)。出于这个原因,转换函数仅在无符号dtype的整个范围上传播带符号dtype 的正值。换句话说,从signed变换为unsigned dtypes时,负值将被剪切为0。(在带符号的dtypes之间转换时保留负值。)要防止此剪切行为,应该事先重新调整图像的缩放比例:

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

此行为是对称的:无符号dtype中的值分布在签名dtype的正范围上。

参考

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