I am writing a simple motion detection program but i want it to be cross platform so im using python and the pyglet library since it provides a simple way to load videos in different formats (specially wmv and mpeg). So far i have the code given below which loads the movie and plays it in a window. Now i need to:
1) grab frame at time t and t-1
2) do a subtraction to see which pixels are active for motion detection.
any ideas on how to grab frames and to skip over frames and is it possible to put the pixel values into a matrix in numpy or something directly from pyglet? or should look into using something other than pyglet?
thanks
kuaywai
import pyglet
import sys
window = pyglet.window.Window(resizable=True)
window.set_minimum_size(320,200)
window.set_caption('Motion detect 1.0')
video_intro = pyglet.resource.media('movie1.wmv')
player = pyglet.media.Player()
player.queue(video_intro)
print 'calculating movie size...'
if not player.source or not player.source.video_format:
sys.exit
myWidth = player.source.video_format.width
myHeight = player.source.video_format.height
if player.source.video_format.sample_aspect > 1:
myWidth *= player.source.video_format.sample_aspect
elif player.source.video_format.sample_aspect < 1:
myHeight /= player.source.video_format.sample_aspect
print 'its size is %d,%d' % (myWidth,myHeight)
player.play()
#window.event
def on_draw():
window.clear()
(w,h) = window.get_size()
player.get_texture().blit(0, h-myHeight,
width=myWidth,
height=myHeight)
pyglet.app.run()
To me it seems like you have to skip the play function and manually step through the video/animation, maybe using source.get_animation().frames which is a list of frames where each frame is a simple image. I'm guessing this isn't really going to be pracitical with large videos but that is generally not something you should be handling in Python anyway.
Related
I have the below code which currently outputs an image on a blank Pyglet window, however there is only one image outputted. I really need there to be a new image added every two second, with the previous images remaining intact also. For example, one image is added, two seconds later another image is added and two seconds after this another image is added. I have added the random library so an image at random can be added.
The code I have for this is below, it only displays the one image - I feel that this is getting stuck somewhere around the draw section.
import pyglet
import time
import random
window = pyglet.window.Window()
while True:
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
choice = images[random.randint(0,2)]
rawImage = pyglet.resource.image(choice)
sprite = pyglet.sprite.Sprite(rawImage)
#window.event
def on_draw():
window.clear()
sprite.draw()
time.sleep(2)
pyglet.app.run()
Any help or advice you can offer with this would be really appreciated.
A few issues/suggestions with your code.
First of all, the following code is redundant:
while True:
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
....
pyglet.app.run()
Because pyglet.app.run() is a blocking call, meaning, the loop will never loop - because pyglet.app.run() is in itself, a loop (more on this later).
Except if your application crashes, but you don't handle those exceptions so not even in that case will the code be re-run/looped.
Secondly, you should never define arrays/lists or anything really inside a loop. Loops are usually meant for logical operations, not creating things. Most contently some times it's useful to create things inside a loop, but those times they are more often than not accompanied by a if statement.
Resources cost a lot for the computer, both to setup and for the memory/hard drives etc. So trying to create your lists as early as possible and outside of any loops are suggested. for instance:
window = pyglet.window.Window()
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
while True:
choice = images[random.randint(0,2)]
Would have been a better option, if - again - the loop actually looped. In this case it's just a matter of tidying things up.
Also, this block of code:
#window.event
def on_draw():
window.clear()
sprite.draw()
Should not be created in a loop either, it's meant to replace your window variables on_draw function. So that should be moved out and put as early as possible in your logic IMO. At least kept separated by all other logic so it's not in between a "adding random image" and inside a "loop".
Now, the main reason your code fails, is that you thought this would loop, it doesn't. Again, pyglet.app.run() will lock your code execution on that row, because it's, well a never ending loop inside that function call.
You could expand your code and copy paste the code from pyglet.py's source code and it would look something like this (just to give you an idea of what's happening):
window = pyglet.window.Window()
while True:
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
choice = images[random.randint(0,2)]
rawImage = pyglet.resource.image(choice)
sprite = pyglet.sprite.Sprite(rawImage)
#window.event
def on_draw():
window.clear()
sprite.draw()
time.sleep(2)
def run(self):
while True:
event = self.dispatch_events()
if event:
self.on_draw()
note how pyglet.app.run() expands into another while True loop, that never really breaks. It's a bit oversimplified, but that's essentially what happens.
So your sprite = pyglet.sprite.Sprite(rawImage) will never be re-triggered.
Then, to your second biggest issue why this code would never work:
You're doing:
def on_draw():
sprite.draw()
But each loop you would have replaced the old sprite object, with a new one by doing sprite = pyglet.sprite.Sprite(rawImage). So what you would want to do, is keep a list/dictionary outside of the loop with all your visible image, and add to it and only render the images added.
Much like this:
import pyglet
import time
import random
width, height = 800, 600
window = pyglet.window.Window(width, height)
## Image options are the options we have,
## while `images` are the visible images, this is where we add images
## so that they can be rendered later
image_options = ["Image 1.png", "Image 2.png", "Image 3.png"]
images = {}
## Keep a timer of when we last added a image
last_add = time.time()
## Just a helper-function to generate a random image and return it
## as a sprite object (good idea to use sprites, more on that later)
def get_random_image():
choice = image_options[random.randint(0, len(image_options)-1)]
return pyglet.sprite.Sprite(pyglet.image.load(choice))
## Here, we define the `on_draw` replacement for `window.on_draw`,
## and it's here we'll check if we should add a nother image or not
## depending on how much time has passed.
#window.event
def on_draw():
window.clear()
## If two seconds have passed, and the ammount of images added are less/equal
## to how many images we have in our "database", aka `image_options`, then we'll
## add another image somewhere randomly in the window.
if time.time() - last_add > 2 and len(images) < len(image_options):
last_add = time.time()
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## Then here, is where we loop over all our added images,
## and render them one by one.
for _id_ in images:
images[_id_].draw()
## Ok, lets start off by adding one image.
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## And then enter the never ending render loop.
pyglet.app.run()
Now, this only works when you press a key or press the mouse inside the window. This is because that's the only time a event will be dispatched. And Pyglet will only render things if there's a event that is being triggered. There's two ways you can get around this, the hard core OOP way which I'll skip for now.
The second is to use what's called a Pyglet Clock, where you schedule something to happen at a interval. I'm not really good at this part, since I tend to use my own scheduler etc.
But here's the gist of it:
def add_image():
images[len(images)] = get_random_image()
pyglet.clock.schedule_interval(add_image, 2) # Every two seconds
This is a lot cleaner than doing the if time.time() - last_add > 2.
The result should look something like this:
import pyglet
import time
import random
width, height = 800, 600
window = pyglet.window.Window(width, height)
## Image options are the options we have,
## while `images` are the visible images, this is where we add images
## so that they can be rendered later
image_options = ["Image 1.png", "Image 2.png", "Image 3.png"]
images = {}
## Just a helper-function to generate a random image and return it
## as a sprite object (good idea to use sprites, more on that later)
def get_random_image():
choice = image_options[random.randint(0, len(image_options)-1)]
return pyglet.sprite.Sprite(pyglet.image.load(choice))
def add_image(actual_time_passed_since_last_clock_tick):
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## Here, we define the `on_draw` replacement for `window.on_draw`,
## and it's here we'll check if we should add a nother image or not
## depending on how much time has passed.
#window.event
def on_draw():
window.clear()
## Then here, is where we loop over all our added ima ges,
## and render them one by one.
for _id_ in images:
images[_id_].draw()
## Ok, lets start off by adding one image.
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## Add the schedule interval of adding a image every two seconds.
pyglet.clock.schedule_interval(add_image, 2)
## And then enter the never ending render loop.
pyglet.app.run()
This way, you won't need to press any keys or the mouse to trigger a event in Pyglet, it will handle that for you and do what you scheduled it to do.
Next up, is a small optimization from my part. It's a bonus, and will speed things up. It's called batched rendering, when you're rendering a lot of images and sprites, you're currently sending one image at a time to the graphics card. This is very CPU intensive. What you want to do is put the labor on the GPU. Because after all, you're working with graphics, right?
So, batched rendering is pretty easy in this case. Every time you call pyglet.sprite.Sprite, it has a parameter called batch=None (default). if you add a batch to the sprite object, you can render the entire batch by calling batch.draw() instead of each individual sprite.draw().
The solution would look something like this:
import pyglet
import time
from random import randint
width, height = 800, 600
window = pyglet.window.Window(width, height)
main_batch = pyglet.graphics.Batch()
## Image options are the options we have,
## while `images` are the visible images, this is where we add images
## so that they can be rendered later
image_options = ["Image 1.png", "Image 2.png", "Image 3.png"]
images = {}
## Just a helper-function to generate a random image and return it
## as a sprite object (good idea to use sprites, more on that later)
def get_random_image(x=0, y=0):
choice = image_options[randint(0, len(image_options)-1)]
return pyglet.sprite.Sprite(pyglet.image.load(choice), x=x, y=y, batch=main_batch)
def add_image(actual_time_passed_since_last_clock_tick=0):
image = get_random_image(x=randint(0, width), y=randint(0, height))
images[len(images)] = image
## Here, we define the `on_draw` replacement for `window.on_draw`,
## and it's here we'll check if we should add a nother image or not
## depending on how much time has passed.
#window.event
def on_draw():
window.clear()
## Instead of looping over each image in `images`,
## just do:
main_batch.draw()
## Ok, lets start off by adding one image.
## Instead of doing it manually, use the function add_image.
add_image()
## Add the schedule interval of adding a image every two seconds.
pyglet.clock.schedule_interval(add_image, 2)
## And then enter the never ending render loop.
pyglet.app.run()
I also made some alterations to add_image and get_random_image, mainly so that you can tell what position the image should be in inside the function, because pyglet.sprite.Sprite also takes two other parameters, x and y. So it makes no sense to change x and y after you've created the sprite, unless you want to move them afterwards (for instance, in a pyglet.clock.schedule_interval call).
So, couple hours ago I found Pyglet best for me for rendering gif animations, so I', new in this. My problem is that the animated gif renders at its original size in fullscreen window, I need to make it match, but I can't figure out how I should do this, any help? My code:
import sys
import pyglet
from pyglet.window import Platform
if len(sys.argv) > 1:
animation = pyglet.image.load_animation(sys.argv[1])
bin = pyglet.image.atlas.TextureBin()
animation.add_to_texture_bin(bin)
else:
animation = pyglet.resource.animation('gaben.gif')
sprite = pyglet.sprite.Sprite(animation)
screen = Platform().get_default_display().get_default_screen()
window = pyglet.window.Window(width=screen.width, height=screen.height)
window.set_fullscreen(True)
pyglet.gl.glClearColor(1, 1, 1, 1)
#window.event
def on_draw():
window.clear()
sprite.draw()
pyglet.app.run()
The result what I get
The easiest way is to use the sprite object's .scale.
It has the ability to scale the image in proportion to it's original dimensions and you don't need to worry about mapping data or filling in pixel-gaps if you resize the image yourself some how.
To help you get going, this is a simple example of a implementation:
(And it looks like this: https://youtu.be/Ly61VvTZnCU)
import pyglet
from pyglet.window import Platform
monitor = Platform().get_default_display().get_default_screen()
sprite = pyglet.sprite.Sprite(pyglet.resource.animation('anim.gif'))
H_ratio = max(sprite.height, monitor.height) / min(sprite.height, monitor.height)
W_ratio = max(sprite.width, monitor.width) / min(sprite.width, monitor.width)
sprite.scale = min(H_ratio, W_ratio) # sprite.scale = 2 would double the size.
# We'll upscale to the lowest of width/height
# to not go out of bounds. Whichever
# value hits the screen edges first essentially.
window = pyglet.window.Window(width=monitor.width, height=monitor.height, fullscreen=True)
pyglet.gl.glClearColor(1, 1, 1, 1)
#window.event
def on_draw():
window.clear()
sprite.draw()
pyglet.app.run()
I removed some of your code for demo/testing purposes.
The code is by no means perfect, but it will hopefully give you an insight as to how this works).
I'm using raspberry pi3 and I have written hand recognition code using OpenCV with python. But now I want to print text like "left to right" on its live video stream screen, when hand is moving from left to right and print text like "right to left", when hand is moving from right to left.
Now my question is using cv2.putText() we can display text but how do I find the direction that hand is moved from left to right or vice-versa?
Anyone have idea how to display this text this? Please reply. Thanks in advance.
If I understand it correctly, you are already recognising the hand (the Region of Interest aka ROI) and just wonder how to know if it moves left or right. In order to recognise this, you should keep some history of its location. Just remember for a few frames where the hand was.
import numpy as np
from collections import deque
cx_hist = deque(maxlen=10) # how many frames history
while True:
...
M = cv2.moments(contour) # the moment of one contour
cx = int(M['m10']/M['m00']) # the x coordinate of the centroid
cx_hist.append(cx)
diff = [cx_hist[i+1]-cx_hist[i] for i in range(len(cx_hist)-1)]
treshold = 2 # some treshold of significant (pixel) movement
if np.mean(diff) > treshold:
print('positive means movement to the right')
elif np.mean(diff) < treshold*-1:
print('negative means movement to the left')
else:
print('below the tresholds there is little movement')
You have to transform it to puttext yourself. I used the centroid coordinates, but you could pick something else. See http://docs.opencv.org/3.1.0/dd/d49/tutorial_py_contour_features.html
To apply boundary fill in a region i need to draw a free hand shape(random) using mouse in python-opencv
You asked how to draw any giver random shape on a picture using your computer's mouse. Here is a simple solution:
First, you will need to design a method that enables you to draw. So let's inspire ourselves from OpenCV: Mouse as a Paint-Brush where a method is used to draw common regular shapes such as a circle or a rectangle using a mouse. In your case, you will need random drawing as you could do with your hand.
So using that method you can draw points using the mouse and perform an interpolation between them using cv2.line() method:
cv2.line(im,(current_former_x,current_former_y),(former_x,former_y),(0,0,255),5)
Where im is the image you read and while you must memorize the former coordinates of the mouse position all the time:
current_former_x = former_x
current_former_y = former_y
Full OpenCV program:
Here is the code. Do not hesitate to comment anything you wouldn't understand:
'''
Created on Apr 3, 2016
#author: Bill BEGUERADJ
'''
import cv2
import numpy as np
drawing=False # true if mouse is pressed
mode=True # if True, draw rectangle. Press 'm' to toggle to curve
# mouse callback function
def begueradj_draw(event,former_x,former_y,flags,param):
global current_former_x,current_former_y,drawing, mode
if event==cv2.EVENT_LBUTTONDOWN:
drawing=True
current_former_x,current_former_y=former_x,former_y
elif event==cv2.EVENT_MOUSEMOVE:
if drawing==True:
if mode==True:
cv2.line(im,(current_former_x,current_former_y),(former_x,former_y),(0,0,255),5)
current_former_x = former_x
current_former_y = former_y
#print former_x,former_y
elif event==cv2.EVENT_LBUTTONUP:
drawing=False
if mode==True:
cv2.line(im,(current_former_x,current_former_y),(former_x,former_y),(0,0,255),5)
current_former_x = former_x
current_former_y = former_y
return former_x,former_y
im = cv2.imread("darwin.jpg")
cv2.namedWindow("Bill BEGUERADJ OpenCV")
cv2.setMouseCallback('Bill BEGUERADJ OpenCV',begueradj_draw)
while(1):
cv2.imshow('Bill BEGUERADJ OpenCV',im)
k=cv2.waitKey(1)&0xFF
if k==27:
break
cv2.destroyAllWindows()
Demo:
This example from the opencv sample directory allows you to draw an arbitrary rectangle in an image and select the ROI:
https://github.com/Itseez/opencv/blob/master/samples/python/mouse_and_match.py
You can easily add alternatives to draw circles or polygons instead, e.g. by pressing a letter first.
This question already has an answer here:
How to capture multiple camera streams with OpenCV?
(1 answer)
Closed 10 months ago.
So I am attempting to capture from two cameras in openCV (python & windows 7). I capture from one camera just fine, youll also notice I am doing some funky stuff to the image but that doesn't matter. This is the code to attempt to use two
import cv
import time
cv.NamedWindow("camera", 1)
cv.NamedWindow("camera2", 1)
capture = cv.CaptureFromCAM(0)
capture2 = cv.CaptureFromCAM(1)
while True:
img = cv.GetMat(cv.QueryFrame(capture))
img2 = cv.GetMat(cv.QueryFrame(capture2))
dst_image = cv.CloneMat(img)
dst_image2 = cv.CloneMat(img2)
cv.ConvertScale(img, dst_image, 255, -59745.0)
cv.ConvertScale(img2, dst_image2, 255, -59745.0)
cv.ShowImage("camera", dst_image)
cv.ShowImage("camera2", dst_image2)
if cv.WaitKey(10) == 27:
cv.DestroyWindow("camera")
cv.DestroyWindow("camera2")
break
Rather simple. However it won't work. Upon trying to create the matrix from the second camera (second line of code in the loop), I am told that the capture is null. The cameras I am using are logitech and are the same model.
Side note: I also couldnt find the command to count cameras connected in python, so if someone could refer me to that I'd much appreciate it.
--Ashley
EDIT:
It might also be useful to know that windows often prompts me to choose which camera I would like to use. I can't seem to avoid this behavior. Additionally I downloaded some security like software that successful runs both cameras at once. It is not open source or anything like that. So clearly, this is possible.
I was having the same problem with two lifecam studio webcams. After a little reading, I think that problem related to overloading the bandwidth on the USB-bus. Both cameras began working if I 1.) lowered the resolution (320 x 240 each) or 2.) lowered the frame rate (~99 msec # 800 x 600). Attached is the code that got I working:
import cv
cv.NamedWindow("Camera 1")
cv.NamedWindow("Camera 2")
video1 = cv.CaptureFromCAM(0)
cv.SetCaptureProperty(video1, cv.CV_CAP_PROP_FRAME_WIDTH, 800)
cv.SetCaptureProperty(video1, cv.CV_CAP_PROP_FRAME_HEIGHT, 600)
video2 = cv.CaptureFromCAM(1)
cv.SetCaptureProperty(video2, cv.CV_CAP_PROP_FRAME_WIDTH, 800)
cv.SetCaptureProperty(video2, cv.CV_CAP_PROP_FRAME_HEIGHT, 600)
loop = True
while(loop == True):
frame1 = cv.QueryFrame(video1)
frame2 = cv.QueryFrame(video2)
cv.ShowImage("Camera 1", frame1)
cv.ShowImage("Camera 2", frame2)
char = cv.WaitKey(99)
if (char == 27):
loop = False
cv.DestroyWindow("Camera 1")
cv.DestroyWindow("Camera 2")
here is a small code:
import VideoCapture
cam0 = VideoCapture.Device(0)
cam1 = VideoCapture.Device(1)
im0 = cam0.getImage()
im1 = cam1.getImage()
im0 and im1 are PIL images. You can now use scipy to convert it into arrays as follows:
import scipy as sp
imarray0 = asarray(im0)
imarray1 = asarray(im1)
imarray0 and imarray1 are numpy 2D arrays, which you can furthere use with openCV functions.
In case you are using windows for coding, why dont you try VideoCapture module. It is very easy to use and gives a PIL image as output. You can later change it to a 2D array.