FFMPEG (libx264) „wysokość nie dzieli się przez 2”


188

Próbuję zakodować wideo .mp4 z zestawu ramek przy użyciu FFMPEG przy użyciu kodeka libx264.

Oto polecenie, które uruchamiam:

/usr/local/bin/ffmpeg -r 24 -i frame_%05d.jpg -vcodec libx264 -y -an video.mp4

Czasami pojawia się następujący błąd:

[libx264 @ 0xa3b85a0] height not divisible by 2 (520x369)

Po dokładnym przeszukaniu wydaje się, że problem ma coś wspólnego z algorytmem skalowania i można go rozwiązać, dodając argument -vf.

Jednak w moim przypadku nie chcę wykonywać żadnego skalowania. Idealnie chcę zachować wymiary dokładnie takie same jak ramki. Jakakolwiek rada? Czy istnieje jakiś współczynnik proporcji, który wymusza h264?


@AleksandrDubinsky Ale odpowiedź Lorda Neckbearda nie zachowuje oryginalnej szerokości i wysokości. Tutaj musimy ręcznie określić szerokość lub wysokość .. a jeśli użyjemy -vf skala = -2: ih lub -vf skala = iw: -2 to nie będzie działa, jeśli wysokość i szerokość są nierówne .. Proszę wyjaśnić, w jaki sposób ta odpowiedź jest bardziej optymalna? .. dzięki
varmashrivastava

1
@varmashrivastava Cóż, sposób SO działa tak, że pierwotnie mogło być jedno pytanie, a następnie Google wysyła grupę ludzi z innym pytaniem, którzy następnie przejmują stronę. Tak to jest, staraj się z nim nie walczyć. Prawidłowa odpowiedź na pierwotne pytanie -vf pad="width=ceil(iw/2)*2:height=ceil(ih/2)*2"brzmi: nie jest to jedna z odpowiedzi. Prawidłowa odpowiedź na pytanie innych należy do LordNeckbeard.
Aleksandr Dubinsky

@varmashrivastava Poszedłem naprzód i naprawiłem pierwszą odpowiedź. Miejmy nadzieję, że nie zostanie zniszczony przez mody.
Aleksandr Dubinsky

@AleksandrDubinsky dzięki .. a użytkownik może użyć "scale="zamiast tego, "pad="jeśli on / ona nie chce kolorowych wypełnień?
varmashrivastava

Odpowiedzi:


269

Odpowiedź na pierwotne pytanie, które nie chce skalować wideo, brzmi:

-vf "pad=ceil(iw/2)*2:ceil(ih/2)*2"

Komenda:

ffmpeg -r 24 -i frame_%05d.jpg -vcodec libx264 -y -an video.mp4 -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2"

Zasadniczo .h264 potrzebuje równych wymiarów, więc ten filtr:

  1. Podziel pierwotną wysokość i szerokość przez 2
  2. Zaokrąglij w górę do najbliższego piksela
  3. Pomnóż go ponownie przez 2, dzięki czemu będzie parzystą liczbą
  4. Dodaj czarne piksele wypełniające do tej liczby

Możesz zmienić kolor wypełnienia, dodając parametr filtru :color=white. Zobacz dokumentację pada .


3
To nie jest błąd. Nie ma znaczenia, że ​​nie wykonujesz skalowania, ponieważ dane wyjściowe odziedziczą rozmiar ramki danych wejściowych.
llogan

5
Dla przypomnienia, po prostu robiłem coś, w którym stworzyłem wideo z obrazu i użyłem yuvj444p jako formatu pikseli; nie dbał o rozmiar wideo. Następnie musiałem przekonwertować go na yuv420p, a potem zależało na rozmiarze wideo. Poszukałem yuv420p na wikipedii, myślę, że jest to kolorowy format wielopikselowy, który potrzebuje określonego rozmiaru obrazu. Nie jestem pewien, dlaczego ma to znaczenie skompresowane.
lahwran

7
Prawdopodobnie lepiej jest użyć padu niż skali, aby dodać czarny wiersz / kolumnę. Skalowanie obrazu o jeden piksel spowoduje jego rozmycie.
Glenn Maynard

