I am trying to make a program where I detect red. However sometimes it is darker than usual so I can't just use one value. What is a good range for detecting different shades of red? I am currently using the range 128, 0, 0 - 255, 60, 60 but sometimes it doesn't even detect a red object I put in front of it.
4 Answers
RGB
is not a good color space for specific color detection. HSV
will be a good choice.
For RED, you can choose the HSV range (0,50,20) ~ (5,255,255)
and (175,50,20)~(180,255,255)
using the following colormap. Of course, the RED range
is not that precise, but it is just ok.
The code taken from my another answer: Detect whether a pixel is red or not
#!/usr/bin/python3
# 2018.07.08 10:39:15 CST
# 2018.07.08 11:09:44 CST
import cv2
import numpy as np
## Read and merge
img = cv2.imread("ColorChecker.png")
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
## Gen lower mask (0-5) and upper mask (175-180) of RED
mask1 = cv2.inRange(img_hsv, (0,50,20), (5,255,255))
mask2 = cv2.inRange(img_hsv, (175,50,20), (180,255,255))
## Merge the mask and crop the red regions
mask = cv2.bitwise_or(mask1, mask2 )
croped = cv2.bitwise_and(img, img, mask=mask)
## Display
cv2.imshow("mask", mask)
cv2.imshow("croped", croped)
cv2.waitKey()
Related answers:
Of course, for the specific question, maybe other color space is also OK.

- 17,681
- 4
- 60
- 74
-
Why does converting BGR to HSV appear to change the color? Why does it seem to match the HSV value based on the original image? – James T. Jun 14 '21 at 03:08
-
This is similar to an answer from opencv https://docs.opencv.org/master/df/d9d/tutorial_py_colorspaces.html They also suggest using opencv to convert from RGB to HSV and using hue +/- 10 and a range of [50, 255] for saturation and value it seems. – James T. Jun 14 '21 at 03:14
-
Why do all other color pickers use a degree value in range [0, 360] for hue while you're using a scalar of [0, 180]? For those glancing over the answer, a little nuance is all color pickers use a (percent) value [0, 100]% for saturation and value. – James T. Jun 14 '21 at 03:18
You could check that the red component is the maximum and others are both clearly lower:
def red(r, g, b):
threshold = max(r, g, b)
return (
threshold > 8 # stay away from black
and r == threshold # red is biggest component
and g < threshold*0.5 # green is much smaller
and b < threshold*0.5 # so is b
)
This can be implemented very efficiently using numpy.
The "right way" would be doing a full conversion to HSV and check there, but it's going to be slower and somewhat trickier (hue is an angle so you cannot just take the absolute value of the difference, moreover colors like (255, 254, 254) are going to be qualified as "red" even if they're considered white for a human).
Note also that human visual system tends to compensate for average, so something could be seen as "blue" even if indeed the biggest component is red, but everything in the image is red, so that "doesn't count" for our brain.
In the image below if you ask a human what color is the part in the circle area most would say "blue" while indeed the biggest component is red:

- 112,025
- 15
- 165
- 265
Please, use HSV or HSL (hue, saturation, luminance) instead of RGB, in HSV the red color can be easily detected using the value of hue
within some threshold.

- 23,228
- 4
- 34
- 43
-
1Checking just `hue` wouldn't work, unless you think that (255, 254, 254) is a perfect "red". – 6502 Jul 08 '18 at 05:59
-
@6502 it's more red, than green or anything else. moreover, OP has problems with darker colours. – lenik Jul 08 '18 at 06:01
Red Color means Red value is higher than Blue and Green.
So you can check the differences between Red and Blue, Red and Green.
You can simply split RGB into individual channels and apply threshold like this.
b,g,r = cv2.split(img_rgb)
rg = r - g
rb = r - b
rg = np.clip(rg, 0, 255)
rb = np.clip(rb, 0, 255)
mask1 = cv2.inRange(rg, 50, 255)
mask2 = cv2.inRange(rb, 50, 255)
mask = cv2.bitwise_and(mask1, mask2)
Hope it can be a solution for your problem.
Thank you.

- 159
- 1
- 2
- 9