Conversion de VHS au format numérique

Récemment je me suis (re)lancé dans la numérisation de vieille VHS (certaines ont plus de 20 ans) pour les conserver dans un format numérique… et aussi pouvoir les regarder (nostalgie).

Voici ma méthode ainsi que quelques conseils

Prérequis

Matériel

D’abord il faut évidemment un lecteur de magnéto VHS, dans mon cas j’en avais déjà un à disposition. Ensuite un (vieux) PC avec une carte d’acquisition vidéo, dans mon cas j’utilise une (vieille) carte Phillips SAA7134 (c’est une carte en PCI).

Bref on reste dans le vieux, mais ce n’est pas bien grave.

Le magnéto VHS dispose d’une sortie SCART, la carte d’acquisition d’une entrée composite et S-Video. Si le signal composite est facile à récupérer, c’est aussi celui ayant la plus mauvaise qualité, on utilise donc un autre (ancien) appareil entre les deux qui est capable de convertir en S-Video (on peut trouver sinon des petits boîtiers à brancher directement pour convertir le SCART).

Donc pour résumer on a :

  • Un magnéto VHS qui sort en SCART.
  • Un appareil qui reçoit du SCART et sort du S-Video + audio sur une prise séparée.
  • Un PC avec une carte acquisition en S-Video, une carte son et un disque dur.
  • Une télé qui reçoit à la fois la sortie de l’appareil intermédiaire et du PC (c’est quand même plus pratique de voir ce que l’on fait).

Logiciels

Le PC est installé avec un Debian 8 (oui c’est vieux, le PC a été installé en 2015 uniquement pour cet usage et n’a jamais été mis à jour depuis).

Sur ce PC on a :

  • Une interface graphique (mate en l’occurrence)
  • Pulseaudio
  • FFmpeg
  • xawtv

Concernant ffmpeg, pour ne pas vous palucher la doc : les filtres vidéos (ou audio), sont séparés par des virgules et les options du filtre avec des deux points.

Étape 1 — la capture

Le PC est vieux, c’est un P4, et il n’est pas assez performant pour effectuer l’acquisition et la conversion en temps réel. De plus en raison d’un décalage entre les signaux de luminance et chrominance, je suis obligé d’effectuer une petite manipulation. De ce fait on effectue la capture en format rawvideo (zéro compression) et rawaudio.

On commence d’abord par initialiser la carte d’acquisition en mode PAL-BG et S-Video avec les deux commandes suivantes :

v4lctl setnorm PAL-BG
v4lctl setinput S-video

Une fois fait, on utilise FFmpeg pour effectuer la capture, j’utilise l’option filter_complex de FFmpeg pour extraire les signaux Y, U et V dans 3 fichiers distincts directement (l’utilisation des [y][u][v] permet de récupérer les flux avec l’option -map).

La variable "$duration" correspond à la durée de la capture, sous la forme "HH:MM:SS" ou "MM:SS".

Les variables $Y, $U, $V et $A correspondent respectivement aux fichiers vidéos Y, U et V et au fichier audio (stocké en .wav).

ffmpeg -y -thread_queue_size 16 -t "$duration" \
        -f alsa -ac 2 -i pulse -t "$duration" -thread_queue_size 16 \
        -f video4linux2  -i /dev/video0  \
        -c:a pcm_s16le -r 25 -filter_complex "[1:v]extractplanes=y+u+v[y][u][v]"  \
        -map "[y]" -f rawvideo $Y \
        -map "[u]" -f rawvideo $U \
        -map "[v]" -f rawvideo $V \
        -map 0 $A \
        -loglevel error -stats

Petite spécification ici, il n’y a pas de codec, container pour les flux vidéos donc l’extension n’a aucune importance.

Le signal capturé est un signal TV donc de 720 × 576 pixels, on est à environ 1Go / minute.

Une fois cette étape finie, la cassette peut être rangée, on a la vidéo dans 3 fichiers et l’audio séparé.