5
@NickeManarin ten filtr powinien pracować, aby dodać 1 piksel biały wyściółką do wymiaru pionowego, ze film umieszczony górny lewy: -vf pad="width=iw:height=ih+1:x=0:y=0:color=white". Dokumentacja pada ffmpeg znajduje się tutaj: ffmpeg.org/ffmpeg-filters.html#pad-1 .
Mark Berry

4
Oto rozwiązanie, które tylko dodaje piksel dopełnienie do wymiarów, które są dziwne: -vf pad="width=ceil(iw/2)*2:height=ceil(ih/2)*2".
danneu

250

Po prostu użyj -2

Z dokumentacji filtra skali :

Jeśli jedna z wartości znajduje -nsię n > 1filtr waga również użyć wartość utrzymuje się współczynnik kształtu obrazu wejściowego, obliczony z innym określonym wymiarze. Następnie upewni się, że obliczony wymiar jest podzielny przez ni w razie potrzeby dostosuje wartość.

Przykłady

Ustaw szerokość na 1280, a wysokość zostanie automatycznie obliczona, aby zachować proporcje, a wysokość będzie podzielna przez 2:

-vf scale=1280:-2

Taki sam jak powyżej, ale zamiast tego z zadeklarowaną wysokością; pozostawiając szerokość, którą ma zająć filtr:

-vf scale=-2:720

„podzielny przez 2”

Zgodnie z wymaganiami x264, „wyniki podzielne przez 2 dla szerokości i wysokości” są potrzebne dla wyjściowych próbek kolorów YUV 4: 2: 0. 4: 2: 2 wymagałoby „podzielności przez 2 dla szerokości”, a 4: 4: 4 nie ma tych ograniczeń. Jednak większość odtwarzaczy nieopartych na FFmpeg może poprawnie poprawnie dekodować tylko 4: 2: 0, dlatego często wyświetla się ffmpegpolecenia z -pix_fmt yuv420popcją podczas przesyłania wideo H.264.

Zastrzeżenie

Niestety nie możesz używać -2zarówno szerokości, jak i wysokości, ale jeśli już określiłeś jeden wymiar, użycie -2jest prostym rozwiązaniem.


14
Myślę, że tihi należy oznaczyć jako właściwą odpowiedź, ponieważ nie ma w tym żadnych „sztuczek”. Chciałbym głosować więcej niż jeden raz
LucaM

1
Dlaczego -vf scale=-2:-2nie działa? W moim przypadku chcę zachować maksymalny rozmiar oryginalnego pliku. To, co zadziałało, było dla mnie -vf scale=-2:ih. Ale to nie działa, jeśli oba h / w są nierówne.
Pascal

2
@tuner Wynikowa wartość -2zależy od deklarowanej wartości innego wymiaru.
llogan

3
w moim przypadku dał mi to następujący błąd: Size values less than -1 are not acceptable.ale odpowiedź @Zbyszek działała idealnie.
Julien


64

Jeśli chcesz ustawić szerokość wyjściową i mieć wyjściowy w takim samym stosunku jak oryginał

scale=720:-1 

i nie wpaść w ten problem, możesz użyć

scale="720:trunc(ow/a/2)*2"

(Tylko dla osób szukających tego, jak to zrobić za pomocą skalowania)


16
A dla stałej wysokości jest toscale="trunc(oh*a/2)*2:720"
Tom

20

Problem z scalerozwiązaniami tutaj polega na tym, że zniekształcają obraz / wideo źródłowe, co prawie nigdy nie jest tym, czego chcesz.

Zamiast tego znalazłem najlepsze rozwiązanie, aby dodać 1-pikselowy pad do nieparzystego wymiaru. (Domyślnie wypełnienie jest czarne i trudne do zauważenia.)

Problem z innymi padrozwiązaniami polega na tym, że nie generalizują one dowolnych wymiarów, ponieważ zawsze padają.

To rozwiązanie dodaje pad 1-pikselowy tylko do wysokości i / lub szerokości, jeśli są nieparzyste:

-vf pad="width=ceil(iw/2)*2:height=ceil(ih/2)*2"

Jest to idealne rozwiązanie, ponieważ zawsze robi to dobrze, nawet gdy nie jest konieczne wypełnianie.


