# Filter Colors Using NumPy in Python

Color filtering is an essential aspect of image processing, it involves isolating specific colors or color ranges in images, allowing for a range of applications from simple photo enhancements to object detection and real-time video analysis.

In this tutorial, you’ll learn how to filter colors using NumPy in Python.

## Understanding Image Color Models

In this section, we’ll dive into the RGB color model, grayscale conversion, and HSV/HSL color models, highlighting their significance in color filtering.

### The RGB Color Model

The RGB color model represents images using the colors Red, Green, and Blue.

We can create a simple RGB color using NumPy:

```import numpy as np
red_color = np.array([255, 0, 0])
print(red_color)
```

Output:

```[255   0   0]
```

This output shows an array representing the color red in the RGB model, with maximum red (255) and no green or blue.

### Grayscale Conversion

Converting an image to grayscale simplifies it by reducing the color information, which is useful for certain processing tasks. Here’s how you convert an RGB color to grayscale:

```# Convert the red color to grayscale
gray_scale_value = np.dot(red_color, [0.2989, 0.5870, 0.1140])
print(gray_scale_value)
```

Output:

```76.2195
```

The grayscale value is a weighted sum of the RGB components, reflecting the perceived brightness of the color.

### HSV/HSL Color Models

HSV (Hue, Saturation, Value) and HSL (Hue, Saturation, Lightness) are alternative representations of the color model.

They are useful in filtering because they separate color information (hue) from lighting (value/lightness).

Here’s how you can create a simple HSV color:

```hsv_color = np.array([30, 100, 100]) # Hue: 30, Saturation: 100, Value: 100
print(hsv_color)
```

Output:

```[ 30 100 100]
```

This represents an HSV color with specific hue, saturation, and value levels.

## Isolating Single Color Channels

By focusing on individual Red, Green, or Blue (RGB) channels, you can gain insights into specific aspects of an image.

Here, we’ll explore how to isolate these color channels using NumPy in Python.

### Isolating the Red Channel

To isolate the red channel, you’ll extract the red component from each pixel while setting the green and blue components to zero. Here’s how to do it:

```import numpy as np

# Sample RGB image represented as a 3x3x3 NumPy array
sample_image = np.array([[[255, 0, 0], [0, 255, 0], [0, 0, 255]],
[[0, 255, 255], [255, 0, 255], [255, 255, 0]],
[[0, 0, 0], [255, 255, 255], [128, 128, 128]]])
red_channel = sample_image.copy()
red_channel[:,:,[1, 2]] = 0
print(red_channel)
```

Output:

```[[[255   0   0]
[  0   0   0]
[  0   0   0]]

[[  0   0   0]
[255   0   0]
[255   0   0]]

[[  0   0   0]
[255   0   0]
[128   0   0]]]
```

In this output, only the red values are retained, while green and blue are set to zero.

### Isolating the Green Channel

Similarly, to isolate the green channel, retain only the green values:

```green_channel = sample_image.copy()
green_channel[:,:,[0, 2]] = 0
print(green_channel)
```

Output:

```[[[  0   0   0]
[  0 255   0]
[  0   0   0]]

[[  0 255   0]
[  0   0   0]
[  0 255   0]]

[[  0   0   0]
[  0 255   0]
[  0 128   0]]]
```

Here, only the green components are visible.

### Isolating the Blue Channel

For the blue channel:

```blue_channel = sample_image.copy()
blue_channel[:,:,[0, 1]] = 0
print(blue_channel)
```

Output:

```[[[  0   0   0]
[  0   0   0]
[  0   0 255]]

[[  0   0 255]
[  0   0 255]
[  0   0   0]]

[[  0   0   0]
[  0   0 255]
[  0   0 128]]]
```

Only blue values are present in this output.

Now, Let’s isolate and display the Red, Green, and Blue channels separately using Matplotlib.

This code will create a randomly generated image and then isolate each color channel for display:

