I would like to process a video that I receive through an upload component in plotly dash. Currently I am creating a tempfile and then reading the video with opencv. However, I would like to avoid writing the file to disk. Is there a way to process the video directly from memory? My code looks like this:
def process_motion(contents, filename):
print("Processing video: " + filename)
content_type, content_string = contents.split(',')
decoded = base64.b64decode(content_string)
with tempfile.NamedTemporaryFile() as temp:
temp.write(decoded)
cap = cv2.VideoCapture(temp.name)
frames = cap.get(cv2.CAP_PROP_FRAME_COUNT)
fps = cap.get(cv2.CAP_PROP_FPS)
# some calculations
Interesting question
as #Dan MaĊĦek has correctly mentioned, the correct keyword to search would be video decoding. The thing is, like OpenCV, must of decoding libraries in python using FFMPEG as the backend and most of them are wrappers around the FFMPEG executable (run FFMPEG in subprocess), and since FFMPEG accepts file path for decoding (or URL for decoding video stream) those wrappers also accept file path.
AFAIK, the only wrapper library which accepts bytes as input to decode video and get all frames is imageio which behind the scene also converts bytes to a temp file and decode it using FFMPEG.
Here is the comparison of using imageio and your solution.
import base64
import imageio.v3 as iio
import cv2
from time import perf_counter
import tempfile
import numpy as np
with open("example.mp4", "rb") as videoFile:
base_text = base64.b64encode(videoFile.read())
buffer = base64.b64decode(base_text)
start = perf_counter()
frames = iio.imread(buffer, index=None, format_hint=".mp4")
print("iio ffmpeg pluing: ", perf_counter() - start)
print(frames.shape)
start = perf_counter()
frames = iio.imread(buffer, index=None, plugin="pyav", format_hint=".mp4")
print("iio pyav pluing: ", perf_counter() - start)
print(frames.shape)
start = perf_counter()
with tempfile.NamedTemporaryFile() as temp:
temp.write(buffer)
cap = cv2.VideoCapture(temp.name)
frames = cap.get(cv2.CAP_PROP_FRAME_COUNT)
fps = cap.get(cv2.CAP_PROP_FPS)
success, frame = cap.read()
frames = [frame]
while success:
success, frame = cap.read()
frames.append(frame)
frames = np.stack(frames[:-1])
print("cv2: ", perf_counter() - start)
print(frames.shape)
============================================
iio ffmpeg pluing: 0.3905044999992242
(901, 270, 480, 3)
No accelerated colorspace conversion found from yuv420p to rgb24.
iio pyav pluing: 0.3710011249931995
(901, 270, 480, 3)
cv2: 0.2388888749992475
(901, 270, 480, 3)
Note: if you are able to create a python wrapper for FFMPEG it is possible to convert a bytes array to a format that is acceptable FFMPEG's stream decoding function. as explained here
Related
I'm trying to make a video from images with python and OpenCV but when I run the script the video is generated but when I try to open it I give this error:
This is the script:
import cv2
import numpy as np
import glob
frameSize = (1920, 1080)
out = cv2.VideoWriter('output_video.avi',cv2.VideoWriter_fourcc(*'DIVX'), 60, frameSize)
for filename in glob.glob('/folder_path/*.jpg'):
img = cv2.imread(filename)
out.write(img)
out.release()
UPDATE:
If I use
out = cv2.VideoWriter('output_video.avi',cv2.VideoWriter_fourcc(*'MJPG'), 60, frameSize)
Instead
out = cv2.VideoWriter('output_video.avi',cv2.VideoWriter_fourcc(*'DIVX'), 60, frameSize)
The video start but I can see anything
Ok, I have found a solution.
This is the final code:
import cv2,os
from os.path import isfile, join
def convert_pictures_to_video(pathIn, pathOut, fps, time):
# this function converts images to video
frame_array=[]
files=[f for f in os.listdir(pathIn) if isfile(join(pathIn,f))]
#files.remove(".DS_Store")
for i in range (len(files)):
filename=pathIn+files[i]
# reading images
img=cv2.imread(filename)
# img=cv2.resize(img,(1400,1000))
height, width, layers = img.shape
size=(width,height)
for k in range (time):
frame_array.append(img)
out=cv2.VideoWriter(pathOut,cv2.VideoWriter_fourcc(*'mp4v'), fps, size)
for i in range(len(frame_array)):
out.write(frame_array[i])
out.release()
pathIn= '/pathIn-folder/'
pathOut='/pathOut-folder/output.avi'
fps=1
time=20 # the duration of each picture in the video
convert_pictures_to_video(pathIn, pathOut, fps, time)
You can find more info and a tutorial here.
REMEMBER
If you have a mac there are two important things to do.
First you have to uncomment #files.remove(".DS_Store") because when you create a new folder on macOS there is an extra hidden file called .DS_Store
You can't open the output video with Quick Time Player
You have to use another software like VLC
I have a working python script that uses the video writer from opencv.
source https://gist.github.com/stanchiang/b4e4890160a054a9c1d65f9152172600
If i take in a file, and regardless of whether I simply pass the video frame through to the writer (effectively duplicating the file) or if i try to edit the frame, the file is always larger. I would like for it to be no larger than the original (since if you read my script i'm blurring a lot of stuff).
After checking their metadata, with ffprobe -v quiet -print_format json -show_format -show_streams inputFile.mp4 I notice that the bitrate of the new file is over 5.5x higher than before.
source https://www.diffchecker.com/8r2syeln
since bitrate is a big determinant of file size, I'm wondering if
i can hardcode the desired bitrate of the new file through the video writer
whether for some reason the heavily increased bit rate is needed
basically this answer https://stackoverflow.com/a/13298538/1079379
# import packages
from PIL import Image
from subprocess import Popen, PIPE
from imutils.video import VideoStream
from imutils.object_detection import non_max_suppression
from imutils import paths
import cv2
import numpy as np
import imutils
# ffmpeg setup
p = Popen(['ffmpeg', '-y', '-f', 'image2pipe', '-vcodec', 'mjpeg', '-r', '24', '-i', '-', '-vcodec', 'h264', '-qscale', '5', '-r', '24', 'video.mp4'], stdin=PIPE)
video = cv2.VideoCapture('videos.mp4')
while True:
ret, frame = video.read()
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
im = Image.fromarray(frame)
im.save(p.stdin, 'JPEG')
else:
break
p.stdin.close()
p.wait()
video.release()
cv2.destroyAllWindows()
My VidGear Python Library's WriteGear API automates the process of pipelining OpenCV frames into FFmpeg on any platform and at the same time provides same opencv-python syntax. Here's a basic python example:
# import libraries
from vidgear.gears import WriteGear
import cv2
output_params = {"-vcodec":"libx264", "-crf": 0, "-preset": "fast"} #define (Codec,CRF,preset) FFmpeg tweak parameters for writer
stream = cv2.VideoCapture(0) #Open live webcam video stream on first index(i.e. 0) device
writer = WriteGear(output_filename = 'Output.mp4', compression_mode = True, logging = True, **output_params) #Define writer with output filename 'Output.mp4'
# infinite loop
while True:
(grabbed, frame) = stream.read()
# read frames
# check if frame empty
if not is grabbed:
#if True break the infinite loop
break
# {do something with frame here}
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# write a modified frame to writer
writer.write(gray)
# Show output window
cv2.imshow("Output Frame", frame)
key = cv2.waitKey(1) & 0xFF
# check for 'q' key-press
if key == ord("q"):
#if 'q' key-pressed break out
break
cv2.destroyAllWindows()
# close output window
stream.release()
# safely close video stream
writer.close()
# safely close writer
Source:https://abhitronix.github.io/vidgear/latest/gears/writegear/compression/usage/#using-compression-mode-with-opencv
You can check out VidGear Docs for more advanced applications and features.
I have a Binary string of mp4 video file and want to convert it back to mp4 file, to get single frame out of there with openCV library
import cv2
import tempfile
temp_path = tempfile.gettempdir()
video_binary_string = 'AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAQC0ttZGF0AQIUGRQmM...'
with open(temp_path+'/video.mp4', 'wb') as wfile:
wfile.write(video_binary_string)
cap = cv2.VideoCapture(temp_path+'/video.mp4')
success, frame = cap.read()
print success
>>> False
If frame was read correctly, it would be True
I understand that this is not the correct way of writing video file. I tried to do it with FFMPEG but I can't understand it's documentation.
Please give an example with simple explanations how to convert binary string back to video, with this or other way.
The solution for my problem is that I have to first decode encoded string
import cv2
import tempfile
import base64
temp_path = tempfile.gettempdir()
video_binary_string = 'AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAQC0ttZGF0AQIUGRQmM...'
decoded_string = base64.b64decode(video_binary_string)
with open(temp_path+'/video.mp4', 'wb') as wfile:
wfile.write(decoded_string)
cap = cv2.VideoCapture(temp_path+'/video.mp4')
success, frame = cap.read()
print success
>>> True
I am trying to export video as .mp4 with openCV. I have tried several codecs but for now I had no success.
This is a function that constructs a video from frames:
def create_movie(self, out_directory, fps, total_frames):
img1 = cv2.imread("temp/scr0.png")
height, width, layers = img1.shape
codec = cv2.cv.CV_FOURCC('X','V','I','D')
video = cv2.VideoWriter(out_directory, codec, fps, (width, height))
for i in range(total_frames):
img_name = "temp/scr" + str(i) + ".png"
img = cv2.imread(img_name)
video.write(img)
video.release()
cv2.destroyAllWindows()
I usually get next error message, using different codecs:
Tag XVID/0x44495658 incompatible with output codec id '13'
Is is possible to do this and how?
There is a non-direct solution. You export as .avi and then convert to .mp4 using python's call which calls terminal command.
from subprocess import call
dir = out_directory.strip(".avi")
command = "avconv -i %s.avi -c:v libx264 -c:a copy %s.mp4" % (dir, dir)
call(command.split())
May be a little bit late to answer this, but if you want to write an .MP4 file with OpenCV try this:
import cv2
#your previous code here
fourcc = cv2.VideoWriter_fourcc(*'a\0\0\0')
out = cv2.VideoWriter('out.mp4', fourcc, fps, res)
#the character '\0' is the Null-Terminator or simply 0x00 in the ASCII-Table
#tag: *'a\0\0\0' corresponds to 0x00000061
#your following code here
I am looking for a way to save the current frame, from a specified live Twitch.tv channel, to disk. Any programming language is welcomed.
So far I've found a possible solution using Python here but unfortunately it doesn't work.
import time, Image
import cv2
from livestreamer import Livestreamer
# change to a stream that is actually online
livestreamer = Livestreamer()
plugin = livestreamer.resolve_url("http://twitch.tv/flosd")
streams = plugin.get_streams()
stream = streams['best']
# download enough data to make sure the first frame is there
fd = stream.open()
data = ''
while len(data) < 3e5:
data += fd.read()
time.sleep(0.1)
fd.close()
fname = 'stream.bin'
open(fname, 'wb').write(data)
capture = cv2.VideoCapture(fname)
imgdata = capture.read()[1]
imgdata = imgdata[...,::-1] # BGR -> RGB
img = Image.fromarray(imgdata)
img.save('frame.png')
Apparently cv2.VideoCapture(fname) returns none although it managed to write about 300K of information to stream.bin