Jakość video po kompresji czyli PSNR i SSIM

Dużo pracuję z video i zawsze na pierwszym miejscu jest u mnie jakość materiału. Najchętniej pracowałbym na kompresji bezstratnej (lossless), ale filmy w 4K zajmują bardzo dużo miejsca, a i obróbka przy olbrzymich plikach jest bardziej uciążliwa… Istnieją mimo to kompromisy na które można się zgodzić, czyli kompresja stratna (lossy). Sprawdźmy kilka popularnych kodeków i jakość video.

Jakość video ma znaczenie

Nie jestem profesjonalnym operatorem kamery, większość rejestruję telefonem, stąd kodeki których używam to w większości x264, x265 oraz czasem mpeg4. Staram się zawsze nagrywać w maksymalnej rozdzielczości jaką daje telefon i maksymalnym FPS (klatki na sekundę) – co zwykle daje 4K w 60fps. Jest to przede wszystkim płynność video, ale także pole do dodatkowej stabilizacji obrazu przy użyciu oprogramowania, które odpowiednio kadrując i zniekształcając obraz daje poczucie stabilnego ujęcia kosztem zmniejszenia rozdzielczości materiału – to jest dla mnie zupełnie akceptowalne. Nagrywanie z „ręki” telefonem potrafi nieźle drgać.

O ile do obróbki video wykorzystuję Davinci Resolve oraz Adobe Premiere Essentials, to pliki wynikowe staram się renderować z maksymalnym bitrate aby nie tracić na jakości. Ostateczną kompresję robię przez ffmpeg i na nim się skupię w tym wpisie. Używam wersji ffmpeg-20200523-26b4509

Kodeki które porównam to x264, x265, VP9 oraz AV1. O ile trzy pierwsze są bardzo popularne, to AV1 dopiero zaczyna podbój rynku. Jest to nowe podejście i nowe algorytmy, które na mniejszym bitrate potrafią uzyskać lepszą jakość niż poprzednicy, co w rezultacie daje mniejsze pliki przy tej samej jakości. Olbrzymim minusem AV1 jest czas kompresji – około 500-700x dłuższy niż x264… Ale świat idzie do przodu, algorytm jest optymalizowany i lada moment trafi jako nowy firmware do naszych telewizorów i wszystko będzie grało.

Czym jest kompresja stratna i bezstratna?

Kompresja stratna to taka, w której chcąc zredukować wielkość pliku, godzimy się na utratę jakości – np. szczegółów, odwzorowania kolorów czy płynności ruchu. Nowoczesne kodeki pozwalają na płynne strojenie tych parametrów, oraz wykorzystują algorytmy dostosowane do odbiorcy czyli ludzkiego oka – które nie zawsze jest w stanie wychwycić te małe różnice, a dzięki temu plik jest znacznie mniejszy.

Kompresja bezstratna charakteryzuje się przenoszeniem obrazu 1:1 względem źródła, zatem plik wyjściowy jest dokładnym odwzorowaniem pliku wejściowego, ale zwykle bardzo dużym. Takie pliki są bardzo dobrym jakościowo materiałem do montażu i obróbki, ale nie nadają się do publikowania w Internecie, ze względu na swój rozmiar.

Po co mi PSNR i SSIM?

PSNR i SSIM to dwa popularne „mierniki” jakości względem materiału źródłowego. Aby sprawdzić jakość kompresji używając wybranego kodeka i bitrate, musimy plik wynikowy porównać z plikiem źródłowym. Dwa identyczne pliki powinny mieć SSIM równy 1.0 oraz PSNR dążący do nieskończoności (infinite). Taki wynik dają kodeki w których wykorzystamy kompresję bezstratną (lossless)

SSIM Y:1.000000 (inf) U:1.000000 (inf) V:1.000000 (inf) All:1.000000 (inf)
PSNR y:inf u:inf v:inf average:inf min:inf max:inf

Wykorzystując kompresję stratną, zgadzamy się na utratę pewnych szczegółów obrazu, ale uzyskujemy mniejszy plik wynikowy.

SSIM Y:0.998753 (29.042157) U:0.999491 (32.934697) V:0.999728 (35.656912) All:0.999039 (30.171505)
PSNR y:56.750038 u:62.900075 v:65.809073 average:58.129866 min:56.936481 max:inf

PSNR określa się jako Szczytowy stosunek sygnału do szumu, gdzie sygnałem są nieskompresowane dane źródłowe, a szumemartefakty (zniekształcenia) spowodowane zastosowaniem kompresji stratnej.

Parametr ten doskonale sprawdza się jako liczbowy parametr jakości kompresji, jednak nie bierze pod uwagę percepcji ludzkiego oka, co z kolei dokładniej odwzorowuje SSIM.

