Originally I had intended on doing a followup post on my Getting Started with Deep Learning Guide, but due to some unfortunate personal events, I wasn’t able to complete the blog post. But don’t worry…I still have a really great tutorial for you today!
Bad tutorials are worse than warm beer.
You want to know why?
Because you can always chug a warm beer down in a few seconds…
But you can spend hours going down the wrong path when reading a misguiding tutorial.
And that’s my inspiration for this post — I really hate all the blog posts I’ve seen that detail how to find the brightest spot of an image using OpenCV.
You see, they are leaving out a single line of code that is absolutely crucial to being more robust to noise.
Now, if you’re like me, that doesn’t sit well.
So sit back. Relax. And know that you’re about to read a good tutorial. You won’t spend hours wasting your time here.
In this blog post I’ll show you how to find the brightest spot in an image using Python and OpenCV…and I’ll show you the single line of pre-processing code you’ll need to improve your results.
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+.
You Need More than
A few weeks ago a PyImageSearch reader wrote in and asked about the best way to find the brightest spot in the image.
You see, they were working with retinal images (see the top of this post for an example). These images are normally orange or yellowish in color, circular, and contain important physical structures of the eye, including the optic nerve and the macula.
This reader wanted to know the best way to find the optic nerve center, which is normally the brightest spot of the retinal image.
To find the brightest spot of the image using Python and OpenCV, you would utilize the cv2.minMaxLoc function.
However, the term “spot” here is a little misleading.
The “brightest spot” of the image according to cv2.minMaxLoc actually isn’t a region — it’s simply the brightest single pixel in the entire image.
This means that the cv2.minMaxLoc function is extremely susceptible to noise if you don’t take the necessary pre-cautionary steps. A single bright pixel in an area where there wouldn’t normally be a bright pixel (in this case, an area other than the optic nerve center) can dramatically throw off your results.
Instead, you are better off examining regions of the image, rather than a single pixel. When you examine regions you let the average balance everything out — and you’re less susceptible to noise.
So how do you mimic this “region” effect without explicitly examining each and every region of the image?
I’ll show you.
The rest of this blog post is dedicated to showing you how to find the brightest spot of an image using Python and OpenCV
Finding the Brightest Spot in an Image using Python and OpenCV
Let’s go ahead and get started.
Open up your favorite editor, create a new file named bright.py , and let’s get started.
# import the necessary packages
import numpy as np
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", help = "path to the image file")
ap.add_argument("-r", "--radius", type = int,
help = "radius of Gaussian blur; must be odd")
args = vars(ap.parse_args())
# load the image and convert it to grayscale
image = cv2.imread(args["image"])
orig = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
On Lines 2-4 we’ll import the packages we’ll need. If you’re a regular PyImageSearch reader, these packages should feel like old hat to you now. We’ll use NumPy for numerical processing, argparse to parse command line arguments, and cv2 for our OpenCV bindings.
From there, we’ll parse our command line arguments on Lines 7-11. Nothing too special here.
The first switch, --image , is the path to the image we are going to find the brightest spot in. The second switch --radius , is an integer indicating the radius of the Gaussian blur we are going to apply to the image.
It’s important to note that this radius value must be odd and not even.
Next up, lets go ahead and load the image on Line 14, make a clone of it on Line 15, and convert it to grayscale on Line 15.
The Susceptible Method:
Now, let’s go ahead and apply the suspectible method method to detecting the brightest spot in the image:
# perform a naive attempt to find the (x, y) coordinates of
# the area of the image with the largest intensity value
(minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(gray)
cv2.circle(image, maxLoc, 5, (255, 0, 0), 2)
# display the results of the naive attempt
The susceptible method to finding the brightest spot in an image is to use the cv2.minMaxLoc function without any pre-processing. This function requires a single argument, which is our grayscale image. Then, this function takes our grayscale image and finds the value and (x, y) location of the pixel with the smallest and largest intensity values, respectively.
To break it down: minVal contains the smallest pixel intensity value, maxVal contains the largest pixel intensity value, minLoc specifies the (x, y) coordinates of minVal, and maxLoc specifies the (x, y) coordinates of maxLoc .
In this application, we’re only concerned with the pixel with the largest value, so we’ll grab that and draw a circle around the region on Line 20 and display it on Line 24.
The biggest problem with this method, and why I call it the susceptible method to find the brightest spot in an image, is that it’s extremely susceptible to noise.
A single pixel could dramatically throw off your results…
So how do we fix this?
We’ll apply a Gaussian blurring through a pre-processing step.
The Slightly More Robust Method:
Let’s go ahead and check out how to do this:
# apply a Gaussian blur to the image then find the brightest
gray = cv2.GaussianBlur(gray, (args["radius"], args["radius"]), 0)
(minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(gray)
image = orig.copy()
cv2.circle(image, maxLoc, args["radius"], (255, 0, 0), 2)
# display the results of our newly improved method
Like I mentioned above, using cv2.minMaxLoc without any pre-processing can leave you extremely susceptible to noise.
Instead, it’s better to first apply a Gaussian blur to the image to remove high frequency noise. This way, even pixels that have very large values (again, due to noise) will be averaged out by their neighbors.
On Line 28 we apply our Gaussian blur with the supplied radius from our command line argument.
Then we once again make a call to cv2.minMaxLoc to find the brightest pixel in the image.
However, since we have applied a blurring pre-processing step, we’ve averaged all pixels together with the supplied radius of each other. Doing this allows us to remove high frequency noise and leaves cv2.minMaxLoc substantially less susceptible.
By using a larger radius we’ll be averaging over a larger neighborhood of pixels — thus mimicking larger regions of the image.
And by using a smaller radius we can average over smaller regions.
Determining the correct radius will heavily dependent on the application you are developing and the task you are trying to solve.
Fire up a terminal and execute the following command:
$ python bright.py --image retina.png --radius 41
If all goes well, you should see the following image:
On the left we have the susceptible method using cv2.minMaxLoc without any pre-processing and on the right we have the robust method.
But you’re probably looking at these images and saying, “Hey Adrian, they both detected the same region of the image!”
You’re right. They did.
But watch what happens when I add a single bright pixel to the top-center part of this image when you issue this command:
$ python bright.py --image images/retina-noise.png --radius 41
Your results should look something like:
Now, the naive cv2.minMaxLoc method finds this white pixel. Let’s be clear. The function is working correctly. It is indeed finding the single brightest pixel in the entire image.
However, this really isn’t what we want.
We are interested in the brightest region of the image, which is the optic nerve center.
Luckily, by utilizing a Gaussian blur, we are able to average a neighborhood of pixels within a given radius, and thus discard the single bright pixel and narrow in on the optic center region without an issue.
Obviously, a big aspect of getting the robust method to work correctly is properly setting your radius size. If your radius size is too small, you won’t be able to find larger, brighter regions of the image.
But if you set the radius size too large, then you’ll be detecting too large of regions, missing out on the smaller ones, leading to sub-par results.
Definitely spend some time playing with the radius size and viewing the results.
In this blog post I showed you why it is critical to apply Gaussian blurring prior to finding the brightest spot in an image.
By applying a Gaussian blur, you are averaging the pixels within a given radius of each other together. Taking the average allows you to remove high frequency noise that would have otherwise thrown off the results of the cv2.minMaxLoc function.
Be sure to explore appropriate values for the radius of Gaussian blur. If you take too small of a value, you’ll mitigate the effects of the average and miss out on the larger, brighter regions. But if your radius is too large, you won’t detect the small bright regions.