ekisu / mpv-webm

Simple WebM maker for mpv, with no external dependencies.
MIT License
576 stars 33 forks source link

GIF quality control #111

Closed ghost closed 3 years ago

ghost commented 3 years ago

mpv version and platform

Windows 10 20H2

mpv 0.33.0-107-gdd86f195a6 Copyright © 2000-2020 mpv/MPlayer/mplayer2 projects
 built on Sun Apr  4 13:12:37 +08 2021
FFmpeg library versions:
   libavutil       56.72.100
   libavcodec      58.135.100
   libavformat     58.77.100
   libswscale      5.10.100
   libavfilter     7.111.100
   libswresample   3.10.100
FFmpeg version: git-2021-04-03-e93875b7

Description

It seems there is no option for GIF quality It always comes out looking like this: test- 00 00 000-00 07 083

ghost commented 3 years ago

for comparison, when I convert the same video to GIF using ffmpeg, the output is: test- 00 00 917-00 07 067 webm I have a batch file for doing this with ffmpeg:

set width=-1
set length=-1
set x=0
set y=0
set /p width= "Enter width max size (empty to ignore): "
set /p length= "Enter length max size (empty to ignore): "
set /p x= "Enter x crop (empty to ignore): "
set /p y= "Enter y crop (empty to ignore): "
echo width is %width%
echo length is %length%
echo y crop is %y%
echo x crop is %x%

pause
setlocal enabledelayedexpansion
for  %%A IN (*.mp4, *.mkv, *.webm) DO (
ffmpeg -i "%%A" -vf fps=24,crop=iw-%x%*2:ih-%y%*2:%x%:%y%,scale=%width%:%length%,palettegen "1/%%A.png"
ffmpeg -i "%%A" -i "1/%%A.png" -filter_complex "fps=24,crop=iw-%x%*2:ih-%y%*2:%x%:%y%,scale=%width%:%length%[a];[a][1:v]paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle" "1/%%A.gif"
)
pause

this outputs very high quality GIFs

ShinkoNet commented 3 years ago

I also kinda want to know why it's all grainy. It seems to be taking the palette of the first frame and then applying that to the remaining frames. Here's a webm and GIF comparison of something I just did with it. Gif: https://files.catbox.moe/7260eb.gif Webm: https://files.catbox.moe/lkqsqk.webm

When I do palettegen and paletteuse in ffmpeg and generate it manually: Gif: https://files.catbox.moe/41kuoo.gif

Vzaa commented 3 years ago

I built a fresh mpv with mpv-player/mpv-build to try this. Weirdly enough, this time mpv's default output looks better than ffmpeg's default. This is comparing the mpv and the ffmpeg binaries coming from the same build inside mpv-build. I still prefer the ffmpeg default, as the file size of the mpv gif was more than 2x the size for my test input, 3 MB vs 8 MB.

mpv command:

./mpv/build/mpv input.webm   --ovc=gif  --o=output_mpv.gif

ffmpeg command:

./ffmpeg_build/ffmpeg -i input.webm output_ffmpeg.gif

There's probably a way to override the defaults. Maybe ffmpeg's defaults can be matched and set to the gif config here.

ghost commented 3 years ago

I built a fresh mpv with mpv-player/mpv-build to try this. Weirdly enough, this time mpv's default output looks better than ffmpeg's default.

In my opinion, if you want quality GIFs, you have to generate and use a palette https://gist.github.com/alexlee-gk/38916bf524dc75ca1b988d113aa30710 However I don't know exactly what's possible through this MPV script.

ghost commented 3 years ago

If this script can get ffmpeg integration, we could also get free lossless trimming of videos without re-encoding and compressing them.

Vzaa commented 3 years ago

However I don't know exactly what's possible through this MPV script.

It invokes a new mpv process to use its encoding capabilities.

In my opinion, if you want quality GIFs, you have to generate and use a palette

Thanks for pointing this out. It looks like you can generate and use a palette similar to how you do it with ffmpeg. I believe it should be possible to integrate the two step process into mpv-webm. I can give it a try when I have the time.

Using @ShinkoNet's samples, these two are equivalent from what I can tell.

ffmpeg -i lkqsqk.webm -vf "palettegen" palette_ffmpeg.png
ffmpeg -i lkqsqk.webm -i palette_ffmpeg.png -filter_complex "paletteuse" out_ffmpeg.gif
mpv lkqsqk.webm --ovc=png --aid=no --vf=palettegen --o=palette_mpv.png
mpv lkqsqk.webm --aid=no --external-file=palette_mpv.png --lavfi-complex="[vid1][vid2]paletteuse[vo]" --o=out_mpv.gif
Vzaa commented 3 years ago

I managed to hack this in an ugly way and it worked. I will try to see if I can clean it up a bit. It will require adding a unique step just for gif which might look weird in the code. I'm not sure how acceptable that is.

In the same video I get a smaller gif that also looks better compared to the current gif option. Of course that may not be the case universally. I don't know if it's better to define a new "GIF with Palette" output format or replace the existing one.

Vzaa commented 3 years ago

I have a proof of concept on my fork if you'd like to give it a try

https://github.com/Vzaa/mpv-webm/releases/download/latest/webm.lua

Related change

https://github.com/Vzaa/mpv-webm/commit/a0a0138d97be2bcd784bf63e80c5db8ca16b18f1

I can turn this into a PR if it looks good

Update: It looks like the palettegen step uses the whole video and not the start/end sections so if the video is long it takes really long. I'll take a look at it later if that can be fixed

Vzaa commented 3 years ago

I've updated the change so that it uses --vf-add=trim instead of --start and --end in the palettegen step, and --lavfi-complex for crop and scale instread of --vf-add=lavfi-scale/lavfi-crop in the encoding step. It is pretty hacky but it works.

