Reputation: 13
I need a mask to make the circle in this image stand out from the background, receiving a binary image, where white is the region of interest (the circle) and black everything else. So I can apply this mask in a video capture, where it is possible to see only the sphere. note: the background will generally be white.
I already created codes using the threshold or inRange, with a simple algorithm, that from a selection made by the user manually, marking the region of the circle, it removes the minimum and maximum rgb value, thus creating a parameter to apply in the inRange or threshold. However, as the background is usually white and clear, very similar to the color of the sphere, the binary mask includes the background, making the code a failure. Any other method for that?
import cv2
import numpy as np
ix,iy = 0,0
def selection_area(event,x,y,flags,param):
global ix,iy
global vx,vy
if event == cv2.EVENT_LBUTTONDBLCLK:
cv2.rectangle(img,(x-5,y-5),(x+5,y+5),(255,255,0),-1)
if ix!=0 and iy!=0:
cv2.rectangle(img,(x,y),(ix,iy),(255,0,0),1)
vx=[x,ix]
vy=[y,iy]
ix,iy = x,y
def analyzeRGB(cimg):
b=[];g=[];r=[];
for j in cimg:
for i in j:
b.append(i[0])
g.append(i[1])
r.append(i[2])
lower_blue= np.array([min(b),min(g),min(r)])
upper_blue= np.array([max(b),max(g),max(r)])
return lower_blue,upper_blue
cap = cv2.VideoCapture(0)
while(True):
ret, frame = cap.read()
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
img=frame
break
cap.release()
cv2.destroyAllWindows()
cv2.imshow('Analyze',img)
cv2.setMouseCallback('Analyze',selection_area)
while(1):
cv2.imshow('Analyze',img)
k = cv2.waitKey(20) & 0xFF
if k == ord('q'):
print (vx,vy)
break
cv2.destroyAllWindows()
cut = img[min(vy)+5:max(vy)-5,min(vx)+5:max(vx)-5]
cv2.imshow("Cut",cut)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(0)
filter_RGB =analyzeRGB(cut)
img = cv2.inRange(img, filter_RGB[0],filter_RGB[1])
cv2.imshow("Ready",img)
cv2.imshow("Cut",cut)
cv2.waitKey(0)
cv2.destroyAllWindows()
cap = cv2.VideoCapture(0)
while(True):
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY);
frame =cv2.inRange(frame,filter_RGB[0],filter_RGB[1])
cv2.imshow("Frame",frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
Upvotes: 1
Views: 3731
Reputation: 32104
Finding the ball is challenging because the color is close to the background, and because of the hand.
The reflections from the ball and the non-uniformity makes it more challenging.
In case you know the exact radius of the ball, you may use cv2.HoughCircles
for searching a circle with the exact radius.
My solution uses cv2.HoughCircles
, but "cleans" the image first.
There is a good change that the solution is too specific to the image you have posted, and not going to work for the general case.
The solution uses the following stages:
cv2.adaptiveThreshold
- find edges with intensity close to the background intensity.cv2.HoughCircles
for finding circles.Here is the code:
import cv2
import numpy as np
# Read input image
img = cv2.imread('ball_in_hand.png')
# Convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply median filter
gray = cv2.medianBlur(gray, 5)
# Apply adaptive threshold with gaussian size 15x15
thresh = cv2.adaptiveThreshold(gray, 255, adaptiveMethod=cv2.ADAPTIVE_THRESH_MEAN_C, thresholdType=cv2.THRESH_BINARY, blockSize=15, C=0)
# Use threshold for finding dark pixels - needs to be masked
_, dark_mask = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# Mask the dark pixels.
thresh = thresh & dark_mask
# Use "opening" morphological operation - cleaning up.
thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5)))
rows, cols = thresh.shape
# Use HoughCircles for finding circles
circles = cv2.HoughCircles(thresh, cv2.HOUGH_GRADIENT, 1, minDist=rows//8, param1=50, param2=30, minRadius=rows//8, maxRadius=rows//2)
# mask will be the desired mask (filled circle)
mask = np.zeros_like(gray)
# Iterate circles
for c in circles[0,:]:
# Draw green circle on the image for testing
cv2.circle(img, (c[0], c[1]), c[2], (0, 255, 0), 2)
# Draw filled circle for creating the mask
cv2.circle(mask, (c[0], c[1]), c[2], 255, cv2.FILLED)
# Show images for testing
cv2.imshow('img', img)
cv2.imshow('gray', gray)
cv2.imshow('thresh', thresh)
cv2.imshow('dark_mask', dark_mask)
cv2.imshow('mask', mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
Images:
Upvotes: 1