La machine que j’utilise pour faire la capture étant particulièrement… vieille, je copie donc mes fichiers bruts sur une autre machine pour passer à la post-conversion (le bonus c’est que je peux continuer à numériser pendant que je fais la post-conversion).

Étape 2 — pré-stabilisation

Cette étape dépend de votre source, dans mon cas il s’agit de vidéo filmé avec un caméscope, l’image bouge légèrement, on va donc en plus de la numériser la stabiliser.

Vous pouvez zapper cette partie si vous n’en avez pas besoin, il faudra aussi supprimer le filtre correspondant à l’étape suivante.

Comme je le disais dans l’étape 1, en raison d’un décalage entre la luminance et la chrominance je suis obligé d’effectuer une manipulation. Le détail complet est disponible en anglais ici : https://stackoverflow.com/questions/37368614/fixing-with-ffmpeg-the-chrominance-position-on-a-video-after-capturing.

Pour faire simple, le signal de chrominance est décalé de quelques lignes par rapport au signal de luminance ce qui donne une image baveuse. Les flux étant en rawvideo, on peut se permettre de « manger » quelques pixels en lisant ignorant quelques octets dans le fichier. C’est ce que fait la commande dd.

FFREPORT="file=ff.logs:level=32" ffmpeg \
        -f rawvideo -framerate 25 -pix_fmt gray -video_size 720x576 -i "$Y" \
        -f rawvideo -framerate 25 -pix_fmt gray -video_size 360x288 -i <(dd status=none if="$U" bs=360 skip=4 ; dd status=none if="$U" bs=360 count=4) \
        -f rawvideo -framerate 25 -pix_fmt gray -video_size 360x288 -i <(dd status=none if="$V" bs=360 skip=4 ; dd status=none if="$V" bs=360 count=4) \
        -aspect 4:3 \
        -filter_complex "[0][1][2]mergeplanes=0x001020:yuv420p,bwdif,vidstabdetect,crop=iw:320:0:0,blackdetect[v]" \
        -map "[v]" \
        -c:v rawvideo \
        -loglevel error -stats \
        -f null -

Les filtres utilisés dans ffmpeg sont donc :

  • mergeplanes, qui vient combiner les flux Y, U et V en un seul flux vidéo couleur
  • bwdif, qui permet de désentrelacer la vidéo (initialement j’utilisais yadif mais le résultat donne une image un peu moins nette – sauf a utilisé yadif avec mcdeint mais le temps CPU devient trop important).
  • vidstabdetect, qui concerne la pré-stabilisation (ce filtre génère un fichier qui sera utilisé à l’étape suivante)
  • crop est utilisé pour ne garder que le haut de l’image
  • blackdetect est utilisé pour détecter la fin du fichier.

Pour ces deux derniers filtres, le magnéto affiche un message avec sur un fond noir indiquant la fin de la VHS, j’utilise donc le filtre crop pour ne regarder que le haut de l’image et blackdetect pour trouver l’apparition du fond noir. Ils peuvent donc être retirés selon votre usage.
À noter qu’ici les logs n’apparaissent pas à l’écran mais sont écrit dans le fichier ff.logs, c’est dans ce fichier qu’on trouvera les informations du filtre blackdetect.

Ici le fichier audio reste inutilisé et aucun fichier n’est produit (à l’exception du fichier transform.trf du filtre vidstabdetect), on indique explicitement à ffmpeg de rien écrire et on utilise force l’utilisation du codec vidéo "rawvideo" (autant éviter des cycles CPU inutiles).

Étape 3 — conversion

C’est à cette étape qu’on obtient une vidéo regardable et que l’on peut réutiliser plus tard !

La commande utilisée est donc la suivante :