With these changes in place, the trimming doesn't break palettegen and resizing doesn't break paletteuse.

It might fail if there are multiple video streams open at once as [vid1] for video and [vid2] for the palette are hard coded.

Related change: https://github.com/Vzaa/mpv-webm/commit/a479d6606709af786faabae1e1b937ba48854983

Test build: https://github.com/Vzaa/mpv-webm/releases/download/latest/webm.lua

ghost commented 3 years ago

@Vzaa That's a great improvement in quality, awesome!

I tried converting the same webm to GIF using your test build and using ffmpeg to compare the results: Source file: Webm

Conversion using your test build: MPV GIF MPV Palette GIF: 42.5 MB Palette: 1.03 KB Great result, basically the same as ffmpeg with palettegen & paletteuse.

Conversion using ffmpeg (just palettegen & paletteuse): FFMPEG GIF FFMPEG Palette GIF: 43.2 MB Palette: 985 KB

Conversion using ffmpeg (palettegen & paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle"): FFMPEG GIF FFMPEG Palette GIF: 35.4 MB Palette: 985 KB Result is lower quality, a lot of color banding.

Vzaa commented 3 years ago

Thanks for the feedback. Since it's using ffmpeg filters it's also possible to add the same dither filter on top.

There are other problems with my current version though. Using other filters with lavfi-complex causes problems with the order of filters applied and some options break paletteuse. This is why I manually replaced resize and crop with their lavfi-complex equivalents in my current version. I will update it to convert --vf options to lavfi-complex instead of manually converting them but it looks like options such as --video-rotate will require manual lookup as they also seem to break paletteuse and are not vf options. I've found that rotate and deinterlace both cause problems and will require the manual coversion, it is doable of course but it starts to get a bit messy to do these one by one.

ghost commented 3 years ago

@Vzaa Now that the quality is splendid, is perhaps the addition of something like this an option? https://kornel.ski/lossygif * So the user can use between quality and file size efficiency?

it appears to be merged into: https://github.com/kohler/gifsicle I just used GIFsicle: `gifsicle -b -O3 --lossy=20 .gif` It reduced a 27MB GIF into a 17MB GIF

Vzaa commented 3 years ago

It looks like they add something like a threshold to LZW encoding to achieve some of the gains. I don't think ffmpeg has parameters for the LZW part of things so I don't see a way to do the same easily without such an option.

In a previous post you compared paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle to paletteuse defaults. Similar options to paletteuse can be added here for some quality/size tradeoff I think.

I didn't have much time to play around with this more. I will try to make this ready for a proper PR at some point. It's just that adding complex filters doesn't seem to fit that well with things here. It requires a specific gif path to be added to the code.

Vzaa commented 3 years ago

https://github.com/Vzaa/mpv-webm/commit/0e9d445cde4cd78aa387617c26d3b82a8cd504a0

I merged the palettegen and paletteuse steps into a single command and modified the filter stuff a bit. I also changed structure and defined a postCommandModifier method so that the code can be put in the gif class. I think this might be the least intrusive it can get without changing too much. Not sure what to do about the dither mode for paletteuse but maybe we can make it the default or create two GIF output formats.

Other than the decision for what to do about the dither/diff_mode options, I think it is PR ready, but I'll test it a bit more and see if I run into any problems.

Test build: https://github.com/Vzaa/mpv-webm/releases/download/latest/webm.lua

Pentaphon commented 2 years ago

I'm still getting the grainy-looking gifs when converting to gif. I am using the latest version of the script with shinchiro's latest mpv build along with the latest ffmpeg.

Am I missing a step here or should it have worked fine on its own?

Does it need to be ported to Windows the same way this WebP generator (which is based on an mpv gif generator port for Windows)

https://github.com/ekisu/mpv-webm/issues/124

Here's the mpv gif generator for Windows if you need to take a look. It's not as robust and user as this script, though, but if it creates quality gifs instead of grainy ones using ffmpeg, it would be worth integrating into this script for Windows users.

https://github.com/Scheliux/mpv-gif-generator

Vzaa commented 2 years ago

along with the latest ffmpeg

This script doesn't actually call an external ffmpeg binary but calls mpv itself (which internally uses ffmpeg).

Am I missing a step here or should it have worked fine on its own?

It should have. Can you share an example short source clip and the gif output along with the settings you use to compare results? I don't have a Windows machine at hand to test, unfortunately. I don't expect Windows to behave differently as long as the same options are available 🤔 (unless mpv was built against ffmpeg with disabled features or something)

OK, I asked a friend to test on Windows and it does look bad there. I'll try to see if I can figure out what goes wrong. It is weird to me though, as @horusra was testing this on Windows according to the original post, and was happy with the results after the changes. I wonder what broke it.

EDIT: It is not Windows see next comment

but if it creates quality gifs instead of grainy ones using ffmpeg, it would be worth integrating into this script for Windows users.

BTW we can import the same options from that project here if they result in better quality gifs.

Vzaa commented 2 years ago

Alright, mystery solved.

The recent filter changes in https://github.com/ekisu/mpv-webm/commit/7041c07f973fee287fda22d7fdb01d33113ed456 seems to have broken it. It is not Windows, I had an outdated version of the script apparently!

@Pentaphon Can you try commenting the line with get_contrast_brightness_and_saturation_filters by adding -- like below in the webm.lua script (around line 1690 on latest version) and try again to see if it fixes the problem for you?

  append(filters, get_scale_filters())
  append(filters, get_fps_filters())
  --append(filters, get_contrast_brightness_and_saturation_filters())
  append(filters, format:getPostFilters())

The gif code is a bit brittle unfortunately so I expect it to break. I'll try to send a fix when I find the time.