FFMPEG (libx264) "hauteur non divisible par 2"

188

J'essaie d'encoder une vidéo .mp4 à partir d'un ensemble d'images en utilisant FFMPEG en utilisant le codec libx264.

Voici la commande que j'exécute:

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

J'obtiens parfois l'erreur suivante:

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

Après quelques recherches, il semble que le problème ait quelque chose à voir avec l'algorithme de mise à l'échelle et peut être résolu en ajoutant un argument -vf.

Cependant, dans mon cas, je ne veux pas faire de mise à l'échelle. Idéalement, je veux garder les dimensions exactement les mêmes que les cadres. Aucun conseil? Existe-t-il une sorte de rapport hauteur / largeur que h264 applique?

Andy Hin
la source
@AleksandrDubinsky Mais la réponse de LordNeckbeard ne conserve pas la largeur et la hauteur d'origine. Ici, nous devons spécifier manuellement la largeur ou la hauteur ... et si w utilisez -vf scale = -2: ih ou -vf scale = iw: -2 ce ne sera pas travailler si la hauteur et la largeur sont inégales..Veuillez expliquer en quoi cette réponse est plus optimale? .. merci
varmashrivastava
1
@varmashrivastava Eh bien, la façon dont fonctionne SO est qu'il y avait peut-être à l'origine une question, puis Google envoie un groupe de personnes avec une question différente qui détournent ensuite la page. C'est ce que c'est, essayez de ne pas le combattre. La bonne réponse à la question initiale est -vf pad="width=ceil(iw/2)*2:height=ceil(ih/2)*2", qui n'est même pas l'une des réponses. La bonne réponse à la question de tout le monde est celle de LordNeckbeard.
Aleksandr Dubinsky
@varmashrivastava Je suis allé de l'avant et j'ai corrigé la première réponse. Espérons qu'il ne soit pas vandalisé par les mods.
Aleksandr Dubinsky
@AleksandrDubinsky merci..et l'utilisateur peut utiliser à la "scale="place de "pad="s'il / elle ne veut pas de pixels de remplissage colorés?
varmashrivastava

Réponses:

269

La réponse à la question d'origine qui ne souhaite pas mettre à l'échelle la vidéo est:

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

Commander:

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

Fondamentalement, .h264 a besoin de dimensions égales, donc ce filtre:

  1. Divisez la hauteur et la largeur d'origine par 2
  2. Arrondissez-le au pixel le plus proche
  3. Multipliez-le par 2 à nouveau, ce qui en fait un nombre pair
  4. Ajouter des pixels de remplissage noirs jusqu'à ce nombre

Vous pouvez changer la couleur du remplissage en ajoutant un paramètre de filtre :color=white. Voir la documentation de pad .

Andy Hin
la source
3
Ce n'est pas un bug. Peu importe que vous n'effectuiez pas de mise à l'échelle, car la sortie héritera de la taille d'image de l'entrée.
llogan
5
Pour mémoire, je faisais juste quelque chose où j'ai créé une vidéo à partir d'une image, et il a utilisé yuvj444p comme format de pixel; il ne se souciait pas de la taille de la vidéo. Ensuite, j'ai dû le convertir en yuv420p, puis il s'est soucié de la taille de la vidéo. J'ai recherché yuv420p sur wikipedia, je pense que c'est un format de couleur multi-pixels, qui a besoin que l'image soit d'une taille spécifique. Je ne sais pas pourquoi c'est compressé, cependant.
lahwran
7
Il vaut probablement mieux utiliser le pad plutôt que l'échelle, pour ajouter une ligne / colonne noire. La mise à l'échelle d'une image d'un pixel la rendra floue.
Glenn Maynard
5
@NickeManarin, ce filtre doit fonctionner pour ajouter 1 pixel de rembourrage blanc à la dimension verticale, avec la vidéo positionnée en haut à gauche: -vf pad="width=iw:height=ih+1:x=0:y=0:color=white". La documentation du pad ffmpeg est ici: ffmpeg.org/ffmpeg-filters.html#pad-1 .
Mark Berry
4
Voici une solution qui ne fait qu'ajouter un pixel de rembourrage aux dimensions qui sont impaires: -vf pad="width=ceil(iw/2)*2:height=ceil(ih/2)*2".
danneu
250

