The image processing field has recently seen quite a growth due to advances in neural networks and other useful machine learning algorithms. Image processing involves the processing of images which can be a range of operations like smoothing images, detecting edges, image segmentation, image filtering, etc. The image generated after image processing can be a final image or it can be an image that will be fed to further algorithms. Many times, we might need to add shapes to images in order to prepare them for some other task like adding circles around faces for face detection, adding rectangles around objects for object detection, etc. Manually doing such tasks can be very cumbersome. It can fasten the process if we can automate such tasks.
Python scikit-image library provides us with sub-module name draw which can help us add shapes (circles, rectangle, ellipses, etc.) of different types to our image. It has methods that can handle both grayscale and RGB images. As a part of this tutorial, we'll explain various methods available through draw sub-module with simple and easy-to-understand examples. We'll be adding shapes to both grayscale and RGB images for explanation purposes.
Below is a list of shapes that we'll discuss as a part of this tutorial.
We'll start by importing the necessary modules and images that we'll be using for explanation purposes.
import skimage
from skimage import data, draw, io
import matplotlib.pyplot as plt
import numpy as np
Below we have read colored (RGB) JPEG image from disk using io module of skimage and printed its dimensions as well as data types. The second colored (RGB) image is of a coffee cup available as a part of data module of skimage.
If you are interested in learning about how to perform basic operations with images (reading/writing images, resize, rescale, rotate, etc) using skimaget then please feel free to check our tutorial on the same. It tries to explain things with simple examples.
Please make a note that scikit-image treats all images as a numpy array of integers (0-255) or floats (0.0-1.0). It performs all operations on the array.
dr_kalam = io.imread("dr_apj_kalam.jpeg")
coffee = skimage.data.coffee()
print("DR Kalam Image Shape : {}, Data Type : {}".format(dr_kalam.shape, dr_kalam.dtype))
print("Coffee Image Shape : {}, Data Type : {}".format(coffee.shape, coffee.dtype))
Below we have created a simple function that takes as input two images and displays them next to each other. We'll be reusing this function for showing different images.
def display_images(img1, img2, cmap=None):
fig, ax = plt.subplots(nrows=1, ncols=2)
fig.set_figwidth(8)
fig.set_figheight(8)
ax[0].imshow(img1, cmap=cmap);
ax[1].imshow(img2, cmap=cmap);
display_images(coffee, dr_kalam)
Below we have loaded two gray scale images from the internet for explanation purposes.
puppy = io.imread("greyscale_2.jpg", as_gray=True)
girl = io.imread("greyscale_1.jpeg", as_gray=True)
print("Puppy Image Shape : {} , Data Type : {}".format(puppy.shape, puppy.dtype))
print("Girl Image Shape : {}, Data Type : {}".format(girl.shape, girl.dtype))
display_images(puppy, girl, cmap="gray")
If you want to follow the tutorial with your images then you are free to do so but you'll have to make changes to method inputs for drawing shapes based on your image size.
The first shape that we'll explain in a simple line. The skimage.draw module provides us with a method named line() which lets us add lines to our image.
Please make a note that the majority of methods inside of **skimage.draw** module works with input in the form of row and column of an image. The row and column start at the top-left end of the image. All methods working with images require us to think as if we are working with a numpy array. Normally we think of coordinates in terms of X and Y which start at the bottom-left corner and that will give us wrong results with these methods. The matplotlib also works with coordinates in terms of X and Y. If we want to use the output of methods with matplotlib then we'll have to consider columns as X-axis values and rows as Y-axis values. Please don't worry if you don't get concepts from the theoretical explanations. We have explained below this with examples to make things clear.
Below we have created a line using line() method which starts at (row-100,column-100) and ends at (row-1200,column-800). We'll be using matplotlib for plotting images. We have then created a copy of the girl’s grayscale image. We have then added the value of 1 in each row and column combination provided by line() method. We can do this by simply indexing girl image which is a numpy array. At last, we have plotted a new modified image. The value of 1
row, col = draw.line(100, 100, 1200, 800)
row,col
fig, ax = plt.subplots(1,1)
girl_copy = girl.copy()
girl_copy[row,col] = 1
ax.imshow(girl_copy, cmap="gray");
Below we have drawn a line to an image by using matplotlib. Please pay ATTENTION to how we have used column values as X values and row values as Y values for drawing the same line using matplotlib. This is the main difference between the coordinate system in numpy array and matplotlib that we need to keep in mind when working with the scikit-image module.
fig, ax = plt.subplots(1,1)
ax.imshow(girl, cmap="gray");
ax.plot(col, row, "--r");
Below we have explained another example demonstrating usage of line() method on RGB image. We are drawing a line from coordinates (100,50) to (200,200). We have then added a line to the copy of the original image using numpy indexing. At last, we have plotted the image for showing changes.
Please pay ATTENTION that as this is RGB image, we have assigned an array of 3 numbers to the image array when indexing. If we provide only a single value then the same value will be assigned to all 3 channels. We need to provide a proper color combination using 3 RGB values.
row, col = draw.line(100, 50, 200, 200)
fig, ax = plt.subplots(1,1)
dr_kalam_copy = dr_kalam.copy()
dr_kalam_copy[row, col] = [255,255,0]
ax.imshow(dr_kalam_copy);
Below we have explained how we can draw a line on image without modifying image and using matplotlib.
fig, ax = plt.subplots(1,1)
ax.imshow(dr_kalam);
ax.plot(col, row, "--y");
As a part of this section, we'll explain how we can draw a circle using circle_perimeter() method of skimage.draw module.
Below we have created a circle of 10 pixels at location (500,500) in the girl image. We have as usual created a copy of the image and modified the value at returned coordinates. We have then plotted the image using matplotlib to verify changes.
row, col = draw.circle_perimeter(500,500, 150)
fig, ax = plt.subplots(1, 1)
girl_copy = girl.copy()
girl_copy[row, col] = 1
ax.imshow(girl_copy, cmap="gray");
Below we have created a circle in RGB image of pixel 50 at coordinates (150,150).
row, col = draw.circle_perimeter(150,150, 50)
dr_kalam_copy = dr_kalam.copy()
dr_kalam_copy[row, col, :] = [255, 255, 0] ## Yellow Color
fig, ax = plt.subplots(1,1)
ax.imshow(dr_kalam_copy);
As a part of this section, we'll explain how we can create a filled circle on an image using disk() method of skimage.draw module.
Below we have created a filled circle of radius 100 pixels at the center (150,250). We have then as usual plotted a modified image to show a circle in an image.
row, col = draw.disk((150,250), 100)
girl_copy = girl.copy()
girl_copy[row, col] = 1
fig, ax = plt.subplots(1,1)
ax.imshow(girl_copy, cmap="gray");
Below we have explained how to add a filled circle to an RGB image.
row, col = draw.disk((150,150), 50)
dr_kalam_copy = dr_kalam.copy()
dr_kalam_copy[row, col, :] = [255, 255, 0] ## Yellow Color
fig, ax = plt.subplots(1,1)
ax.imshow(dr_kalam_copy);
As a part of this section, we'll explain how to create an ellipse using ellipse() method of skimage.draw module.
Below we have created an ellipse at the center (150,250) in the girl image with a height of 60 pixels and a width of 120 pixels.
row, col = draw.ellipse(150,250, 60, 120)
girl_copy = girl.copy()
girl_copy[row, col] = 1
fig, ax = plt.subplots(1,1)
ax.imshow(girl_copy, cmap="gray");
Below we have created an ellipse at the center (150,150) with a width of 15 pixels and a height of 50 pixels in the RGB image.
row, col = draw.ellipse(150,150, 50, 15)
dr_kalam_copy = dr_kalam.copy()
dr_kalam_copy[row, col, :] = [255, 255, 0] ## Yellow Color
fig, ax = plt.subplots(1,1)
ax.imshow(dr_kalam_copy);
Below we have explained how we can create an ellipse perimeter using ellipse_perimeter() method of skimage.draw module.
Below we have drawn an ellipse perimeter in the girl grayscale image.
row, col = draw.ellipse_perimeter(150,250, 60, 120)
girl_copy = girl.copy()
girl_copy[row, col] = 1
fig, ax = plt.subplots(1,1)
ax.imshow(girl_copy, cmap="gray");
Below we have drawn an ellipse perimeter in an RGB image for explanation purposes.
row, col = draw.ellipse_perimeter(150,150, 50, 15)
dr_kalam_copy = dr_kalam.copy()
dr_kalam_copy[row, col, :] = [255, 255, 0] ## Yellow Color
fig, ax = plt.subplots(1,1)
ax.imshow(dr_kalam_copy);
As a part of this section, we have explained how we can create a filled rectangle/square using rectangle() method of skimage.draw module.
Below we have drawn a rectangle between points (150,150) and (250,300) in our grayscale image.
row, col = draw.rectangle(start=(150,150), end=(250, 300))
girl_copy = girl.copy()
girl_copy[row, col] = 1
fig, ax = plt.subplots(1,1)
ax.imshow(girl_copy, cmap="gray");
Below we have drawn a rectangle between points (150,150) and (175,200) in our RGB image.
row, col = draw.rectangle(start=(150,150), end=(175, 200))
dr_kalam_copy = dr_kalam.copy()
dr_kalam_copy[row, col, :] = [255, 255, 0] ## Yellow Color
fig, ax = plt.subplots(1,1)
ax.imshow(dr_kalam_copy);
As a part of this section, we have explained how we can create a rectangle perimeter using rectangle_perimeter() method of skimage.draw module.
Below we have drawn a rectangle perimeter between coordinates (150,150) and (250,300) in our grayscale image.
row, col = draw.rectangle_perimeter(start=(150,150), end=(250, 300))
girl_copy = girl.copy()
girl_copy[row, col] = 1
fig, ax = plt.subplots(1,1)
ax.imshow(girl_copy, cmap="gray");
fig, ax = plt.subplots(1,1)
ax.imshow(girl, cmap="gray");
ax.plot(col, row, "--y");
Below we have drawn a rectangle perimeter between coordinates (150,150) and (175,200) in our RGB image.
row, col = draw.rectangle_perimeter(start=(150,150), end=(175, 200))
dr_kalam_copy = dr_kalam.copy()
dr_kalam_copy[row, col, :] = [255, 255, 0] ## Yellow Color
fig, ax = plt.subplots(1,1)
ax.imshow(dr_kalam_copy);
fig, ax = plt.subplots(1,1)
ax.imshow(dr_kalam);
ax.plot(col, row, "--y");
As a part of this section, we have explained how we can create a polygon using polygon() method of skimage.draw module.
Below we have drawn a triangle in our grayscale image using polygon() method.
row, col = draw.polygon((100,200,800), (100,700,400))
girl_copy = girl.copy()
girl_copy[row, col] = 1
fig, ax = plt.subplots(1,1)
ax.imshow(girl_copy, cmap="gray");
Below we have drawn a polygon with 4 points in our gray scale image.
row, col = draw.polygon((100,200,800, 800), (100,700,400, 200))
girl_copy = girl.copy()
girl_copy[row, col] = 1
fig, ax = plt.subplots(1,1)
ax.imshow(girl_copy, cmap="gray");
Below we have drawn a triangle in our RGB image.
row, col = draw.polygon((100,150, 230), (100,130, 110))
dr_kalam_copy = dr_kalam.copy()
dr_kalam_copy[row, col, :] = [255, 255, 0] ## Yellow Color
fig, ax = plt.subplots(1,1)
ax.imshow(dr_kalam_copy);
Below we have drawn a polygon with 4 points in our RGB image.
row, col = draw.polygon((100,150, 230, 230), (100,130, 110, 50))
dr_kalam_copy = dr_kalam.copy()
dr_kalam_copy[row, col, :] = [255, 255, 0] ## Yellow Color
fig, ax = plt.subplots(1,1)
ax.imshow(dr_kalam_copy);
As a part of this section, we have explained how we can draw polygon perimeter using polygon_perimeter() method of skimage.draw module.
Below we have explained the usage of the method on our grayscale and RGB image.
row, col = draw.polygon_perimeter((100,200,800), (100,700,400))
girl_copy = girl.copy()
girl_copy[row, col] = 1
fig, ax = plt.subplots(1,1)
ax.imshow(girl_copy, cmap="gray");
row, col = draw.polygon_perimeter((100,150, 230, 230), (100,130, 110, 50))
dr_kalam_copy = dr_kalam.copy()
dr_kalam_copy[row, col, :] = [255, 255, 0] ## Yellow Color
fig, ax = plt.subplots(1,1)
ax.imshow(dr_kalam_copy);
As a part of this section, we have explained how we can create a bezier curve shape on an image using bezier_curve() method of skimage.draw module. The bezier curves are used in the computer graphics field to create curves that appear smooth at almost all scales.
Below we have drawn a bezier curve on a grayscale image for explanation purposes.
row, col = draw.bezier_curve(100,800, 250,200, 500, 800, 2)
girl_copy = girl.copy()
girl_copy[row, col] = 1
fig, ax = plt.subplots(1,1)
ax.imshow(girl_copy, cmap="gray");
Below we have drawn a bezier curve on an RGB image for explanation purposes.
row, col = draw.bezier_curve(50,175, 175,50, 200, 175, 2)
dr_kalam_copy = dr_kalam.copy()
dr_kalam_copy[row, col, :] = [255, 255, 0] ## Yellow Color
fig, ax = plt.subplots(1,1)
ax.imshow(dr_kalam_copy);
As a part of this section, we'll explain how we can create a mask from polygon using polygon2mask() method of skimage.draw module.
Below we have explained how we can create a mask using polygon2mask() method. We have plotted the original grayscale image and mask next to each other. Then we have modified the image to add a mask to the image and plotted it as well in the next code cell.
girl_mask = draw.polygon2mask(girl.shape, [(100,100),(200,700),(800,400), (800,200)])
fig, ax = plt.subplots(1,2)
ax[0].imshow(girl, cmap="gray");
ax[1].imshow(girl_mask, cmap="gray");
girl_copy = girl.copy()
girl_copy[girl_mask] = 0
plt.imshow(girl_copy, cmap="gray");
As a part of this section, we have explained how we can change the color of the list of coordinates of an image using set_color() method of skimage.draw module.
Below we have first created a bezier curve and stored its points in a variable. We have then given copied image, bezier curve coordinates, and color value of 1 (white) to set_color() method. The set_color() method will modify the color of coordinates specified by the bezier curve.
bezier_curve = draw.bezier_curve(100,800, 250,200, 500, 800, 2)
girl_copy = girl.copy()
draw.set_color(girl_copy, bezier_curve, 1)
plt.imshow(girl_copy, cmap="gray");
Below we have drawn a polygon on an image using set_color() method.
polygon = draw.polygon((100,200,800, 800), (100,700,400, 200))
girl_copy = girl.copy()
draw.set_color(girl_copy, polygon, 0)
plt.imshow(girl_copy, cmap="gray");
Below we have explained the usage of set_color() on an RGB image.
bezier_curve = draw.bezier_curve(50,175, 175,50, 200, 175, 2)
dr_kalam_copy = dr_kalam.copy()
draw.set_color(dr_kalam_copy, bezier_curve, [0,255,0])
plt.imshow(dr_kalam_copy);
polygon = draw.polygon((100,150, 230, 230), (100,130, 110, 50))
dr_kalam_copy = dr_kalam.copy()
draw.set_color(dr_kalam_copy, polygon, [0,255,0])
plt.imshow(dr_kalam_copy);
As a part of this section, we'll explain how we can add random shapes with random sizes, random locations, and random color to our image using random_shapes() method of skimage.draw module.
Below we have generated 3 random shapes for our grayscale image. We have then included logic to include those shapes in our image. We have then plotted images with shapes and shape image returned by a method.
girl_random, labels = draw.random_shapes(girl.shape,
max_shapes=3,
min_size=5,
multichannel=False,
random_seed=1234)
girl_random = skimage.img_as_float(girl_random)
girl_copy = girl.copy()
girl_copy = np.where(girl_random == 1.0, girl_copy, girl_random)
fig,ax = plt.subplots(1,2)
ax[0].imshow(girl_copy, cmap="gray");
ax[1].imshow(girl_random, cmap="gray");
Below we have printed labels returned by a method.
labels
Below we have explained the usage of a method to generate shapes on an RGB image. The shapes are not overlapping in this image.
dr_kalam_random, labels = draw.random_shapes(dr_kalam.shape, max_shapes=3, min_size=5,
multichannel=True,
random_seed=1234)
dr_kalam_copy = dr_kalam.copy()
dr_kalam_copy = np.where(dr_kalam_random==255, dr_kalam_copy, dr_kalam_random)
fig,ax = plt.subplots(1,2)
ax[0].imshow(dr_kalam_copy);
ax[1].imshow(dr_kalam_random);
labels
Below we have explained another example specifying the usage of the method to create random shapes. We have included overlapping shapes this time.
dr_kalam_random, labels = draw.random_shapes(dr_kalam.shape, max_shapes=3, min_size=5,
multichannel=True,
allow_overlap=True,
random_seed=1234)
dr_kalam_copy = dr_kalam.copy()
dr_kalam_copy = np.where(dr_kalam_random==255, dr_kalam_copy, dr_kalam_random)
fig,ax = plt.subplots(1,2)
ax[0].imshow(dr_kalam_copy);
ax[1].imshow(dr_kalam_random);
This ends our small tutorial explaining how we can use draw module of skimage library to draw shapes of various types and sizes in images. Please feel free to let us know your views in the comments section.
If you are more comfortable learning through video tutorials then we would recommend that you subscribe to our YouTube channel.
When going through coding examples, it's quite common to have doubts and errors.
If you have doubts about some code examples or are stuck somewhere when trying our code, send us an email at coderzcolumn07@gmail.com. We'll help you or point you in the direction where you can find a solution to your problem.
You can even send us a mail if you are trying something new and need guidance regarding coding. We'll try to respond as soon as possible.
If you want to