SSIM to miara wskaźnika podobieństwa strukturalnego i jest metodą przewidywania postrzeganej jakości telewizji cyfrowej i zdjęć kinowych, a także innych rodzajów obrazów cyfrowych i filmów.

Chcąc wykorzystać te dwa parametry musimy zrozumieć jakie wartości tych parametrów charakteryzują jakość kompresowanego video. PSNR dąży do nieskończoności – czyli im jest wyższy tym lepiej. Standardowo waha się między 30 a 50. Przy kompresji bezstratnej określany jako „infinity” – co oznacza że pliki źródłowy i wynikowe są identyczne. SSIM z kolei ma zakres od 0.0 do 1.0 i opisuje podobieństwo wizualne. Przy wartości 1.0 plik źródłowy oraz plik wyjściowy są identyczne. Co za tym idzie, mniejsza niż 1.0 wartość SSIM oznacza stratę jakości.

Wielkość pliku przy kompresji bezstratnej (lossless)

Plik źródłowy 1080p w 30fps kompresja h264 – skopiowane pierwsze 800 klatek z pominięciem audio, bo tym się nie zajmujemy, rozdzielczość do 720p i lossless x265 jako plik wzorca.

$ ffmpeg -i "source.mp4" -vframes 800 -vf scale=-1:720 -c:v libx265 -x265-params lossless=1 -an source.mkv

Output #0, matroska, to 'source.mkv':
  Metadata:
    major_brand     : isom
    minor_version   : 1
    compatible_brands: isomavc1
    composer        : Sacha Goedegebure
    title           : Big Buck Bunny, Sunflower version
    artist          : Blender Foundation 2008, Janus Bager Kristensen 2013
    comment         : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net
    genre           : Animation
    encoder         : Lavf58.43.100
    Stream #0:0(und): Video: hevc (libx265), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], q=-1--1, 30 fps, 1k tbn, 30 tbc (default)

Następnie plik ten został skompresowany przy wykorzystaniu x264, x265, VP9 oraz AV1 z opcją kompresji bezstratej, komendy poniżej:

$ ffmpeg -i source.mkv -c:v libx264 -qp 0 x264_lossless.mkv
$ ffmpeg -i source.mkv -c:v libx265 -x265-params lossless=1 x265_lossless.mkv
$ ffmpeg -i source.mkv -c:v libvpx-vp9 -b:v 0 -lossless 1 VP9_lossless.mkv

Porównujemy x264 względem źródła, mamy SSIM = 1.0 oraz PSNR = inf. czyli kompresja była bezstratna.

$ ffmpeg -i x264_lossless.mkv -i source.mkv -lavfi "ssim;[0:v][1:v]psnr" -f null -
SSIM Y:1.000000 (inf) U:1.000000 (inf) V:1.000000 (inf) All:1.000000 (inf)
PSNR y:inf u:inf v:inf average:inf min:inf max:inf

Następnie kompresujemy z użyciem x265 względem źródła, mamy SSIM = 1.0 oraz PSNR = inf. czyli kompresja była bezstratna.

$ ffmpeg -i x265_lossless.mkv -i source.mkv -lavfi "ssim;[0:v][1:v]psnr" -f null -
SSIM Y:1.000000 (inf) U:1.000000 (inf) V:1.000000 (inf) All:1.000000 (inf)
PSNR y:inf u:inf v:inf average:inf min:inf max:inf

Na koniec kompresujemy VP9 względem źródła, mamy SSIM = 1.0 oraz PSNR = inf. czyli kompresja była bezstratna.

$ ffmpeg -i VP9_lossless.mkv -i source.mkv -lavfi "ssim;[0:v][1:v]psnr" -f null -
SSIM Y:1.000000 (inf) U:1.000000 (inf) V:1.000000 (inf) All:1.000000 (inf)
PSNR y:inf u:inf v:inf average:inf min:inf max:inf

Na sam koniec zostaje nam AV1 względem źródła, mamy SSIM = 1.0 oraz PSNR = inf. czyli kompresja była bezstratna.

Podsumujmy kompresję bezstratną (lossless)

Kodek Objętość
źródło (x265) 108,683,556 bajtów
x264 117,931,148 bajtów
x265 108,683,556 bajtów
VP9 171,929,297 bajtów
AV1 112,405,222 bajtów

Przy kompresji bezstratnej różnice w wielkości plików mają znaczenie, gdyż pliki wyjściowe są dużo większe niż przy kompresji stratnej. W takim wypadku 10-20% różnicy może oznaczać kilka-kilkadziesiąt gigabajtów więcej na dysku.

Jakość video przy kompresji stratnej (lossy)

