5

I'm new to opencv and for a school project i need to detect a red and a green circle with a camera, so i've use blobdetection, but it detect me the two colors, i think that my mask is bad, each color is linked to a specific action.

At the moment my code detect well red and green circle on the same page but i want it to detect only red circle on a white page.

Thank for your help

# Standard imports
import cv2
import numpy as np;

    # Read image
im = cv2.VideoCapture(0)

# Setup SimpleBlobDetector parameters.
params = cv2.SimpleBlobDetector_Params()

# Change thresholds
params.minThreshold = 100;
params.maxThreshold = 200;

# Filter by Area.
params.filterByArea = True
params.minArea = 200
params.maxArea = 20000

# Filter by Circularity
params.filterByCircularity = True
params.minCircularity = 0.1

# Filter by Convexity
params.filterByConvexity = True
params.minConvexity = 0.1

# Filter by Inertia
params.filterByInertia = True
params.minInertiaRatio = 0.1


blueLower = (0,85,170)  #100,130,50
blueUpper = (140,110,255) #200,200,130


while(1):

    ret, frame=im.read()

    mask = cv2.inRange(frame, blueLower, blueUpper)
    mask = cv2.erode(mask, None, iterations=0)
    mask = cv2.dilate(mask, None, iterations=0)
    frame = cv2.bitwise_and(frame,frame,mask = mask)

# Set up the detector with default parameters.
    detector = cv2.SimpleBlobDetector_create(params)

# Detect blobs.
    keypoints = detector.detect(mask)

# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size of blob
    im_with_keypoints = cv2.drawKeypoints(mask, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)


# Display the resulting frame

    frame = cv2.bitwise_and(frame,im_with_keypoints,mask = mask)

    cv2.imshow('frame',frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# When everything done, release the capture
im.release()
cv2.destroyAllWindows()

EDIT 1: Code update

Now i got a issue where my full circle isn't detected.

No Blob Detection

Second Version

# Standard imports
import cv2
import numpy as np;

# Read image
im = cv2.VideoCapture(0)

while(1):
        ret, frame=im.read()


        lower = (130,150,80)  #130,150,80
        upper = (250,250,120) #250,250,120
        mask = cv2.inRange(frame, lower, upper)
        lower, contours, upper = 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"]))
        canvas = im.copy()
        cv2.circle(canvas, center, 2, (0,0,255), -1)

        cv2.imshow('frame',frame)

        if cv2.waitKey(1) & 0xFF == ord('q'):
                break
im.release()
cv2.destroyAllWindows()
Simon Penn
  • 53
  • 1
  • 1
  • 8

2 Answers2

3

You need to work out what the BGR numbers for your green are (let's say for arguments sake [0, 255, 0]), then create a mask that ignores any colours outside a tolerance around your green:

mask = cv2.inRange(image, lower, upper)

Take a look at this tutorial for a step by step.

Play around with lower and upper to get the right behaviour. Then you can find the contours in the mask:

_, contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, 
                                                    cv2.CHAIN_APPROX_NONE)

Then go through the contours list to find the biggest one (filter out any possible noise):

blob = max(contours, key=lambda el: cv2.contourArea(el))

And that's your final 'blob'. You can find the center by doing:

M = cv2.moments(blob)
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))

You can draw this center onto a copy of your image, for checking:

canvas = im.copy()
cv2.circle(canvas, center, 2, (0,0,255), -1)

Obviously, this makes the assumption that there's only one green ball and nothing else green in the image. But it's a start.

EDIT - RESPONSE TO SECOND POST

I think the following should work. I haven't tested it, but you should be able to at least do a bit more debugging with the canvas and mask displayed:

# Standard imports
import cv2
import numpy as np;

# Read image
cam = cv2.VideoCapture(0)

