I'm learning OpenCV (and Python) and have managed to get OpenCV to detect my nose and move the mouse using the movement of my nose, but since it looses track of my nose often I want it to fall back to moving using my face instead of my nose if needed. I've managed to draw rectangles around my face and my nose in video.
I tried being cheeky and just putting the loop for my face rectangle in "if cv2.rectangle" (for the nose), but it is always true. My question is how can I create a test to see if nose is detected fallback to move mouse with face, and if nose is re-detected go back to using the nose.
My loops as of now
# Here we draw the square around the nose, face and eyes that is detected.
for (x,y,w,h) in nose_rect:
cv2.rectangle(frame, (x,y), (x+w,y+h), (0,0,255), 3)
if cv2.rectangle:
m.move(x * 4, y * 4) # TODO: Write and if that goes into face if nose is not visible
break
else:
for (x, y, w, h) in face_rect:
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 3)
break
for (x,y,w,h) in eye_rect:
cv2.rectangle(frame, (x,y), (x+w,y+h), (205,0,0), 3)
break
I can post my entire program if that helps, I've tried doing a bunch of the OpenCV official tutorials but did not managed to find an answer to my question there.
Thank you for all replies!
PS: I'm using Python 3.5
Here's the snippet you should use in your code-
if(len(nose_rect)>0):
print ("Only Nose")
for (x,y,w,h) in nose_rect:
cv2.rectangle(frame, (x,y), (x+w,y+h), (0,0,255), 3)
#Here we say that m (the variable created before, should move the mouse using the x, and y variable from the nose rect.
# We have acellerated movement speed by 4 to make it possible to navigate the cursor through the whole screen.
m.move(x * 4, y * 4) # TODO: Write and if that goes into face if nose is not visible
elif (len(face_rect)>0):
print ("Only Face")
for (x,y,w,h) in face_rect:
cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 3)
elif (len(face_rect)>0):
print ("Only Eye")
for (x,y,w,h) in eye_rect:
cv2.rectangle(frame, (x,y), (x+w,y+h), (205,0,0), 3)
else:
print ("Nothing detected.")
Also, for waiting use the time.sleep() method
time.sleep(0.001) # Waiting 1 millisecond to show the next frame.
if (cv2.waitKey(1) & 0xFF == ord('q')):#exit on pressing 'q'
break
Related
I'm attempting to create a program in which my code analyses a video from my security camera and locates the cars that have been spotted. I've figured out how to find the cars and draw red rectangles around them, but I'd like to add a condition that only draws boxes if there are more than 5 cars detected. However, I am unable to do so due to the presence of arrays. How would I go about fixing this code?
import cv2
classifier_file = 'cars.xml'
car_tracker = cv2.CascadeClassifier(classifier_file)
video = cv2.VideoCapture("footage.mp4")
while True:
(read_successful, frame) = video.read()
if read_successful:
grayscaled_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
else:
break
cars = car_tracker.detectMultiScale(grayscaled_frame)
if cars > 4:
for (x, y, w, h) in cars:
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
cv2.imshow('Car Detector', frame)
cv2.waitKey(0)
By the way, I am using an anaconda environment along with python 3.8.8
You have an array of detections.
All you need is to call len() on the array to get the number of elements. This is a built-in function of Python.
cars = car_tracker.detectMultiScale(grayscaled_frame)
if len(cars) >= 5:
for (x, y, w, h) in cars:
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
No need for any other libraries.
Here is one example using a different technique, in this case you can use cvlib it has a class object_detection to detect multiple classes. This uses the yolov4 weights, you will be capable of detect any car in the road or in this case from one webcam, only make sure that the car are ahead the camera not in 2d position.
import cv2
import matplotlib.pyplot as plt
# pip install cvlib
import cvlib as cv
from cvlib.object_detection import draw_bbox
im = cv2.imread('cars3.jpg')
#cv2.imshow("cars", im)
#cv2.waitKey(0)
bbox, label, conf = cv.detect_common_objects(im)
#yolov4 weights will be downloaded. Check: C:\Users\USER_NAME\.cvlib\object_detection\yolo
# if the download was not successful delete yolo folder and try again, it will be downloaded again
# after download the weights keep running the script it will say 0 cars, then close Anaconda spyder anda reopen it and try again.
#get bbox
output_image_with_bbox = draw_bbox(im, bbox, label, conf)
number_cars = label.count('car')
if number_cars > 5:
print('Number of cars in the image is: '+ str(number_cars))
plt.imshow(output_image_with_bbox)
plt.show()
else:
print('Number of cars in the image is: '+ str(number_cars))
plt.imshow(im)
plt.show()
output:
Greetings.
I'm working on an project where I would like to take a bounding box already drawn on a subject and select it (via mouse click) so I can have something like a text dialogue box hover above the image, so I can then type in a label. I'm already using OpenCV to detect the object and draw an initial bounding box on it using the Haar Cascade classifier, but so far I can't find the right combination of OpenCV directives to be able to select that bounding box and then annotate it. The relevant code is below.
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
)
# Draw a rectangle around the faces
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
Would appreciate some good pointers. Thanks.
You can take the x/y position of the mouse and compare that to the bounding boxes.
The code below descibes how you could do that.
First, to be able to process mouse input, you have to crate a namedWindow. You can then attach a mouseCallback to that window:
# create window
cv2.namedWindow("Frame")
# attach a callback to the window, that calls 'getFace'
cv2.setMouseCallback("Frame", getFace)
In the getFace method, you check for button press, then loop through the faces and check if the x/y of the mouse is within the bounds of the bounding box of a face. If so, return the index of the face.
def getFace(event, x,y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
# if mousepressed
for i in range(len(faces)):
# loop through faces
(face_x,face_y,w,h) = faces[i]
# unpack variables
if x > face_x and x < face_x + w:
# if x is within x-range of face
if y > face_y and y < face_y + h:
# if y is also in y-range of face
return i
# then return the index of the face
Win 10 x64, Python 2.7, Spyder IDE
I'm using some code from Adrian Rosebrock's OpenCV blog...
import pyzbar
import cv2
# load the input image
image = cv2.imread("barcode_example.png")
# find the barcodes in the image and decode each of the barcodes
barcodes = pyzbar.pyzbar.decode(image)
# loop over the detected barcodes
for barcode in barcodes:
# extract the bounding box location of the barcode and draw the
# bounding box surrounding the barcode on the image
(x, y, w, h) = barcode.rect
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)
# the barcode data is a bytes object so if we want to draw it on
# our output image we need to convert it to a string first
barcodeData = barcode.data.decode("utf-8")
barcodeType = barcode.type
# draw the barcode data and barcode type on the image
text = "{} ({})".format(barcodeData, barcodeType)
cv2.putText(image, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX,
0.5, (0, 0, 255), 2)
# show the output image
cv2.imshow("Image", image)
cv2.waitKey(0)
I keep getting the following error...
AttributeError: 'module' object has no attribute 'pyzbar'
Yet when I check the module in Spyder it does indeed have said artibute...
I've tried running from the command line with the same result.
I have also checked to see if my installation of zbar is working & it is with no problems
Is this an issue with the Python bindings or something really obvious?
Try:
import pyzbar.pyzbar as pyzbar
Works for me.
I am working on a school project which is related to Image Processing using OpenCV Python and Raspberry Pi 3.
The hardware of Raspberry Pi 3 cannot handle the video from camera consecutively, therefore I'm thinking about only capture a picture after every 5 seconds from the camera and use it to recognize what I need, then continue.
I did some research on the internet and found a function called time.sleep(5), however this function only pause the camera 5 seconds and then continue.
Can anyone help me to solve my problem. Thank you so much.
Sorry for my bad English, here is my code so far. In my code, I use a video to test first, then when figure out the solution, I will apply on camera.
import cv2
import numpy as np
import time
headcc = cv2.CascadeClassifier('lib/heads_cascade.xml')
cap = cv2.VideoCapture('image/hockey_game.avi')
def video():
ret, frame = cap.read()
# gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
head = headcc.detectMultiScale(frame, 1.2, 2, 0 , (20, 20), (40, 40))
print type(head)
print head
print head.shape
print "Number of heads detected: " + str(head.shape[0])
if len(head) > 0:
for (x, y, w, h) in head:
cv2.rectangle(frame, (x,y), (x+w, y+h), (0, 255, 255), 1)
cv2.rectangle(frame, ((0,frame.shape[0] -25)),(270, frame.shape[0]), (255,255,255), -1)
cv2.putText(frame, "Number of head detected: " + str(head.shape[0]), (0,frame.shape[0] -10), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (0,0,0), 1)
cv2.namedWindow('Camera',cv2.WINDOW_NORMAL)
cv2.imshow('Camera',frame)
while(cap.isOpened()):
video()
time.sleep(5)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
Update March 15th, 2018:
Thanks to comment from Joe, I have found the way to skip frames. However, the program still cannot know whether the video ends, and also the fps code still appear 0.0. So if there is any help for these errors, I will really appreciate. Here is the code so far:
import cv2
import numpy as np
import time
headcc = cv2.CascadeClassifier('lib/heads_cascade.xml')
cap = cv2.VideoCapture('image/hockey_game.avi')
fps = cap.get(cv2.CAP_PROP_POS_FRAMES)
def video():
ret, frame = cap.read()
# gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
head = headcc.detectMultiScale(frame, 1.2, 2, 0 , (20, 20), (40, 40))
# print type(head)
# print head
# print head.shape
print "Number of heads detected: " + str(head.shape[0])
if len(head) > 0:
for (x, y, w, h) in head:
cv2.rectangle(frame, (x,y), (x+w, y+h), (0, 255, 255), 1)
# cv2.rectangle(frame, ((0,frame.shape[0] -25)),(270, frame.shape[0]), (255,255,255), -1)
# cv2.putText(frame, "Number of head detected: " + str(head.shape[0]), (0,frame.shape[0] -10), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (0,0,0), 1)
cv2.namedWindow('Camera',cv2.WINDOW_NORMAL)
cv2.imshow('Camera',frame)
while(cap.isOpened()):
video()
cf = cap.get(cv2.CAP_PROP_POS_FRAMES) - 1
cap.set(cv2.CAP_PROP_POS_FRAMES, cf+50)
# cv2.setTrackbarPos("pos_trackbar", "Frame Grabber",
int(cap.get(cv2.CAP_PROP_FPS)))
time.sleep(2)
if (cv2.waitKey(1) & 0xFF == ord('q')):
break
print fps
cap.release()
cv2.destroyAllWindows()
The documentation states
VideoCapture::read
Grabs, decodes and returns the next video frame.
Your video probably has 25 frames per second and you are reading them one by one.
The video is not playing in the background if that is what you expected.
Your video() function just iterates through it frame by frame.
Solutions would be to really grab from the device (the read function can also take a device id, see doc).
device – id of the opened video capturing device (i.e. a camera index).
Something like
# something like
cap = cv2.VideoCapture('/dev/video0')
Another way would be to skip frames using
VideoCapture::grab
as described here
This way you can quickly bypass however many frames you want.
grab() seems to rather fast, as it does not decode the frame. So you could find out how many frames per second your video has. Then call .read() and after that call .grab() as often as your video has frames in 5 seconds. Then call .read() again.
Or really save the pictures to disk and work on them.
I'm working with Python and OpenCV and I'm a newbie in both.
For my project, I need to move a sliding window over a picture; for each position of the window the area outside the window must be shown darker than the area inside the window.
This is the part of my code that takes care of the picture and window visualization (the valid positions for the sliding window are calculated somewhere else)
for (x, y, window) in valid_positions:
if window.shape[0] != winH or window.shape[1] != winW:
continue
# Put here stuff to process the window content
# i.e apply a classifier
clone = image.copy()
cv2.rectangle(clone, (x, y), (x + winW, y + winH), (0, 255, 0), 2)
cv2.imshow("Window", clone)
cv2.waitKey(1)
time.sleep(0.025)
The window is created and it slides on the valid positions, so that part works well. But I have absolutely no idea on how to make the picture outside the window appear darker.
Any suggestions?
Thanks in advance.
EDIT: i forgot to add an important detail: my input images are always in black and white (not even greyscale, just black and white pixels). Maybe this makes it easier to alter the brightness/darkness?
In general, you can preserve the content inside the window and lower the intensity of the entire image. Then replace the area inside the window with original content. That trick should work. This part of the code may look like
clone = image.copy()
windowArea = clone[y:y + winH, x:x + winW].copy()
clone = np.floor(clone * 0.5).astype('uint8') # 0.5 can be adjusted
clone[y:y + winH, x:x + winW] = windowArea
cv2.rectangle(clone, (x, y), (x + winW, y + winH), (0, 255, 0), 2)