FFMPEG: crop a video without losing the quality

40,868

Solution 1

You can't perform any filtering without losing quality when encoding to a lossy format, but you have some options.

Crop with your player

A possible solution would be to crop during playback, so you don't even need to re-encode. It is also useful to preview a crop.

This method will not create an output file. This will use your video player to crop while it is playing. See one of the other methods below if you want an output file.

With ffplay and crop filter:

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

With vlc (or cvlc):

vlc input.mp4 --crop=480x270+200+100

Or you could crop with the VLC GUI: Tools → Effects & Filters → Video Effects → Crop.

Accept some quality loss (you may not even notice a difference)

Give it enough bits and you may not be able to tell there is a quality difference:

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

See FFmpeg Wiki: H.264 Video Encoding Guide for more info.

Use a bitstream filter

The h264_metadata and hevc_metadata bitstream filters can set crop dimensions without modifying the video itself.

Note: Your player may not support this method.

Example for H.264 video:

ffmpeg -i input.mp4 -c copy -bsf:v h264_metadata=crop_left=100:crop_right=20:crop_top=10:crop_bottom=10 output.mp4
  • Sets the frame cropping offsets in the SPS. These values will replace the current ones if the stream is already cropped.

  • These fields are set in pixels. Note that some sizes may not be representable if the chroma is subsampled or the stream is interlaced (see H.264 section 7.4.2.1.1).

Use a lossless format

ffmpeg can encode with several lossless encoders: ffv1, huffyuv, ffvhuff, utvideo, libx264 (using -crf 0 or -qp 0). The output will be lossless but the output file will be huge.

Note: Your player may not support this method.

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

or

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

If your input is MJPEG

Stream copy the individual images with ffmpeg, crop them losslessly with jpegtran, then remux them with ffmpeg. This will result in no loss, but you will be restricted to the ancient MJPEG format.

Solution 2

At a basic level you cannot make use of a lossy encoding and then expect it to not lose quality when you decode and then encode again. The only way that works is to make use of a lossless codec, for example Quicktime with the Animation codec. This is just a basic truth of digital video production that you cannot work around by just passing command line options to ffmpeg.

Solution 3

This is not possible using ffmpeg.

As alternative you can embed your video in a Matroska (.mkv) container and set a cropping tag in the file header, but it must be supported by your player.

Reportedly, for H264-encoded videos H264info can also be used, but i still need to figure out how to use it...

Solution 4

For some video formats at least, the soft solution based on metadata should be possible with FFmpeg as well by using bitstream filters from libavcodec, e.g. hevc_metadata or h264_metadata.

Unlike filters like crop and cropdetect, this does not require decoding. The syntax slightly differs, though, because you can only set the amount of cropping from the four edges, not the target rectangle size. For the OP's 480×270 area at position (200,200) from the top left in a 1920×1080 full HD frame, we get:

  • ffmpeg -i input.mp4 -codec copy -bsf:v h264_metadata=crop_left=200:crop_right=1240:crop_top=200:crop_bottom=610 output.mp4

Since this is codec metadata, this should work regardless the container format, i.e. not just in MP4, but in MKV or AVI as well. Alas, I have not tested it myself yet and cannot say anything about support in software and hardware players. (A first simple check failed miserably, though.)

For more detailed info, FFmpeg's documentation refers to sections 7.4.3.2.1 and 7.4.2.1.1 of the H.265 and H.264 specifications respectively, which are freely available from ITU and are basically equivalent:

  • frame_cropping_flag equal to 1 specifies that the frame cropping offset parameters follow next in the sequence parameter set. frame_cropping_flag equal to 0 specifies that the frame cropping offset parameters are not present.
  • frame_crop_left_offset, frame_crop_right_offset, frame_crop_top_offset, frame_crop_bottom_offset specify the samples of the pictures in the coded video sequence that are output from the decoding process, in terms of a rectangular region specified in frame coordinates for output. The variables CropUnitX and CropUnitY are derived as follows:

    – If ChromaArrayType is equal to 0, CropUnitX and CropUnitY are derived as: CropUnitX = 1 CropUnitY = 2 − frame_mbs_only_flag – Otherwise (ChromaArrayType is equal to 1, 2, or 3), CropUnitX and CropUnitY are derived as: CropUnitX = SubWidthC CropUnitY = SubHeightC * ( 2 − frame_mbs_only_flag )

The frame cropping rectangle contains luma samples with horizontal frame coordinates from CropUnitX * frame_crop_left_offset to PicWidthInSamplesL − ( CropUnitX * frame_crop_right_offset + 1 ) and vertical frame coordinates from CropUnitY * frame_crop_top_offset to ( 16 * FrameHeightInMbs ) − ( CropUnitY * frame_crop_bottom_offset + 1 ), inclusive.

The value of frame_crop_left_offset shall be in the range of 0 to ( PicWidthInSamplesL / CropUnitX ) − ( frame_crop_right_offset + 1 ), inclusive; and the value of frame_crop_top_offset shall be in the range of 0 to ( 16 * FrameHeightInMbs / CropUnitY ) − ( frame_crop_bottom_offset + 1 ), inclusive.

When frame_cropping_flag is equal to 0, the values of frame_crop_left_offset, frame_crop_right_offset, frame_crop_top_offset, and frame_crop_bottom_offset shall be inferred to be equal to 0.

When ChromaArrayType is not equal to 0, the corresponding specified samples of the two chroma arrays are the samples having frame coordinates ( x / SubWidthC, y / SubHeightC ), where ( x, y ) are the frame coordinates of the specified luma samples.

For decoded fields, the specified samples of the decoded field are the samples that fall within the rectangle specified in frame coordinates.

Share:
40,868
mrana
Author by

mrana

Updated on July 09, 2022

Comments

  • mrana
    mrana almost 2 years

    I have mp4 video of 1920x1080. I would like to crop the video to 480x270 without quality loss.

    I am using the following command:

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

    I also tried:

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

    I used -crf 23 and -qp 0 for loseless video cropping, but after cropping video has lost quality.

    Does anyone know how I can crop the video without losing the quality?