I am trying to use ImageIO to create a video from a sequence of 3 PNG's. I need the frames of the video to be exactly the same as the 3 PNG's, so I am using the copy
video codec. If I use codecs that are compressed (like H.264), the code works perfectly fine, but the frames in the video are different.
I am using ImageIO version 2.26.0 and have FFMpeg version 5.1.2 installed, though the code seems to be using version 4.2.2.
Here is my code:
from imageio import get_writer, imread
video = get_writer('encoded.mp4',fps=60,codec='copy')
for i in range(3):
video.append_data(imread(f'image{str(i)}.png'))
video.close()
But an error occurs:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/imageio_ffmpeg/_io.py", line 615, in write_frames
p.stdin.write(bb)
BrokenPipeError: [Errno 32] Broken pipe
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/Raine/Documents/FileEnconderInVideo/main.py", line 7, in <module>
video.append_data(imread(f'image{str(i)}.png'))
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/imageio/core/format.py", line 590, in append_data
return self._append_data(im, total_meta)
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/imageio/plugins/ffmpeg.py", line 600, in _append_data
self._write_gen.send(im)
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/imageio_ffmpeg/_io.py", line 622, in write_frames
raise IOError(msg)
OSError: [Errno 32] Broken pipe
FFMPEG COMMAND:
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/imageio_ffmpeg/binaries/ffmpeg-osx64-v4.2.2 -y -f rawvideo -vcodec rawvideo -s 3840x2160 -pix_fmt rgb24 -r 60.00 -i - -an -vcodec bmp -pix_fmt yuv420p -v warning /Users/Raine/Documents/FileEnconderInVideo/video.mp4
FFMPEG STDERR OUTPUT:
What is wrong?
Thank you!
What is wrong?
The short answer: Fundamentals.
The long answer: (1) The copy
"codec" isn't used for transcoding, but for transmuxing, i.e., it copies the compressed packets stored in one video container into another video container without converting them to frames in-between. There is no way of doing this when going from image containers to video containers; you have to transcode instead. (2) Video codecs are lossy, which is how they achieve such high compression ratios. Lossless codecs do exist, but they are unicorns with very limited/niche use cases. In most cases, you are better off using WEBP or TIFF instead if you find yourself in need of lossless encoding. (3) The default color format for video is (subsampled) YUV, not RGB(A). Again, codecs supporting RGB(A) exist, but they, too, are niche.
If, for what ever reason, you still must create a video that stores frames using lossless 8-bit RGB, you can do so via our pyav plugin:
import imageio.v3 as iio
import numpy as np
frames = iio.imread("imageio:newtonscradle.gif", mode="RGB")
with iio.imopen("unicorn.mp4", "w", plugin="pyav") as file:
file.init_video_stream(
"png", fps=60, pixel_format="rgb24", max_keyframe_interval=1
)
for frame in frames:
file.write_frame(frame)
re_read = iio.imread("unicorn.mp4", plugin="pyav")
np.allclose(frames, re_read)
(For this to work, you need a recent version of both ImageIO and PyAV installed.)
While the above produces a valid video that you can read in python and test against the PNGs, it will not be playable by the majority of media players out there, as the result is very niche and not widely supported.
I am using ImageIO version 2.26.0 and have FFMpeg version 5.1.2 installed, though the code seems to be using version 4.2.2.
If you are using imageio-ffmpeg
then it uses an internal version of ffmpeg unless you point it to your system's installation.