ball detection in python runs very slow, any tips on impoving it? - python

I am working on a sensor for PID controler, so basically I have a camera recording a scene. In order for control to work I need to extract tennis ball's position on a scene in each frame. Scene is set with white background and orange tennis ball as you can see on image:
And when I run it on my lenovo ideapad 100 (which is quite slow pc) I get ball's position each let's say 1.2 secs.
I think this could be done way faster but I don't know what to do.
Any tips and suggestions are welcome.
Im expecting a system that can generate at least 2 position each second
Here is my code:
import numpy as np
import cv2
import time
cap = cv2.VideoCapture(0)
lower_red = np.array([0,100,100])
upper_red = np.array([20,255,255])
while(1):
start = time.time()
# Load an color image in grayscale
_,img = cap.read()
print("read")
#get the image's width and height
blurred = cv2.GaussianBlur(img, (11, 11), 0)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
hsv2 = cv2.cvtColor(blurred,cv2.COLOR_BGR2HSV)
mask2 = cv2.inRange(hsv2, lower_red, upper_red)
mask2 = cv2.erode(mask2, None, iterations=2)
mask2 = cv2.dilate(mask2, None, iterations=2)
imageWidth = mask2.shape[1]
imageHeight = mask2.shape[0]
sum_i_white = 1
num_white = 1
sum_j_white = 1
for i in range(0,imageHeight):
for j in range(0,imageWidth):
if mask2[i][j] == 255:
sum_i_white += i
sum_j_white += j
num_white += 1
cv2.circle(img,(int(sum_j_white/num_white),int(sum_i_white/num_white)),3,(0,0,255), -1)
cv2.putText(img,'here',(int(sum_j_white/num_white),int(sum_i_white/num_white)), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 1, cv2.LINE_AA)
cv2.imshow("final",img)
print(time.time()-start)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()

Do you know what is slow?
I know you can run C program with Python.
So, if your double for is very slow, you can make a C program running this.
If you have juste array and int as input, and int as output, I think it shouldn't be hard.
But I don't know if this would be useful.

Related

Enhancing OpenCV Masking

I made a program that applies a mask over an object as described in this StackOverflow question. I did so using colour thresholding and making the mask select only the colour range of human skin (I don't know if it works for white people as I am not white and it works well for me). the problem is when I run it, some greys (grey area on the wall or a shadow) are also picked up on the mask and it is applied there.
I wanted to know whether there was a way to remove the unnecessary bits in the background, and/or if there was a way using object detection I could solve this. PS I tried using createBackgroundSubtractorGMG/MOG/etc but that came out very weird and way worse.
Here is my code:
import cv2
from cv2 import bitwise_and
from cv2 import COLOR_HSV2BGR
import numpy as np
from matplotlib import pyplot as plt
cap = cv2.VideoCapture(0)
image = cv2.imread('yesh1.jpg')
bg = cv2.imread('kruger.jpg')
bg = cv2.cvtColor(bg, cv2.COLOR_BGR2RGB)
kernel1 = np.ones((1,1),np.uint8)
kernel2 = np.ones((10,10),np.uint8)
while (1):
ret, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lowerBound = np.array([1, 1, 1])
upperBound = np.array([140, 255 ,140])
mask = cv2.inRange(hsv, lowerBound, upperBound)
blur = cv2.GaussianBlur(mask,(5,5),0)
ret1,mask = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel1)
contourthickness = cv2.cvtColor(mask, cv2.IMREAD_COLOR)
res = bitwise_and(frame, frame, mask = mask)
crop_bg = bg[0:480, 0:640]
final = frame + res
final = np.where(contourthickness != 0, crop_bg, final)
cv2.imshow('frame', frame)
cv2.imshow('Final', final) # TIS WORKED BBYY
key = cv2.waitKey(1) & 0xFF
if key == 27:
break
cv2.destroyAllWindows()
EDIT:
Following #fmw42 's comment, I am adding the original image as well as a screenshot of how the different frames look. The masked image also changes colour. Something to fix that will also be helpful.
#Jeremi. Your code working 100%. Using White wall for background. Avoid door(it is not white, it is cream), shadow around edge, to prevent noising. If you have white bed sheet or white walls. I am using Raspberry pi 4b/8gb, 4k monitor. I can't get actual size of window.
Here is output:
What you see on my output. I placed my hand behind white sheet closer to camera. I do not have white wall on my room. My room is greener. That why you see logo on background. Btw, I can move my hand no problem.

