From 303cf3a802bcbd2fa42a4b6f5ee8528f47f9c743 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sun, 5 Feb 2012 16:04:47 -0500 Subject: [PATCH] DOC: Improve description of image data types in user's guide. --- doc/source/user_guide/data_types.txt | 166 ++++++++++++++++++++------- 1 file changed, 122 insertions(+), 44 deletions(-) diff --git a/doc/source/user_guide/data_types.txt b/doc/source/user_guide/data_types.txt index f49fdfb4..7ce032f1 100644 --- a/doc/source/user_guide/data_types.txt +++ b/doc/source/user_guide/data_types.txt @@ -1,61 +1,139 @@ + +=================================== Image data types and what they mean =================================== -.. note:: +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: - This is a preliminary design document. +========= =============== +Data type Range +========= =============== +uint8 0 to 255 +uint16 0 to 65535 +float 0 to 1 +int8 -128 to 127 +int16 -32768 to 32767 +========= =============== -In ``skimage``, we assume the following dtype ranges: +Note that float images are restricted to the range 0 to 1 even though the data +type itself can exceed this range. + +Functions in ``skimage`` are designed so that they accept any of these dtypes, +but, for efficiency, *may return an image of a different dtype* (see `Output +types`_). If you need a particular dtype, ``skimage`` provides utility +functions that convert dtypes and properly rescale image intensities (see +`Input types`_). You should **never use** ``astype`` on an image, because it +violates these assumptions about the dtype range. -=========== ================ -Data type Range -=========== ================ -uint8 0 to 255 -uint16 0 to 65535 -float 0 to 1 -int8 -128 to 127 -int16 -32768 to 32767 -=========== ================ Input types ------------ +=========== -Functions may choose to support only a subset of these data-types. In -such a case, the input will be converted to the required type (if -possible) and a warning message printed to the log if a memory copy is -needed. Type requirements should be noted in the docstrings. +Functions may choose to support only a subset of these data-types. In such +a case, the input will be converted to the required type (if possible), and +a warning message is printed to the log if a memory copy is needed. Type +requirements should be noted in the docstrings. -The following utility functions are available to developers and -users: +The following utility functions in the main package are available to developers +and users: -(*Tentative list only -- not implemented yet*) +============= ================================= +Function name Description +============= ================================= +img_as_float Convert to 64-bit floating point. +img_as_ubyte Convert to 8-bit uint. +img_as_uint Convert to 16-bit uint. +img_as_int Convert to 16-bit int. +============= ================================= -=============== ======================================= -Function name Description -=============== ======================================= -img_as_float Convert to 64-bit floating point repr. - Scaled to 0/1. -img_as_uint Convert to 16-bit uint repr. - Scaled to 0-65535. -img_as_int Convert to 16-bit int repr. - Scaled to -32767 to 32768. -=============== ======================================= +These functions convert images to the desired dtype and *properly rescale their +values*. If conversion reduces the precision of the image, then a warning is +issued:: + + >>> 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) + +Wherever possible, functions should try to handle input without explicit +conversion. For example, there is no need to force values to a specific type +for doing a convolution; a plotting function, on the other hand, needs to know +the range of the input. -Wherever possible, functions should try to handle input -without explicit conversion (e.g., there is no need to -force values to a specific type for doing a convolution; -a plotting function, on the other hand, needs to know the -range of the input). Output types ------------- -The output type of a function is determined by the function author, -and documented for the benefit of the user. While this requires the -user to explicitly convert the output to whichever format is needed, -it ensures that no unnecessary data copies take place. +============ -A user that requires a specific type of output (e.g., for display -purposes), may do:: +The output type of a function is determined by the function author and is +documented for the benefit of the user. While this requires the user to +explicitly convert the output to whichever format is needed, it ensures that no +unnecessary data copies take place. + +A user that requires a specific type of output (e.g., for display purposes), +may write:: + + >>> from skimage import img_as_uint + >>> out = img_as_uint(sobel(image)) + >>> plt.imshow(out) + + +Image processing pipeline +========================= + +This dtype behavior allows you to string together any ``skimage`` function +without worrying about the image dtype. On the other hand, if you want to use +a custom function that requires a particular dtype, you should call one of the +dtype conversion functions (here, ``func1`` and ``func2`` are ``skimage`` +functions):: + + >>> from skimage import img_as_float + >>> image = img_as_float(func1(func2(image))) + >>> processed_image = custom_func(image) + +Better yet, you can convert the image internally and use a simplified +processing pipeline:: + + >>> def custom_func(image): + ... image = img_as_float(image) + ... # do something + ... + >>> processed_image = custom_func(func1(func2(image))) + + +Rescaling intensity values +========================== + +When possible, functions should avoid blindly stretching image intensities +(e.g. rescaling a float image so that the min and max intensities are +0 and 1), since this can heavily distort an image. For example, if you're +looking for bright markers in dark images, there may be an image where no +markers are present; stretching its input intensity to span the full range +would make background noise look like markers. + +Sometimes, however, you have images that should span the entire intensity +range but do not. For example, some cameras store images with 10-, 12-, or +14-bit depth per pixel. If these images are stored in an array with dtype +uint16, then the image won't extend over the full intensity range, and thus, +would appear dimmer than it should. To correct for this, you can use the +``rescale_intensity`` function to rescale the image so that it uses the full +dtype range:: + + >>> from skimage import exposure + >>> image = exposure.rescale_intensity(img10bit, in_range=(0, 2**10 - 1)) + +Here, the ``in_range`` argument is set to the maximum range for a 10-bit image. +By default, ``rescale_intensity`` stretches the values of ``in_range`` to match +the range of the dtype. + + +References +========== + +.. _numpy: http://docs.scipy.org/doc/numpy/user/ +.. [1] http://docs.scipy.org/doc/numpy/user/basics.types.html - out = img_as_uint(sobel(img)) - plt.imshow(out)