-
-
Save CarlosGS/b8462a8a1cb69f55d8356cbb0f3a4d63 to your computer and use it in GitHub Desktop.
# Fast reading from the raspberry camera with Python, Numpy, and OpenCV | |
# Allows to process grayscale video up to 124 FPS (tested in Raspberry Zero Wifi with V2.1 camera) | |
# | |
# Made by @CarlosGS in May 2017 | |
# Club de Robotica - Universidad Autonoma de Madrid | |
# http://crm.ii.uam.es/ | |
# License: Public Domain, attribution appreciated | |
import cv2 | |
import numpy as np | |
import subprocess as sp | |
import time | |
import atexit | |
frames = [] # stores the video sequence for the demo | |
max_frames = 300 | |
N_frames = 0 | |
# Video capture parameters | |
(w,h) = (640,240) | |
bytesPerFrame = w * h | |
fps = 250 # setting to 250 will request the maximum framerate possible | |
# "raspividyuv" is the command that provides camera frames in YUV format | |
# "--output -" specifies stdout as the output | |
# "--timeout 0" specifies continuous video | |
# "--luma" discards chroma channels, only luminance is sent through the pipeline | |
# see "raspividyuv --help" for more information on the parameters | |
videoCmd = "raspividyuv -w "+str(w)+" -h "+str(h)+" --output - --timeout 0 --framerate "+str(fps)+" --luma --nopreview" | |
videoCmd = videoCmd.split() # Popen requires that each parameter is a separate string | |
cameraProcess = sp.Popen(videoCmd, stdout=sp.PIPE) # start the camera | |
atexit.register(cameraProcess.terminate) # this closes the camera process in case the python scripts exits unexpectedly | |
# wait for the first frame and discard it (only done to measure time more accurately) | |
rawStream = cameraProcess.stdout.read(bytesPerFrame) | |
print("Recording...") | |
start_time = time.time() | |
while True: | |
cameraProcess.stdout.flush() # discard any frames that we were not able to process in time | |
# Parse the raw stream into a numpy array | |
frame = np.fromfile(cameraProcess.stdout, count=bytesPerFrame, dtype=np.uint8) | |
if frame.size != bytesPerFrame: | |
print("Error: Camera stream closed unexpectedly") | |
break | |
frame.shape = (h,w) # set the correct dimensions for the numpy array | |
# The frame can be processed here using any function in the OpenCV library. | |
# Full image processing will slow down the pipeline, so the requested FPS should be set accordingly. | |
#frame = cv2.Canny(frame, 50,150) | |
# For instance, in this example you can enable the Canny edge function above. | |
# You will see that the frame rate drops to ~35fps and video playback is erratic. | |
# If you then set fps = 30 at the beginning of the script, there will be enough cycle time between frames to provide accurate video. | |
# One optimization could be to work with a decimated (downscaled) version of the image: deci = frame[::2, ::2] | |
frames.append(frame) # save the frame (for the demo) | |
#del frame # free the allocated memory | |
N_frames += 1 | |
if N_frames > max_frames: break | |
end_time = time.time() | |
cameraProcess.terminate() # stop the camera | |
elapsed_seconds = end_time-start_time | |
print("Done! Result: "+str(N_frames/elapsed_seconds)+" fps") | |
print("Writing frames to disk...") | |
out = cv2.VideoWriter("slow_motion.avi", cv2.cv.CV_FOURCC(*"MJPG"), 30, (w,h)) | |
for n in range(N_frames): | |
#cv2.imwrite("frame"+str(n)+".png", frames[n]) # save frame as a PNG image | |
frame_rgb = cv2.cvtColor(frames[n],cv2.COLOR_GRAY2RGB) # video codec requires RGB image | |
out.write(frame_rgb) | |
out.release() | |
print("Display frames with OpenCV...") | |
for frame in frames: | |
cv2.imshow("Slow Motion", frame) | |
cv2.waitKey(1) # request maximum refresh rate | |
cv2.destroyAllWindows() | |
Hi,
Thanks for the update, I had to modify few other things, this is the relevant part which works now in Python3:
print("Done! Result: "+str(N_frames/elapsed_seconds)+" fps")
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
print("Writing frames to disk...")
out = cv2.VideoWriter("slow_motion.avi", fourcc, 30, (w,h))_**
Thanks a lot for sharing!! :)
Heyy, I had the same issues, I solved them but now I am not getting frame.size equal to bytesPerFrame
Recording... BytesPerFrame: 307200 Frame.size: 65536 Camera Process Stopped!~~ Writing frames to disk...
Please help me to solve this issue!!!
here`s the code with all fixes done:
# Fast reading from the raspberry camera with Python, Numpy, and OpenCV
# Allows to process grayscale video up to 124 FPS (tested in Raspberry Zero Wifi with V2.1 camera)
#
# Made by @CarlosGS in May 2017
# Club de Robotica - Universidad Autonoma de Madrid
# http://crm.ii.uam.es/
# License: Public Domain, attribution appreciated
import cv2
import numpy as np
import subprocess as sp
import time
import atexit
frames = [] # stores the video sequence for the demo
max_frames = 300
N_frames = 0
# Video capture parameters
(w,h) = (640,240)
bytesPerFrame = w * h
fps = 250 # setting to 250 will request the maximum framerate possible
# "raspividyuv" is the command that provides camera frames in YUV format
# "--output -" specifies stdout as the output
# "--timeout 0" specifies continuous video
# "--luma" discards chroma channels, only luminance is sent through the pipeline
# see "raspividyuv --help" for more information on the parameters
videoCmd = "raspividyuv -w "+str(w)+" -h "+str(h)+" --output - --timeout 0 --framerate "+str(fps)+" --luma --nopreview"
videoCmd = videoCmd.split() # Popen requires that each parameter is a separate string
#cameraProcess = sp.Popen(videoCmd, stdout=sp.PIPE) # start the camera
cameraProcess = sp.Popen(videoCmd, stdout=sp.PIPE, bufsize=1)
atexit.register(cameraProcess.terminate) # this closes the camera process in case the python scripts exits unexpectedly
# wait for the first frame and discard it (only done to measure time more accurately)
rawStream = cameraProcess.stdout.read(bytesPerFrame)
print("Recording...")
start_time = time.time()
while True:
cameraProcess.stdout.flush() # discard any frames that we were not able to process in time
# Parse the raw stream into a numpy array
#frame = np.fromfile(cameraProcess.stdout, count=bytesPerFrame, dtype=np.uint8)
frame = np.frombuffer(cameraProcess.stdout.read(bytesPerFrame), dtype=np.uint8)
if frame.size != bytesPerFrame:
print("Error: Camera stream closed unexpectedly")
break
frame.shape = (h,w) # set the correct dimensions for the numpy array
# The frame can be processed here using any function in the OpenCV library.
# Full image processing will slow down the pipeline, so the requested FPS should be set accordingly.
#frame = cv2.Canny(frame, 50,150)
# For instance, in this example you can enable the Canny edge function above.
# You will see that the frame rate drops to ~35fps and video playback is erratic.
# If you then set fps = 30 at the beginning of the script, there will be enough cycle time between frames to provide accurate video.
# One optimization could be to work with a decimated (downscaled) version of the image: deci = frame[::2, ::2]
frames.append(frame) # save the frame (for the demo)
#del frame # free the allocated memory
N_frames += 1
if N_frames > max_frames: break
end_time = time.time()
cameraProcess.terminate() # stop the camera
elapsed_seconds = end_time-start_time
print("Done! Result: "+str(N_frames/elapsed_seconds)+" fps")
print("Writing frames to disk...")
#out = cv2.VideoWriter("slow_motion.avi", cv2.cv.CV_FOURCC(*"MJPG"), 30, (w,h))
fourcc = cv2.VideoWriter_fourcc(*"MJPG")
out = cv2.VideoWriter("slow_motion.avi", fourcc, 30, (w,h))
for n in range(N_frames):
#cv2.imwrite("frame"+str(n)+".png", frames[n]) # save frame as a PNG image
frame_rgb = cv2.cvtColor(frames[n],cv2.COLOR_GRAY2RGB) # video codec requires RGB image
out.write(frame_rgb)
out.release()
print("Display frames with OpenCV...")
for frame in frames:
cv2.imshow("Slow Motion", frame)
cv2.waitKey(1) # request maximum refresh rate
cv2.destroyAllWindows()
@zoldaten , you are my hero! Thank you very much for your fixes!
I made a C/C++ version of this script. My NoIR Pi Zero camera is limited to 90fps, so I could not gain any benefit from using raspividyuv. But maybe this is helpful for other users:
// License: Public Domain, attribution appreciated
#include <stdio.h>
#include <stdint.h>
#include <sstream>
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>
int main(int argc, char** argv)
{
int max_frames = 300;
std::vector<cv::Mat> frames(max_frames); // stores the video sequence for the demo
// Video capture parameters
int width = 640;
int height = 240;
int bytesPerFrame = width*height;
int fps = 250; // setting to 250 will request the maximum framerate possible
// "raspividyuv" is the command that provides camera frames in YUV format
// "--output -" specifies stdout as the output
// "--timeout 0" specifies continuous video
// "--luma" discards chroma channels, only luminance is sent through the pipeline
// see "raspividyuv --help" for more information on the parameters
std::stringstream ss;
ss << "/bin/raspividyuv -w " << std::to_string(width) << " -h " << std::to_string(height) << " --output - --timeout 0 --framerate " << std::to_string(fps) << " --luma --nopreview";
std::string videoCmd = ss.str();
// start the camera
FILE *cameraProcess;
if ((cameraProcess = popen(videoCmd.c_str(), "r")) == NULL) {
printf("Error starting raspividyuv\n");
return -1;
}
// create buffer for camera data
char* buffer = new char[bytesPerFrame];
cv::Mat frame(height, width, CV_8UC1, (unsigned char*)buffer);
// wait for the first frame and discard it (only done to measure time more accurately)
fread(buffer, bytesPerFrame, 1, cameraProcess);
printf("Recording...\n");
long long start_time = cv::getTickCount();
for (int frameNo = 0; frameNo < max_frames; frameNo++)
{
// Parse the raw stream into our buffer
fread(buffer, bytesPerFrame, 1, cameraProcess);
// The frame can be processed here using any function in the OpenCV library.
// ...
frame.copyTo(frames[frameNo]); // save the frame (for the demo)
}
long long end_time = cv::getTickCount();
pclose(cameraProcess);
delete[] buffer;
printf("Done! Result: %f fps\n", (cv::getTickFrequency() / (end_time - start_time))*max_frames);
printf("Writing frames to disk...\n");
cv::VideoWriter out("/home/mypi/slow_motion.avi", CV_FOURCC('M', 'J', 'P', 'G'), 30, cv::Size(width, height));
cv::Mat rgbFrame;
for (int frameNo = 0; frameNo < max_frames; frameNo++) {
cvtColor(frames[frameNo], rgbFrame, cv::COLOR_GRAY2BGR); // video codec requires BGR image
out.write(rgbFrame);
}
printf("Display frames with OpenCV...\n");
for (int frameNo = 0; frameNo < max_frames; frameNo++) {
cv::imshow("Slow Motion", frames[frameNo]);
cv::waitKey(1); // request maximum refresh rate
}
return 0;
}
For resolution of 640x480 etc, the frames seem to get cropped to a smaller area at FPS above 40. This seems to be an issue after an update since it was working ok before the update.
1280x720 : Image ok & not cropped - fps at capped at ~50
960x480: Image ok & not cropped - fps at capped at ~50
640x480: Image cropped - fps at 120
640x480: Image NOT cropped - fps at 30
320x240: Image cropped - fps at 120
320x240: Image NOT cropped - fps at 30
Any idea how to get the full image instead of the cropped image at 640x480 back? Thanks.
To fix this issue in Python3, you just need to replace:
with