So, here I am. Riding the Amtrak 158 train, coming home after a long business trip.

It’s hot. The AC is barely working. A baby is screaming right next to me while the accompanying mother looks forlornly out the window, clearly questioning whether or not having a child was the right life decision.

And to top it all off, the Wi-Fi doesn’t work.

Luckily, I brought along my Game Boy and collection of Pokemon games.

As I slid my trusty Blue version into my Game Boy, I thought to myself, instead of battling Gary Oak for the thousandth time, maybe I can do a little computer vision.

Honestly, wouldn’t it be really cool to be able to segment each of the game cartridges using nothing but color?

Grab yourself a nice cool glass of water to combat the failed AC and a pair of ear plugs to block out the wailing child. Because in this post I’ll show you how to use OpenCV and Python to perform color detection.

Looking for the source code to this post? Jump Right To The Downloads Section

OpenCV and Python versions:

This example will run on Python 2.7/Python 3.4+ and OpenCV 2.4.X/OpenCV 3.0+.

OpenCV and Python Color Detection

Let’s go ahead and get this started.

Open up your favorite editor and create a file named detect_color.py :

# import the necessary packages import numpy as np import argparse import cv2 # construct the argument parse and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-i", "--image", help = "path to the image") args = vars(ap.parse_args()) # load the image image = cv2.imread(args["image"])

We’ll start by importing our necessary packages on Lines 2-4. We’ll use NumPy for numerical processing, argparse to parse our command line arguments, and cv2 for our OpenCV bindings.

Lines 7-9 then handle parsing our command line arguments. We’ll need just a single switch, --image , which is the path to where our image resides on disk.

Then, on Line 12, we load our image off disk.

Now, here comes the interesting part.

We want to be able to detect each of the Game Boy cartridges in the image. That means we’ll have to recognize red, blue, yellow, and gray colors in the image.

Let’s go ahead and define this list of colors:

# define the list of boundaries boundaries = [ ([17, 15, 100], [50, 56, 200]), ([86, 31, 4], [220, 88, 50]), ([25, 146, 190], [62, 174, 250]), ([103, 86, 65], [145, 133, 128]) ]

All we are doing here is defining a list of boundaries in the RGB color space (or rather, BGR, since OpenCV represents images as NumPy arrays in reverse order), where each entry in the list is a tuple with two values: a list of lower limits and a list of upper limits.

For example, let’s take a look at the tuple ([17, 15, 100], [50, 56, 200]) .

Here, we are saying that all pixels in our image that have a R >= 100, B >= 15, and G >= 17 along with R <= 200, B <= 56, and G <= 50 will be considered red.

Now that we have our list of boundaries, we can use the cv2.inRange function to perform the actual color detection.

Let’s take a look:

# loop over the boundaries for (lower, upper) in boundaries: # create NumPy arrays from the boundaries lower = np.array(lower, dtype = "uint8") upper = np.array(upper, dtype = "uint8") # find the colors within the specified boundaries and apply # the mask mask = cv2.inRange(image, lower, upper) output = cv2.bitwise_and(image, image, mask = mask) # show the images cv2.imshow("images", np.hstack([image, output])) cv2.waitKey(0)

We start looping over our upper and lower boundaries on Line 23, then convert the upper and lower limits to NumPy arrays on Line 25 and 26. These two lines seem like they can be omitted, but when you are working with OpenCV Python bindings, OpenCV expects these limits to be NumPy arrays. Furthermore, since these are pixel values that fall within the range [0, 256] we can use the unsigned 8-bit integer data type.

To perform the actual color detection using OpenCV, take a look at Line 29 where we use the cv2.inRange function.

The cv2.inRange function expects three arguments: the first is the image were we are going to perform color detection, the second is the lower limit of the color you want to detect, and the third argument is the upper limit of the color you want to detect.

After calling cv2.inRange , a binary mask is returned, where white pixels (255) represent pixels that fall into the upper and lower limit range and black pixels (0) do not.

Note: We are performing color detection in the RGB color space. But you can easily do this in the HSV or L*a*b* color space as well. You would simply need to adjust your upper and lower limits to the respective color space.

To create the output image, we apply our mask on Line 31. This line simply makes a call to cv2.bitwise_and , showing only pixels in the image that have a corresponding white (255) value in the mask .

Finally, our output images are displayed on Lines 34 and 35.

Not bad. Only 35 lines of code, and the vast majority is imports, argument parsing, and comments.

Let’s go ahead and run our script:

$ python detect_color.py --image pokemon_games.png

If your environment is configured correctly (meaning you have OpenCV with Python bindings installed), you should see this as your output image:

As you can see, the Red Pokemon cartridge is easily detected!

Now let’s try the blue one:

Nope, no problem there!

And a similar story for Yellow version:

Lastly, the outline of the gray Game Boy cartridge is also found:

Summary

In this blog post I showed you how to perform color detection using OpenCV and Python.

To detect colors in images, the first thing you need to do is define the upper and lower limits for your pixel values.

Once you have defined your upper and lower limits, you then make a call to the cv2.inRange method which returns a mask, specifying which pixels fall into your specified upper and lower range.

Finally, now that you have the mask, you can apply it to your image using the cv2.bitwise_and function.

My train is just a few stops away from home, so I better wrap this post up. I hope you found it useful!

And if you have any questions, as always, feel free to leave a comment or shoot me a message.

