FFMPEG:如何裁剪视频而不失去质量?

44

我有一段1920x1080的mp4视频。我想将其裁剪到480x270而不会损失画质

我正在使用以下命令:

ffmpeg -i input.mp4 -filter:v "crop=480:270:200:200" -crf 23 output.mp4

我也尝试了:

ffmpeg -i input.mp4 -filter:v "crop=480:270:200:100" -c:a copy -qp 0 output.mp4

我使用-crf 23-qp 0进行无损视频裁剪,但是裁剪后视频质量有所降低。

有人知道如何在不丢失质量的情况下裁剪视频吗?


请展示您的ffmpeg命令的完整控制台输出。 - llogan
您可以在此处找到控制台输出: https://www.dropbox.com/s/1r4pdgdmtj9ilnm/consol-output.rtf?dl=0谢谢。 - mrana
你应该编辑你的问题,包括控制台输出。你可以在播放期间进行裁剪。这将是一个可接受的解决方案吗? - llogan
播放期间裁剪?我需要在这个命令中添加什么来实现呢?<br> ffmpeg -i input.mp4 -filter:v "crop=480:270:200:100" -c:a copy -qp 0 output.mp4 - mrana
我相信某些h264可以无损裁剪。例如,如果每个帧都是关键帧,并且压缩是在独立区域内完成的。我尝试过使用一个滤镜,只复制裁剪矩形中的数据,而省略其他区域的数据。但是,在输出任何具有非关键帧的内容时,边框周围会出现伪影。 - Ondra Žižka
显示剩余4条评论
4个回答

64

如果使用有损格式进行编码,就无法在不降低质量的情况下进行任何过滤,但是你有一些选择。

使用播放器裁剪

一个可能的解决方案是在播放时进行裁剪,这样你甚至不需要重新编码。这也有助于预览裁剪效果。

此方法不会创建输出文件。这将使用您的视频播放器在播放时进行裁剪。如果需要输出文件,请参见以下其他方法之一。

使用ffplaycrop filter:

ffplay -vf "crop=480:270:200:100" input.mp4

使用 vlc (或 cvlc):
vlc input.mp4 --crop=480x270+200+100

或者您可以使用VLC GUI进行裁剪:工具→效果和滤镜→视频效果→裁剪。

接受一些质量损失(您甚至可能注意不到差异)

给它足够的位数,您可能无法分辨出质量差异:

ffmpeg -i input -vf "crop=480:270:200:100" -c:v libx264 -crf 17 -c:a copy ouput.mp4

请查看FFmpeg Wiki: H.264视频编码指南以获取更多信息。

使用比特流过滤器

h264_metadatahevc_metadata比特流过滤器可以在不修改视频本身的情况下设置裁剪尺寸。

注意:您的播放器可能不支持此方法。

H.264视频示例:

ffmpeg -i input.mp4 -c copy -bsf:v h264_metadata=crop_left=100:crop_right=20:crop_top=10:crop_bottom=10 output.mp4
  • 设置SPS中的帧裁剪偏移量。如果流已经被裁剪,则这些值将替换当前值。

  • 这些字段以像素为单位设置。请注意,如果色度子采样或流是交错的(请参见H.264第7.4.2.1.1节),则可能无法表示某些大小。

使用无损格式

ffmpeg可以使用多种无损编码器进行编码:ffv1、huffyuv、ffvhuff、utvideo、libx264(使用-crf 0-qp 0)。输出将是无损的,但输出文件将非常大。

注意:您的播放器可能不支持此方法。

ffmpeg -i input.mp4 -vf "crop=480:270:200:100" -c:v ffv1 -c:a copy output.mkv

或者

ffmpeg -i input.mp4 -vf "crop=480:270:200:100" -c:v libx264 -crf 0 -c:a copy output.mp4

如果您的输入是MJPEG

使用ffmpeg流复制单个图像, 使用jpegtran无损地裁剪它们,然后再使用ffmpeg重新混合它们。这将不会有任何损失,但您将受到古老的MJPEG格式的限制。


是的,我知道,但我总是收到“未找到ffplay”的消息:Masuds-MacBook-Pro:golf masudrana$ cd selected Masuds-MacBook-Pro:selected masudrana$ ffplay -vf "crop=480:270:200:100" golf.mp4 -bash: command not found:ffplay - mrana
@MasudRana 可能没有安装或者不在你的 PATH 中。我对 OS X 不熟悉,所以无法提供其他建议。尝试搜索 ffplay 二进制文件。或者,从 http://evermeet.cx/ffmpeg/ 获取 ffplay - llogan
好的,我已经在Windows上处理过了,现在正在尝试使用ffplay。 你给出的命令ffplay -vf "crop=480:270:504:720" credits.mp4, 它给了我一个警告:数据未对齐!这可能会导致速度下降。 [swscaler @ 0000001cab897d20] Warning: data is not aligned! This can lead to a speedloss 553.98 M-V: 0.000 fd= 0 aq= 0KB vq= 0KB sq= 0B。我的原始视频是1920x1080。 - mrana
@mrana 播放期间出现了裁剪问题,没有生成新的视频。过滤需要重新编码,而重新编码(到有损格式)会导致质量世代损失。因此,您的选择是在播放期间进行裁剪,将其裁剪并重新编码为无损格式,从而产生巨大的文件,或接受一些质量损失。 - llogan
2
在mplayer中播放新视频时,使用比特流过滤器效果完美,但在VLC(3.0.9.2)和Kodi(18)中会出现问题。 - Programster
显示剩余13条评论