Juste utiliser -2

À partir de la documentation du filtre de balance :

Si l'une des valeurs est -navec n > 1, le filtre d'échelle utilisera également une valeur qui conserve le rapport hauteur / largeur de l'image d'entrée, calculé à partir de l'autre dimension spécifiée. Après cela, il s'assurera cependant que la dimension calculée est divisible par net ajuste la valeur si nécessaire.

Exemples

Définissez la largeur sur 1280, et la hauteur sera automatiquement calculée pour conserver le rapport hauteur / largeur, et la hauteur sera divisible par 2:

-vf scale=1280:-2

Identique à ci-dessus, mais avec une hauteur déclarée à la place; laissant la largeur à traiter par le filtre:

-vf scale=-2:720

"divisible par 2"

Comme requis par x264, le "divisible par 2 pour la largeur et la hauteur" est nécessaire pour les sorties sous-échantillonnées de chrominance YUV 4: 2: 0. 4: 2: 2 aurait besoin de "divisible par 2 pour la largeur", et 4: 4: 4 n'a pas ces restrictions. Cependant, la plupart des lecteurs non basés sur FFmpeg ne peuvent décoder correctement que 4: 2: 0, c'est pourquoi vous voyez souvent des ffmpegcommandes avec l' -pix_fmt yuv420poption lors de la sortie de vidéo H.264.

Caveat

Malheureusement, vous ne pouvez pas utiliser -2à la fois la largeur et la hauteur, mais si vous avez déjà spécifié une dimension, l'utilisation -2est une solution simple.

llogan
la source
14
Je pense que les tihis devraient être marqués comme la bonne réponse car aucun "truc" n'est impliqué. Souhaite voter plus d'une fois
LucaM
1
Pourquoi ça -vf scale=-2:-2ne marche pas? Dans mon cas, je souhaite conserver autant que possible la taille du fichier d'origine. Ce qui a fonctionné pour moi était -vf scale=-2:ih. Mais cela ne fonctionne pas si les deux h / w sont inégaux.
Pascal
2
@tuner La valeur résultante de -2dépend de la valeur déclarée de l'autre dimension.
llogan
3
dans mon cas, cela m'a donné l'erreur suivante: Size values less than -1 are not acceptable.mais la réponse de @Zbyszek a parfaitement fonctionné.
Julien
64

Si vous souhaitez définir une largeur de sortie et obtenir une sortie avec le même rapport que l'original

scale=720:-1 

et pour ne pas tomber avec ce problème, vous pouvez utiliser

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