OpenCV - Adaptive thresholding / Trackbar manipulation

I am still new to OpenCV(Python) and am trying out cv2.adaptiveThreshold() to draw proper contours with a webcam running when lighting is changing. The main problem is the insane amount of noise I am getting when drawing contours so I tried to set a cv2.countourArea() threshold but this seems not like the best solution.
Later on I deciced to try and manipulate the values of cv2.adaptiveThreshold with a simple trackbar.
Specifically the blockSize and CValue. Everything works fine on the CValue but I do really struggle on the blockSize since it needs to be an odd number. I tried something along the line of checking if the value of the empty callback function is even and adding +1. But this does not seem to work properly. Later on I will most likely use machine-learning to change these values but for now I'd like the trackbars to work for debugging purposes.
What is the best solution here to manipulate the blockSize with a trackbar?
Thank you in advance! :)
import cv2
import numpy as np
#####################################
winWidth = 640
winHeight = 840
brightness = 100
cap = cv2.VideoCapture(0)
cap.set(3, winWidth)
cap.set(4, winHeight)
cap.set(10, brightness)
kernel = (5, 5)
bSize_default = 1
#######################################################################
def empty(a):
pass
cv2.namedWindow("TrackBars")
cv2.resizeWindow("TrackBars", 640, 240)
cv2.createTrackbar("cVal", "TrackBars", 2, 20, empty)
def preprocessing(frame, cVal):
imgGray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# mask = cv2.inRange(imgHsv, lower, upper)
imgBlurred = cv2.GaussianBlur(imgGray, kernel, 3)
gaussC = cv2.adaptiveThreshold(imgBlurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, cVal)
imgDial = cv2.dilate(gaussC, kernel, iterations=3)
imgErode = cv2.erode(imgDial, kernel, iterations=1)
return imgDial
def getContours(imPrePro):
contours, hierarchy = cv2.findContours(imPrePro, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
for cnt in contours:
area = cv2.contourArea(cnt)
if area > 60:
cv2.drawContours(imgCon, cnt, -1, (255, 0, 0), 3)
#######################################################################################################
while (cap.isOpened()):
success, frame = cap.read()
cVal = cv2.getTrackbarPos("cVal", "TrackBars")
if success == True:
frame = cv2.flip(frame, 1)
imgCon = frame.copy()
imPrePro = preprocessing(frame, cVal)
getContours(imPrePro)
cv2.imshow("Preprocessed", imPrePro)
cv2.imshow("Original", imgCon)
if cv2.waitKey(1) & 0xFF == ord("q"):
cv2.destroyAllWindows()
break
The minimum value of blocksize must be 3 and also blocksize must be odd therefore:
value_BSize= cv2.getTrackbarPos("bSize", "TrackBars")
value_BSize = max(3,value_BSize)
if (value_BSize % 2 == 0):
value_BSize += 1
tbar = (cv2.getTrackbarPos('trackbar', 'window')&~1)+3 # for smooth operation
tbar = cv2.getTrackbarPos('trackbar', 'window')*2+3 # for fast operation

Convert an opencv script

I found this code from this website (https://www.hackster.io/tinkernut/raspberry-pi-smart-car-8641ca) and try to rewrite it so that it works with a USB webcam and not with the PI camera.
I wanted to add the recorded material to a Numpy array, but I can't get any further. After a lot of fiddling around and searching the internet I didn't find anything to solve the problem.
I don't have much experience in Python or opencv programming so I hope someone can help me because I am overwhelmed and my brain just feels like a crushed potato.
Generally to my project, I want to build an infotainment system with a Raspberry Pi and everything works except the rear view camera. So navigation, music and video playback and so on.
As main program to use all other programs I use Kodi with the CarPc-Carbonskin.
Translated with www.DeepL.com/Translator
import time
import cv2
import numpy as np
from picamera.array import PiRGBArray
from picamera import PiCamera
import RPi.GPIO as GPIO
buzzer = 22
GPIO.setmode(GPIO.BCM)
GPIO.setup(buzzer, GPIO.OUT)
camera = PiCamera()
camera.resolution = (320, 240) #a smaller resolution means faster processing
camera.framerate = 24
rawCapture = PiRGBArray(camera, size=(320, 240))
kernel = np.ones((2,2),np.uint8)
time.sleep(0.1)
for still in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):
GPIO.output(buzzer, False)
image = still.array
#create a detection area
widthAlert = np.size(image, 1) #get width of image
heightAlert = np.size(image, 0) #get height of image
yAlert = (heightAlert/2) + 100 #determine y coordinates for area
cv2.line(image, (0,yAlert), (widthAlert,yAlert),(0,0,255),2) #draw a line to show area
lower = [1, 0, 20]
upper = [60, 40, 200]
lower = np.array(lower, dtype="uint8")
upper = np.array(upper, dtype="uint8")
#use the color range to create a mask for the image and apply it to the image
mask = cv2.inRange(image, lower, upper)
output = cv2.bitwise_and(image, image, mask=mask)
dilation = cv2.dilate(mask, kernel, iterations = 3)
closing = cv2.morphologyEx(dilation, cv2.MORPH_GRADIENT, kernel)
closing = cv2.morphologyEx(dilation, cv2.MORPH_CLOSE, kernel)
edge = cv2.Canny(closing, 175, 175)
contours, hierarchy = cv2.findContours(closing, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
threshold_area = 400
centres = []
if len(contours) !=0:
for x in contours:
#find the area of each contour
area = cv2.contourArea(x)
#find the center of each contour
moments = cv2.moments(x)
#weed out the contours that are less than our threshold
if area > threshold_area:
(x,y,w,h) = cv2.boundingRect(x)
centerX = (x+x+w)/2
centerY = (y+y+h)/2
cv2.circle(image,(centerX, centerY), 7, (255, 255, 255), -1)
if ((y+h) > yAlert):
cv2.putText(image, "ALERT!", (centerX -20, centerY -20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255),2)
GPIO.output(buzzer, True)
cv2.imshow("Display", image)
rawCapture.truncate(0)
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
GPIO.output(buzzer, False)
break

Drone Feedback: Object Detection / Color Detection? Is there way just to isolate detection on a specific object

I have been working on a code where an A.R Drone 2.0 will detect color and put a red dot in the middle of the image. I am using streaming for the drone. The goal is for the drone to detect a white gutter and fly straight over it from one point to the other. Essentially following a line. I noticed when I changed the BGR to 0, 0, 255, I get the entire gutter to be distinguished but it detects white spots as well. Is there to isolate my detection just to see the gutter. Maybe using shapes, once the gutter is detected, put a bounding box. And my finally question is how do I tell my drone to follow the red dot or maybe drawing a line. I looked at python-AR drone libraries but don't know how to apply it.This is my code.
import numpy as np
import cv2
# open the camera
cap = cv2.VideoCapture('tcp://192.168.1.1:5555')
def nothing(x):
pass
cv2.namedWindow('result')
# Starting with 100's to prevent error while masking
h,s,v = 100,100,100
# Creating track bar
cv2.createTrackbar('h', 'result',0,179,nothing)
cv2.createTrackbar('s', 'result',0,255,nothing)
cv2.createTrackbar('v', 'result',0,255,nothing)
while True:
#read the image from the camera
ret, frame = cap.read()
#You will need this later
frame = cv2.cvtColor(frame, 35)
#converting to HSV
hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
# get info from track bar and appy to result
h = cv2.getTrackbarPos('h','result')
s = cv2.getTrackbarPos('s','result')
v = cv2.getTrackbarPos('v','result')
# Normal masking algorithm
lower_blue = np.array([h,s,v])
upper_blue = np.array([180,255,255])
mask = cv2.inRange(hsv,lower_blue, upper_blue)
result = cv2.bitwise_and(frame,frame,mask = mask)
cv2.imshow('result',result)
#find center
cnts=cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
center=None
if len(cnts)>0:
c=max(cnts, key=cv2.contourArea)
((x,y),radius)=cv2.minEnclosingCircle(c)
M=cv2.moments(c)
center=(int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
if radius>10:
#cv2.circle(frame, (int(x),int(y)), int(radius), 2)
cv2.circle(frame, center,5,(0,0,255),-1)
# color detection limits
lB = 5
lG = 50
lR = 50
hB = 15
hG = 255
hR = 255
lowerLimits = np.array([lB, lG, lR])
upperLimits = np.array([hB, hG, hR])
# Our operations on the frame come here
thresholded = cv2.inRange(frame, lowerLimits, upperLimits)
outimage = cv2.bitwise_and(frame, frame, mask = thresholded)
cv2.imshow('original', frame)
# Display the resulting frame
cv2.imshow('processed',outimage)
# Quit the program when Q is pressed
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything done, release the capture
print 'closing program'
cap.release()
cv2.destroyAllWindows()

Find the red circle in video

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

Categories