Extraction des sous-titres d'une vidéo

Scripts bash permettant de convertir plusieurs pistes de sous-titre vobsub d'une vidéo enregistrer sur la TNT par exemple et de les convertir en sous-titres texte .srt.

L'extraction de sous-titres d'une vidéo multicanal demande d'identifier les canaux sous-titres puis d'effectuer une reconnaissance de caractère pour les transformer en fichiers srt (texte) moins volumineux et plus faciles à inclure dans un fichier vidéo mkv Matroska.

Inspiré de l'algorithme : Conversion de sous-titres VobSub en SRT

Les 3 dernières étapes peuvent être réalisées avec vobsub2srt
  • Repérer les canaux de sous-titres d'un fichier vidéo avec ffmpeg
  • extraire sous-titres vobsub dans une vidéos mastroika
  • extraire les sous-titres des vidéos mastroïka avec mkvextract
  • convertir fichier .sub et .idx en tiff;
  • reconnaissance de caractère de chaque tiff avec cunéiform
  • création d'un fichier srt

Script bash correspondant à l'algorithme

dvsub2srt.bash

# !/bin/bash
# OUTPUT-COLORING
red=$( tput setaf 1 )
green=$( tput setaf 2 )
NC=$( tput sgr0 )      # or perhaps: tput sgr0
#NC=$( tput setaf 0 )      # or perhaps: tput sgr0
 
# Dépendances : ffmpeg,  mkvtoolnix, vobsub2srt
# Signale quel programme l'on exécute
# puis la composition du répertoire où le script s'exécute
echo -e "programme pour extraire des canaux de sous-titres d'une vidéo\n
Composition du répertoire courant :\n
$(ls)"
 
# Invite de commande pour entrer le fichier vidéo à traiter
echo -n "Entrer le fichier vidéo choisi :";
read film_a_traiter;
 
# Message pour informer l'utilisateur de son choix
echo -e "Le fichier vidéo choisi est : \n $film_a_traiter"
 
# Exemple film_a_traiter="RetourVersLeFutur2.mp4"
# film_a_traiter="RetourVersLeFutur2.mp4"
 
# soustitres_array= ("4|fra" "5|fra")
soustitres_array=($(ffprobe $film_a_traiter -v quiet -show_entries stream=index:stream_tags=language -select_streams s -of compact=p=0:nk=1))
 
