I want to track a moving cap of a coke bottle in the webcam Feed with OpenCV in Python (or C++). I tried to search for all the red in the frame and then I used some kind of HOUGH TRANSFORM to search for circles.
I cant find the right radius for the circle and fix it so it doesn't change every frame.the process time is not so important I dont want a real time detection but I do want a precise red circle detection.
This is what I have so far:
import cv2
import numpy as np
import cv2.cv as cv
cap = cv2.VideoCapture(0)
while(1):
# Take each frame
_, frame = cap.read()
# Convert BGR to HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of blue color in HSV
lower_red = np.array([160,140,50])
upper_red = np.array([180,255,255])
imgThreshHigh = cv2.inRange(hsv, lower_red, upper_red)
imgray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
thresh = 18
edges = cv2.Canny(imgray,thresh,thresh*3)
circles = cv2.HoughCircles(imgThreshHigh, cv.CV_HOUGH_GRADIENT, 1, 500, 25, 75, 5, 15)
maxRadius= 0
xc = 0.00
yc = 0.00
found = False
if circles is not None:
found = True
for i in circles[0,:3]:
if i[2] < 100:
if i[2] > maxRadius:
maxRadius = i[2]
if maxRadius > 1.0:
# draw the outer circle
cv2.circle(frame,(i[0],i[1]),maxRadius,(0,0,255),2)
# draw the center of the circle
cv2.circle(frame,(i[0],i[1]),1,(0,0,255),3)
xc = i[0]
yc = i[1]
if found:
print "ball detected at position:",xc, ",", yc, " with radius:", maxRadius
else:
print "no ball"
cv2.imshow('frame',frame)
cv2.imshow('edges',edges)
k = cv2.waitKey(5) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
I dont think HOUGH TRANSFORM works for this. So I want to use the edges.
How can I use an equation like (X-Xc)^2 + (Y-Yc)^2 =R^2 and the contours to find circles?
Also if there is an improvement for Hough Transform I will appreciate it if you share with me
Contours:
contours,hierarchy=cv2.findContours(edges,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
My example image: ---> this is not my example Image.this is the object I want to find in videos.
Well for the color you have to find the right range, for the human eye the bottle cap is red, but for the camera could be orange or something like that. Also is affected by the light so the camera could get some white. I learned this on the hard way haha. You could do a code with trackbars and hsv values to get the exact rang for your objet.
Now, are you looking for the center (x,y)? You can try with cv2.moments()
and for
Here is an example
For your code is something like this
import cv2
import numpy as np
import cv2.cv as cv
cap = cv2.VideoCapture(0)
coords = []
while(1):
# Take each frame
_, frame = cap.read()
# Convert BGR to HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of blue color in HSV
lower_red = np.array([160,140,50])
upper_red = np.array([180,255,255])
imgThreshHigh = cv2.inRange(hsv, lower_red, upper_red)
thresh = imgThreshHigh.copy()
countours,_ = cv2.findContours(thresh, cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
for cnt in countours:
area = cv2.contourArea(cnt)
if area > max_area:
max_area = area
best_cnt = cnt
M = cv2.moments(best_cnt)
cx,cy = int(M['m10']/M['m00']), int(M['m01']/M['m00'])
coord = cx, cy #This are your coordinates for the circle
# area = moments['m00'] save the object area
#perimeter = cv2.arcLength(best_cnt,True) is the object perimeter
#Save the coords every frame on a list
#Here you can make more conditions if you don't want repeated coordinates
points.append(coord)
cv2.imshow('frame',frame)
cv2.imshow('Object',thresh)
k = cv2.waitKey(5) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
With moments you don't have the raidius hough transform that you mentioned because calculate the relative center of the object every time.
Here is the code I use to know which is the exact value, hope it helps you HSV detection code
Related
I'm trying to follow movement of a part using red dots. I tried with white dots and thresholding before, but there is too much reflection from the smartphone I'm using. The plan is to recognize a dot as a contour, find the center and fill the array with the coordinates of all contour centers for further calculation.
The code is posted bellow, it recognizes the correct number of dots, but I get the division by zero error. Does anyone know what I'm doing wrong?
Image:https://imgur.com/a/GLXGCPP
import cv2
import numpy as np
from matplotlib import pyplot as plt
import imutils
#load image
img = cv2.imread('dot4_red.jpg')
#apply median blur, 15 means it's smoothing image 15x15 pixels
blur = cv2.medianBlur(img,15)
#convert to hsv
hsv = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV)
#color definition
red_lower = np.array([0,0,240])
red_upper = np.array([10,10,255])
#red color mask (sort of thresholding, actually segmentation)
mask = cv2.inRange(hsv, red_lower, red_upper)
#copy image for, .findContours distorts the source image
mask_copy = mask.copy()
#find contours
cnts = cv2.findContours(mask_copy,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
#extract contours from the list??
cnts = imutils.grab_contours(cnts)
#count number of conoturs of specific size
s1 = 500
s2 = 10000
xcnts = []
for cnt in cnts:
if s1<cv2.contourArea(cnt)<s2:
xcnts.append(cnt)
n = len(xcnts)
#pre-allocate array for extraction of centers of contours
s = (n,2)
array = np.zeros(s)
#fill array of center coordinates
for i in range(0,n):
cnt = cnts[i]
moment = cv2.moments(cnt)
c_x = int(moment["m10"]/moment["m00"])
c_y = int(moment["m01"]/moment["m00"])
array[i,:] = [c_x, c_y]
#display image
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.imshow('image', mask)
cv2.waitKey(0) & 0xFF
cv2.destroyAllWindows()
#print results
print ('number of dots, should be 4:',n)
print ('array of dot center coordinates:',array)
The problem was the wrong color range. Because of this, there were holes in the mask of the circles. Due to division by zero. M00. You can choose the correct color range or pre-fill the holes in the mask. Or use this code:
import cv2
import numpy as np
#load image
img = cv2.imread('ZayrMep.jpg')
#apply median blur, 15 means it's smoothing image 15x15 pixels
blur = cv2.medianBlur(img,15)
#convert to hsv
hsv = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV)
#color definition
red_lower = np.array([160,210,230])
red_upper = np.array([180,255,255])
#red color mask (sort of thresholding, actually segmentation)
mask = cv2.inRange(hsv, red_lower, red_upper)
connectivity = 4
# Perform the operation
output = cv2.connectedComponentsWithStats(mask, connectivity, cv2.CV_32S)
# Get the results
num_labels = output[0]-1
centroids = output[3][1:]
#print results
print ('number of dots, should be 4:',num_labels )
print ('array of dot center coordinates:',centroids)
moments00 (area) can be 0 for some shapes according to cv documentation. This is probably what is happening here:
Note Since the contour moments are computed using Green formula, you
may get seemingly odd results for contours with self-intersections,
e.g. a zero area (m00) for butterfly-shaped contours.
From: https://docs.opencv.org/3.4/d8/d23/classcv_1_1Moments.html#a8b1b4917d1123abc3a3c16b007a7319b
You need to ensure the area (m00) is not 0 before using it for division.
I'm writing a code that detects parakeets eyes. Currently, I'm using a already written code that i found on youtube. It's working great with the pictures that i have, but i don't know how to display a colored version of the selected area.
The results: (https://imgur.com/a/zCARrVC)
I've tried using masks and use cv2.drawcontourns to repeat the already drawn contour on them. It worked, but i couldn't make the mask overlap the original image and crop. I think it is because the contour wasn't filled, but i don't know for sure and i don't know if a filled contour won't mess up with the rest of the code.
import cv2
import numpy as np
import imutils
def nothing(x):
pass
# Load an image
img = cv2.imread('papagaio.png')
# Resize The image
if img.shape[1] > 600:
img = imutils.resize(img, width=600)
# Create a window
cv2.namedWindow('Treshed')
# create trackbars for treshold change
cv2.createTrackbar('Treshold','Treshed',0,255,nothing)
while(1):
# Clone original image to not overlap drawings
clone = img.copy()
# Convert to gray
gray = cv2.cvtColor(clone, cv2.COLOR_BGR2GRAY)
# get current positions of four trackbars
r = cv2.getTrackbarPos('Treshold','Treshed')
# Thresholding the gray image
ret,gray_threshed = cv2.threshold(gray,r,255,cv2.THRESH_BINARY)
# Blur an image
bilateral_filtered_image = cv2.bilateralFilter(gray_threshed, 5, 175, 175)
# Detect edges
edge_detected_image = cv2.Canny(bilateral_filtered_image, 75, 200)
# Find contours
contours, _= cv2.findContours(edge_detected_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contour_list = []
for contour in contours:
# approximte for circles
approx = cv2.approxPolyDP(contour,0.01*cv2.arcLength(contour,True),True)
area = cv2.contourArea(contour)
if ((len(approx) > 8) & (area > 30) ):
contour_list.append(contour)
# Draw contours on the original image
cv2.drawContours(clone, contour_list, -1, (255,0,0), 2)
# there is an outer boundary and inner boundary for each eadge, so contours double
print('Number of found circles: {}'.format(int(len(contour_list)/2)))
#Displaying the results
cv2.imshow('Objects Detected', clone)
cv2.imshow("Treshed", gray_threshed)
# ESC to break
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
# close all open windows
cv2.destroyAllWindows()'
Like you said, you can create mask and then apply it on the RGB image. Here's some way to do it:
mask = np.zeros( (clone.shape[0], clone.shape[1]), np.uint8) #create single channel mask
for contour in contours:
cv.fillPoly(mask, pts=[contour], color=(255)) #cv.drawContours with thickness parameter = -1 should also work
cv.bitwise_and(clone, clone, mask)
I am working on a line follower bot which follows black line on white background The code is something like this
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
try:
while(1):
## Read the image
ret, img = cap.read()
#cv2.imshow('image',img)
img = img[160:320,:,:]
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,170,255,cv2.THRESH_BINARY_INV)
#Applying Dilation Morphological Operation To Dilate the image
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
dilation = cv2.dilate(thresh, kernel, iterations = 5)
#Apllying Gaussian Bluring Algorithm to reduce the number of contours
blur3 = cv2.medianBlur (dilation,5)
i = 0
contours, hierarchy = cv2.findContours(blur3,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,contours,-1,(0,255,0),3)
cv2.imshow('image',img)
M = cv2.moments(contours[i])
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
if cx < 190:
print "LEFT"
if cx > 450:
print "RIGHT "
if ((cx > 189) & (cx < 449)):
print "FRONT"
finally:
cap.release()
cv2.destroyAllWindows()
I want to also detect blue and red color while the bot is following the line using the same camera.
I tried reading center pixel of the centroid center_px = img[cy,cx]
But reading just one pixel dosent prove efficient in determining the color.
So is there any other approach to do this
I am using python2.7 with cv2
I have a pair of black scissors (image below) and I want to calculate its angle. The angle is between 180 and 0.so I used cv2.line() to get two lines for angle calculation but I could only detect angel if its 180 or 0.how can I find a " > " shape with contours?(the handle is also black)
#Decapitary
import cv2
import numpy as np
import cv2.cv as cv
import math
import sys
cap = cv2.VideoCapture(0)
max_area =0
while(1):
# Take each frame
_, frame = cap.read()
# Convert BGR to HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower_black = np.array([0,0,0])
upper_black = np.array([180,255,30])
mask= cv2.inRange(hsv, lower_red, upper_red)
mask = cv2.GaussianBlur(mask,(1,1),2)
mask = cv2.dilate(mask, np.ones((7,7),np.uint8))
mask = cv2.erode(mask, np.ones((5,5),np.uint8))
edges = cv2.Canny(mask,thresh,thresh*5)
contours, hierarchy = cv2.findContours(edges,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) ### use this to calculte the angel.
for c in contours:
area = cv2.contourArea(c)
if area > max_area:
max_area = area
yu = c
nss = np.asarray(yu,dtype=np.int32)
cv2.polylines(frame, [nss], 0 , (0,0,250))
cv2.imshow('frame',frame)
cv2.imshow('edges',edges)
cv2.imshow('mask',mask)
k = cv2.waitKey(5) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
Im open to ideas and really need help.just show me the right direction please
EDIT:
Can I train a cascade to find a specific point a an object(in this case 3points on edges to calculate angel) ???
I am trying to detect red triangles and yellow triangles differentiating them using openCV in Python. I am a beginner.
I would like, on a first hand, detectecing, counting (yellow and red) and mark with a rectangle all the triangles the camera can see. I would like also to find their mass-center.
For the moment, I just detect one single triangle at a time without finding it color.
My calcul of mass center does not work, giving me the error:
centroid_x = int(M['m10']/M['m00'])
ZeroDivisionError: float division by zero
I have wrote the following code inspired from examples from the web
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
print cap.get(3)
print cap.get(4)
# changing display size
ret = cap.set(3,320)
ret = cap.set(4,240)
def getthresholdedimg(hsv):
yellow = cv2.inRange(hsv,np.array((10,100,100)),np.array((30,255,255)))
red = cv2.inRange(hsv,np.array((0,0,0)),np.array((190,255,255)))
both = cv2.add(yellow,red)
return both
def nothing(x):
pass
# Create a black image, a window
img = np.zeros((300,512,3), np.uint8)
cv2.namedWindow('image')
while(True):
thr1 = 50
thr2 = 110
# Capture frame-by-frame
ret, frame = cap.read()
# Our operations on the frame come here
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gaussian_blur = cv2.GaussianBlur(gray,(5,5),0)
# Our operations on the frame come here
canny = cv2.Canny(gray,thr1,thr2)
canny_blur = cv2.Canny(gaussian_blur,thr1,thr2)
# Our operations on the frame come here
contours,hier = cv2.findContours(canny,1,2)
for cnt in contours:
approx = cv2.approxPolyDP(cnt,0.02*cv2.arcLength(cnt,True),True)
if len(approx)==3:
cv2.drawContours(frame,[cnt],0,(0,255,0),2)
tri = approx
M = cv2.moments(cnt)
centroid_x = int(M['m10']/M['m00'])
centroid_y = int(M['m01']/M['m00'])
cv2.circle(img,(centroid_x,centroid_y),3,255,-1)
for vertex in tri:
cv2.circle(frame,(vertex[0][0],vertex[0][1]),3,(64,0,128),-1)
cv2.line(img,(vertex[0][0],vertex[0][1]),(centroid_x,centroid_y),(0,0,255),1)
# Display the resulting frame
cv2.imshow('normal flux',frame)
cv2.imshow('gray conversion',gray)
cv2.imshow('canny edges conversion',canny)
cv2.imshow('canny edges gaussian blur',canny_blur)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
Can you help me please?
Maybe you want to do
M = cv2.moments(tri)
instead of M = cv2.moments(cnt) ?
Your camera might not be reading it.
Try this after your cap = cv2.videoCapture(0):
while(1):
# Gets retval and frames from each video
ret ,frame = cap.read()
#Check to see if retval is not None or empty
if not ret
break;