jomsamago
jomsamago

Reputation: 27

Recognize a circle in color detection in OPEN CV PYTHON

I have a code here that detects LASER light but I'm experiencing problems in different light conditions. So I think I might solve it if I added a code that checks if that light is a circle.

The problem is I don't know how to apply it here. Here is what the laser light looks like in the mask.

I'm hoping that you can help me with my code.

Here's my code:

import cv2
import numpy as np

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) convert from bgr to hsv color space

    lower = np.array([0,0,255]) #range of laser light
    upper = np.array([255, 255, 255])

    mask = cv2.inRange(hsv, lower, upper) 
    maskcopy = mask.copy()

    circles = cv2.HoughCircles(maskcopy, cv2.HOUGH_GRADIENT, 1, 500,
                      param1 = 20, param2 = 10,
                      minRadius = 1, maxRadius = 3)
    _,cont,_ = cv2.findContours(maskcopy, cv2.RETR_LIST,
                            cv2.CHAIN_APPROX_SIMPLE)
    if circles is not None:
        circles = np.round(circles[0,:]).astype('int')

        for(x,y,r) in circles:
            cv2.circle(frame, (x,y), r, (0,255,0),4)

    cv2.imshow('mask', mask)
    cv2.imshow('frame', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()

Screenshot:

enter image description here

Upvotes: 0

Views: 905

Answers (2)

Fred Guth
Fred Guth

Reputation: 1667

I came up with a solution with a different approach.

My idea was to create a circle with center in the center of the white region of the mask and with radius equal to half the width of the white region of the mask. Then I check how similar is this circle from the mask.

Here is the code:

white = np.where(mask>250) # you can also make it == 255
white = np.asarray(white)
minx = min(white[0])
maxx = max(white[0])
miny = min(white[1])
maxy = max(white[1])
radius = int((maxx-minx)/2)
cx = minx + radius
cy = miny + radius
black = mask.copy()
black[:,:]=0
cv2.circle(black, (cy,cx), radius, (255,255,255),-1)
diff = cv2.bitwise_xor(black, mask)
diffPercentage = len(diff>0)/diff.size
print (diffPercentage)

Then you have to come up with what percentage threshold is "similar" enough for you.

The code above was tested reading a mask from disk, but a video is just a sequence of images. Without your webcam input I cannot test the code with video, but it should work like this:

import cv2
import numpy as np

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    lower = np.array([0,0,255]) #range of laser light
    upper = np.array([255, 255, 255])

    mask = cv2.inRange(hsv, lower, upper) 
    white = np.where(mask>250) # you can also make it == 255
    white = np.asarray(white)
    minx = min(white[0])
    maxx = max(white[0])
    miny = min(white[1])
    maxy = max(white[1])
    radius = int((maxx-minx)/2)
    cx = minx + radius
    cy = miny + radius
    black = mask.copy()
    black[:,:]=0
    cv2.circle(black, (cy,cx), radius, (255,255,255),-1)
    diff = cv2.bitwise_xor(black, mask)
    diffPercentage = len(diff>0)/diff.size
    print (diffPercentage)

    cv2.imshow('mask', mask)
    cvw.imshow('diff', diff)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()

Upvotes: 0

kavko
kavko

Reputation: 2841

I tried something similar once and the best solution for me was:

(I saved your image to my hard disk and made a sample code)

import cv2
import math

img = cv2.imread('laser.jpg')
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray_image,100,255,cv2.THRESH_BINARY)
im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
area = sorted(contours, key=cv2.contourArea, reverse=True)
contour = area[0]
(x,y),radius = cv2.minEnclosingCircle(contour)
radius = int(radius)
area = cv2.contourArea(contour)
circ = 4*area/(math.pi*(radius*2)**2)
cv2.drawContours(img, [contour], 0, (0,255,0), 2)
cv2.imshow('img', img)
print(circ)

So the idea is to find your contour with cv2.findContours (laser point) and enclosing circle to it so you can get the radius, then get the area with cv2.contourArea of your contour and check its circularity with the formula circ = 4*area/(math.pi*(radius*2)**2). The perfect citrcle would return the result of 1. The more it goes to 0 the less "circuar" your contour is (in pictures below). Hope it helps!

enter image description here

enter image description here

so your code should be something like this and it will return no error (tried it and it works)

import cv2
import numpy as np
import math

cap = cv2.VideoCapture(0)

while True:
    try:
        ret, frame = cap.read()
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) #convert from bgr to hsv color space

        lower = np.array([0,0,255]) #range of laser light
        upper = np.array([255, 255, 255])

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

        im2, contours, hierarchy = cv2.findContours(mask,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
        area = sorted(contours, key=cv2.contourArea, reverse=True)
        contour = area[0]
        (x,y),radius = cv2.minEnclosingCircle(contour)
        radius = int(radius)
        area = cv2.contourArea(contour)
        circ = 4*area/(math.pi*(radius*2)**2)
        print(circ)
    except:
        pass

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

cap.release()
cv2.destroyAllWindows()

Upvotes: 1

Related Questions