2

I used inRange function to detect a colorful blob in a mp4 video, which I found the idea from this stackoverflow link:

dark = (130, 140, 30) #BGR
light = (200, 190, 100) #BGR
mask = cv2.inRange(frame, dark, light)
try:
    center = get_center(mask)
    cv2.circle(canvas, center, 6, light, -1)
except (ValueError, ZeroDivisionError):
    print("Error")
    pass
cv2.imshow('mask green', mask)

and

def get_center(mask):
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
    blob = max(contours, key=lambda el: cv2.contourArea(el))
    M = cv2.moments(blob)
    center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
    return center

The problem is that this idea is not very reliable for my case; because the object I want to find its position, moves in the room and it becomes lighter or darker with changes in light intensity.

Update In the image below, all the pieces are exactly the same shape with the same color, but they become dark and light due to light and shadow.

enter image description here

Another thing I need to say is that I have to identify 4 pieces with three different colors (green, pink and blue). The image above is sample for just one of these pieces.

I also changed the color space like this with no luck:

frame1=frame.copy()
frame1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2HSV)
#mask= cv2.inRange(frame, (low_H, low_S, low_V), (high_H, high_S, high_V))
mask = cv2.inRange(frame1, (160, 30, 20), (180, 100, 80))
Shamshirsaz.Navid
  • 2,224
  • 3
  • 22
  • 36
  • can you post examples of the object around the room (try to include all of the most difficult images you want to be able to track)? – Ian Chu Feb 23 '21 at 16:56
  • thanks for your response. I attached an image. But I do not have all the possible modes because the software has to work in environments with different lights (for example, yellow or white light with different intensities) that are not even known to me. Of course, the method I sent the code works, but not always (not reliable) because the color changes due to the change of light and shadow. – Shamshirsaz.Navid Feb 23 '21 at 17:11
  • 2
    Your attempt using the HSV color space doesn't work, because you used not fitting thresholds. Try `mask = cv2.inRange(frame1, (35, 70, 0), (100, 255, 255))`. In my "HSV masking tool", I get pretty clear masks for all eight objects in the given image. Have a thorough look at OpenCV's [color conversions](https://docs.opencv.org/4.4.0/de/d25/imgproc_color_conversions.html) for proper HSV values that can be used. – HansHirse Feb 23 '21 at 21:01
  • @HansHirse Thanks I can say this works really great. But I was a little confused about how to find the right color range myself. – Shamshirsaz.Navid Feb 24 '21 at 04:22

1 Answers1

2

I asked this question 5 months ago, but at that time I did not understand the range of HSV colors. I will update, maybe it will be useful for other people later. The point is, we typically use graphical softwares such as Gimp to select the HSV color range. Graphics softwares generally has a different HSV range than OpenCV. So when we select the color range, we need a function that converts that range to understandable OpenCV numbers. link to other answer

def fixHSVRange(h, s, v):
    # Normal H,S,V: (0-360,0-100%,0-100%)
    # OpenCV H,S,V: (0-180,0-255 ,0-255)
    return (180 * h / 360, 255 * s / 100, 255 * v / 100)

Final source code:

import sys
import cv2
import numpy as np

# Find current path and load image
dir = sys.path[0]
im = cv2.imread(dir+'/green.png')
H, W = im.shape[:2]

# A function to fix HSV range
def fixHSVRange(h, s, v):
    # Normal H,S,V: (0-360,0-100%,0-100%)
    # OpenCV H,S,V: (0-180,0-255 ,0-255)
    return (180 * h / 360, 255 * s / 100, 255 * v / 100)

# Make a copy of Image; find the HSV range; convert it to OpenCV
# undrestandble range and make a mask from it
frm=im.copy()
frm = cv2.cvtColor(frm, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(frm, fixHSVRange(150, 30, 0), fixHSVRange(185, 100, 100))

# Remove the noise
noise=cv2.dilate(mask,np.ones((5,5)))
noise=cv2.erode(mask,np.ones((5,5)))
noise=cv2.medianBlur(mask,7)

# Change image channels
mask=cv2.cvtColor(mask,cv2.COLOR_GRAY2BGR)
noise=cv2.cvtColor(noise,cv2.COLOR_GRAY2BGR)
cleanMask=~noise

# Make a new mask without noise
centerMask=cv2.cvtColor(cleanMask.copy(),cv2.COLOR_BGR2GRAY)
out=im.copy()

# Find contours and sort them by width
cnts, _ = cv2.findContours(centerMask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnts.sort(key=lambda p: cv2.boundingRect(p)[2])

# Find and draw blocks
for cnt in cnts:
    x, y, w, h = cv2.boundingRect(cnt)
    if w<W:
        cv2.rectangle(out, (x, y), (x+w, y+h), (0, 255, 0), 1)
        cv2.circle(out,(x+w//2,y+h//2),radius=5,thickness=5,color=(127,0,220))
        cv2.rectangle(centerMask, (x, y), (x+w, y+h), 127, 1)

# Save the output
centerMask=cv2.cvtColor(centerMask,cv2.COLOR_GRAY2BGR)
top=np.hstack((im,mask,noise))
btm=np.hstack((cleanMask,centerMask,out))
cv2.imshow('mask green',np.vstack((top,btm)))
cv2.imwrite(dir+'/out.png',np.vstack((top,btm)))
cv2.waitKey(0)

enter image description here

Shamshirsaz.Navid
  • 2,224
  • 3
  • 22
  • 36