Updated: Jun 11, 2020
Here is the full script to load both color and gray versions of an RGB image, show them both, then write the gray version back to a file:
from skimage import io from skimage.util import img_as_ubyte # You need to change this to a valid RGB file on your computer input_image = 'C:/FILES/leaf.tif' image_color = io.imread(input_image) io.imshow(image_color); io.show() image_gray = io.imread(input_image, as_gray=True) io.imshow(image_gray); io.show() image_gray = img_as_ubyte(image_gray) io.imsave('C:/FILES/leaf_gray.tif', image_gray)
This will work with any RGB image, but if you want to use the same image you can find it here.
You can also find this script on our GitHub.
Line by Line
Let's import the functionality we’ll need:
from skimage import io from skimage.util import img_as_ubyte
For convenience, let’s set a string variable containing the path to our file:
input_image = 'C:/FILES/leaf.tif'
We'll use the imread function to load the image. The defaults of the function are set such that any additional dimensions in the image besides XY are read as colors.
image_color = io.imread(input_image)
The imshow function builds a plot to display the image, but nothing is shown until the show function is actually called. Interactive code editors typically provide a hotkey (usually the up arrow) to cycle through your command history. By calling imshow and show on the same line with a semicolon separation, it allows us to string the functions together as a single command in our history that we only have to execute once every time we want to display an image.
A popup is displayed that shows our image, and the script is paused until we close it:
Here we show an optional as_gray parameter of imread set to True, which automatically combines the color channels from the input image into a single grayscale representation of luminance using scikit-image’s rgb2gray function.
image_gray = io.imread(input_image, as_gray=True)
If using an interactive code editor, we can use our history hotkey (the up arrow as shown here in IdleX) to cycle back two executions and repeat this single command with the new image_gray variable to show the image as gray.
At some point our image_gray array was converted to the type float64, which doesn’t work well with most other software packages. Here we use the img_as_ubyte function from the skimage.util submodule to convert the image back to 8bit:
image_gray = img_as_ubyte(image_gray)
Finally, the image is saved to the same folder it’s read from using the imsave function.
There are a few Python packages that can accomplish these simple tasks. Here we’ve used scikit-image, but Pillow, OpenCV, of tifffile can also be used. The major differences between these, I’ve found, is the context in which they’re used:
tifffile is a more dedicated image I/O library which is used by many others in the background (the I/O functions in scikit-image, for example, are simply wrappers for this library when a TIFF file is passed).
scikit-image is one of many scikit packages built for special purposes – in this case, scientific image analysis – on top of scipy and numpy. Their io submodule has a lot of great tools for accessing and saving images which we see here.
Import statements were one of the first things that confused me when I started Python. If you’re just starting and want more background info on how import statements work and why there are different styles of them, try these posts.
The shape and dtype attributes of arrays are very valuable tools when working with images in Python and will help you when debugging errors. When I was building this script, I ran into an error where Windows complained about the file I saved using imsave. Using image_gray.dtype, I found that Numpy had converted my grayscale image to float64 (Numpy loves float64). Windows did not recognize the 64bit float image, so the thumbnail wasn’t generated, and the file would not open in any other software I tried. I was able to add the img_to_ubyte function from the skimage.util submodule to convert the array back to 8bit before saving, and all worked out well. The graphic below shows what the thumbnail for this image looked like before and after conversion.
In the Context of Aivia
You can see any of our recipes for examples of reading and writing images within the context of Aivia. Since Aivia uses temporary files to send image channels back and forth between Python, every single image processing script will use some form of image I/O. Most of the time we use the imread and imsave functions from scikit-image, but other times we directly use the tifffile library.
The simplest example of a Python Aivia recipe is our recipe for converting RGB to luminance. The script provides the ability to select which Aivia channels represent the red, green, and blue channels of an image in the Aivia UI:
Each channel is then read separately from their respective temporary files using skimage.io.imread.
The new luminance array is written back to another temporary file using skimage.io.imsave.
Finally, Aivia automatically loads it back as a new channel.