How image capturing with Pygame actually works? - python

My goal is to set up a webcam with Raspberry Pi to be active during a certain time (let's say 2 minutes). During this time I should be able to capture a still image at any time and save that image to a folder. I chose to work with pygame. My project is about capturing an image as soon as a sensor has been triggered, so it needs to be very responsive.
According to the documentation for Pygame camera here it says:
open()
Opens the camera device, attempts to initialize it, and begins
recording images to a buffer. The camera must be started before any of
the below functions can be used.
get_image()
Pulls an image off of the buffer as an RGB Surface. It can optionally
reuse an existing Surface to save time. The bit-depth of the surface
is either 24 bits or the same as the optionally supplied Surface.
So for my case, the get_image() simply seems to pull out the first image captured after start() has been called. My question is, how can I reach the buffer with all captured images or how does the capturing actually work? I can't find a solution for capturing and saving a still image (at any time) after in between I call start() and stop() on the pygame camera. Since the start() function initiates during a few seconds, it is simply too slow to just call start(), get_image() and stop() after one another. Any help or suggestions would be appreciated.
See my python code below:
def activateCapturing:
pygame.init()
pygame.camera.init()
cam = pygame.camera.Camera("/dev/video0",(320,180))
cam.start()
pngdata = None
imageAsbytes = []
activated = True
while activated:
if readSensor():
img = cam.get_image()
pygame.image.save(img, "/tmp/image.png")
activated = False
with open("/tmp/image.png", 'rb') as f:
pngdata = f.read()
imageAsbytes = bytearray(pngdata)
cam.stop()
return imageAsbytes
Thanks in advance!

You simpy do not stop the camera after capturing one image.
See https://www.pygame.org/docs/tut/CameraIntro.html.
get_image() gets the image the camera currently sees from the buffer - the buffer NOT being "all pictures since start()" but simply the currently viewed image.
You use stop() after your 3s of "capturing window" to stop aquiering more images.
If you are after performance, you might want to scroll down that page and review the section about Capturing a Live Stream - if you did the same (w/o displaying the stream) and just saved 1 image to disk when needed you should get a decent framerate.
Api: get_image()

Related

Save video via python on windows logoff

I am using this code to capture everything on my monitor and save it to mp4 file:
# screen recording config
sct = mss()
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(recording_file_name_location, fourcc, 20.0, (1920, 1080))
# while loop that captures the while screen image by image
while True:
sct_img = sct.grab(screen_resolution)
out.write(np.array(sct_img))
The problem is I want this code to stop recording once I logoff from my Windows machine, that does happen, and the video is saved on my Desktop, but it is not playable.
If I run this code via PyCharm and I end the code via PyCharm (CTRL + C or I use PyCharm stop button), then it does save the mp4 file and I can play it without any problems.
Also, if I start the code from the CMD it works fine, but if I close CMD windows on the "X" in the top right corner, video is again unplayable.
I guess that this happens due to killing Python process in the background, so the script/code never saves the file properly.
Do you guys have any suggestion for this problem? How to save the mp4 video once I logoff or turn off my Windows machine?
Thank you in advance
The process was killed abruptly.
The video is not playable because out.release() could not be called. Tthe video writer was not given the chance to write index structures into the video file, which can only be done when the file is complete.
Simple fix: make a .ts file instead of any other format/extension. These "transport stream" formats never need finalizing.
Perhaps you need to learn about how Windows ends processes when you log off. Usually Windows sends each application/window a WM_QUERYENDSESSION and WM_ENDSESSION message, telling it to end itself before it gets ended by force.

BeagleBone Black OpenCV Python is too slow

I try to get images from webcam wtih opencv and python. Code is so basic like:
import cv2
import time
cap=cv2.VideoCapture(0)
cap.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH,640)
cap.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT,480)
cap.set(cv2.cv.CV_CAP_PROP_FPS, 20)
a=30
t=time.time()
while (a>0):
now=time.time()
print now-t
t=now
ret,frame=cap.read()
#Some processes
print a,ret
print frame.shape
a=a-1
k=cv2.waitKey(20)
if k==27:
break
cv2.destroyAllWindows()
But it works slowly. output of program:
VIDIOC_QUERYMENU: Invalid argument
VIDIOC_QUERYMENU: Invalid argument
VIDIOC_QUERYMENU: Invalid argument
VIDIOC_QUERYMENU: Invalid argument
VIDIOC_QUERYMENU: Invalid argument
VIDIOC_QUERYMENU: Invalid argument
HIGHGUI ERROR: V4L: Property <unknown property string>(5) not supported by device
8.82148742676e-06
select timeout
30 True
(480, 640, 3)
2.10035800934
select timeout
29 True
(480, 640, 3)
2.06729602814
select timeout
28 True
(480, 640, 3)
2.07144904137
select timeout
Configuration:
Beaglebone Black RevC
Debian-wheezly
opencv 2.4
python 2.7
The "secret" to obtaining higher FPS when processing video streams with OpenCV is to move the I/O (i.e., the reading of frames from the camera sensor) to a separate thread.
When calling read() method along with cv2.VideoCapture function, it makes the entire process very slow as it has to wait for each I/O operation to be completed for it to move on to the next one (Blocking Process).
In order to accomplish this FPS increase/latency decrease, our goal is to move the reading of frames from a webcam or USB device to an entirely different thread, totally separate from our main Python script.
This will allow frames to be read continuously from the I/O thread, all while our root thread processes the current frame. Once the root thread has finished processing its frame, it simply needs to grab the current frame from the I/O thread. This is accomplished without having to wait for blocking I/O operations.
You can read Increasing webcam FPS with Python and OpenCV to know the steps in implementing threads.
EDIT
Based on the discussions in our comments, I feel you could rewrite the code as follows:
import cv2
cv2.namedWindow("output")
cap = cv2.VideoCapture(0)
if cap.isOpened(): # Getting the first frame
ret, frame = cap.read()
else:
ret = False
while ret:
cv2.imshow("output", frame)
ret, frame = cap.read()
key = cv2.waitKey(20)
if key == 27: # exit on Escape key
break
cv2.destroyWindow("output")
I encountered a similar problem when I was working on a project using OpenCV 2.4.9 on the Intel Edison platform. Before doing any processing, it was taking roughly 80ms just to perform the frame grab. It turns out that OpenCV's camera capture logic for Linux doesn't seem to be implemented properly, at least in the 2.4.9 release. The underlying driver only uses one buffer, so it's not possible to use multi-threading in the application layer to work around it - until you attempt to grab the next frame, the only buffer in the V4L2 driver is locked.
The solution is to not use OpenCV's VideoCapture class. Maybe it was fixed to use a sensible number of buffers at some point, but as of 2.4.9, it wasn't. In fact, if you look at this article by the same author as the link provided by #Nickil Maveli, you'll find that as soon as he provides suggestions for improving the FPS on a Raspberry Pi, he stops using OpenCV's VideoCapture. I don't believe that is a coincidence.
Here's my post about it on the Intel Edison forum: https://communities.intel.com/thread/58544.
I basically wound up writing my own class to handle the frame grabs, directly using V4L2. That way you can provide a circular list of buffers and allow the frame grabbing and application logic to be properly decoupled. That was done in C++ though, for a C++ application. Assuming the above link delivers on its promises, that might be a far easier approach. I'm not sure whether it would work on BeagleBone, but maybe there's something similar to PiCamera out there. Good luck.
EDIT: I took a look at the source code for 2.4.11 of OpenCV. It looks like they now default to using 4 buffers, but you must be using V4L2 to take advantage of that. If you look closely at your error message HIGHGUI ERROR: V4L: Property..., you see that it references V4L, not V4L2. That means that the build of OpenCV you're using is falling back on the old V4L driver. In addition to the singular buffer causing performance issues, you're using an ancient driver that probably has many limitations and performance problems of its own.
Your best bet would be to build OpenCV yourself to make sure that it uses V4L2. If I recall correctly, the OpenCV configuration process checks whether the V4L2 drivers are installed on the machine and builds it accordingly, so you'll want to make sure that V4L2 and any related dev packages are installed on the machine you use to build OpenCV.
try this one ! I replaced some code in the cap.set() section
import cv2
import time
cap=cv2.VideoCapture(0)
cap.set(3,640)
cap.set(4,480)
cap.set(5, 20)
a=30
t=time.time()
while (a>0):
now=time.time()
print now-t
t=now
ret,frame=cap.read()
#Some processes
print a,ret
print frame.shape
a=a-1
k=cv2.waitKey(20)
if k==27:
break
cv2.destroyAllWindows()
output (pc webcam) your code was wrong for me.
>>0.0
>>30 True
>>(480, 640, 3)
>>0.246999979019
>>29 True
>>(480, 640, 3)
>>0.0249998569489
>>28 True
>>(480, 640, 3)
>>0.0280001163483
>>27 True
>>(480, 640, 3)
>>0.0320000648499

