Is there a more efficient way to search an image for a specific pixel?

Viewed 238

I am currently working on an computer vision project with python and openCV.

I need to search in an image with a size of about 620x420 pixels for the green pixel, which is the closest to the bottom of the image. There are multiple green pixels (e.g. 100) in the image from previous contour analysis.

Here is an example image:

Here is an example image

I already implemented it with the following code:

    # Search for the most bottom contour point
    xLowestContour = 0                         # x coordinate of the lowest contour point
    yLowestContour = 0                         # y coordinate of the lowest contour point
    for y in range(roi_h):                     # roi_h is the heigth of the image
        for x in range(roi_w):                 # roi_w is the width of the image
            (b, g, r) = roi_copy[y, x]         # roi_copy is the actual image
            if g == 255 and b == 0 and r == 0:
                xLowestContour = x
                yLowestContour = y

This code works. But there is a big problem with it. It looks like that this way of searching for a specific pixel in an image is very inefficient. The framerate drops from 25 FPS to 2 FPS with this codesnippet. The CPU utilization is only at 10 % when using this codesnippet.

Is there a more efficient way to do this operation? I would also like to utilize more CPU power and achieve a higher framerate.

2 Answers

Avoid loops, use numpy!

You can use numpy's argwhere(). The statement returns an array of coordinates where a certain condition is satified.

a = np.argwhere(image == [0,255,0])

# returns array a with three columns,
# since you need only the coordinates, you can delete the third column:
a = np.delete(a, 2, 1)  # delete third column in a

a would return following sample:

array([[  0,  18],
       [  0,  19],
       [  0,  21],
       ...,
       [539, 675],
       [539, 677],
       [539, 677]], dtype=int64)

The above returns coordinates where the green pixel values [0, 255, 0] are present.

Based on the returned coordinates, you can filter the ones closer to the bottom of the image.

def get_last_pixel(image, rgb):
    color_rows, color_cols = np.where((image[:, :, 0] == rgb[0]) & (image[:, :, 1] == rgb[1]) & (
        image[:, :, 2] == rgb[2]))  # Get coordinates of pixels wit the requested rgb value
    if len(color_rows) == 0:
        raise Exception("no pixels of the requested color")
    row_max = np.argmax(color_rows)  # Find the index of the biggest row
    pixel_coords = (color_rows[row_max], color_cols[row_max])
    return pixel_coords


example_input = cv2.imread("example_input.png")
last_green = get_last_pixel(example_input, (0, 255, 0))
example_output = cv2.circle(
    example_input, last_green[::-1], radius=5, color=(0, 0, 255), thickness=1)
cv2.imwrite("example_output.png", example_output)

Example input: enter image description here

Example output: enter image description here

Related