How to effectively loop through each pixel for saving time with numpy?

Question:

As you know looping through each pixels and accessing their values with opencv takes too long. As a beginner I’m trying to learn opencv myself when I tried this approach it took me around 7-10 seconds of time to loop through image and perform operations.

code is as below

original_image = cv2.imread(img_f)

image = np.array(original_image)
for y in range(image.shape[0]):
    for x in range(image.shape[1]):
        # remove grey background
        if 150 <= image[y, x, 0] <= 180 and 
                150 <= image[y, x, 1] <= 180 and 
                150 <= image[y, x, 2] <= 180:
            image[y, x, 0] = 0
            image[y, x, 1] = 0
            image[y, x, 2] = 0

        # remove green dashes
        if image[y, x, 0] == 0 and 
                image[y, x, 1] == 169 and 
                image[y, x, 2] == 0:
            image[y, x, 0] = 0
            image[y, x, 1] = 0
            image[y, x, 2] = 0

in above code i’m just trying to remove grey and green pixel colors.

I found similar question asked here but im not able to understand how to use numpy in my usecase as i’m beginner in python and numpy.

Any help or suggestion for solving this will be appreciated thanks

Asked By: appguy

||

Answers:

You can apply numpy filtering to image. In your scenario it would be:

mask_gray = (
    (150 <= image[:, :, 0]) & (image[:, :, 0] <= 180) & 
    (150 <= image[:, :, 1]) & (image[:, :, 1] <= 180) & 
    (150 <= image[:, :, 2]) & (image[:, :, 2] <= 180)
)

image[mask_gray] = 0

mask_green = (
    (image[:, :, 0] == 0) &
    (image[:, :, 1] == 169) &
    (image[:, :, 2] == 0)
)

image[mask_green] = 0

mask_gray and mask_green here are boolean masks

Answered By: Phinnik

You can take advantage of NumPy’s vectorized operations to eliminate all loops which should be much faster.

# Remove grey background
is_grey = ((150 <= image) & (image <= 180)).all(axis=2, keepdims=True)
image = np.where(is_grey, 0, image)

# Remove green dashes
is_green_dash = (image[..., 0] == 0) & (image[..., 1] == 169) & (image[..., 2] == 0)
is_green_dash = is_green_dash[..., np.newaxis]  # append a new dim at the end
image = np.where(is_green_dash, 0, image)

Both invocations of np.where rely on NumPy’s broadcasting.

Answered By: kmkurn

One way to improve the performance of your code would be to use the cv2.inRange() function to find pixels with the desired colors, and then use the cv2.bitwise_and() function to remove those pixels from the image. This can be done more efficiently than looping through each pixel individually, which can be slow and computationally intensive. Here is an example of how this could be implemented:

import cv2
import numpy as np

# Read the image
original_image = cv2.imread('image.jpg')

# Define the colors to be removed as ranges of BGR values
grey_min = np.array([150, 150, 150], np.uint8)
grey_max = np.array([180, 180, 180], np.uint8)
green_min = np.array([0, 0, 0], np.uint8)
green_max = np.array([0, 169, 0], np.uint8)

# Use inRange() to find pixels with the desired colors
grey_mask = cv2.inRange(original_image, grey_min, grey_max)
green_mask = cv2.inRange(original_image, green_min, green_max)

# Use bitwise_and() to remove the pixels with
Answered By: chivalrous-nerd

If you insist on writing your own loops…

You could just use numba. It’s a JIT compiler for Python code.

from numba import njit

@njit
def your_function(input):
    ...
    return output

input = cv.imread(...) # yes this will shadow the builtin of the same name
output = your_function(input)

The first time you call your_function, it’ll take a second to compile. All further calls are blazingly fast.

Answered By: Christoph Rackwitz