Reputation: 2362
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.
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
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)
Upvotes: 3