I have implemented a Python script which displays a video to the user and records it. The video is saved either compressed or uncompressed.
In an old program I have seen, that the "DIVX" MPEG-4 and the "IYUV" codecs were used. For some reason, they don't work on my computer (OUTPUT_VIDEO is a MP4-file).
OpenCV: FFMPEG: tag 0x58564944/'DIVX' is not supported with codec id 13 and format 'mp4 / MP4 (MPEG-4 Part 14)'
OpenCV: FFMPEG: fallback to use tag 0x00000020/' ???'
The "MPJPG" codec works with ".avi" files.
Because I'm not sure about the codecs, I would like to ask, which codecs should I use for my script to achieve the following requirements:
the video sequence can be saved as .mp3, .mp4 file (both compressed) or as .avi file (uncompressed).
Python script should work on Windows and Linux platforms.
This is my source code:
main.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import cv2
import platform
#=========== TO CHANGE ===========
INPUT_VIDEO = os.path.join("..", "resources", "video.mp4")
OUTPUT_VIDEO = os.path.join("..", "recorded", "recorded.avi")
compress = False
#=========== TO CHANGE ===========
WINDOW_NAME = "Video Recorder"
player = cv2.VideoCapture(INPUT_VIDEO)
# Get the frames per second (fps) of the video.
fps = player.get(cv2.CAP_PROP_FPS)
# Get width and height via the video capture property.
width = player.get(cv2.CAP_PROP_FRAME_WIDTH)
height = player.get(cv2.CAP_PROP_FRAME_HEIGHT)
# Define the codec and create VideoWriter object according to the used operating system.
four_cc = None
if platform.system() == "Windows":
if compress:
four_cc = cv2.VideoWriter_fourcc(*"MJPG") # *"DIVX") # DIVX MPEG-4 codec.
else:
four_cc = cv2.VideoWriter_fourcc(*"MJPG") # *"IYUV") # Uncompressed yuv420p in avi container.
elif platform.system() == "Linux":
if compress:
four_cc = cv2.VideoWriter_fourcc(*"DIVX") # DIVX MPEG-4 codec.
else:
four_cc = cv2.VideoWriter_fourcc(*"IYUV") # Uncompressed yuv420p in avi container.
recorder = cv2.VideoWriter(OUTPUT_VIDEO, four_cc, fps, (int(width), int(height)))
if player is None:
quit()
while player.isOpened():
ret, frame = player.read()
if ret:
cv2.imshow(WINDOW_NAME, frame)
recorder.write(frame)
else:
break
key_code = cv2.waitKey(1)
# Closes the window if the ESC key was pressed.
if key_code == 27:
break
# Closes the window if the X button of the window was clicked.
if cv2.getWindowProperty(WINDOW_NAME, 1) == -1:
break
player.release()
recorder.release()
cv2.destroyAllWindows()
I'm using a Windows 7 computer, with opencv-contrib-python 3.4.0.12 and Python 3.6.
Related
When I am recording videos in skvideo with the following code
import skvideo.io
# open the webcam video stream
webcam = cv2.VideoCapture(0)
print(cv2.__version__)
# open output video file stream
video = skvideo.io.FFmpegWriter("video.avi",{}, {"-q":"0", "-color_primaries": "3", "-color_trc": "1", "-colorspace": "1"})
# main loop
while True:
# get the frame from the webcam
stream_ok, frame = webcam.read()
# if webcam stream is ok
if stream_ok:
# display current frame
cv2.imshow('Webcam', frame)
# write frame to the video file
video.writeFrame(frame)
# escape condition on Esc key pressed
if cv2.waitKey(1) & 0xFF == 27:
break
# clean ups
cv2.destroyAllWindows()
# release web camera stream
webcam.release()
# release video output file stream
video.close()
The colour of the picture is changing
Original
Recording
Can anyone tell me how can I fix it?
I have ffmpeg version git-2020-04-13-59e3a9a Copyright (c) 2000-2020 the FFmpeg developers
built with gcc 9.3.1 (GCC) 20200328
Python version 3.9.6
Opencv version 4.5.4
Skvideo version 1.1.10
Given its link, I'd like to capture an online video (say from YouTube) for further processing without downloading it on the disk. What I mean by this is that I'd like to load it directly to memory whenever possible. According to these links:
http://answers.opencv.org/question/24012/reading-video-stream-from-ip-camera-in-opencv-java/#24013
http://answers.opencv.org/question/24154/how-to-using-opencv-api-get-web-video-stream/#24156
http://answers.opencv.org/question/133/how-do-i-access-an-ip-camera/
https://pypi.org/project/pafy/
it should be doable. My attempt looks like this:
import cv2
import pafy
vid = pafy.new("https://www.youtube.com/watch?v=QuELiw8tbx8")
vid_cap = cv2.VideoCapture()
vid_cap.open(vid.getbest(preftype="webm").url)
However it fails with an error
(python:12925): GLib-GObject-CRITICAL **: 14:48:56.168: g_object_set: assertion 'G_IS_OBJECT (object)' failed
False
How can I achieve my goal using python?
You can achieve this by using youtube-dl and ffmpeg:
Install the latest version of youtube-dl.
Then do sudo pip install --upgrade youtube_dl
Build ffmpeg with HTTPS support. You can do this by turning on the --enable-gnutls option.
Once the installations are complete, it's time to test the youtube-dl in terminal. We'll be using this youtube video for testing.
First we get the list of formats available for this video:
youtube-dl --list-formats https://www.youtube.com/watch?v=HECa3bAFAYk
Select a format code of your choice. I want the 144p resolution so I select 160.
Next we get the video url for our format of choice by:
youtube-dl --format 160 --get-url https://www.youtube.com/watch?v=HECa3bAFAYk
https://r3---sn-4g5e6nz7.googlevideo.com/videoplayback?clen=184077&aitags=133%2C134%2C160%2C242%2C243%2C278&fvip=3&requiressl=yes&signature=5D21FFD906226C7680B26ACEF996B78B6A31F7C9.31B1115DB13F096AA5968DB2838E22A0D6A2EDCB&source=youtube&mn=sn-4g5e6nz7%2Csn-h0jeen7y&xtags=tx%3D9486108&itag=160&mime=video%2Fmp4&mt=1529091799&ms=au%2Conr&ei=XxckW-73GNCogQfqrryQAg&expire=1529113535&mm=31%2C26&c=WEB&keepalive=yes&id=o-AJExEG49WtIUkrF7OikaaGBCfKntDl75xCoO5_9cL-eP&ip=95.91.202.147&sparams=aitags%2Cclen%2Cdur%2Cei%2Cgir%2Cid%2Cinitcwndbps%2Cip%2Cipbits%2Citag%2Ckeepalive%2Clmt%2Cmime%2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Crequiressl%2Csource%2Cxtags%2Cexpire&key=yt6&lmt=1526699176943888&dur=25.375&pl=22&gir=yes&mv=m&initcwndbps=1155000&ipbits=0&ratebypass=yes
Finally we can play this video url in either ffplay or vlc. But instead of copying and pasting, we can do this in one command:
ffplay -i $(youtube-dl --format 160 --get-url https://www.youtube.com/watch?v=HECa3bAFAYk)
Now that we have confirmed that youtube-dl and ffmpeg works, we can write a Python script to process the frames in OpenCV. See this link for more Python options.
import cv2
import numpy as np
import youtube_dl
if __name__ == '__main__':
video_url = 'https://www.youtube.com/watch?v=HECa3bAFAYkq'
ydl_opts = {}
# create youtube-dl object
ydl = youtube_dl.YoutubeDL(ydl_opts)
# set video url, extract video information
info_dict = ydl.extract_info(video_url, download=False)
# get video formats available
formats = info_dict.get('formats',None)
for f in formats:
# I want the lowest resolution, so I set resolution as 144p
if f.get('format_note',None) == '144p':
#get the video url
url = f.get('url',None)
# open url with opencv
cap = cv2.VideoCapture(url)
# check if url was opened
if not cap.isOpened():
print('video not opened')
exit(-1)
while True:
# read frame
ret, frame = cap.read()
# check if frame is empty
if not ret:
break
# display frame
cv2.imshow('frame', frame)
if cv2.waitKey(30)&0xFF == ord('q'):
break
# release VideoCapture
cap.release()
cv2.destroyAllWindows()
First of all Update youtube-dl using the command pip install -U youtube-dl
Then use my VidGear Python Library, then automates the pipelining of YouTube Video using its URL address only. Here's a complete python example:
For VidGear v0.1.9 below:
# import libraries
from vidgear.gears import CamGear
import cv2
stream = CamGear(source='https://youtu.be/dQw4w9WgXcQ', y_tube = True, logging=True).start() # YouTube Video URL as input
# infinite loop
while True:
frame = stream.read()
# read frames
# check if frame is None
if frame is None:
#if True break the infinite loop
break
# do something with frame here
cv2.imshow("Output Frame", frame)
# Show output window
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
# safely close video stream.
stream.stop()
For VidGear v0.2.0 and above: (y_tube changed to stream_mode)
# import libraries
from vidgear.gears import CamGear
import cv2
stream = CamGear(source='https://youtu.be/dQw4w9WgXcQ', stream_mode = True, logging=True).start() # YouTube Video URL as input
# infinite loop
while True:
frame = stream.read()
# read frames
# check if frame is None
if frame is None:
#if True break the infinite loop
break
# do something with frame here
cv2.imshow("Output Frame", frame)
# Show output window
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
# safely close video stream.
stream.stop()
Code Source
If still get some error, raise an issue here in its GitHub repo.
Using pafy you can have a more elegant solution:
import cv2
import pafy
url = "https://www.youtube.com/watch?v=NKpuX_yzdYs"
video = pafy.new(url)
best = video.getbest(preftype="mp4")
capture = cv2.VideoCapture()
capture.open(best.url)
success,image = capture.read()
while success:
cv2.imshow('frame', image)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
success,image = capture.read()
cv2.destroyAllWindows()
capture.release()
I want to highlight the issue I faced while running was a open-cv version problem, I was using OpenCV 3.4.x and the video feed was exiting before being read into the while loop, so, i upgraded my open cv to "opencv-contrib-python== 4.2.0.34".
Given its link, I'd like to capture an online video (say from YouTube) for further processing without downloading it on the disk. What I mean by this is that I'd like to load it directly to memory whenever possible. According to these links:
http://answers.opencv.org/question/24012/reading-video-stream-from-ip-camera-in-opencv-java/#24013
http://answers.opencv.org/question/24154/how-to-using-opencv-api-get-web-video-stream/#24156
http://answers.opencv.org/question/133/how-do-i-access-an-ip-camera/
https://pypi.org/project/pafy/
it should be doable. My attempt looks like this:
import cv2
import pafy
vid = pafy.new("https://www.youtube.com/watch?v=QuELiw8tbx8")
vid_cap = cv2.VideoCapture()
vid_cap.open(vid.getbest(preftype="webm").url)
However it fails with an error
(python:12925): GLib-GObject-CRITICAL **: 14:48:56.168: g_object_set: assertion 'G_IS_OBJECT (object)' failed
False
How can I achieve my goal using python?
You can achieve this by using youtube-dl and ffmpeg:
Install the latest version of youtube-dl.
Then do sudo pip install --upgrade youtube_dl
Build ffmpeg with HTTPS support. You can do this by turning on the --enable-gnutls option.
Once the installations are complete, it's time to test the youtube-dl in terminal. We'll be using this youtube video for testing.
First we get the list of formats available for this video:
youtube-dl --list-formats https://www.youtube.com/watch?v=HECa3bAFAYk
Select a format code of your choice. I want the 144p resolution so I select 160.
Next we get the video url for our format of choice by:
youtube-dl --format 160 --get-url https://www.youtube.com/watch?v=HECa3bAFAYk
https://r3---sn-4g5e6nz7.googlevideo.com/videoplayback?clen=184077&aitags=133%2C134%2C160%2C242%2C243%2C278&fvip=3&requiressl=yes&signature=5D21FFD906226C7680B26ACEF996B78B6A31F7C9.31B1115DB13F096AA5968DB2838E22A0D6A2EDCB&source=youtube&mn=sn-4g5e6nz7%2Csn-h0jeen7y&xtags=tx%3D9486108&itag=160&mime=video%2Fmp4&mt=1529091799&ms=au%2Conr&ei=XxckW-73GNCogQfqrryQAg&expire=1529113535&mm=31%2C26&c=WEB&keepalive=yes&id=o-AJExEG49WtIUkrF7OikaaGBCfKntDl75xCoO5_9cL-eP&ip=95.91.202.147&sparams=aitags%2Cclen%2Cdur%2Cei%2Cgir%2Cid%2Cinitcwndbps%2Cip%2Cipbits%2Citag%2Ckeepalive%2Clmt%2Cmime%2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Crequiressl%2Csource%2Cxtags%2Cexpire&key=yt6&lmt=1526699176943888&dur=25.375&pl=22&gir=yes&mv=m&initcwndbps=1155000&ipbits=0&ratebypass=yes
Finally we can play this video url in either ffplay or vlc. But instead of copying and pasting, we can do this in one command:
ffplay -i $(youtube-dl --format 160 --get-url https://www.youtube.com/watch?v=HECa3bAFAYk)
Now that we have confirmed that youtube-dl and ffmpeg works, we can write a Python script to process the frames in OpenCV. See this link for more Python options.
import cv2
import numpy as np
import youtube_dl
if __name__ == '__main__':
video_url = 'https://www.youtube.com/watch?v=HECa3bAFAYkq'
ydl_opts = {}
# create youtube-dl object
ydl = youtube_dl.YoutubeDL(ydl_opts)
# set video url, extract video information
info_dict = ydl.extract_info(video_url, download=False)
# get video formats available
formats = info_dict.get('formats',None)
for f in formats:
# I want the lowest resolution, so I set resolution as 144p
if f.get('format_note',None) == '144p':
#get the video url
url = f.get('url',None)
# open url with opencv
cap = cv2.VideoCapture(url)
# check if url was opened
if not cap.isOpened():
print('video not opened')
exit(-1)
while True:
# read frame
ret, frame = cap.read()
# check if frame is empty
if not ret:
break
# display frame
cv2.imshow('frame', frame)
if cv2.waitKey(30)&0xFF == ord('q'):
break
# release VideoCapture
cap.release()
cv2.destroyAllWindows()
First of all Update youtube-dl using the command pip install -U youtube-dl
Then use my VidGear Python Library, then automates the pipelining of YouTube Video using its URL address only. Here's a complete python example:
For VidGear v0.1.9 below:
# import libraries
from vidgear.gears import CamGear
import cv2
stream = CamGear(source='https://youtu.be/dQw4w9WgXcQ', y_tube = True, logging=True).start() # YouTube Video URL as input
# infinite loop
while True:
frame = stream.read()
# read frames
# check if frame is None
if frame is None:
#if True break the infinite loop
break
# do something with frame here
cv2.imshow("Output Frame", frame)
# Show output window
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
# safely close video stream.
stream.stop()
For VidGear v0.2.0 and above: (y_tube changed to stream_mode)
# import libraries
from vidgear.gears import CamGear
import cv2
stream = CamGear(source='https://youtu.be/dQw4w9WgXcQ', stream_mode = True, logging=True).start() # YouTube Video URL as input
# infinite loop
while True:
frame = stream.read()
# read frames
# check if frame is None
if frame is None:
#if True break the infinite loop
break
# do something with frame here
cv2.imshow("Output Frame", frame)
# Show output window
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
# safely close video stream.
stream.stop()
Code Source
If still get some error, raise an issue here in its GitHub repo.
Using pafy you can have a more elegant solution:
import cv2
import pafy
url = "https://www.youtube.com/watch?v=NKpuX_yzdYs"
video = pafy.new(url)
best = video.getbest(preftype="mp4")
capture = cv2.VideoCapture()
capture.open(best.url)
success,image = capture.read()
while success:
cv2.imshow('frame', image)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
success,image = capture.read()
cv2.destroyAllWindows()
capture.release()
I want to highlight the issue I faced while running was a open-cv version problem, I was using OpenCV 3.4.x and the video feed was exiting before being read into the while loop, so, i upgraded my open cv to "opencv-contrib-python== 4.2.0.34".
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 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