Ponownie ten sam plik źródłowy 1080p w 30fps kompresja h264. Po pierwsze skopiowane 800 klatek z pominięciem audio. Tym się nie zajmujemy. Po drugie rozdzielczość do 720p i lossless x265 jako plik wzorca.

Tym razem celujemy w bardzo zbliżone wartości PSNR oraz SSIM dla wszystkich plików wynikowych, próbując dostroić parametry kompresji. Dzięki temu będziemy wiedzieć że pliki wyjściowe mają bardzo zbliżoną do siebie jakość i porównamy stopień kompresji czyli wielkość pliku wynikowego.

W pierwszej kolejności idzie VP9, ze względu na brak możliwości ustawiania parametru -crf jako ułamek z częścią po przecinku. Wynikiem będzie nasza jakość video do której dążymy, pozostałe kodeki będziemy doprowadzać do jakości kodeka VP9. VP9 posiada najlepszą jakość przy dwu-przebiegowej kompresji, gdzie pozostałe kodeki mają minimalny i wręcz pomijalny uzysk z dwóch przebiegów. Jako parametry w które celujemy to PSNR average:45.321 oraz SSIM All:0.992. Wizualnie utraciliśmy 0.8% szczegółów o czym mówi SSIM.

$ ffmpeg -i source.mkv -y -c:v libvpx-vp9 -pass 1 -crf 32 -b:v 0 -tune psnr -f webm VP9_psnr_pass1.mkv
$ ffmpeg -i source.mkv -c:v libvpx-vp9 -pass 2 -crf 32 -b:v 0 -tune psnr VP9_psnr_pass2.mkv
$ ffmpeg -i VP9_psnr_pass2.mkv -i source.mkv -lavfi "ssim;[0:v][1:v]psnr" -f null -

SSIM Y:0.992829 (21.444045) U:0.991546 (20.729279) V:0.991933 (20.932731) All:0.992466 (21.229502)
PSNR y:44.354215 u:47.979801 v:48.693489 average:45.321377 min:41.582973 max:inf

Jako pierwszy dostroiłem x264 uzyskując PSNR: 45.358 i SSIM:0.9916

$ ffmpeg -i source.mkv -c:v libx264 -tune psnr -crf 21.6 x264_psnr.mkv
$ ffmpeg -i x264_psnr.mkv -i source.mkv -lavfi "ssim;[0:v][1:v]psnr" -f null -

SSIM Y:0.993020 (21.561526) U:0.988144 (19.260510) V:0.989391 (19.743134) All:0.991602 (20.758494)
PSNR y:44.739910 u:46.548467 v:47.379091 average:45.358051 min:39.754114 max:inf

Następnie zająłem się x265 i otrzymałem wynik PSNR: 45.347 i SSIM:0.9919

$ ffmpeg -i source.mkv -c:v libx265 -tune psnr -crf 20.7 x265_psnr.mkv
$ ffmpeg -i x265_psnr.mkv -i source.mkv -lavfi "ssim;[0:v][1:v]psnr" -f null -

SSIM Y:0.992588 (21.300356) U:0.990183 (20.080185) V:0.990867 (20.393656) All:0.991900 (20.915107)
PSNR y:44.520794 u:47.337848 v:48.110384 average:45.347215 min:40.447379 max:inf

Na koniec AV1 gdzie PSNR:45.361 i SSIM:0.9920

$ ffmpeg -i source.mkv -c:v libaom-av1 -row-mt 1 -tiles 8x4 -crf 25 -strict experimental AV1_psnr.mkv
$ ffmpeg.exe -i AV1_psnr.mkv -i source.mkv -lavfi "ssim;[0:v][1:v]psnr" -f null -

SSIM Y:0.996144 (24.138723) U:0.994546 (22.632922) V:0.994623 (22.694995) All:0.992024 (23.589543)
PSNR y:45.340784 u:45.142517 v:45.210798 average:45.361 min:43.819632 max:inf

Podsumujmy jakość video przy kompresji stratnej

Kodek PSNR/SSIM Objętość CRF
źródło (x265) PSNR:inf.
SSIM: 1.0
108,683,556 bajtów 0 (lossless)
x264 PSNR:45.358
SSIM:0.9916
6,628,297 bajtów 21.6
x265 PSNR:45.347
SSIM:0.9919
5,518,725 bajtów 20.7
VP9 PSNR:45.321
SSIM:0.9924
4,285,884 bajtów 32.0
AV1 PSNR:45.361
SSIM:0.9920
3,321,936 bajtów 25

Łatwo zauważyć że pomiędzy x264 a AV1 jest różnica wielkości pliku na poziomie 50% przy zachowaniu bardzo zbliżonej jakości. Jest to fantastyczny wynik, gdyby nie to, że kodowanie x264 czy x265 liczyłem w minutach, a dla tego samego materiału w AV1 były to dosłownie godziny.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *