2

I am trying to perform object detection based on the color with cv2.inRange (python 2.7). Everything seems to work fine when using BGR color. However, when I map the BGR color to HSV, I can't get a correct mask. See the example below:

1) threshold in bgr

img_test = cv2.imread("test_img/mario.jpeg")
#define color range for object detection
step = 10
r,g,b = 203, 31, 25 #red
lower_bgr = np.uint8([b-step, g-step, r-step])
upper_bgr = np.uint8([b + step, g + step, r + step])

# plot mario in BGR and corresponding mask
plt.figure(figsize=(20,10))
plt.subplot(1,2,1)
plt.imshow(cv2.cvtColor(img_test, cv2.COLOR_BGR2RGB))


mask = cv2.inRange(img_test, lower_bgr, upper_bgr)
plt.subplot(1,2,2)
plt.imshow(mask, cmap='gray')

mario_bgr

2) threshold (not working properly) in hsv

# first convert the img, and the associated lower and upper bound to HSV
hsv_img_test = cv2.cvtColor(img_test, cv2.COLOR_BGR2HSV)
lower_hsv = cv2.cvtColor(np.uint8([[[b-step,g-step,r-step]]]), cv2.COLOR_BGR2HSV)
upper_hsv = cv2.cvtColor(np.uint8([[[b+step,g+step,r+step]]]), cv2.COLOR_BGR2HSV)


plt.figure(figsize=(20,10))
plt.subplot(1,2,1)
plt.imshow(cv2.cvtColor(hsv_img_test, cv2.COLOR_BGR2RGB))

# apply threshold on hsv image
mask = cv2.inRange(hsv_img_test, lower_hsv, upper_hsv)
plt.subplot(1,2,2)
plt.imshow(mask, cmap='gray')

mario_hsv

...which is clearly not correct. I can't figure out what is wrong in the code, any help will be much appreciated!

greg hor
  • 682
  • 6
  • 17

1 Answers1

2

It seems the unexpected behavior comes from uint8 array format. I did not figure out the exact reason but you should use carefully operations with unsigned integers (ex.: 0 - 1 = 255).

Finally I think I managed to obtain the result you might be seeking:

# first convert the img to HSV
img_test_hsv = cv2.cvtColor(img_test, cv2.COLOR_BGR2HSV)

# convert the target color to HSV
target_color = np.uint8([[[b, g, r]]])
target_color_hsv = cv2.cvtColor(target_color, cv2.COLOR_BGR2HSV)

# boundaries for Hue define the proper color boundaries, saturation and values can vary a lot
target_color_h = target_color_hsv[0,0,0]
tolerance = 2
lower_hsv = np.array([max(0, target_color_h - tolerance), 10, 10])
upper_hsv = np.array([min(179, target_color_h + tolerance), 250, 250])

plt.figure(figsize=(20,10))
plt.subplot(1,2,1)
plt.imshow(cv2.cvtColor(img_test_hsv, cv2.COLOR_HSV2RGB));

# apply threshold on hsv image
mask = cv2.inRange(img_test_hsv, lower_hsv, upper_hsv)
plt.subplot(1,2,2)
plt.imshow(mask, cmap='gray');

The other point to take into consideration is the difference of topology of the the different color spaces RGB and HSV. A box in RGB space does not translate into a box in HSV coordinates. Refer to the following Wikipedia article: HSL ans HSV Color Spaces

ivankeller
  • 1,923
  • 1
  • 19
  • 20
  • This works if `target_color_h` is a signed type. To be on the save side, maybe use `max(tolerance, target_color_h) - tolerance` – Kjell Oct 02 '17 at 08:56