1. Introduction
Image processing is the technique of altering the characteristics of an image by manipulating its pixels values. One of the most common image processing techniques is Image Enhancement. Image enhancement aims to improve the visual appearance of an image by increasing the level of detail that can be perceived by the human eye. A common problem in images is the presence of shadows. Shadows can greatly affect the quality and visual appeal of an image. This tutorial will discuss how to remove shadows from images using OpenCV and Python.
2. Removing Shadows using OpenCV
OpenCV (Open Source Computer Vision) is an open source library of computer vision and machine learning algorithms. It is used for tasks such as image processing, object detection, and more. In order to remove shadows from an image using OpenCV, we first need to understand how shadows are formed. Shadows are formed when an object obstructs the path of light. This obstruction causes a decrease in the amount of light that reaches certain areas of the image, resulting in darker areas which we perceive as shadows.
One technique for removing shadows is to use the normalised cut algorithm in conjunction with a Gaussian mixture model. The normalised cut algorithm provides a way to segment an image into multiple regions based on its color and texture. A Gaussian mixture model is used to model the pixel distribution of each region.
2.1 Normalised Cut Algorithm
The normalized cut algorithm is a graph-based image segmentation algorithm which can be used to segment the image into multiple regions. The algorithm utilises the graph structure of the image to group pixels together based on similarity criteria. The algorithm takes as input an image and outputs a set of segments which are represented by nodes in the graph. The algorithm attempts to minimise the cut distance between the segments while maximising the distance within the segments. The algorithm works as follows:
A graph is constructed from the image where each pixel is represented by a node in the graph.
An edge is created between each pair of nodes in the graph. The edge weight is a function of the pixel color and texture differences between the two pixels being connected.
The algorithm searches for the optimal cut by partitioning the graph into two disjoint sets while minimising the cut distance between the two sets.
The algorithm repeats step 3 for all possible partitionings until it finds the optimal cut.
2.2 Gaussian Mixture Model
A Gaussian mixture model (GMM) is a probabilistic model that assumes the presence of a finite number of Gaussian distributions in the data. The GMM is used to characterise the pixel distribution of each segment obtained from the normalised cut algorithm. Each segment is modelled using a mixture of K Gaussian distributions, where K is pre-defined based on the image characteristics. The GMM is trained on the pixel values of each segment, providing a probability distribution for each pixel belonging to each Gaussian.
3. Removing Shadows using Python
Removing shadows from an image can be accomplished using Python and OpenCV. The following is a Python code snippet that employs the normalised cut algorithm and the Gaussian mixture model to remove shadows from an image.
import cv2
import numpy as np
def normalize_cut(img):
# Convert image to L*a*b* color space
img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
# Convert image to double precision
img_lab = img_lab.astype(np.float64)
# Normalize image
img_lab /= 255.0
# Obtain height and width of image
height, width, _ = img_lab.shape
# Reshape image to 2D array
img_lab_2d = img_lab.reshape((height * width, 3))
# Set number of components for GMM
num_components = 5
# Create GMM object
gmm = cv2.ml.EM_create()
# Set number of components
gmm.setClustersNumber(num_components)
# Set GMM covariance type
gmm.setCovarianceMatrixType(cv2.ml.EM_COV_MAT_DIAGONAL)
# Set termination criteria
gmm.setTermCriteria((cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 5, 0.1))
# Train GMM on image
gmm.trainEM(np.float32(img_lab_2d))
# Obtain probabilities of pixels belonging to each Gaussian component
probs = gmm.predict2(np.float32(img_lab_2d))[1]
# Reshape probabilities to image shape
probs_2d = probs.reshape((height, width, num_components))
# Obtain maximum probability for each pixel
max_probs = np.max(probs_2d, axis=2)
# Convert probabilities to mask
mask = (max_probs > 0.5)
# Obtain mean pixel values for each segment
means = gmm.getMeans()
# Obtain image without shadow
img_no_shadow = np.zeros_like(img_lab)
for i in range(num_components):
img_no_shadow[mask, 0] = means[i][0]
img_no_shadow[mask, 1] = means[i][1]
img_no_shadow[mask, 2] = means[i][2]
# Convert image to RGB color space
img_no_shadow = cv2.cvtColor(np.uint8(img_no_shadow * 255), cv2.COLOR_LAB2BGR)
return img_no_shadow
# Load image
img = cv2.imread('image.jpg')
# Remove shadows from image
img_no_shadow = normalize_cut(img)
# Display image without shadow
cv2.imshow('Image Without Shadow', img_no_shadow)
cv2.waitKey(0)
cv2.destroyAllWindows()
Conclusion
In conclusion, removing shadows from an image is an important task in image processing. OpenCV and python provide a powerful toolkit for developing image processing algorithms. In this tutorial, we discussed how to remove shadows from an image using the normalised cut algorithm and the Gaussian mixture model in OpenCV and Python. The code provided is a starting point for applications such as object recognition and image segmentation.