How can I extract a good quality JPEG image from a video file with ffmpeg?
Asked Answered
T

3

177

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?

Tarrasa answered 19/4, 2012 at 9:39 Comment(2)
I believe ffmpeg is not the way to go. Use ffmpeg to output png. Then use imagemagick to convert png to jpg.Abundant
Using ffmpeg to output jpg is a mess. The quality is still bad using -q:v 1 argument. Compare to png.Abundant
D
325

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

Dulsea answered 19/4, 2012 at 18:3 Comment(12)
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.Kasi
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.Conlon
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.Pavilion
@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.Schick
@Kostanos You can try -qmin 1 -q:v 1.Dulsea
@LordNeckbeard "Effective range for JPEG is 2-31 with 31 being the worst quality"..why -q:v 1 not in effective range?Oscular
@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.Dulsea
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.Florist
@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.Dulsea
Indeed, sorry, I was confused by the error message "Supported codecs are: mjpeg".Florist
It is better to extract .png and then use mozjpeg with better jpeg algos.Broddie
ffmpeg -i MVI_5624.MP4 -r 1 images/thumb%04d.pngKnot
G
20

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.

Glassine answered 2/11, 2019 at 15:53 Comment(12)
ffmpeg outputs PNG8 files which use only 256 colors (same as GIF). so it is actually very lossy.Hibben
@Hibben 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 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?Glassine
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.Hibben
@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.Glassine
I found your solution working to generate high quality key-frames, nonetheless I think that it must be arranged in a more clearer way.Eumenides
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.R
@Eumenides I edited my answer to make it more clear, thanks for the feedback.Glassine
@R 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.Glassine
Just use lossless jpeg or jpeg ls or jpeg 2000 lossless.Broddie
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.Affaire
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.Glassine
I rather use XnView to do batch conversions from PNG to JPG. You can disable subsampling and use JPEG quality of 94-95 which is the best in my experience for photos.Ecchymosis
T
1

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)

Tussock answered 19/4, 2012 at 9:39 Comment(1)
This is similar to my answer, in the sense that it says to extract the frames in a lossless format then convert to JPEG outside of ffmpeg. It adds value by providing some automated scripting suggestions to complete the task.Glassine

© 2022 - 2024 — McMap. All rights reserved.