ffmpeg -y \
        -f rawvideo -framerate 25 -pix_fmt gray -video_size 720x576 -i "$Y" \
        -f rawvideo -framerate 25 -pix_fmt gray -video_size 360x288 -i <(dd status=none if="$U" bs=360 skip=4 ; dd status=none if="$U" bs=360 count=4) \
        -f rawvideo -framerate 25 -pix_fmt gray -video_size 360x288 -i <(dd status=none if="$V" bs=360 skip=4 ; dd status=none if="$V" bs=360 count=4) \
        -i "$A" \
        -filter_complex "[0][1][2]mergeplanes=0x001020:yuv420p,bwdif,vidstabtransform,fftfilt=dc_Y=0:weight_Y='1+squish(1-(Y+X)/100)',fftdnoiz,eq=brightness=-0.1:contrast=0.8[v]" \
        -map "[v]" \
        -map 3:a \
        -c:a aac -b:a 192k \
        -c:v libx264  -crf 25 -preset medium -aspect 4:3 \
        -metadata title="$title" \
        -stats \
        -loglevel error \
        "mavideo_stabilise.mkv"

Les filtres ici deviennent un peu plus… complexe !

  • mergeplanes, comme à l’étape 2 on refait un signal vidéo en YUV420p à partir des trois fichiers.
  • bwdif, comme à l’étape 2 on applique un filtre pour retirer l’entrelacement.
  • vidstabstransform applique la stabilisation de la vidéo ce qui améliore la qualité de visionnage (surtout pour une source filmée avec un caméscope à bout de bras).
  • fftfilt est utilisé pour augmenter la netteté de l’image, je n’ai pas inventé cette ligne elle se trouve dans la doc ffmpeg ! J’ai fini par utiliser ce filtre à la place d’un autre filtre qui fait la même chose (unsharp) car je trouve le résultat de meilleure qualité (même s’il est plus lent)
  • fftdnoiz est un filtre qui permet de retirer le bruit de la vidéo, ce n’est pas le meilleur (nlmeans à l’air encore plus performant) mais en termes de qualité / rapidité sur plusieurs tests, c’est celui que j’ai retenu.
  • eq permet de diminuer un peu la luminosité et le contraste, à adapter en fonction de la source.

Concernant les filtres antibruits, j’ai testé:

  • hqdn3d, il est très rapide mais le résultat était plutôt bof
  • nlmeans, le résultat est très propre… mais bien trop long sur mon i5 (la vitesse tombe à 1 image par seconde)
  • fftdnoiz, le résultat est propre et le temps d’encodage reste raisonnable.

Ensuite en termes de codec vidéo on utilise le classique H264 avec la libx264, le "Constant Rate Factor" à 25 au lieu 23 par défauts (0 signifie une compression sans perte et 51 une image très compressée). En termes de poids vidéo final / qualité c’est parfaitement raisonnable (on convertit des VHS il ne s’agit pas de film en 4k).

Pour le codec audio je suis parti sur AAC, pas parce que c’est le meilleur… simplement parce que ça facilite la compatibilité dans 99% des appareils.

À noter que cette vidéo n’est pas coupée avec les valeurs détectées par le filtre blackdetect de l’étape précédente, j’effectue cette découpe dans une dernière passe (j’ai toujours un doute sur la capacité du filtre à trouver la fin du fichier correctement).

Une fois toutes ces étapes passées on obtient donc un fichier d’environ 350Mo pour 30 minutes de film.

La machine utilisée pour faire la post-conversion est un NUC Intel j5005 et il faut environ 4h pour 30 minutes de film, ce n’est pas très rapide, mais on a le temps et comme la vidéo convertie est plutôt bien par rapport à la source, autant prendre un peu de temps !

Étape 4 — bonus – la coupure de fin

Cette étape est clairement facultative, comme je disais précédemment, le magnétoscope indique la fin de la VHS, a l’étape 2 on a cette information dans le fichier de log "ff.logs".

En utilisant cette expression régulière :

lastblack=$(tr -d '\r' < "ff.logs" | sed -rne 's/.*\[blackdetect.*\] black_start:([^ ]*) .*$/\1/p' | tail -n1)

On récupère le message du filtre qui correspond à la phrase du magnéto, précisément on récupère juste le début de l’image noire.

On repasse ça a ffmpeg pour lui dire de copier notre fichier "mavideo_stabilise.mkv" jusqu’à la durée trouvée.

