从YUV文件中提取出特定的某一帧有哪几种办法?
这种场合一般发生在需要帧同步调试/特别比照某两帧/检查内容/批处理的场合,不过其实专门评论意义有限。由于往往都有图形化界面自在检查。
因而这其实是一个相对无聊的问题。当然,换一种办法也能够:关于像YUV这样一个规整的格局,有多少种工具能够拆分文件?
p.s. 以下都以YUV420格局为例子。其每帧巨细为WH32Wtimes H times frac{3}{2}.
FFmpeg
- 假如手头有FFmpeg工具,这是坠吼滴。
ffmpeg -s [width]x[height] -i input.yuv -vframes 1 -f rawvideo -pix_fmt yuv420p output.yuv
-vframes 1
表明只处理一个视频帧。
- 视场景设置,改换为帧率指定也是能够的。
例如,关于30fps的视频,提取第10秒的一帧。即第300帧:
ffmpeg -s [width]x[height] -r 30 -i input.yuv -ss 00:00:10 -vframes 1 -f rawvideo -pix_fmt yuv420p output.yuv
Python
高档言语就恣意折腾就好。一般都是系统之一的模块。
width = 1920 # 示例宽度
height = 1080 # 示例高度
frame_number = 1 # 提取第1帧
# YUV420格局一帧数据巨细
frame_size = width * height (width // 2) * (height // 2) * 2
with open('input.yuv', 'rb') as file:
file.seek(frame_size * (frame_number - 1))
frame_data = file.read(frame_size)
with open(f'output_{width}x{height}.yuv', 'wb') as output_file:
output_file.write(frame_data)
C
同上,高档言语恣意折腾。惯例 File IO.
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *file = fopen("input.yuv", "rb");
if (file == NULL)
{
perror("Error opening file");
return 1;
}
int width = 1920; // 宽度
int height = 1080; // 高度
int frame_number = 1; // 提取的帧号
// YUV420格局一帧数据巨细
int frame_size = width * height (width / 2) * (height / 2) * 2;
unsigned char *frame_data = (unsigned char *)malloc(frame_size);
if (frame_data == NULL)
{
perror("Memory allocation failed");
fclose(file);
return 1;
}
fseek(file, (long)frame_size * (frame_number - 1), SEEK_SET);
fread(frame_data, 1, frame_size, file);
FILE *output_file = fopen("output.yuv", "wb");
if (output_file != NULL)
{
fwrite(frame_data, 1, frame_size, output_file);
fclose(output_file);
}
else
{
perror("Error opening output file");
}
free(frame_data);
fclose(file);
return 0;
}
Command line
其实主要是想折腾这种办法,即在没有FFmpeg和高档言语支持下怎么(随手)完成规整文件读写这样的小事。
dd
能够考虑dd
,直接指定文件读写的位置和巨细。
例如,1920×1080 YUV 420 提取第ii帧:
dd if=input.yuv of=output_frame.yuv bs=1 count=$((1920*1080*3/2)) skip=$(( [i-1] *1920*1080*3/2))
其中, -if=input.yuv
指定输入文件; -of=output_frame.yuv
指定输出文件; -bs=1
设置块巨细为1字节; -count=$((1920*1080*3/2))
指定复制的字节数,即一帧的巨细; -skip=$((4*1920*1080*3/2))
越过前i−1i-1帧的数据量,以提取第ii帧.
split & cat
或者直接运用split
分帧(split
分帧是确实便利,便是文件命名比较折腾)
# 分帧,以frame_xx命名,xx遵从字母计数法。
split -b $((1920*1080*3/2)) input.yuv frame_
# 例如,将某一帧(_aa,26x25 帧规模内转化出来的榜首帧)转化为输出文件
cat frame_aa > output.yuv
当然,由于split
命令生成的文件后缀是字母计数法。是按照字母顺序递增的,从 aa
开始,然后是 ab
,以此类推。所以会留传一堆不需要的分帧。
解决这个问题的话能够通过一个粗糙的shell脚本,其编写了convert_number_to_suffix
函数,完成了最简略粗暴的字母计数法转化(ASCII码直转),能够处理帧数为26~676的视频。
#!/bin/bash
# 字母计数法和阿拉伯数字转化脚本,规模为 26~676(即26x26)
convert_number_to_suffix() {
local num=$1
local -i charcode_a=97
local -i first_digit=$(( (num - 1) / 26 charcode_a ))
local -i second_digit=$(( (num - 1) % 26 charcode_a ))
local first_char=$(printf $(printf 'o' $first_digit) )
local second_char=$(printf $(printf 'o' $second_digit) )
echo "$first_char$second_char"
}
if [ "$#" -ne 4 ]; then
echo "Usage: $0 <input.yuv> <width> <height> <frame_number>"
exit 1
fi
# 输入的YUV文件途径
INPUT_FILE=$1
# 视频的宽度
WIDTH=$2
# 视频的高度
HEIGHT=$3
# 提取的帧序号
FRAME_NUMBER=$4
FRAME_SIZE=$((WIDTH * HEIGHT * 3 / 2))
FILE_SUFFIX=$(convert_number_to_suffix $FRAME_NUMBER)
split -b $FRAME_SIZE $INPUT_FILE frame_
cat "frame_$FILE_SUFFIX" > "output_frame_$FRAME_NUMBER.yuv"
# 清理其他分割文件
rm frame_*
与其说是在折腾分帧,不如说是在折腾命名办法
总结
现已折腾得简略问题杂乱化了。
TODO:
- 比照高档言语脚本完成与dd/cat办法的性能分析
- 字母序列计数法的完成评论,我们能完成一个更完善的版别(当然,那会更杂乱,并且属于另一个问题了)