```import numpy as np
import matplotlib.pyplot as plt

# Sample image array
image = np.random.rand(100, 100, 3)  # Randomly generated sample image

# Function to isolate a color channel
def isolate_color_channel(image, channel_index):
# Creating a copy of the image
filtered_image = image.copy()
# Setting the other two channels to 0
zero_indices = [i for i in range(3) if i != channel_index]
filtered_image[:, :, zero_indices] = 0
return filtered_image

# Isolating each color channel
red_filtered = isolate_color_channel(image, 0)
green_filtered = isolate_color_channel(image, 1)
blue_filtered = isolate_color_channel(image, 2)
plt.figure(figsize=(15, 5))

# Displaying original image
plt.subplot(1, 4, 1)
plt.imshow(image)
plt.title("Original Image")

# Displaying red channel image
plt.subplot(1, 4, 2)
plt.imshow(red_filtered)
plt.title("Red Color Filtered Image")

# Displaying green channel image
plt.subplot(1, 4, 3)
plt.imshow(green_filtered)
plt.title("Green Color Filtered Image")

# Displaying blue channel image
plt.subplot(1, 4, 4)
plt.imshow(blue_filtered)
plt.title("Blue Color Filtered Image")
plt.show()
```

Output:

This code creates a sample image and then isolates each of the RGB channels by setting the other two channels to zero in each case using `isolate_color_channel` function.

## Creating Binary Masks Based on Color Thresholds

Creating binary masks based on color thresholds involves setting a threshold value to isolate specific colors or color ranges in an image.

These masks are useful for various applications like object detection, background removal, and more.

Let’s create a threshold to isolate a specific color range.

```import numpy as np
import matplotlib.pyplot as plt

# Generating a random image for demonstration
image = np.random.rand(100, 100, 3)

# Defining color thresholds for red
lower_red = np.array([0.5, 0, 0])  # Lower bound for red
upper_red = np.array([1, 0.2, 0.2])  # Upper bound for red
plt.imshow(image)
plt.title("Sample Image")
plt.show()
```

Output:

Now, create a binary mask that identifies pixels within the red color range:

```# Creating a binary mask
mask = np.all(np.logical_and(lower_red <= image, image <= upper_red), axis=-1)

plt.title("Binary Mask for Red Color Range")
plt.show()
```

Output:

This code generates a binary mask where white areas (True) indicate pixels that fall within the defined red color range, and black areas (False) represent other colors.

Finally, apply this mask to the original image to see the result:

```masked_image = np.where(mask[..., None], image, 0)
plt.title("Image After Applying Red Color Mask")
plt.show()
```

Output:

The masked image highlights only the areas that meet the red color criteria, effectively filtering the image based on the color threshold.

## Combining and Splitting Color Channels

First, let’s split an image into its individual Red, Green, and Blue (RGB) channels.

```import numpy as np
import matplotlib.pyplot as plt

# Generating a random image
image = np.random.rand(100, 100, 3)

# Splitting the color channels
red_channel = image[:, :, 0]
green_channel = image[:, :, 1]
blue_channel = image[:, :, 2]

# Displaying the color channels
plt.figure(figsize=(15, 5))

plt.subplot(1, 3, 1)
plt.imshow(red_channel, cmap='Reds')
plt.title("Red Channel")

plt.subplot(1, 3, 2)
plt.imshow(green_channel, cmap='Greens')
plt.title("Green Channel")

plt.subplot(1, 3, 3)
plt.imshow(blue_channel, cmap='Blues')
plt.title("Blue Channel")
plt.show()
```

Output:

This code separates the image into its red, green, and blue components, displaying each channel individually.

The `cmap` parameter in `imshow` ensures that each channel is visualized in its respective color.

Now, let’s combine different color channels to form a new image. For instance, we can combine the red and green channels to create a yellowish image:

```# Combining red and green channels to form yellow
yellow_image = np.stack([red_channel, green_channel, np.zeros_like(blue_channel)], axis=-1)
plt.imshow(yellow_image)
plt.title("Combined Yellow Image (Red + Green)")
plt.show()
```

