ffmpeg can convert sequences of images to movies and is capable of producing lossless output. Is it possible to directly produce a movie from a multi-page tiff file? If I try one of my multipage tiffs ffmpeg only processes the first frame.
2 Answers
Workaround
FFmpeg does not support multipage tiff yet, even in 2021! In the actual FFmpeg feature ticket, I found a workaround: use convert (from ImageMagick) to pipe individual images to FFmpeg.
Ref: https://trac.ffmpeg.org/ticket/8644
In my case, the file format is gray16le (16bits grayscale LE, from a thermal IR camera):
convert sample.tif gray:- | ffmpeg -f rawvideo -s 1280x1024 -pix_fmt gray16le -r 30 -i - -vcodec ffv1 sample-ffv1.mkv
The ffv1 is a lossless format, but it is still compressed. In my case it was about 30% of the original size.
I used the -r 30 to set the output framerate.
Note
Some may wonder why we need lossless 16bit format: I needed to simulate a 16bit grayscale V4L source using V4L-loopback:
ffmpeg -stream_loop -1 -re -i sample-ffv1.mkv -map 0:v -f v4l2 /dev/video0
- 131
What about this trick. It will create a stream process that receives image binaries from a tiff file:
import subprocess
from PIL import Image
import numpy as np
Define the input TIFF file and output video file
tiff_file = 'output.tiff' # Input multi-page TIFF file
video_file = 'output.mp4' # Output video file
Open the multi-page TIFF file
with Image.open(tiff_file) as img:
# Prepare the FFmpeg command
command = [
'ffmpeg',
'-y', # Overwrite output files without asking
'-f', 'rawvideo', # Input format
'-pixel_format', 'rgba', # Set the pixel format to RGBA
'-video_size', f'{img.width}x{img.height}', # Set video size
'-framerate', '25', # Set frame rate
'-i', '-', # Input from stdin
'-c:v', 'libx264', # Video codec
'-pix_fmt', 'yuv420p', # Pixel format for compatibility
video_file
]
# Start the FFmpeg process
process = subprocess.Popen(command, stdin=subprocess.PIPE)
# Iterate over each frame in the TIFF
for i in range(img.n_frames):
img.seek(i) # Move to the next frame
# Convert the image to a raw byte array and write to the FFmpeg stdin
process.stdin.write(img.tobytes())
# Close the input stream and wait for the process to finish
process.stdin.close()
process.wait()
print("Video created successfully!")
- 11