# metadata_sub="-map 0:4 -metadata:s:s:1 language=fra -map 0:5 -metadata:s:s:2 language=fra"
metadata_sub=$(for (( c=0; c<${#soustitres_array[@]}; c++)); do  echo -map 0:$(echo ${soustitres_array[$c]} | cut -d "|" -f1) -metadata:s:s:$(($c + 1)) language=$(echo ${soustitres_array[$c]} | cut -d "|" -f2) ; done)
 
# command1="ffmpeg -i RetourVersLeFutur2.mp4 -map 0:4 -metadata:s:s:1 language=fra -map 0:5 -metadata:s:s:2 language=fra -c:s dvdsub sous_titres_RetourVersLeFutur2.mp4.mk"
command1=$(echo "ffmpeg -i $film_a_traiter $metadata_sub -c:s dvdsub sous_titres_$film_a_traiter.mkv")
 
# Execution commande n°1 $command1
$command1
 
# vobsub_piste="0:0_ 1:1_"
vobsub_piste=$(for (( c=0; c<${#soustitres_array[@]}; c++));do echo $c:$c"_"; done)
 
#command2="mkvextract tracks sous_titres_RetourVersLeFutur2.mp4.mkv -c ISO8859-1 0:0_ 1:1_"
command2=$(echo "mkvextract tracks sous_titres_$film_a_traiter.mkv -c ISO8859-1 $vobsub_piste")
 
# Execution commande n°2 $command2
$command2
 
# Exécution Roc des fichiers de sous-titres
#vobsub2srt 0_; vobsub2srt 1_;"
for (( c=0; c<${#soustitres_array[@]}; c++));
do
 vobsub2srt $c"_";
done
exit 0;

Ancien Exemple de séquence bash correspondant à l'algorithme

ffmpeg -i data0003.ts 2>&1 | grep subtitle
mkdir data0003
ffmpeg -i data0003.ts -map 0:4 -map 0:5 -vn -an -scodec dvdsub data0003.mkv
mkvextract tracks "data0003.mkv" -c ISO8859-1 0:data0003/0_
mkvextract tracks "data0003.mkv" -c ISO8859-1 1:data0003/1_
subp2tiff --sid=0 -n data0003/0_
subp2tiff --sid=1 -n data0003/1_
for eachTiff in data0003/*.tif; do cuneiform -l fra -f text -o $eachTiff.txt $eachTiff; done
subptools -s -w -t srt -i data0003/0_.xml -o 0_.srt
subptools -s -w -t srt -i data0003/1_.xml -o 1_.srt

Exemple de conversion de fichier TS en MKV avec FFMPEG

La commande suivante permet d'obtenir l'info :

ffmpeg -i <vidéos étudiée> 

9 pistes :

0:0 vidéo

0:1 ne contenant pas de données

0:2 Audio

0:3 ne contenant pas de données

0:5 Subtite

0:6 subtitle

0:7 piste non reconnue par ffmpeg

0:8 piste non reconnue par ffmpeg

ffmpeg -threads 4 -i data0003.ts -map 0:0 -map 0:2 -map 0:5 -map 0:6 -acodec copy -vcodec copy -scodec dvdsub output.mkv
-threads pour pouvoir utiliser un traitement multiprocesseurs

- map pour spécifier toutes les pistes à utilisables

-acodec suivi de copy pour le traitements des pistes audio

-vcodec suivi de copy pour le traitement de la piste vidéo -scodec suivi de dvdsub pour le traitement des sous-titres

ffmpeg - une collection de logiciels libres destinés au traitement de flux audio ou vidéo

mkvtoolnix éventuellement aussi mkvtoolnix-gui - un ensemble d'outils permettant de créer, de modifier et d'inspecter des fichiers Matroska

vobsub2srt - Commande de reconnaissance optique de caractères multi-langue pour extraire des sous-titres

cuneiform - Système de reconnaissance optique de caractères multi-langue

ogmrip - Application pour extraire et encoder des DVDs

Script ancien : pas de gestion des erreurs de reconnaissances optiques

ts2srt.bash

# ----------------------------------------------------
# Script''ts2srt''
# ----------------------------------------------------
 
# Par ''Alban MARTEL''
# Courriel : albanmartel(POINT)developpeur(AT)gmail(POINT)com
# Utilisant comme base de travail le script de beguam
# https://doc.ubuntu-fr.org/tutoriel/vobsub_srt
# License : GNU GPL
# Ce script permet d'extraire les sous-titres d'une video TS et de les transformer en SRT éditable.
#
# Depends :
# ffmpeg est une collection de logiciels libres destinés au traitement de flux audio ou vidéo
# mkvToolnix (interface graphique pour mkvmerge) est un ensemble d'outils permettant de créer, de modifier et d'inspecter des fichiers Matroska
# cuneiform - Système de reconnaissance optique de caractères multi-langue
# ogmrip - Application pour extraire et encoder des DVDs
#
# Date : 26/08/2015
# version : 0.1
# Mise-à-jour :
# ----------------------------------------------------
 
# !/bin/bash
# OUTPUT-COLORING
red=$( tput setaf 1 )
green=$( tput setaf 2 )
NC=$( tput sgr0 )      # or perhaps: tput sgr0
#NC=$( tput setaf 0 )      # or perhaps: tput sgr0
 
 
function readDirectoryPath(){
  echo -n "chemin absolu du répertoire des vidéos où extraire les sous-titres : "
  read directory;
  courant_directory=$(pwd);
  if [[ ! -e "$directory" ]]; then
    echo "incorrect";
    readDirectoryPath;    
  fi
  cd $directory;
  directory=$(pwd);
  cd $courant_directory;
  echo $directory;
  #testIfAnyFileIsPresent=$(find . -maxdepth 1 -iname "*.$extension" | wc -l);
}
 
 
function presentationOfFileDirectory(){
  message=$("<<"$directory">> contient les fichiers suivants :  ");
  files=$(ls $directory/*.*);
  print "%$\n" "${green}$message${NC}";
  print "%$\n" "${green}$files${NC}";
}
 
 
function readVideoExtension(){
  echo -n "Extension des fichiers vidéos à traiter ("$((4-$count))" tentatives restantes) : "
  read extension;
  testIfAnyFileIsPresent=$(find $directory -maxdepth 1 -iname "*.$extension" | wc -l);
}
 
 
function choiseTypeOfVideo(){
  local tmp_videos=""
  count=0;
  readDirectoryPath;
  presentationOfFileDirectory;
  testIfAnyFileIsPresent=0;
  while [ $testIfAnyFileIsPresent = 0 ] && [ $count != 3 ] ; do
    count=$(($count+1));
    readVideoExtension;
  done
  if [ $count = 3 ] ; then
    print "%$\n" "${red}""3 mauvaises tentatives entrainent l\'arrêt du programme""${NC}";
    print "%$\n" "${red}""Abandon""${NC}";
    exit 100;
  fi
  #/home/alban/Vidéos/fr3/annez.ts" | sed "s/\(.*\)\/\([Aa-Zz]*.$extension\)/\2/g"
  #Example : data0001.ts data0002.ts data0003.ts
  cd $directory;
  videoFiles=($( ls *.$extension ));
  cd $courant_directory;
}
 
 
function cleanVideoInformations() {
    cat $1 | grep Imput > $2;
    cat $1 | grep Duration >> $2;
    cat $1 | grep Stream >> $2;
    rm $1;  
}
 
 
function prepareCommandToObtainSubtitlesTrackNumer(){
  local a;
  #local j=0;
  for (( i=0 ; i < ${#videoFiles[@]} ; i++ )) ; do
    #/home/alban/Vidéos/fr3/ENEMY/data0003.ts
    data_videos_files[i]=$(echo $directory"/"${videoFiles[i]});
    #/tmp/data0001_subtitle_infos.txt
    tmp_video_info[i]=$(echo "/tmp/"${videoFiles[i]} | sed "s/.$extension/_subtitle_infos.txt/g");
    #/tmp/data0003.ts.info
    video_info_file[i]=$(echo /tmp/${videoFiles[i]}.info);
    #ffprobe /home/alban/Vidéos/fr3/ENEMY/data0003.ts 2>&1 | ffmpeg -i EnnemyMine.ts -vn -an 2>&1 | grep '[A-Z][a-z]\{4\}' | sed "s/\(^[ ]*\)\([[:alnum:]]\)/\2/g" >/tmp/data0001_subtitle_infos.txt 
    ffprobe ${data_videos_files[i]} 2>&1 | grep '[A-Z][a-z]\{4\}' | sed "s/\(^[ ]*\)\([[:alnum:]]\)/\2/g" >${tmp_video_info[i]};
    #create a cleanning file  of video information
    cleanVideoInformations "${tmp_video_info[i]}" "${video_info_file[i]}";
    #cat /tmp/video-info.txt | grep Subtitle | sed "s/\(^.* \#\)\([0-9]:[0-9]\)(\([[:alnum:]]\{3\}\))\(.*\)/\2#\3/g"
    # tracks_Info[1] =Stream #0:5(fra): Subtitle: dvd_subtitle (default) Stream #0:6(ger): Subtitle: dvd_subtitle Stream #0:7(fra): Subtitle: dvd_subtitle
    tracks_Info[i]=$(cat ${video_info_file[i]} | grep Subtitle | sed "s/\(^.* \#\)\([0-9]:[0-9]\)(\([[:alnum:]]\{3\}\))\(.*\)/\2#\3/g" );
    rm ${video_info_file[i]};
  done
}
 
 
function createDirectoryIfNotExist(){
  if [[ ! -e $1 ]] ; then
    mkdir $1;
  fi
 
}
 
function ExtractSubtitleFromVideoInMKV(){
  local j=0;
  local k=0;
  $1
  echo  'tracks_Info: '${tracks_Info[@]};
  for ((i=0 ; i< ${#tracks_Info[@]}; i++)); do
      #From : data0001.ts to: data0001
      extract_work_files[i]=$(echo ${videoFiles[i]} | sed "s/.$extension//g");
      #echo ${directory}/${extract_work_files[i]};
      createDirectoryIfNotExist "${directory}/${extract_work_files[i]}";
      tmp=($(echo ${tracks_Info[i]}));
      for each in ${tmp[@]};do
	# De : 0:4#fra à : 0.4 fra
	#0:4
	track_number=$(echo $each | cut -d'#' -f1);
	#fra
	track_lang=$(echo $each | cut -d'#' -f2);
	#ffmpeg -threads 4 -i /home/alban/Vidéos/fr3/ENEMY/data0001.ts -map 0:5 -vn -an -scodec dvdsub /home/alban/Vidéos/fr3/ENEMY/data0001/data0001_0_fra.mkv
	ffmpeg -threads 4 -i ${directory}/${videoFiles[i]} -map $track_number -vn -an -scodec dvdsub ${directory}/${extract_work_files[i]}/${extract_work_files[i]}\_$j\_$track_lang.mkv;	
	mkv_files[k]=$(echo ${directory}/${extract_work_files[i]}/${extract_work_files[i]}\_$j\_$track_lang.mkv);
	mkv_directories[k]=$(echo ${directory}/${extract_work_files[i]});
	subtitle_sub_id[k]=$(echo ${extract_work_files[i]}\_$j\_$track_lang);
	subtitle_lang[k]=$(echo $track_lang);
	j=$(($j + 1));
	k=$(($k + 1))
      done
  done
}
 
 
 
function OpticalRecognitionCharacterOfTiff(){
  for eachTiff in $1*.tif; do 
    cuneiform -l $2 -f text -o $eachTiff.txt $eachTiff; 
  done 
 
}
 
 
function convertMKVSubtitleInSRT(){
  local j=0
  for (( i=0 ; i < ${#mkv_files[@]}; i++ )); do 
    #mkvextract tracks /home/alban/Vidéos/fr3/EnnemyMine/EnnemyMine_0_fra.mkv -c ISO8859-1 0:/home/alban/Vidéos/fr3/EnnemyMine/EnnemyMine_0_fra
    mkvextract tracks ${mkv_files[i]} -c ISO8859-1 0:${mkv_directories[i]}/${subtitle_sub_id[i]};
    # if sub file existe and has a size equal to 0 than erase sub and idx files
    if [ ! -s ${mkv_directories[i]}/${subtitle_sub_id[i]}.sub ]; then 
      rm ${mkv_directories[i]}/${subtitle_sub_id[i]}.sub;
      rm ${mkv_directories[i]}/${subtitle_sub_id[i]}.idx;
    fi
  done
 
  for (( i=0 ; i < ${#videoFiles[@]} ; i++ )) ; do
    work_directories=$(echo ${videoFiles[i]} | sed "s/.$extension//g") ;
    #mkv_directories=$(echo $directory/work_directories  kv_directories${videoFiles[i]} | sed "s/.$extension//g");
    for each in $directory/$work_directories"/*.idx"; do
      subtitle_sub=($(echo $each));
      for filesub in ${subtitle_sub[@]}; do
	#id_file=/home/alban/Vidéos/clanDesSabresVollants/de/Le_Secret_des_poignards_Volants_base/Le_Secret_des_poignards_Volants_base__fra
	id_file=$(echo $filesub | sed "s/.idx//g");
	subp2tiff --sid=0 -n $id_file;
	#fra
	sub_lang=$(echo $filesub | sed "s/\(.*\)\([a-z]\{3\}\)\(.idx\)/\2/g");
	#subtitle_file_name : /home/alban/Vidéos/clanDesSabresVollants/de/Le_Secret_des_poignards_Volants_base/Le_Secret_des_poignards_Volants_base__fra.srt
	subtitle_file_name=$(echo $filesub |  sed "s/.idx//g");
	echo "subtitle_file_name : "$subtitle_file_name;
	#sub_id=$directory/$work_directories$(echo $filesub | sed "s/\(.*\)\(\_[0-9]\_\)\([a-z]\{3\}\)\(.idx\)/\2\3/g");
	#OpticalRecognitionCharacterOfTiff "$sub_id" "$sub_lang";
	OpticalRecognitionCharacterOfTiff "$subtitle_file_name" "$sub_lang";
	#echo $directory/$(echo ${videoFiles[i]} | sed "s/.$extension//g")/;
	# tmp= Le_Secret_des_poignards_Volants_base__fra
	tmp=$(echo $filesub | sed "s/\(.*\)\/\(.*\)\(.idx\)/\2/g" );
	echo "commande subptools : subptools -s -w -t srt -i $id_file.xml -o $directory/$tmp.srt";
	subptools -s -w -t srt -i $id_file.xml -o $directory/$tmp.srt
      done
      rm -rf $directory/$work_directories;
    done
  done
}
 
choiseTypeOfVideo;
prepareCommandToObtainSubtitlesTrackNumer;
ExtractSubtitleFromVideoInMKV
convertMKVSubtitleInSRT;
exit 0;
  • extraire_sous-titres_video.txt
  • Dernière modification: Le 11/09/2022, 12:17
  • par moths-art