Output:

This code combines the red and green channels while setting the blue channel to zero, resulting in a yellow-colored image.

This demonstrates how combining different color channels can create various color effects.

## Detecting and Isolating Specific Color Ranges

Detecting and isolating specific color ranges in images allows for the identification and extraction of objects or features based on their color.

It’s useful in applications like object tracking, image segmentation, and automated quality control.

First, define the color range you want to detect. For this example, let’s focus on isolating a specific shade of blue. We start by setting up the color thresholds:

```import numpy as np
import matplotlib.pyplot as plt

# Generating a random image
image = np.random.rand(100, 100, 3)

# Defining color thresholds for blue
lower_blue = np.array([0, 0, 0.5])  # Lower bound for blue
upper_blue = np.array([0.3, 0.3, 1])  # Upper bound for blue
plt.imshow(image)
plt.title("Sample Image")
plt.show()
```

Output:

Next, create a mask that detects the pixels within the specified blue color range:

```# Creating a binary mask for the blue color range
blue_mask = np.all(np.logical_and(lower_blue <= image, image <= upper_blue), axis=-1)
plt.title("Binary Mask for Blue Color Range")
plt.show()
```

Output:

This binary mask shows the areas in the image that fall within the specified blue color range.

Now, apply this mask to the original image to isolate the blue color range:

```# Applying the mask to the original image
isolated_blue = np.where(blue_mask[..., None], image, 0)

plt.imshow(isolated_blue)
plt.title("Image After Isolating Blue Color Range")
plt.show()
```

Output:

The resulting image highlights the regions that match the defined blue color range. Other areas are set to black, effectively isolating the color of interest.

## Dealing with Varying Lighting Conditions

One of the primary challenges in color filtering is the variation in how colors appear under different lighting conditions.

For example, an object that appears red in daylight might look quite different under artificial light.

This variation can significantly affect the accuracy of color-based image processing.

To deal with varying lighting conditions, consider the following strategies:

Adjust color thresholds dynamically based on the lighting conditions of the image. This can be done through real-time analysis of the image’s overall brightness and contrast.

```average_brightness = np.mean(image)
adjusted_lower_blue = lower_blue * average_brightness
adjusted_upper_blue = upper_blue * average_brightness
```

In this example, the blue color thresholds are adjusted based on the average brightness of the image.

Using HSV/HSL Color Models:

The HSV (Hue, Saturation, Value) and HSL (Hue, Saturation, Lightness) models are less sensitive to lighting variations compared to RGB.

Converting your image to one of these models before applying color filters can improve consistency.

```from matplotlib.colors import rgb_to_hsv
hsv_image = rgb_to_hsv(image)
hsv_mask = np.all(np.logical_and(lower_hsv <= hsv_image, hsv_image <= upper_hsv), axis=-1)
```

This code converts an RGB image to HSV and applies a mask based on HSV thresholds.

## Real-Time Color Filtering in Video Streams

Processing a video stream is different from processing a static image. A video is a sequence of images (frames), and real-time processing involves applying color filtering to each frame as it is received.

### Essential Steps for Real-Time Color Filtering

Frame Capture:

Continuously capture frames from the video stream. Python libraries like OpenCV are commonly used for this purpose.

```import cv2
import numpy as np
video_capture = cv2.VideoCapture(0)
```

This code initializes video capture from the default webcam.

Applying Color Filters:

Apply the desired color filter to each frame. This involves selecting a color range and creating a mask, similar to image processing.

```while True:
ret, frame = video_capture.read()  # Read a frame
if not ret:
break

# Convert frame to HSV (better for color filtering)
hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

# Example: Detecting red color
lower_red = np.array([160, 100, 100])
upper_red = np.array([180, 255, 255])
mask = cv2.inRange(hsv_frame, lower_red, upper_red)

# Apply the mask to the frame