ffmpeg -y \
                -loglevel error \
                -stats \
                -i "mavideo_stabilise.mkv" \
                -c copy \
                -to "$lastblack" \
                "mavideo_stab_coupe.mkv"

Conseils

Déjà vous vous doutez bien que je ne retape pas toutes les commandes à chaque fois, j’ai donc deux scripts pour faire tout ça plus ou moins automatiquement.

  1. pour effectuer la capture sur la vieille machine.
  2. pour effectuer la post-conversion sur une autre machine.

Ensuite, même si votre machine est performante et que vous n’avez pas besoin de bricoler comme moi les différends les signaux vidéos, je vous recommande de faire en 2 étapes :

  1. capturer en rawvideo
  2. post-conversion (filtre etc) dans un format numérique « pour visionage »

Pourquoi : Parce qu’a un moment on se rend compte qu’il faudrait changer le contraste, qu’il y’a trop de bruit, trop de ça, que finalement ça serait bien de stabiliser la vidéo, etc. Si on a les fichiers bruts on peut repartir de fichiers pour appliquer les filtres sans pertes de qualité par rapport à la capture.
Autrement, on part d’une image comprimée, qu’on bricole pour la re-comprimé, on perds a chaque étapes des informations et donc de qualité.

Et puis le stockage ne coute plus si cher que ça aujourd’hui, on trouve facilement des disques de 2To ou plus à des prix raisonnables, il n’y a pas besoin de SSD ! Et avec un disque de 2To on peut stocker environ 30 heures de film en rawvideo de qualité TV.

Enfin la capture en rawvideo minimise la charge CPU et assure une capture fluide de la vidéo, la plupart des filtres vidéos sont gourmands en ressource et dans le cas d’une capture ffmpeg ne peut pas dire au magnéto « ralenti, je n’arrive pas à suivre ».

Enfin faites vos tests !!

Les filtres que j’utilise sont bien pour mes vidéos et mon installation, ça ne sera pas forcément toujours le cas !

Capturez donc quelques minutes d’une VHS avec des pleins de couleurs et travailler sur ce petit extrait de vidéo pour avoir un rendu correct.

Vous pouvez utiliser les options -to et -ss de ffmpeg pour extraire un petit bout.

Aussi, pour le signal vidéo, si vous trouvez une carte d’acquisition qui supporte directement le signal RVB utilisez ça, ensuite S-Video mais évitez à tout prix le signal composite !

HeaPi ou piloter des convecteurs électrique avec un Raspberry Pi

HeaPi est un projet que j’ai réalisé (en me basant sur plein de trucs déjà existant) pour contrôler l'ensemble mes convecteurs avec un RaspberryPi et un peu de code pour remplacer un boîtier électronique de gestion des convecteurs. Mais avant de rentrer dans le sujet, un peu de théorie et sur le  […]

Lire la suite

Capteurs de température dans les salles de Tetaneutral.net

Depuis quelques jours, des courbes de température des 3 salles associatives de Tetaneutral (Myrys & TLS00) sont visibles sur un graphique ici : https://temper.aplu.fr/ et les données peuvent être téléchargées en CSV. Cela vient compléter le capteur de température et d'humidité déjà présent à  […]

Lire la suite

Quelle version de LineageOS pour le Wileyfox Swift ?

Ce billet sera mis à jour au fil des mises à jour de LineageOS pour vous indiquer quelle version de LineageOS j’utilise, vous pouvez bien sûr prendre les devants et passer sur une autre version, mais parfois certaines mises à jour sont problématiques…

Au 26 avril 2020 : lineage-16.0-20200423-microG-crackling.zip, pas de soucis rencontré.
Le patch de sécurité est celui du 5 avril 2020.

Lire la suite

Les télécommandes infrarouge sous Linux

Les télécommandes infrarouge sous Linux Article mis-à-jour en différenciant les informations spécifiques à Ubuntu 20.04 en vert, et celles spécifiques à Ubuntu 18.04 en orange; Historique Les télécommandes infrarouge étaient classiquement gérées par LIRC (Linux Infrared Remote Control) sous Linux  […]

Lire la suite

Haut de page