When we hear the word ‘threshold’, what is the first thing that comes to our mind? It is that limit or limit of magnitude that must be exceeded for a certain reaction to happen, right? It’s the same in image processing. For each pixel in image, if pixel value is greater than the threshold, convert that pixel to white, else convert it to black. Likewise, when using range of values as threshold, if a given pixel falls under a given range, make it white else it is background, make it black.
In the above example, we can see that almost all surface area of multi-meter has white pixels while the other colors have black pixels. For this example, we are choosing Range of HSV(Hue-Saturation-Value) values such that only red color with a given intensity of color falls under range and background outside of the range.
How to find the perfect range for an object?
In order to find the perfect range for any object, we need to understand a little about HSV color model first.
- H (or Hue) is the color portion of the model. Or, we can think of it as 360° color wheel where 0° corresponds to the red, 120° corresponds to the green and 240° to the blue. However, in OpenCV, this range is compressed over 0 to 180 to fit into 8-bit. For example, red color is represented by 0-30 as well as 150-180 values.
- S (or Saturation) describes the amount of gray in a particular color, from 0 to 255. Lower value of S means more gray or faded color.
- V (or Value) works in conjunction with saturation and describes the brightness or intensity of the color. It has range 0-255 in OpenCV.
Calibrating HSV values
To calibrate the range of HSV value, we are going to use OpenCVs track bars. Lets see how to do it.
import cv2 as cv
import numpy as np
# Do nothing callback function
def do_nothing(*args, **kwargs):
# Create window named Trackbars to hold multiple trackbars.
cv.createTrackbar('Low H', 'Trackbars' , 0, 255, do_nothing)
cv.createTrackbar('High H', 'Trackbars' , 14, 255, do_nothing)
cv.createTrackbar('Low S', 'Trackbars' , 124, 255, do_nothing)
cv.createTrackbar('High S', 'Trackbars' , 255, 255, do_nothing)
cv.createTrackbar('Low V', 'Trackbars' , 152, 255, do_nothing)
cv.createTrackbar('High V', 'Trackbars' , 255, 255, do_nothing)
Here in Line No 10, statement
cv.namedWindow('Trackbars') creates and opens a window named
Trackbars for us. we will use this window to place track bars. Here we can change the value of track bar by sliding slider over track bar. These track bars are created using function
From Line no 11-16 we are creating different track bars with range 0-179 for H and 0-255 for S and V. Third argument in
createTrackbar is initial value of slider . And last argument,
createTrackbar is callback function. This function is called when we change the position of slider over track bar. But for now we don’t use this function and we can define this function as do nothing function. After completion of code(not yet) track bars looks like this.
# Function to return current position of trackbars
low_H = cv.getTrackbarPos('Low H','Trackbars')
high_H = cv.getTrackbarPos('High H','Trackbars')
low_S = cv.getTrackbarPos('Low S','Trackbars')
high_S = cv.getTrackbarPos('High S','Trackbars')
low_V = cv.getTrackbarPos('Low V','Trackbars')
high_V = cv.getTrackbarPos('High V','Trackbars')
return low_H,high_H, low_S, high_S, low_V, high_V
To get current values from track bars, we will use the
get_trackbar_pos function. Now it’s time to read frames from camera and apply threshold operation.
Applying Threshold Operation
cap = cv.VideoCapture(0)
# Loop over frames
frame, ret = cap.read()
if frame is None:
# Lets do some image pre-processing
blurred_frame = cv.GaussianBlur(frame, (11, 11), 0)
frame_HSV = cv.cvtColor(blurred_frame, cv.COLOR_BGR2HSV)
# Reading from trackbars
low_H,high_H, low_S, high_S, low_V, high_V,sw = get_trackbar_pos()
# Threshold image after thresholding
threshold_frame = cv.inRange(frame_HSV, (low_H, low_S, low_V), (high_H, high_S, high_V))
# More of image processing to remove noise
threshold_frame = cv.erode(threshold_frame ,None,iterations = 2)
threshold_frame = cv.dilate(threshold_frame ,None,iterations=2)
# Time for show
if key == ord('q') or key == 27:
except Exception as e:
videoCapture object in Line No 1 of this block, we create two separate window to show two images at a time.
Line no 6 starts a loop that will continue until
- we press the q key, indicating that we want to end the program.
- or, our video file reaches its end and runs out of frames.
Here in Line 11 and 12, we are doing image pre-processing. It consists of
- Blurring image to minimize noise in image.
- And, converting image to HSV color space. ( Find more on this topic in this link.)
Line No 14: Read value from track bars. We could change these values could by changing the slider position over track bars.
In Line No 17 we are applying
inRange function to get binary image of captured frame. In output binary image, output pixels are white in which input pixel values falls between lower HSV value
(low_H, low_S, low_V) and upper HSV value
(high_H, high_S, high_V) .
After some noise removal in line no 20 and 21. finally, we display images using
Note: We should always release resources and destroy opened window after we intend to end the program loop or in case of any exception.
Steps to find HSV range.
- First of all set S (Saturation) and V(Value) sliders to minimum and maximum. But for H(Hue or color), find the color range for object from above diagram or from google, divide it by 2 ( because in OpenCV it’s 0-180) and set the range in slider.
- On doing this we would have white pixels for concerned object as well as for other noise as well.
- Now narrow down the H range by moving slider for H. And select the narrowest range in such way that our object is still exist in binary image.
- After this, narrow down the S range and V range one after another to have perfect binary image of object only.
In above’s example video, I am using red ball and trying to find perfect range.
Full Source code could be found on this github link.