(Juste pour les personnes qui cherchent comment faire cela avec la mise à l'échelle)

Zbyszek
la source
16
Et pour une hauteur fixe c'estscale="trunc(oh*a/2)*2:720"
Tom
20

Le problème avec les scalesolutions ici est qu'elles déforment l'image / la vidéo source, ce qui n'est presque jamais ce que vous voulez.

Au lieu de cela, j'ai trouvé que la meilleure solution consiste à ajouter un tampon de 1 pixel à la dimension impaire. (Par défaut, le rembourrage est noir et difficile à remarquer.)

Le problème avec les autres padsolutions est qu'elles ne se généralisent pas sur des dimensions arbitraires car elles bourrent toujours.

Cette solution n'ajoute un pad de 1 pixel à la hauteur et / ou à la largeur que s'ils sont impairs:

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

C'est idéal car il fait toujours ce qu'il faut, même si aucun rembourrage n'est nécessaire.

Danneu
la source
Les solutions d'échelle modifient le nombre de pixels de 1 au maximum. Cela déforme à peine l'image. Si vous vous inquiétez de la vitesse de filtrage, utilisez scale=iw+mod(iw,2):ih+mod(ih,2):flags=neighbor. Cela ne peut augmenter chaque dimension que de 1, si nécessaire, et dupliquera la dernière ligne / colonne.
Gyan
@Gyan Cela fait trop longtemps que je n'ai pas eu le problème que cela résolu (ma réponse a été extraite d'un commentaire que j'ai fait il y a longtemps), mais je me souviens que la mise à l'échelle par un seul pixel a introduit des artefacts visuels visibles dans certaines conditions, c'est pourquoi j'ai dérangé en premier lieu. Je ne me souviens pas exactement, peut-être une quantité disproportionnée de flou dû à un seul changement de pixel? Peut-être seulement sur certains formats de vid / image? Tout ce que je peux dire, c'est que j'ai traité des milliers de vidéos avec ce correctif et que c'était la transformation favorable.
danneu
19

Cela est probablement dû au fait que la vidéo H264 est généralement convertie de l'espace RVB en espace YUV au format 4: 2: 0 avant d'appliquer la compression (bien que la conversion de format elle-même soit un algorithme de compression avec perte entraînant une économie d'espace de 50%).

Le YUV-420 commence par une image RVB (rouge vert bleu) et la convertit en YUV (essentiellement un canal d'intensité et deux canaux "teinte"). Les canaux Hue sont ensuite sous-échantillonnés en créant un échantillon de teinte pour chaque carré 2X2 de cette teinte.

Si vous avez un nombre impair de pixels RVB horizontalement ou verticalement, vous aurez des données incomplètes pour la dernière colonne ou ligne de pixels dans l'espace de teinte sous-échantillonné du cadre YUV.

Adisak
la source
2
Un autre fait intéressant ... lorsque vous décodez avec Microsoft Media Foundation, vous devez utiliser des multiples de 16 pour H264. Ainsi, la vidéo 1080P se décode en fait dans un tampon de 1088 haut (bien que vous ignoriez les 8 dernières lignes).
Adisak le
2

LordNeckbeard a la bonne réponse, très vite

-vf scale=1280:-2

Pour Android, n'oubliez pas d'ajouter

"-preset ultrafast" and|or "-threads n"
fallouter
la source
Vous n'avez pas besoin de déclarer les threads: cela est traité automatiquement. Je crois que la lenteur d'Andriod lors de l'encodage en H.264 est due au fait que les gens utilisent le populaire "WritingMinds / ffmpeg-android" qui l'utilise --disable-asmdans son script de construction x264 . Cela entraîne une lenteur inutile et significative (vous pouvez vérifier le journal ffmpeg et s'il apparaît, using cpu capabilties: none!c'est mauvais). Je ne sais pas pourquoi ils ont ajouté cela, mais je ne suis pas un développeur Android.
llogan
1

Vous pouvez également utiliser la bitandfonction au lieu de trunc:

bitand (x, 65534)

fera la même chose trunc(x/2)*2et il est plus transparent à mon avis.
(Considérez 65534 comme un nombre magique ici;))


Ma tâche consistait à mettre automatiquement à l'échelle un grand nombre de fichiers vidéo en demi-résolution .

scale=-2,ih/2conduisent à des images légèrement floues

raison:

  • les vidéos d'entrée avaient leur format d'affichage (DAR) défini
  • scale met à l'échelle les dimensions réelles du cadre
  • pendant l'aperçu, les tailles des nouvelles vidéos doivent être corrigées à l'aide du DAR, ce qui peut entraîner un flou dans le cas d'une vidéo à faible résolution (360x288, DAR 16: 9)

Solution:

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

explication:

  • output_height = hauteur_entrée / 2
  • largeur_sortie = hauteur_sortie * rapport_aspect_affichage_original
  • à la fois output_width et output_height sont maintenant arrondies à divisible plus petit nombre le plus proche de 2
  • setsar=1signifie que output_dimensions est désormais définitif, aucune correction de rapport hauteur / largeur ne doit être appliquée

Quelqu'un pourrait trouver cela utile.

endigo
la source