Rozwiązania skalujące zmieniają liczbę pikseli maksymalnie o 1. To prawie nie zniekształca obrazu. Jeśli martwisz się o szybkość filtrowania, użyj scale=iw+mod(iw,2):ih+mod(ih,2):flags=neighbor. Może to zwiększyć każdy wymiar tylko o 1, w razie potrzeby, i powieli ostatni wiersz / kolumnę.
Gyan

@Gyan To było zbyt dawno temu miałem problem ten rozwiązano (moja odpowiedź została pozyskana z komentarzem zrobiłem dawno temu), ale pamiętam, że skalowaniem przez pojedynczego piksela nie wprowadzić zauważalne artefakty w pewnych warunkach i dlatego nie przeszkadzało na pierwszym miejscu. Nie pamiętam dokładnie, może nieproporcjonalnie dużo rozmycia po zmianie jednego piksela? Może tylko w niektórych formatach vid / image? Mogę tylko powiedzieć, że z tą poprawką przetworzyłem tysiące filmów i była to korzystna transformacja.
danneu

19

Jest to prawdopodobnie spowodowane faktem, że wideo H264 jest zwykle konwertowane z przestrzeni RGB na przestrzeń YUV jako 4: 2: 0 przed zastosowaniem kompresji (chociaż sama konwersja formatu jest algorytmem kompresji stratnej, co daje 50% oszczędności miejsca).

YUV-420 zaczyna się obrazem RGB (czerwony zielony niebieski niebieski) i konwertuje go na YUV (w zasadzie jeden kanał intensywności i dwa kanały „odcienia”). Kanały odcienia są następnie podpróbkowane przez utworzenie jednej próbki odcienia dla każdego kwadratu 2 x 2 tego odcienia.

Jeśli masz nieparzystą liczbę pikseli RGB, poziomo lub pionowo, będziesz mieć niekompletne dane dla ostatniej kolumny lub wiersza pikseli w podpróbkowanej przestrzeni barwy ramki YUV.


2
Kolejny interesujący fakt ... kiedy dekodujesz za pomocą Microsoft Media Foundation, musisz użyć wielokrotności 16 dla H264. Tak więc wideo 1080P faktycznie dekoduje się do bufora o wysokości 1088 (chociaż ignorujesz ostatnie 8 linii).
Adisak

2

Lord Neckbeard ma właściwą odpowiedź, bardzo szybko

-vf scale=1280:-2

Dla Androida nie zapomnij dodać

"-preset ultrafast" and|or "-threads n"

Nie musisz deklarować wątków: jest to obsługiwane automatycznie. Uważam, że powolność Andrioda podczas kodowania do H.264 wynika z tego, że ludzie używają popularnego „WritingMinds / ffmpeg-android”, który wykorzystuje --disable-asmw swoim skrypcie kompilacji x264 . Powoduje to niepotrzebne i znaczne spowolnienie (możesz sprawdzić dziennik ffmpeg i jeśli pokazuje, using cpu capabilties: none!że to źle). Nie jestem pewien, dlaczego to dodali, ale nie jestem programistą Androida.
llogan

1

Możesz także użyć bitandfunkcji zamiast trunc:

bitand (x, 65534)

zrobi to samo co trunc(x/2)*2i moim zdaniem jest bardziej przejrzyste.
(Rozważ 65534 magiczną liczbę tutaj;))


Moim zadaniem było automatyczne skalowanie wielu plików wideo do połowy rozdzielczości .

scale=-2,ih/2prowadzić do lekko rozmytych obrazów

powód:

  • wejściowe filmy wideo miały ustawiony format wyświetlania (DAR)
  • scale skaluje rzeczywiste wymiary ramki
  • podczas podglądu rozmiary nowych filmów muszą zostać poprawione za pomocą DAR, który w przypadku filmów o dość niskiej rozdzielczości (360x288, DAR 16: 9) może prowadzić do rozmycia

rozwiązanie:

-vf "scale='bitand(oh*dar, 65534)':'bitand(ih/2, 65534)', setsar=1"

wyjaśnienie:

  • Output_height = Input_height / 2
  • Output_width = Output_height * Original_display_aspect_ratio
  • zarówno output_width i output_height są teraz zaokrąglane do najbliższej mniejszej liczby podzielnej przez 2
  • setsar=1 znaczy wymiary wyjściowe są teraz ostateczne, nie należy stosować korekcji współczynnika kształtu

Ktoś może uznać to za pomocne.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.