I have the follow image:
What I want to do is to keep all red figures.
Using this code..
import cv2
import numpy as np
def callback(x):
pass
cap = cv2.VideoCapture(0)
cv2.namedWindow('image')
ilowH = 0
ihighH = 179
ilowS = 0
ihighS = 255
ilowV = 0
ihighV = 255
# create trackbars for color change
cv2.createTrackbar('lowH', 'image', ilowH, 179, callback)
cv2.createTrackbar('highH', 'image', ihighH, 179, callback)
cv2.createTrackbar('lowS', 'image', ilowS, 255, callback)
cv2.createTrackbar('highS', 'image', ihighS, 255, callback)
cv2.createTrackbar('lowV', 'image', ilowV, 255, callback)
cv2.createTrackbar('highV', 'image', ihighV, 255, callback)
while True:
# grab the frame
frame = cv2.imread('color_test.png')
# get trackbar positions
ilowH = cv2.getTrackbarPos('lowH', 'image')
ihighH = cv2.getTrackbarPos('highH', 'image')
ilowS = cv2.getTrackbarPos('lowS', 'image')
ihighS = cv2.getTrackbarPos('highS', 'image')
ilowV = cv2.getTrackbarPos('lowV', 'image')
ihighV = cv2.getTrackbarPos('highV', 'image')
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower_hsv = np.array([ilowH, ilowS, ilowV])
higher_hsv = np.array([ihighH, ihighS, ihighV])
mask = cv2.inRange(hsv, lower_hsv, higher_hsv)
frame = cv2.bitwise_and(frame, frame, mask=mask)
# show thresholded image
cv2.imshow('image', frame)
k = cv2.waitKey(1) & 0xFF # large wait time to remove freezing
if k == 113 or k == 27:
break
cv2.destroyAllWindows()
cap.release()
... max I can obtain is this:
How can I get rid of yellow colour and keep the 3 red figures ? Is HSL a good alternative to use in cases like this ? Keep in mind that the center red it's not the same as the other 2; one is full red (255, 0, 0) the other are less (237, 28, 36) RGB.
I did something similar a while back and ended up defining my own basic colors in HSV space, including monochrome definitions (which I admit are a little arbitrary).
Anyway, as those in the comments stated, in HSV, the red hue is split, so I made a simple function to combine any colors I defined to easily create masks for them:
import cv2
import numpy as np
HSV_RANGES = {
# red is a major color
'red': [
{
'lower': np.array([0, 39, 64]),
'upper': np.array([20, 255, 255])
},
{
'lower': np.array([161, 39, 64]),
'upper': np.array([180, 255, 255])
}
],
# yellow is a minor color
'yellow': [
{
'lower': np.array([21, 39, 64]),
'upper': np.array([40, 255, 255])
}
],
# green is a major color
'green': [
{
'lower': np.array([41, 39, 64]),
'upper': np.array([80, 255, 255])
}
],
# cyan is a minor color
'cyan': [
{
'lower': np.array([81, 39, 64]),
'upper': np.array([100, 255, 255])
}
],
# blue is a major color
'blue': [
{
'lower': np.array([101, 39, 64]),
'upper': np.array([140, 255, 255])
}
],
# violet is a minor color
'violet': [
{
'lower': np.array([141, 39, 64]),
'upper': np.array([160, 255, 255])
}
],
# next are the monochrome ranges
# black is all H & S values, but only the lower 25% of V
'black': [
{
'lower': np.array([0, 0, 0]),
'upper': np.array([180, 255, 63])
}
],
# gray is all H values, lower 15% of S, & between 26-89% of V
'gray': [
{
'lower': np.array([0, 0, 64]),
'upper': np.array([180, 38, 228])
}
],
# white is all H values, lower 15% of S, & upper 10% of V
'white': [
{
'lower': np.array([0, 0, 229]),
'upper': np.array([180, 38, 255])
}
]
}
def create_mask(hsv_img, colors):
"""
Creates a binary mask from HSV image using given colors.
"""
# noinspection PyUnresolvedReferences
mask = np.zeros((hsv_img.shape[0], hsv_img.shape[1]), dtype=np.uint8)
for color in colors:
for color_range in HSV_RANGES[color]:
# noinspection PyUnresolvedReferences
mask += cv2.inRange(
hsv_img,
color_range['lower'],
color_range['upper']
)
return mask
Applying it to your example (which I named "color_shapes.png"), I get good results:
img = cv2.imread('color_shapes.png')
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
red_mask = create_mask(img_hsv, ['red'])
mask_img = cv2.bitwise_and(img_hsv, img_hsv, mask=red_mask)