python opencv color tracking

Below is my python code for tracking white color objects.
It works - but only for a few seconds and then the whole screen turns black and in some times it not work.
I experimented with blue color and it works - but white and green are giving me problems:

import cv2
import numpy as np

cap = cv2.VideoCapture(0)

while(1):

_, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

# define range of white color in HSV
# change it according to your need !
sensitivity = 15
lower_white = np.array([0,0,255-sensitivity])
upper_white = np.array([255,sensitivity,255])

# Threshold the HSV image to get only white colors
mask = cv2.inRange(hsv, lower_white, upper_white)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(frame,frame, mask= mask)

cv2.imshow('frame',frame)
cv2.imshow('mask',mask)
cv2.imshow('res',res)

k = cv2.waitKey(5) & 0xFF
if k == 27:
break

cv2.destroyAllWindows()

Well, first thing you should know what color space you are using. Just a small tutorial of color spaces in OpenCV for Mat of type CV_8UC3. (Images from Wikipedia)

HSV

enter image description here

In the HSV (Hue, Saturation, Value) color space, H gives the color dominant color, S the saturation of the color, V the lightness. In OpenCV, the ranges are different. S,V are in [0,255], while H is in [0, 180]. Typically H is in range [0,360] (the full circle), but to fit in a byte (256 different values) it's value is halved.

In HSV space is easier to separate a single color, since you can simply set the proper range for H, and just take care that S is not too small (it will be almost white), and V is not too small (it will be dark).

So for example, if you need almost blue colors, you need H to be around the value 120 (say in [110,130]), and S,V not too small (say in [100,255]).

White is not a hue (the rainbow doesn't have white color in it), but is a combination of color.

In HSV, you need to take all range of H (H in [0, 180]), very small S values (say S in [0, 25]), and very high V values (say V in [230, 255]). This basically corresponds to the upper part of the central axis of the cone.


So to make it track white objects in HSV space, you need:

lower_white = np.array([0, 0, 230])
upper_white = np.array([180, 25, 255])

Or, since you defined a sensitivity value, like:

sensitivity = 15
lower_white = np.array([0, 0, 255-sensitivity])
upper_white = np.array([180, sensitivity, 255])

For other colors:

green = 60;
blue = 120;
yellow = 30;
...
sensitivity = 15

// Change color with your actual color
lower_color = np.array([color - sensitivity, 100, 100]) 
upper_color = np.array([color + sensitivity, 255, 255])

Red H value is 0, so you need to take two ranges and "OR" them together:

sensitivity = 15
lower_red_0 = np.array([0, 100, 100]) 
upper_red_0 = np.array([sensitivity, 255, 255])
lower_red_1 = np.array([180 - sensitivity, 100, 100]) 
upper_red_1 = np.array([180, 255, 255])

mask_0 = cv2.inRange(hsv, lower_red_0 , upper_red_0);
mask_1 = cv2.inRange(hsv, lower_red_1 , upper_red_1 );

mask = cv2.bitwise_or(mask1, mask2)

Now you should be able to track any color!