Shamshirsaz.Navid
Shamshirsaz.Navid

Reputation: 2362

Reliable way to detect the center of color blob in different amount of light?

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))

Upvotes: 2

Views: 658

Answers (1)

Shamshirsaz.Navid
Shamshirsaz.Navid

Reputation: 2362

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

Upvotes: 3

Related Questions