165

Currently I am using this command to extract the images:

ffmpeg -i input.mp4 output_%03d.jpeg

But how can I improve the JPEG image quality?

llogan
  • 121,796
  • 28
  • 232
  • 243
Daniel Gartmann
  • 11,678
  • 12
  • 45
  • 60

3 Answers3

309

Use -qscale:v to control quality

Use -qscale:v (or the alias -q:v) as an output option.

  • Normal range for JPEG is 2-31 with 31 being the worst quality.
  • The scale is linear with double the qscale being roughly half the bitrate.
  • Recommend trying values of 2-5.
  • You can use a value of 1 but you must add the -qmin 1 output option (because the default is -qmin 2).

To output a series of images:

ffmpeg -i input.mp4 -qscale:v 2 output_%03d.jpg

See the image muxer documentation for more options involving image outputs.

To output a single image at ~60 seconds duration:

ffmpeg -ss 60 -i input.mp4 -qscale:v 4 -frames:v 1 output.jpg

To continuously overwrite/update/save to a single image

Use -update 1 image muxer option. Example for once per second from a live streaming input:

ffmpeg -i rtmp://input.foo -q:v 4 -r 1 -update 1 output.jpg

Also see

llogan
  • 121,796
  • 28
  • 232
  • 243
  • This seems to have no effect for me-- qscale 1 and 2 both give identical file sizes and (to my naked eye) appear the same as without qscale at all. – felwithe Jan 28 '15 at 23:03
  • Can you post the complete commandline you're using? Also please post the complete, uncut output from ffmpeg on the commandline. Note that _placement_ of options is relevant, so -qscale:v 2 needs to be placed after the -i inputfile option, but before the output file option, to have any effect. – Ronald S. Bultje Apr 12 '15 at 12:10
  • 1
    For me adding `-qmin 1 -qmax 1` in addition to `-q:v 1` doubled the file size. And I can seem to see a very slight improvement also. – complistic Jun 27 '15 at 00:43
  • 1
    @complistic: `-qmin 1 -qmax 1` resulted in larger file, but gives me an exact same image. I validated this via photoshop, 2 layers and difference filter. The pixels are the same. – cherouvim Nov 30 '15 at 15:41
  • @Kostanos You can try `-qmin 1 -q:v 1`. – llogan Oct 14 '17 at 23:00
  • @LordNeckbeard "Effective range for JPEG is 2-31 with 31 being the worst quality"..why -q:v 1 not in effective range? – Android Developer Dec 08 '18 at 09:17
  • @BhuvneshVarma Because, if I recall correctly (can't check at the moment), `-qmin` default is `2`. You can try `-q:v 1` if you add `-qmin 1`. – llogan Dec 08 '18 at 18:03
  • Got an error with your last command: *Codec 'h264' (27) is not supported by the bitstream filter 'mjpeg2jpeg'. Supported codecs are: mjpeg (7) Error initializing bitstream filter: mjpeg2jpeg*. – Luis A. Florit Jul 27 '20 at 20:39
  • @LuisA.Florit That command is only for MJPEG inputs: *"If your input is MJPEG (Motion JPEG) then the images can be extracted without any quality loss."* Your input appears to be H.264, so you'll have to use the first command. – llogan Jul 27 '20 at 23:42
  • Indeed, sorry, I was confused by the error message "Supported codecs are: mjpeg". – Luis A. Florit Jul 29 '20 at 02:09
  • 1
    It is better to extract .png and then use mozjpeg with better jpeg algos. – Валерий Заподовников Dec 31 '21 at 02:16
  • 1
    ffmpeg -i MVI_5624.MP4 -r 1 images/thumb%04d.png – Nasser Ali Karimi Jul 13 '22 at 05:49
17

Output the images in a lossless format such as PNG:

mkdir stills
ffmpeg -i my-film.mp4 -vsync 0 -f image2 stills/my-film-%06d.png

Then use another program (where you can more precisely specify quality, subsampling and DCT method – e.g. GIMP) to convert the PNGs you want to JPEG.

It is possible to obtain slightly sharper images in JPEG format this way than is possible with -qmin 1 -q:v 1 and outputting as JPEG directly from ffmpeg.

If you want to extract only the key frames (which are likely to be of higher quality post-edit) you can use something like this:

ffmpeg -skip_frame nokey -i my-film.mp4 -vsync 0 -f image2 stills/my-film-%06d.png

The -vsync 0 parameter avoids needing to specify the frame rate with -r and means all frames in the input file are treated as, um, a frame.

Jake
  • 948
  • 8
  • 19
  • `ffmpeg` outputs `PNG8` files which use only 256 colors (same as GIF). so it is actually *very* lossy. – lapin Feb 17 '20 at 06:51
  • 3
    @lapin The PNGs I extracted using this method are 24 bit (even for frames with fewer than 256 colours, though others have many more). This was using version 4.2.1 x64 on Windows. Also written [here](https://forum.videohelp.com/threads/334333-Help-with-lossless-ffmpeg-command-%5bvideo-png-back-to-video%5d) is: "If I pull png's from an mp4, with this [`ffmpeg`] command, I get high quality png's that are of identical quality to the original video." What version of `ffmpeg` are you using that is outputting `PNG8` files, and what is your input format? – Jake Feb 20 '20 at 00:29
  • Actually I think you're right, the problem was that `identify image.png` gives result "8-bit" when actually its not really single channel 8-bit, but 8-bit for R, G and B. IDK how the average `identify` user is supposed to understand that tho. – lapin Feb 20 '20 at 11:12
  • 1
    @DonnyV. You can't get downvoted for a comment (only 'flagged as inapporpriate'). Glad this helped. Thanks for reminding me I posted this. I've rechecked the example command I gave and updated my answer to offer some better alternatives that are more akin to what I'm using now I know a bit more about `ffmpeg`. – Jake Dec 10 '20 at 06:18
  • 1
    I found your solution working to generate high quality key-frames, nonetheless I think that it must be arranged in a more clearer way. – BalrogOfMoria Jan 04 '21 at 10:16
  • No matter which format you use ffmeg downgrades image quality. With `-qmin 1 -q:v 1` JPG quality is the same as PNG quality. Both suck. – pouya Oct 20 '21 at 17:33
  • @BalrogOfMoria I edited my answer to make it more clear, thanks for the feedback. – Jake Oct 25 '21 at 00:34
  • 2
    @pouya Can you get anything better when you pause the video during playback? If not, then you don't have a good enough quality copy of the original video for what you are trying to do. I have seen the source code where `ffmpeg` sets the JPEG compression quality to 90 (instead of 70) if you have `-q2` set, but that is still lower than I would normally use. Also note it is recompressing a still from a movie that has already been compressed, and that 'key frames' will be of better quality. But whilst MPEG is based on JPEG, it seems not possible to lift even key frames directly as JPEG images. – Jake Oct 25 '21 at 00:44
  • Just use lossless jpeg or jpeg ls or jpeg 2000 lossless. – Валерий Заподовников Mar 05 '22 at 20:31
  • 2
    I experimented with the ```-qscale:v``` parameter when trying to split an .mp4 video into frames. The simple change from .jpg to .png produced the greatest improvement in quality. – Radespy Jul 10 '22 at 23:05
  • You would have thought that given MPEG is based on JPEG, you might be able to extract frames in JPEG format without any loss of quality. It seems woefully not the case. – Jake Jul 01 '23 at 06:52
0

note down the number of a specific frame

mpv --osd-msg1='${estimated-frame-number} / ${estimated-frame-count}' vid1.mp4

Save frame numbers in a file one below other eg. frm.txt then run:

sed -i 's/^/eq(n\\,/' frm.txt; sed -i 's/$/)\+/'  frm.txt; sed -i '$ s/.$//' frm.txt;        #adds eq(n\, #adds )+ at the end of each line #remove + in lastline (last digit)

then extract them as bmp or png

frms=$(cat frm.txt); ffmpeg -i vid_1.mp4  -vf "select='$frms'" -fps_mode drop "frames_%03d.bmp"

then convert to jpg - the difference is HUGE 350kb (ffmpeg jpg of best quality vs 2MB (bmp converting to jpg)!!!!

for pic in *.bmp; do convert  -units PixelsPerInch -density 300 -quality 100  "$pic" "${pic//}_j.jpg"; done

(you need imagemagick's convert of course)

Estatistics
  • 874
  • 9
  • 24