Related
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
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!
Instead of having to guess and check the HSV lower/upper color ranges, you can use a HSV color thresholder script to determine the ranges with trackbars. This makes it very easy to define the ranges for whatever color you're trying to segment. Just change the input image in cv2.imread. Example to segment white
import cv2
import numpy as np
def nothing(x):
pass
# Load image
image = cv2.imread('1.jpg')
# Create a window
cv2.namedWindow('image')
# Create trackbars for color change
# Hue is from 0-179 for Opencv
cv2.createTrackbar('HMin', 'image', 0, 179, nothing)
cv2.createTrackbar('SMin', 'image', 0, 255, nothing)
cv2.createTrackbar('VMin', 'image', 0, 255, nothing)
cv2.createTrackbar('HMax', 'image', 0, 179, nothing)
cv2.createTrackbar('SMax', 'image', 0, 255, nothing)
cv2.createTrackbar('VMax', 'image', 0, 255, nothing)
# Set default value for Max HSV trackbars
cv2.setTrackbarPos('HMax', 'image', 179)
cv2.setTrackbarPos('SMax', 'image', 255)
cv2.setTrackbarPos('VMax', 'image', 255)
# Initialize HSV min/max values
hMin = sMin = vMin = hMax = sMax = vMax = 0
phMin = psMin = pvMin = phMax = psMax = pvMax = 0
while(True):
# Get current positions of all trackbars
hMin = cv2.getTrackbarPos('HMin', 'image')
sMin = cv2.getTrackbarPos('SMin', 'image')
vMin = cv2.getTrackbarPos('VMin', 'image')
hMax = cv2.getTrackbarPos('HMax', 'image')
sMax = cv2.getTrackbarPos('SMax', 'image')
vMax = cv2.getTrackbarPos('VMax', 'image')
# Set minimum and maximum HSV values to display
lower = np.array([hMin, sMin, vMin])
upper = np.array([hMax, sMax, vMax])
# Convert to HSV format and color threshold
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower, upper)
result = cv2.bitwise_and(image, image, mask=mask)
# Print if there is a change in HSV value
if((phMin != hMin) | (psMin != sMin) | (pvMin != vMin) | (phMax != hMax) | (psMax != sMax) | (pvMax != vMax) ):
print("(hMin = %d , sMin = %d, vMin = %d), (hMax = %d , sMax = %d, vMax = %d)" % (hMin , sMin , vMin, hMax, sMax , vMax))
phMin = hMin
psMin = sMin
pvMin = vMin
phMax = hMax
psMax = sMax
pvMax = vMax
# Display result image
cv2.imshow('image', result)
if cv2.waitKey(10) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
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
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!
Instead of having to guess and check the HSV lower/upper color ranges, you can use a HSV color thresholder script to determine the ranges with trackbars. This makes it very easy to define the ranges for whatever color you're trying to segment. Just change the input image in cv2.imread. Example to segment white
import cv2
import numpy as np
def nothing(x):
pass
# Load image
image = cv2.imread('1.jpg')
# Create a window
cv2.namedWindow('image')
# Create trackbars for color change
# Hue is from 0-179 for Opencv
cv2.createTrackbar('HMin', 'image', 0, 179, nothing)
cv2.createTrackbar('SMin', 'image', 0, 255, nothing)
cv2.createTrackbar('VMin', 'image', 0, 255, nothing)
cv2.createTrackbar('HMax', 'image', 0, 179, nothing)
cv2.createTrackbar('SMax', 'image', 0, 255, nothing)
cv2.createTrackbar('VMax', 'image', 0, 255, nothing)
# Set default value for Max HSV trackbars
cv2.setTrackbarPos('HMax', 'image', 179)
cv2.setTrackbarPos('SMax', 'image', 255)
cv2.setTrackbarPos('VMax', 'image', 255)
# Initialize HSV min/max values
hMin = sMin = vMin = hMax = sMax = vMax = 0
phMin = psMin = pvMin = phMax = psMax = pvMax = 0
while(True):
# Get current positions of all trackbars
hMin = cv2.getTrackbarPos('HMin', 'image')
sMin = cv2.getTrackbarPos('SMin', 'image')
vMin = cv2.getTrackbarPos('VMin', 'image')
hMax = cv2.getTrackbarPos('HMax', 'image')
sMax = cv2.getTrackbarPos('SMax', 'image')
vMax = cv2.getTrackbarPos('VMax', 'image')
# Set minimum and maximum HSV values to display
lower = np.array([hMin, sMin, vMin])
upper = np.array([hMax, sMax, vMax])
# Convert to HSV format and color threshold
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower, upper)
result = cv2.bitwise_and(image, image, mask=mask)
# Print if there is a change in HSV value
if((phMin != hMin) | (psMin != sMin) | (pvMin != vMin) | (phMax != hMax) | (psMax != sMax) | (pvMax != vMax) ):
print("(hMin = %d , sMin = %d, vMin = %d), (hMax = %d , sMax = %d, vMax = %d)" % (hMin , sMin , vMin, hMax, sMax , vMax))
phMin = hMin
psMin = sMin
pvMin = vMin
phMax = hMax
psMax = sMax
pvMax = vMax
# Display result image
cv2.imshow('image', result)
if cv2.waitKey(10) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
I have a series of concentric rectangles and wish to obtain the means of the outer rectangle excluding the inner rectangle. See the attached diagram , I need to get the mean for the shaded area.
So I am using a mask of the inner rectangle to pass into the cv2.mean method, but I am not sure how to set the mask. I have the following code:
for i in xrange(0,len(wins)-2,1):
means_1 = cv2.mean(wins[i])[0]
msk = cv2.bitwise_and(np.ones_like((wins[i+1]), np.uint8),np.zeros_like((wins[i]), np.uint8))
means_2 = cv2.mean(wins[i+1],mask=msk)
means_3 = cv2.mean(wins[i+1])[0]
print means_1,means_2,means_3
I get this error for the means_2 (means_3 works fine).:
error:
/Users/jenkins/miniconda/0/2.7/conda-bld/work/opencv-2.4.11/modules/core/src/arithm.cpp:1021:
error: (-209) The operation is neither 'array op array' (where arrays
have the same size and type), nor 'array op scalar', nor 'scalar op
array' in function binary_op
The mask here refers to a binary mask which has 0 as background and 255 as foreground, So You need to create an empty mask with default color = 0 and then paint the Region of Interest where you want to find the mean with 255. Suppose I have input image [512 x 512]:
Lets's assume 2 concentric rectangles as:
outer_rect = [100, 100, 400, 400] # top, left, bottom, right
inner_rect = [200, 200, 300, 300]
Now create the binary mask using these rectangles as:
mask = np.zeros(image.shape[:2], dtype=np.uint8)
cv2.rectangle(mask, (outer_rect[0], outer_rect[1]), (outer_rect[2], outer_rect[3]), 255, -1)
cv2.rectangle(mask, (inner_rect[0], inner_rect[1]), (inner_rect[2], inner_rect[3]), 0, -1)
Now you may call the cv2.mean() to get the mean of foreground area, labelled with 255 as:
lena_mean = cv2.mean(image, mask)
>>> (109.98813432835821, 96.60768656716418, 173.57567164179105, 0.0)
In Python/OpenCV or any software, if you have a masked image and the binary mask, then the mean of the non-black pixels in the image (i.e. ROI) is the mean of the masked image divided by the mean of the mask
Input:
Mask:
import cv2
import numpy as np
# load image
img = cv2.imread('lena_g.png', cv2.IMREAD_GRAYSCALE)
# load mask
mask = cv2.imread('lena_mask.png', cv2.IMREAD_GRAYSCALE)
# compute means
mean_img = np.mean(img)
mean_mask = np.mean(mask)
# compute 255*mean_img/mean_mask
mean_roi = 255 * mean_img / mean_mask
# print mean of each
print("mean of image:", mean_img)
print("mean of mask:", mean_mask)
print("mean of roi:", mean_roi)
mean of image: 98.50196838378906
mean of mask: 216.090087890625
mean of roi: 116.23856597522328
I have an image from which I extract a colour into a mask as shown in the code below. The mask gives a black and white image. White being the colour I detect. The pixel value of white is 255 and black is 0.
I want to get the bottommost x and Y pixel of the white portion of the mask. How do I do this?
My code is as follows:
image = cv2.imread(FILENAME)
# THE COLOURS ARE IN RGB
lower_blue = np.array([50, 0, 0])
upper_blue = np.array([255, 50, 50])
# loop over the boundaries
# for (lower, upper) in boundaries:
# create NumPy arrays from the boundaries
lower = np.array(lower_blue, dtype = "uint8")
upper = np.array(upper_blue, dtype = "uint8")
# find the colors within the specified boundaries and apply
# the mask
mask = cv2.inRange(image, lower, upper)
you can use numpy's where to search your mask for a specific value:
np.max(np.where(np.max(img_binary,axis=1)==255)
I want to merge 2 one-channel, gray-scale images with OpenCv merge method. It is the code below:
...
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
zeros = numpy.zeros(img_gray.shape)
merged = cv2.merge([img_gray, zeros])
...
The problem is that gray-scale image doesn't have depth attribute that should be 1 and merge function require the same size of images and the same depth. I get error:
error: /build/buildd/opencv-2.4.8+dfsg1/modules/core/src/convert.cpp:296: error: (-215) mv[i].size == mv[0].size && mv[i].depth() == depth in function merge
How can i merge this arrays?
Solved, i had to change dtype of img_gray from uint8 to float64
img_gray = numpy.float64(img_gray)
OpenCV Version 2.4.11
import numpy as np
# Load the image
img1 = cv2.imread(paths[0], cv2.IMREAD_UNCHANGED)
# could also use cv2.split() but per the docs (link below) it's time consuming
# split the channels using Numpy indexing, notice it's a zero based index unlike MATLAB
b = img1[:, :, 0]
g = img1[:, :, 1]
r = img1[:, :, 2]
# to avoid overflows and truncation in turn, clip the image in [0.0, 1.0] inclusive range
b = b.astype(np.float)
b /= 255
manipulate the channels ... in my case, adding Gaussian noise to blue channel ( b => b1 )
b1 = b1.astype(np.float)
g = g.astype(np.float)
r = r.astype(np.float)
# gotcha : notice the parameter is an array of channels
noisy_blue = cv2.merge((b1, g, r))
# store the outcome to disk
cv2.imwrite('output/NoisyBlue.png', noisy_blue)
N.B.:
Alternatively, you may also use np.double instead np.float in astype for type casting
Open CV Documentation Link