Taking a snapshot from webcam during using the camera in anther function

I am using the camera to detect cars 24/7 using Python, Opencv and a normal usb webcam.
In order to take a snapshot I made a function to call it when needed
def SendPic () :
capture = cv.CaptureFromCAM(0)
img = cv.QueryFrame(capture)
cv.SaveImage('pic.jpg', img)
It works fine when used alone but when used inside my code this error comes up
libv4l1: error setting pixformat: Device or resource busy
HIGHGUI ERROR: libv4l unable to ioctl VIDIOCSPICT
And the image is not saved or even captured
How to take this snapshot without stopping the camera from detecting the cars? What command can I use to stop the camera to take a capture and then return to its main function?
If I was You, I'd make the part taking pictures a separate module, and make the car detection module and snapshot module call to the first one using mutexes. You cannot have two separate entities controlling the same hardware piece.

Asynchronous image load in pygame

I am writing a small application in python 2.7 using pygame in which I would like to smoothly display animated tiles on screen. I am writing on Ubuntu, but target platform is Raspberry Pi if that's relevant. The challenge is that the textures for these animated tiles are stored on a web server and are to be loaded dynamically over time, not all at once. I'd like to be able to load these images into pygame with no noticeable hitch in my animation or input response. The load frequency is pretty low, like grabbing a couple jpgs every 30 seconds. I am willing to wait a long time for the image to load in the background if it means the main input/animation thread remains unhitched.
So using the multiprocessing module, I am able to download images from a server asynchronously into a buffer, and then pass this buffer to my main pygame process over a multiprocessing.queues.SimpleQueue object. However, once the buffer is accessible in the pygame process, there is still a hitch in my application while the buffer is loaded into a Surface for blitting via pygame.image.frombuffer().
Is there a way to make this pygame.image.load() call asynchronous so that my animation in the game, etc is not blocked? I can't think of an obvious solution due to GIL.
If I was writing a regular OpenGL program in C, I would be able to asynchronously write data to the GPU using a pixel buffer object, correct? Does pygame expose any part of this API by any chance? I can't seem to find it in the pygame docs, which I am pretty new to, so pardon me if the answer is obvious. Any help pointing out how pygame's terminology translates to OpenGL API would be a big help, as well any relevant examples in which pygame can initialize a texture asynchronously would be amazing!
If pygame doesn't offer this functionality, what are my options? Is there a way to do this is with PySDL2?
EDIT: Ok, so I tried using pygame.image.frombuffer, and it doesn't really cut down on the hitching I am seeing. Any ideas of how I can make this image load truly asynchronous? Here is some code snippets illustrating what I am currently doing.
Here's the async code I have that sits in a separate process from pygame
def _worker(in_queue, out_queue):
done = False
while not done:
if not in_queue.empty():
obj = in_queue.get()
# if a bool is passed down the queue, set the done flag
if isinstance(obj, bool):
done = obj
else:
url, w, h = obj
# grab jpg at given url. It is compressed so we use PIL to parse it
jpg_encoded_str = urllib2.urlopen(url).read()
# PIL.ImageFile
parser = ImageFile.Parser()
parser.feed(jpg_encoded_str)
pil_image = parser.close()
buff = pil_image.tostring()
# place decompressed buffer into queue for consumption by main thread
out_queue.put((url, w, h, buff))
# and so I create a subprocess that runs _worker function
Here's my update loop that runs in the main thread. It looks to see if the _Worker process has put anything into the out_queue, and if so loads it into pygame:
def update():
if not out_queue.empty():
url, w, h, buff = img_buffer_queue.get()
# This call is where I get a hitch in my application
image = pygame.image.frombuffer(buff, (w, h), "RGB")
# Place loaded image on the pygame.Sprite, etc in the callback
callback = on_load_callbacks.pop(url, None)
if callback:
callback(image, w, h)
Perhaps you could try converting the image from a buffer object to a more efficient object in the worker function, perhaps a surface, and then send that computed surface to the out queue?
This way, generation of the surface object won't block the main thread.