while(1):
        ret, frame = cam.read()

        if not ret:
            break

        canvas = frame.copy()


        lower = (130,150,80)  #130,150,80
        upper = (250,250,120) #250,250,120
        mask = cv2.inRange(frame, lower, upper)
        try:
            # NB: using _ as the variable name for two of the outputs, as they're not used
            _, 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"]))

            cv2.circle(canvas, center, 2, (0,0,255), -1)

        except (ValueError, ZeroDivisionError):
            pass

        cv2.imshow('frame',frame)
        cv2.imshow('canvas',canvas)
        cv2.imshow('mask',mask)

        if cv2.waitKey(1) & 0xFF == ord('q'):
                break
im.release()
cv2.destroyAllWindows()
Aidenhjj
  • 1,249
  • 1
  • 14
  • 27
  • Thank, my mask is a lot better now, i got a nice circle, but there is a noisy grey background But i'm not able to use cv2.bitwise_and, Moreover my SimpleBlobDetector didn't work with the red circle i got now, did i need to invert the mask/colors ? Thanks a lot – Simon Penn Mar 21 '17 at 14:57
  • 1
    Create a mask on the frame image and then `bitwise_and` your mask with the frame to get your masked frame. Then run your BlobDetector. – Aidenhjj Mar 21 '17 at 17:26
  • I got nice circle of my colors but they aren't detected as blob because they aren't empty i think. I use this https://www.learnopencv.com/blob-detection-using-opencv-python-c/ But it didn't work with me – Simon Penn Mar 24 '17 at 07:43
  • I've not used the SimpleBlobDetector. You could do it yourself and get more control. Find the contour around your ball using `a, contours, b = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)`, then you can get the centroid of this and use that to track. – Aidenhjj Mar 24 '17 at 10:56
  • Edited answer with more detail – Aidenhjj Mar 24 '17 at 11:04
  • it seems to work on te paper but i get an error : `max() arg is an empty sequence` I need to initialise blob but i really don't know how to in that case.. – Simon Penn Mar 24 '17 at 15:57
  • @SimonPenn - are you applying the `findContours` function to the mask? – Aidenhjj Mar 24 '17 at 15:59
  • @SimonPenn - try wrapping the whole thing in a `try`/`except`, as it will fail if the ball isn't on screen all the time (then the contour list will be empty) – Aidenhjj Mar 24 '17 at 16:04
  • Oh yeah i didn't think about a try... I've updated the 1st post with the second version code – Simon Penn Mar 24 '17 at 16:09
  • @SimonPenn - does the new answer help? – Aidenhjj Mar 24 '17 at 16:49
  • Okn i've just modify line 15 `canvas = copy.copy(img)` beacause it wasn't working, now it tell me that img is not a numpy array, neither a scalar. It can work for a picture, but for a video flux ? @Aidenhjj – Simon Penn Mar 31 '17 at 07:30
  • @SimonPenn - there was an error in the code, I was trying to copy the `VideoCapture` object rather than the still `frame` read off it. Try my corrected code now. – Aidenhjj Mar 31 '17 at 09:31
  • `File "stack.py", line 25, in center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])) ZeroDivisionError: float division by zero` m00 seem to be equals to zero, but why the try didn't work ? it's considered like a fatal error ? – Simon Penn Mar 31 '17 at 09:48
  • Because we were only catching `ValueError`, we need to extend our `except` line to include `ZeroDivisionError`. See corrected code – Aidenhjj Mar 31 '17 at 09:56
  • Oh yep, i didn't use try/except a lot, but that's clear ! It work well now ! Thanks a lot ! – Simon Penn Mar 31 '17 at 10:25
  • It detect well multi-connected pixel of the same color but not the full-circle – Simon Penn Mar 31 '17 at 14:35
1

You should use HSV color space for better results if you wanna make filter by color.

ret, frame=im.read()

frame= cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # Add this to your code

mask = cv2.inRange(frame, blueLower, blueUpper)
Ibrahim
  • 320
  • 2
  • 7
  • filters are ok, but SimpleBlobDetector didn't work with full circle – Simon Penn Mar 24 '17 at 15:08
  • SimpleBlobDetector does not work sometimes, please check this for contours based detection-> https://github.com/mribrahim/Blob-Detection. You can easily write the same for python. Also if you use python, i suggest you to use skimage segmentation, it gives more properties about blobs. – Ibrahim Sep 05 '18 at 06:26