6
对于至少某些视频格式而言,基于元数据的软件解决方案应该也可以使用FFmpeg基于libavcodec的比特流过滤器实现,例如hevc_metadatah264_metadata
cropcropdetect等滤镜不同,这不需要进行解码。语法稍有不同,因为你只能从四个边缘设置裁剪量,而不能设置目标矩形大小。对于 OP 的在 1920×1080 全高清帧的左上角位置(200,200)处的480×270 区域,我们得到如下命令行:
  • ffmpeg -i input.mp4 -codec copy -bsf:v h264_metadata=crop_left=200:crop_right=1240:crop_top=200:crop_bottom=610 output.mp4
由于这是编解码器的元数据,所以无论容器格式是什么(不仅仅是MP4,还包括MKV或AVI),都应该可以正常工作。但是,我自己尚未测试过,也无法说明在软件和硬件播放器中的支持情况(尽管第一次简单检查结果惨不忍睹)。
有关更详细的信息,请参阅FFmpeg文档中有关H.265和H.264规范的第7.4.3.2.1节和第7.4.2.1.1节,它们可以从ITU免费获取,并且基本上是相等的。
  • frame_cropping_flag等于1时,指定在序列参数集中下一个是帧裁剪偏移参数。 当frame_cropping_flag等于0时,指定帧裁剪偏移参数不存在。
  • frame_crop_left_offsetframe_crop_right_offsetframe_crop_top_offsetframe_crop_bottom_offset以输出解码处理过程中编码视频序列中图片样本的形式指定矩形区域, 这些样本位于框架坐标中。变量CropUnitXCropUnitY的计算如下:

    – 如果ChromaArrayType等于0,则CropUnitXCropUnitY的计算为: CropUnitX = 1 CropUnitY = 2 - frame_mbs_only_flag – 否则(ChromaArrayType等于1、2或3),CropUnitXCropUnitY的计算为: CropUnitX = SubWidthC CropUnitY = SubHeightC * ( 2 - frame_mbs_only_flag )

帧裁剪矩形包含水平帧坐标为 CropUnitX * frame_crop_left_offset to PicWidthInSamplesL - ( CropUnitX * frame_crop_right_offset + 1 )的亮度样本, 包含垂直帧坐标为 CropUnitY * frame_crop_top_offset to ( 16 * FrameHeightInMbs ) - ( CropUnitY * frame_crop_bottom_offset + 1 )的亮度样本。

frame_crop_left_offset的值应在0到( PicWidthInSamplesL / CropUnitX ) - ( frame_crop_right_offset + 1 )范围内,包括边界; frame_crop_top_offset的值应在0到( 16 * FrameHeightInMbs / CropUnitY ) - ( frame_crop_bottom_offset + 1 )范围内,包括边界。

frame_cropping_flag等于0时,frame_crop_left_offsetframe_crop_right_offsetframe_crop_top_offsetframe_crop_bottom_offset的值推断为0。

ChromaArrayType不等于0时,两个色度数组的相应指定样本是具有框架坐标( x / SubWidthC, y / SubHeightC )的样本,其中( x, y )是指定亮度样本的帧坐标。

对于解码的场,解码场的指定样本是落在框架坐标指定矩形内的样本。


2
这是一个不错的解决方案。它确实没有重新编码h264视频。我尝试了我的一个视频文件,但在不同的视频播放器之间可靠性不高。我正在使用VLC播放器,它错误地解释了crop_left和crop_top,视频从右侧和底部被裁剪了。然后我尝试了Windows Media Player,在那里相同的视频从顶部和左侧被裁剪。我需要在所有播放器中以相同的方式裁剪视频,所以我不能使用此选项。 - MarekJ47
@MarekJ47 VLC非常容易出错,谁在乎呢。 - Валерий Заподовников

3
这是使用ffmpeg无法实现的。
作为替代方案,您可以将视频嵌入Matroska (.mkv)容器中,并在文件头中设置裁剪标签,但必须由您的播放器支持
据报道,对于H264编码的视频,H264info也可以使用,但我仍需要弄清如何使用它...

这也可以通过FFmpeg比特流过滤器实现,可以看看我的答案(最初是对这个的评论)。 - Crissov

3

基本上,您不能使用有损编码,然后期望在解码和重新编码时不失去质量。唯一可行的方法是使用无损编解码器,例如带有Animation编解码器的Quicktime。这是数字视频制作的基本真理,您不能通过仅向FFmpeg传递命令行选项来避免。


一定有办法,因为我不是在压缩视频而是在裁剪它。 - mrana
3
但是你正在压缩视频。视频在被压缩的形式下无法被修改。就像其他任何压缩内容,比如 zip 文件一样。必须先解压缩,修改(裁剪),然后再次压缩。话虽如此,只要比特率足够高,你不应该失去太多质量。 - szatmary
Masud,你为什么要问问题,如果你打算忽略答案呢?有关背景信息,请参见:http://www.howtogeek.com/142174/what-lossless-file-formats-are-why-you-shouldnt-convert-lossy-to-lossless/ - MoDJ
@MoDJ 抱歉,我是 Stack Overflow 的新手。 - mrana
3
请注意,这个答案谈到了“解码再编码”。问题提出者似乎不知道裁剪需要进行解码和编码。这是必要的,因为裁剪会改变图像数据(如果你想要丢弃未使用的数据)。 - Kissaki
即使压缩是有损的,你仍然可以进行无损裁剪,只需看看JPEG、mp3、ogg vorbis等格式即可。 - pipe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接