Problem with Python and IP camera

I'm having problems to get a video stream from a IP camera I have. I'm using opencv to get the images from it. Here's the code i have:
import sys
import cv
video="http://prot-on.dyndns.org:8080/video2.mjpeg"
capture =cv.CaptureFromFile(video)
cv.NamedWindow('Video Stream', 1 )
while True:
# capture the current frame
frame = cv.QueryFrame(capture)
if frame is None:
break
else:
#detect(frame)
cv.ShowImage('Video Stream', frame)
if k == 0x1b: # ESC
print 'ESC pressed. Exiting ...'
break
Actually, this thing works, but it takes too much time to display the images. I'm guessing it's because of this error from ffmpeg.
[mjpeg # 0x8cd0940]max_analyze_duration reached
[mjpeg # 0x8cd0940]Estimating duration from bitrate, this may be inaccurate
I'm not a python expert so any help would be appreciated!
First, mjpeg is a relatively simple video format. If you read your IP camera's manual, it's very like that you can find how to display the video in browser with a bit JavaScript code. In fact, if you open link of http://prot-on.dyndns.org:8080/video2.mjpeg in Google Chrome, you would see the video without any problem. (Maybe you shouldn't leave the real URL of your camera)
Second, as far as I can see, the frame rate of your camera is pretty slow. That might due to Internet latency or your camera's setting. Compare what you see in Chrome with the video displayed by your code, if they are of same quality, then it's not your code's problem.

Categories