# FIRST import all the necessary libraries and modules!
import cv2 # import OpenCV
import numpy as np # import NumPy
# import instructor made functions
import sys
sys.path.insert(0, '../..')
from utils import *
Let's learn how to paint on live video using colored objects!
To do this, we will need to combine everything we've learned so far: drawing, color tracking, and contours!
HSV tends to be better for tracking an object by color than BGR.
Do you remember why this is? Discuss with your partner!
We can mask video frames using lower and upper bounds for hue (H), saturation (S), and value (V) to track objects by their color.
First, we need to detect the colored objects we will be drawing with!
Here is an example of a GOOD MASK:
Here is an example of a BAD MASK:
Exercise:
hsv_select_live()
to select HSV upper/lower bounds so that only the object's color is visible. Everything else should be MASKED/BLACK. hsv_select_live()
Exercise:
Let's create a function that returns a masked frame using input HSV values:
cv2.cvtColor
cv2.inRange
to create a mask using the given inputs
def mask_frame(frame, hsv_lower, hsv_upper):
# TASK #1: Convert "frame" to from BGR to HSV using cv2.cvtColor
# TASK #2: Mask the frame using cv2.inRange, with "hsv_lower" and "hsv_upper" as inputs
# Assume hsv_lower and hsv_upper are the correct data types!
# TASK #3: Return the mask
Exercise:
Let's test our mask on a colored object!
Set hsv_lower
and hsv_upper
using the values you recorded for one of your colored objects.
# TASK: Define the color ranges for your object
hsv_lower = (None, None, None) # Replace NONE with integers!
hsv_upper = (None, None, None) # Replace NONE with integers!
Run the code below to see your mask work in real time!
It should look something like this:
def show_mask(frame):
mask = mask_frame(frame, hsv_lower, hsv_upper) # calculates the mask
cv2.imshow('Mask', mask) # displays the mask
video(show_mask)
We can track the location of your colored object using contours.
We can calculate a list of contours based on a given mask like this:
contours = cv2.findContours(mask, 3, 2)[0]
Exercise:
In the function below, find and return the list of all contours.
def find_contours(mask):
# TASK #1: Find all contours
# TASK #2: Return the list of contours
Run the code block below to see your contours drawn on the frame!
It should look similar to this:
def show_contours(frame):
mask = mask_frame(frame, hsv_lower, hsv_upper) # calculates the mask
contours = find_contours(mask) # finds the contours
cv2.drawContours(frame, contours, -1, (0, 255, 0), 3) # draw contours over the frame
cv2.imshow('Contours', frame)
video(show_contours)
As you can see, there are extra contours :(
We just want ONE contour for our colored object!
Let's only consider contours with area larger than 1000
pixels.
We can find the area of a single contour like this:
area = cv2.contourArea(contour)
Exercise:
Fill in the function below to return only contours larger than 1000 pixels:
1000
to be our object contour.None
.def object_contour(mask):
contours = find_contours(mask) # finds and saves all the contours
obj_contour = None # keeps track of object contour (if unchanged, function will return None)
max_area = 0 # keeps track of maximum area
if len(contours) > 0:
for contour in contours:
# TASK #1: Find and save the area using cv2.contourArea
# TASK #2: If area is larger than 1000 AND area is larger than max_area,
# save the current contour in obj_contour, and update max_area
# TASK #3: Return the object contour
Run the code below to display this contour!
It should look similar to this:
def show_object_contour(frame):
mask = mask_frame(frame, hsv_lower, hsv_upper) # calculates the mask
obj_contour = object_contour(mask) # finds the object contour
if obj_contour is not None: # if the contour exists,
cv2.drawContours(frame, [obj_contour], -1, (0, 255, 0), 3) # draws the contour
cv2.imshow('Object Contour', frame)
video(show_object_contour)
The instructor-made functions find_center(<contour>)
and find_radius(<contour>)
find the center coordinates and radius of the smallest circle that fits around our object.
Exercise:
Using these functions, draw a circle around the object.
Choose the color and thickness of the circle yourself!
def draw_circle(frame, contour):
# TASK #1: Find and save the center using find_center
# TASK #2: Find and save the radius using find_radius
# TASK #3: Draw a circle (on the frame) that encloses the object using cv2.circle
Run the code below to see your circle drawn on the frame in real time!
It should look like this:
def display_circle(frame):
mask = mask_frame(frame, hsv_lower, hsv_upper) # calculates the mask
contour = object_contour(mask) # find the object contour
if contour is not None: # if the contour exists
draw_circle(frame, contour) # draws circle around object
cv2.imshow('Circle Tracking', frame)
video(display_circle)
In the draw
function below, we will:
Exercise:
Fill in the draw
function below to draw with your object!
def draw(frame, contour, color, prev_pos):
prev_x = prev_pos[0] # gets px from prev_pos
prev_y = prev_pos[1] # gets py from prev_pos
drawing = prev_pos != [-1, -1] # True if we are already drawing
if contour is not None:
draw_circle(frame, contour) # displays circle around object
# TASK #1: Find and save the center coordinates using find_center
# TASK #2: The center is a tuple of the form (x, y)
# Use indexing to find and save x and y
# TASK #3: If we are already drawing (use an if statement)
# TASK #4: Append to lines list a tuple of: (previous coordinates, current coordinates, color)
# Hint: Use (prev_x,prev_y) for previous coords, (x,y) for current coords, and use the input color
# Hint: Assume "lines" is a given list
# TASK #5: Update prev_x and prev_y
# TASK #6: If we are not drawing (use an elif or else statement)
# TASK #7: Change the drawing variable
# Hint: If we see the object, should we be drawing or not drawing?
# TASK #8: Update prev_x and prev_y
else:
# TASK #9: Change the drawing variable
# Hint: if we no longer see the object, should we be drawing or not drawing?
# updates prev_pos based on drawing variable, prev_x, and prev_y
if drawing == False:
prev_pos[0] = -1
prev_pos[1] = -1
else:
prev_pos[0] = prev_x
prev_pos[1] = prev_y
Exercise:
In the function below, call draw
on a color of your choice!
Then draw the lines saved in lines
onto the frame.
lines = [] # stores the lines
prev_pos = [-1, -1] # stores previous position (starts in not already drawing state)
def show_draw(frame):
mask = mask_frame(frame, hsv_lower, hsv_upper) # calculates the mask
contour = object_contour(mask) # finds the object contour
# TASK #1: Choose and save a color of your choice
draw(frame, contour, color, prev_pos) # calls draw using chosen color
for line in lines:
# TASK #2: Draw each line using the (previous position, current position, and color) we stored in "line"
# Choose the line thickness yourself! Use cv2.line and the line list.
cv2.imshow('Draw', frame)
Run the code below. You should be able to draw on top of the video with your object!
It should look something like this:
lines = [] # reset lines list
video(show_draw)
Exercise:
To draw with multiple objects, we need to:
prev_pos
# TASK #1: Replace <color1> with name of first color
<color1>_prev_pos = [-1, -1]
# TASK #2: Replace <h_min>, <s_min>, etc. with your values
<color1>_hsv_lower = np.array([<h_lower>, <s_lower>, <v_lower>])
<color1>_hsv_upper = np.array([<h_upper>, <s_upper>, <v_upper>])
# TASK #3: Repeat TASKS #1 and #2 with EACH COLOR you are using
Exercise:
Now we need to update show_draw
to include multiple colors.
def show_draw(frame):
# TASK #1: Replace <color1> with your first color.
<color1>_color =
<color1>_mask = mask_frame(frame, <color1>_hsv_lower, <color1>_hsv_upper) # calculates the mask
<color1>_contour = object_contour(<color1>_mask) # finds the object contour
draw(frame, <color1>_contour, <color1>_color, <color1>_prev_pos) # calls draw for this color
# TASK #2: Repeat TASK #1 with EACH COLOR you are using
for line in lines:
# TASK #3: Draw each line using the (previous position, current position, and color) we stored in "line"
# Choose the line thickness yourself! Use cv2.line and the line list.
cv2.imshow('Multiple Draw', frame)
Run the code below. Each object should now draw a different color!
It should look something like this:
lines = [] # reset lines list
video(show_draw)