HectorDuino

Placez ici vos trucs et astuces, étalez sans retenue votre savoir-faire et votre science qui va nous permettre de redonner une apparence neuve et fonctionnelle à nos bouzes.

Modérateurs : Papy.G, fneck, Carl

__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: HectorDuino

Message par __sam__ »

$read doit finir par 0 quand on atteint la fin de fichier. C'est pas normal s'il est toujours à 256 car ca signifie que le fichier source n'est pas terminé. Y aurait-il du bruit en fin de fichier source ne correspondant à aucune image ? Tu pourrais m'indiquer où trouver le fichier source que j'y jette un oeil?

(c'est vrai que c'est pas facile à maitriser ces histoires de buffer pour les I/O de process parallèles.)
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Avatar de l’utilisateur
yo_fr
Messages : 1336
Inscription : 13 août 2009 18:24
Localisation : 78...
Contact :

Re: HectorDuino

Message par yo_fr »

pour éviter tous soucis (de ce genre), je travaille avec 2 mp4 :
* Un petit film fait au portable de 6 secondes (c'est pas trop long !)
* un mp4 de Kylie Minogue (can't get out of my head) transférer depuis youtube avec un convertisseur on line.
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: HectorDuino

Message par __sam__ »

Ok, je vois ce qu'il se passe. MMPEG n'aime pas le passage par "stdin" pour cette vidéo là. Exemple, par STDIN (-):

Code : Tout sélectionner

$ ./ffmpeg.exe -r 10 -s 204x127 -an tmp/img%05d.bmp -i - < 'c:/Users/Samuel/Downloads/md.mp4'
ffmpeg version N-71042-g83020f8 Copyright (c) 2000-2015 the FFmpeg developers
  built with gcc 4.9.2 (GCC)
  configuration: --enable-gpl --enable-version3 --disable-w32threads --enable-avisynth --enable-bzlib --enable-fontconfig --enable-frei0r --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libfreetype --enable-libgme --enable-libgsm --enable-libilbc --enable-libmodplug --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-libschroedinger --enable-libsoxr --enable-libspeex --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs --enable-libxvid --enable-lzma --enable-decklink --enable-zlib
  libavutil      54. 20.100 / 54. 20.100
  libavcodec     56. 29.100 / 56. 29.100
  libavformat    56. 26.101 / 56. 26.101
  libavdevice    56.  4.100 / 56.  4.100
  libavfilter     5. 13.101 /  5. 13.101
  libswscale      3.  1.101 /  3.  1.101
  libswresample   1.  1.100 /  1.  1.100
  libpostproc    53.  3.100 / 53.  3.100
[mov,mp4,m4a,3gp,3g2,mj2 @ 03038460] stream 1, offset 0x20: partial file
[mov,mp4,m4a,3gp,3g2,mj2 @ 03038460] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none(tv, bt709), 1920x1080, 17112 kb/s): unspecified pixel format
Consider increasing the value for the 'analyzeduration' and 'probesize' options
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'pipe:':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isommp42
    creation_time   : 2017-09-22 10:35:53
  Duration: 00:00:05.23, bitrate: N/A
    Stream #0:0(eng): Video: h264 (avc1 / 0x31637661), none(tv, bt709), 1920x1080, 17112 kb/s, SAR 1:1 DAR 16:9, 30.01 fps, 30 tbr, 90k tbn, 180k tbc (default)
    Metadata:
      creation_time   : 2017-09-22 10:35:53
      handler_name    : VideoHandle
    Stream #0:1(eng): Audio: aac (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 255 kb/s (default)
    Metadata:
      creation_time   : 2017-09-22 10:35:53
      handler_name    : SoundHandle
[buffer @ 02eba500] Unable to parse option value "-1" as pixel format
    Last message repeated 1 times
[buffer @ 02eba500] Error setting option pix_fmt to value -1.
[graph 0 input from stream 0:0 @ 0303ef20] Error applying options to the filter.
Error opening filters!
En lecture directe du fichier:

Code : Tout sélectionner

$ ./ffmpeg.exe -r 10 -s 204x127 -an tmp/img%05d.bmp -i 'c:/Users/Samuel/Downloads/md.mp4'
ffmpeg version N-71042-g83020f8 Copyright (c) 2000-2015 the FFmpeg developers
  built with gcc 4.9.2 (GCC)
  configuration: --enable-gpl --enable-version3 --disable-w32threads --enable-avisynth --enable-bzlib --enable-fontconfig --enable-frei0r --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libfreetype --enable-libgme --enable-libgsm --enable-libilbc --enable-libmodplug --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-libschroedinger --enable-libsoxr --enable-libspeex --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs --enable-libxvid --enable-lzma --enable-decklink --enable-zlib
  libavutil      54. 20.100 / 54. 20.100
  libavcodec     56. 29.100 / 56. 29.100
  libavformat    56. 26.101 / 56. 26.101
  libavdevice    56.  4.100 / 56.  4.100
  libavfilter     5. 13.101 /  5. 13.101
  libswscale      3.  1.101 /  3.  1.101
  libswresample   1.  1.100 /  1.  1.100
  libpostproc    53.  3.100 / 53.  3.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'c:/Users/Samuel/Downloads/md.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isommp42
    creation_time   : 2017-09-22 10:35:53
  Duration: 00:00:05.23, start: 0.000000, bitrate: 17375 kb/s
    Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 1920x1080, 17112 kb/s, SAR 1:1 DAR 16:9, 30.01 fps, 30 tbr, 90k tbn, 180k tbc (default)
    Metadata:
      creation_time   : 2017-09-22 10:35:53
      handler_name    : VideoHandle
    Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 255 kb/s (default)
    Metadata:
      creation_time   : 2017-09-22 10:35:53
      handler_name    : SoundHandle
Output #0, image2, to 'tmp/img%05d.bmp':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isommp42
    encoder         : Lavf56.26.101
    Stream #0:0(eng): Video: bmp, bgr24, 204x127 [SAR 508:459 DAR 16:9], q=2-31, 200 kb/s, 10 fps, 10 tbn, 10 tbc (default)
    Metadata:
      creation_time   : 2017-09-22 10:35:53
      handler_name    : VideoHandle
      encoder         : Lavc56.29.100 bmp
Stream mapping:
  Stream #0:0 -> #0:0 (h264 (native) -> bmp (native))
Press [q] to stop, [?] for help
frame=   26 fps=0.0 q=0.0 size=N/A time=00:00:02.60 bitrate=N/A dup=0 drop=46   frame=   51 fps= 50 q=0.0 size=N/A time=00:00:05.10 bitrate=N/A dup=0 drop=97   frame=   54 fps= 50 q=0.0 Lsize=N/A time=00:00:05.40 bitrate=N/A dup=0 drop=103 
video:4102kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
l'erreur est semble-t-il lié à ce message
[mov,mp4,m4a,3gp,3g2,mj2 @ 03038460] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none(tv, bt709), 1920x1080, 17112 kb/s): unspecified pixel format
Consider increasing the value for the 'analyzeduration' and 'probesize' options
C'est un problème connu de FFMPEG: https://trac.ffmpeg.org/ticket/6056

J'ai essayé avec une version plus récente de ffmpeg.exe (https://www.ffmpeg.org/download.html, clicker sur l'icone windows), ca marché une fois, mais après plus du tout. :twisted:

:idea: Mais j'ai trouvé la solution :!:

Il faut d'abord convertir le mp4 en avi

Code : Tout sélectionner

ffmpeg md.mp4 md.avi
et lancer le script "hector" sur le fichier avi généré

Code : Tout sélectionner

perl hector.pl md.avi
et là ca marche sans rien changer au script ni à l'executable ffmpeg.
Pièces jointes
md.gif
md.gif (169.93 Kio) Consulté 5101 fois
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Avatar de l’utilisateur
yo_fr
Messages : 1336
Inscription : 13 août 2009 18:24
Localisation : 78...
Contact :

Re: HectorDuino

Message par yo_fr »

Bien joué !
Je n'ai pas pensé lancer ffmpeg pour voir si le pb venait de là, d'autant plus qu toutes les images étaient présente !
Merci et ouf !

EDIT : J'ai parlé trop vite : chez moi ça marche pas ... :x
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: HectorDuino

Message par __sam__ »

Ca marche pas ? Regarde la correction de typo plus bas, ton dernier soucis peut venir de là. Peut-être aussi que ca vaudrait le coup de reprendre le script perl intégral non modifié plus haut dans ce fil de discussion car il marche bien (c'est lui que j'ai utilisé pour créer le gif ci-dessus.)
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: HectorDuino

Message par __sam__ »

Je reprends ici avec son accord un MP que yo_fr m'a adressé car finalement ses questions sont légitimes et peuvent intéresser les autres lecteurs.
yo_fr a écrit :J'essaye de comprendre ton script et j'ai toujours des petits soucis. Si tu as 2 minutes pour m'expliquer le fonctionnement de ce bout de script car ça fait pas mal de temps que je cherche et j'ai un peu l'impression d'être un débile...
D'abord je ne suis pas familier avec les notions de flux et de pipe, excuse moi d'avance de cela...et cela expliquera surement mes questions (idiotes).

Si tu peux me confirmer (où contredire!) la compréhension que j'en ai, se serais donc sympa.

J'ai mis en commentaire ce que je pense être (j'ai mis des N° pour pouvoir parler des lignes) :
C'est vrai que la syntaxe du perl est absconse, et si la dessus on greffe des concepts unix tels que les pipes, il y a de quoi se faire des noeuds au cerveau. Je vais donc essayer d'expliquer
yo_fr a écrit :

Code : Tout sélectionner

open(OUT,"| ./ffmpeg -i $file -v 0 -r $fps -s ${w}x${h} -an tmp/img%05d.bmp");
# 1-pipe de commande, qui va lancer dans un thread différent ffmpeg qui génére les images.
# cette génération est continue, et peut durer jusqu'à la fin d'exécution de la boucle while.
En fait c'est la partie la plus importante du script, et oui :)

Tout d'abord je corrige une typo tout aussi importante: après l'argument "-i", on ne trouve pas "$file" mais "-" (un simple tiret). Cela indique à ffmpeg.exe non pas de lire un fichier sur disk, mais de lire le fichier depuis son entrée standard (stdin en C, souvent symbolisé dans les commandes unix par le pseudofichier "-").

Ce qu'il se passe avec le pipe (tuyau) ici, c'est que le fichier de sortie OUT de perl est relié à l'entrée standard de ffmpeg. Tout ce qu'on envoie sur OUT se retrouve à l'autre bout du tuyau dans l'entrée standard de ffmpeg. Or avec le "-i -", on instruit ffmpeg de lire la video non pas depuis un fichier disk, mais depuis son entrée standard. C'est à dire que ffmpeg va lire ce qu'on lui aura envoyé de la video via le fichier OUT.

Toute l'astuce réside là pour controller quand on aura atteint la fin du décodage. On va lire le fichier disk bout par bout dans le script perl, récupérer au fur et à mesure les images générées, et quand on aura lu tout le fichier, on sait qu'on aura récupéré l'intégralité des images disponibles.
yo_fr a écrit :

Code : Tout sélectionner

open(IN, "<$file");
# 2-là ça commence mal : je ne vois pas du tout ce que "<" va faire avec le fichier film source....
Ce "<" en début de nom de fichier, c'est pour indiquer à perl d'ouvrir le fichier en lecture (on aurait utilisé ">" ou rien du tout pour specifier ouvrir en sortie).
yo_fr a écrit :

Code : Tout sélectionner

&init_magick;
# 3- lancement de la procédure d'init plus bas (création du fichier de seuils et init de MAGIC)

$gif = Image::Magick->new(size=>"${w}x${h}");
# 4-Creation du conteneur de l'image GIF
oui et oui
yo_fr a écrit :

Code : Tout sélectionner

$cpt = 1; my @c = (0)x8;
binmode(IN);
# 5- bon, ça rejoint le 2 : aucune idée...
Déclaration que le fichier source est à utiliser en mode binaire et non texte,
Oui c'est exactement ca, on veut lire le fichier disk en binaire pour éviter que les "\n" (chr$(10)) soient transformés en cr+lf (chr$(13)+chr$(10)).
yo_fr a écrit :

Code : Tout sélectionner

binmode(OUT);
# 6- pas mieux, voir même pire puisse qu'il s'agit, pour moi, de l'exécution de ffmpeg... !
Oui voilà, on veut écrire dans OUT en considérant que c'est du binaire pur pour ne pas avoir de conversion de fin de ligne. A noter OUT n'est pas l'execution de ffmpeg, mais le bout du tuyau auquel ffmpeg est relié et à partir duquel il va lire les données vidéo.
yo_fr a écrit :

Code : Tout sélectionner

$expected_size = $h*(($w*3 + 3)&~3) + 54 ;
# 7-Calcul de la taille théorique du BMP


while($ok) {
$name = sprintf("tmp/img%05d.bmp", $cpt);
#8-Nom du BMP à traiter dans cette itération de la boucle

# taille théorique d'une image, taille du fichier pointé
if($expected_size != -s $name) {
# image pas complete: on continue de nourrir ffmpeg
oui à tout :)
yo_fr a écrit :

Code : Tout sélectionner

my $buf;
$read = read(IN,$buf,256);
# 9-Lecture du flux IN ??? Nb octets lus dans read
Oui on lit jusqu'à 256 octets d'un coup (c'était 4096 à l'origine) du fichier vidéo sur disk. $read vaut 0 en fin de fichier, sinon il nous dit combien sur les 256 (ou les 4096 dans le script d'origine) on a effectivement lu.
yo_fr a écrit :

Code : Tout sélectionner

$ok = 0 unless $read; # était: last unless $read;
# 10 - ok = 0 si nb octet lu = 0 -> fini...
# mais on a lu le flux IN c'est à dire le film source...
Oui voilà: si on a lu 0, on est en fin de fichier IN (fichier source video, sur disk).
yo_fr a écrit :

Code : Tout sélectionner

syswrite OUT, $buf, $read ;
# 11-Sortie dans OUT (le flux ffmpeg ???) du fichier lu (<$flie, soit le nom du film source) du buffer lu du flux IN.
# C'est ce que je lis mais ne comprend pas !!
On vient de lire dans $buf une certaine portion du fichier disk ($read octets) . On envoit alors cette partie lue (dans $buf) dans le bout du tuyau OUT, pour que ffmpeg le récupère de l'autre coté. Ainsi en parallèle, ffmpeg pourra manger à son tour ces $read octets supplementaires, décoder une nouvelle image et générer un nouveau fichier BMP dans le dossier tmp/.
yo_fr a écrit :

Code : Tout sélectionner

} else
{
# image complete!
# 12-le reste est ok ...
Pour moi ce que je ne comprend pas c'est que ffmpeg lui travaille tout seul dans son thread et nourri le répertoire d'images BMP.
Le fait de lire IN (9) et d'écrire OUT (10) n'as pas de sens... seul le fait de savoir que l'on est sur la dernière image devrait suffire :
Ne rien faire si l'image n'est pas entière et si on est pas à la fin de la génération de ffmpeg serait le but,
Maintenant c'est peut être la solution que tu utilises, mais je ne vois pas le mécanisme...
J'espère que mon explication ci-dessus a pu t'éclairer.

Une autre façon de faire aurait été de travailler sans ce fameux pipe. On aurait initialement lancé ffmpeg sur le fichier disk, et lui indiquer de générer TOUTES les images dans le dossier TMP. Ensuite on aurait lu une à une les images jusqu'à la dernière et on les aurait traités pour générer le gif sans avoir à nourir FFMPEG en cours de route via le PIPE car il aurait fait tout le boulot en amont.

Je n'ai pas choisi cette approche pour deux raisons
  • Elle est beaucoup plus lente: il faut attendre que ffmpeg ait traité tout le fichier (1h ou 2h de video parfois) avant de faire la conversion des BMP.
  • Elle occupe une place disk phénoménale. 1h de film à 10fps, ca fait 36000 images dans un seul dossier, et windows n'aime pas ca du tout. D'une part c'est très gros en volume (200Mo/heure environ), et surtout il met 3 plombes à effacer l'ensemble de ces fichiers.
  • En plus j'ai jugé pas très malin de générer 36000 images pour n'en retenir que les 3600 premières correspondantes aux 5 minutes maxi du GIF (un GIF plus long mets 3 plombes aussi à être créé/optimisé).

    (Oui ca fait 3 raisons. Je sais pas compter :P )
La solution du pipe permet au script perl et à ffmpeg de marcher de concert. Le fichier disk est lu petit bout par petit bout avec perl, puis chaque petit bout est envoyés à ffmpeg chacun à son tour. De temps en temps ffmpeg nous produit une nouvelle image que le script traite et efface avant de relire un nouveau petit bout du fichier video pour l'envoyer à ffmpeg, etc. En gros on a sur disk qu'une seule image à la fois dans le dossier TMP. Comme je travaille sur une machine pas puissante avec un disk presque plein, cette solution me convienait mieux. Mais c'est sur que si j'avais eu 1To de disk libre, j'aurais pu faire autrement (encore que windows n'aime pas trop non plus les repertoires trop pleins de fichiers.)
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Avatar de l’utilisateur
yo_fr
Messages : 1336
Inscription : 13 août 2009 18:24
Localisation : 78...
Contact :

Re: HectorDuino

Message par yo_fr »

Salut,

Effectivement cette façon de voir le pipe, il faut encore que je relise correctement ça, je commence à le comprendre : on nourri ffmpeg par le flux qui "passe travers" ffmpeg pour qu'il fasse son job (des images à générer) et lorsqu'il y a terminé le read donnera forcément 0.

Néanmoins, en parallèle, j'ai écrit ça (en gardant le $file dans la ligne de cde de ffmpeg) :

Code : Tout sélectionner

while($ok) {
	$name = sprintf("tmp/img%05d.bmp", $cpt);
	# taille théorique d'une image, taille du fichier pointé
	if($expected_size != -s $name) {
		# image pas complete: on continue de nourrir ffmpeg
	print "go to sleep\n";
	sleep(2);
	print " walk up !`\n";
	my $read = read($name,$buf,256);
	$ok = 0 unless $read; # était: last unless $read;
	#		last unless $read;
	#	syswrite OUT, $buf, $read ;
	} else  
	{...
et ça fonctionne. Je pense que le $file fait que la conversion par ffmpeg est totale (ce que tu ne voulais pas), donc le pipe n'as pas besoin d'être alimenté et le sleep() permet de calmer les thread. En effet je suis sur une bête de course (i7 et cygwin et installé sur C: qui est un SSD) ...
Je vais reprendre tout ça et mettre en ordre pour bien piger.

Un autre détail : l'utilisation des print ne semble pas toujours fonctionner. Il y as des différences entre print 'coucou' et print "coucou" (sur la sortie écran s'entends, car les 2 syntaxes, pour moi, permettent des structures différentes, le " permet des syntaxe print proche du C)

Bon je relis 4 / 5 fois et je comprendrai (sûrement),
Merci sam :P

PS : Le $file, j'ai du le modifier il y a 2 ans, car le script ne voulais pas se lancer (c'est vers la page 10 de ce fil) :
Alors pour obtenir quelque chose, j'ai modifié dans les 1ere ligne l'ouverture de OUT en y indiquant le nom du fichier directement en remplacement du "-" après l'option -i ... comme ceci :

open(OUT,"| ./ffmpeg.exe -i - -v 0 -r $fps -s ${w}x${h} -an tmp/img%05d.bmp");

devient :
open(OUT,"| ./ffmpeg -i bad2.mp4 -v 0 -r $fps -s ${w}x${h} -an tmp/img%05d.bmp");
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: HectorDuino

Message par __sam__ »

Oui si tu prégères avec le "$file" en argument de ffmpeg, le mieux et de virer les trucs en référence avec OUT et IN et remplacer le

Code : Tout sélectionner

open(OUT, "| ffmpeg -i $file ....blablah....")
par

Code : Tout sélectionner

system("ffmpeg -i $file ...blablah....")
C'est plus propre. Cette commande lance ffmpeg comme un processus dos standard et va générer plein de fichier dans TMP. Une fois tous les fichiers BMP générés, la commande system rend la main et la boucle while($ok) démarre. Elle traitera alors un à un les BMP à pleine vitesse. Tu pourra donc retirer le sleep(2). Sur une grosse machine ca marchera sans soucis (moi je tourne avec un XP qui a 40Go de disk au total déja très chargé. J'ai pas la place pour 500Mo de BMP sachant que je converti des films de 2h type starwars avec mes scripts ;) )

Si j'ai 2 secs je vais poster une version 2 du script qui marche sur ce principe. Stay tuned :)
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Avatar de l’utilisateur
yo_fr
Messages : 1336
Inscription : 13 août 2009 18:24
Localisation : 78...
Contact :

Re: HectorDuino

Message par yo_fr »

oui, c'est ce que j'avais effectivement fait (sans toutefois utiliser system, et donc ffmpeg fonctionnait en // sur un autre thread), et à la fin ça fonctionnait avec pour le test de fin avec un :
my $read = read($name,$buf,256);
$ok = 0 unless $read; # était: last unless $read;

Sans utilisation du buffer ! (plus d'image => sortie !)
Mais effectivement avec le pipe c'est beaucoup plus propre !

JJ
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: HectorDuino

Message par __sam__ »

__sam__ a écrit : 25 sept. 2017 19:21Si j'ai 2 secs je vais poster une version 2 du script qui marche sur ce principe. Stay tuned :)
Voili, voilou, voilà:

Code : Tout sélectionner

#/bin/perl
# Conversion video en gif 4 couls. V2 pour grosses
# machines avec plein d'espace disque.
#
# Samuel DEVULDER, Sept 2017
#

# parametres
($W, $H) = (204, 153); # HR
$fps     = 10;
$dither  = "vac8";
($RED,$GRN,$BLU) = (2,4,1);

# fichier entree
$file    = $ARGV[0];

$out = $file;
$out =~ s/\.[^\.]*$/.gif/;
exit if -e $out;

($x,$y, $aspect_ratio) = (160,100,"16:9");
open(IN, "./ffmpeg -i \"$file\" 2>&1 |");
while(<IN>) {
	if(/, (\d+)x(\d+)/) {
		($x,$y) = ($1, $2);
		# 4:3
		if(abs($x - 4/3*$y) < abs($x - 16/9*$y)) {
			($w,$h,$aspect_ratio) = (133,100,"4:3");
		}
	}
}
close(IN);
$h = int(($w=$W)*$y/$x);
$w = int(($h=$H)*$x/$y) if $h>$H;
print $file," : ${x}x${y} ($aspect_ratio) -> ${w}x${h}\n";

# dossier temporaire
mkdir "tmp";
# effacement images précédentes
unlink(<tmp/img*.bmp>);
# conversion video --> BMP
system("./ffmpeg -i '$file' -v 0 -r $fps -s ${w}x${h} -an tmp/img%05d.bmp");

&init_magick;
$gif = Image::Magick->new(size=>"${w}x${h}");
	
$cpt = 1; my @c = (0)x8;
while(1) {
	$name = sprintf("tmp/img%05d.bmp", $cpt);
	last unless -e $name; # fichier n'existe pas ==> on a fini
	print STDERR int($cpt++/$fps),"s\r";
	sleep(5) if ($cpt%1200)==0; # on fait une pause régulière pour ne pas surchauffer le processeur
		
	# lecture de l'image
	my $img = Image::Magick->new();
	$img->Read($name);
	unlink $name; # effacement fichier
		
	# on force la saturation (140%) pour avoir des couleurs plus franches
	$img->Modulate(saturation=>140);
	$img->Evaluate(operator=>'Multiply', value=>255/245);
		
	my $tmp = Image::Magick->new(size=>"${W}x${H}");
	$tmp->Read("xc:black");
	$tmp->Composite(image=>$img, Operator=>"Over", x=>($W-$w)>>1, y=>($H-$h)>>1);
	undef $img; $img = $tmp;
	my $orig = $img->Clone();

	# trammage
	$img->Set(colorspace=>$LINEAR_SPACE);
	$img->OrderedDither($dither);
		
	# détermination des 4 couleurs les plus fréquentes (histograme + passe-bas)
	my $z = $orig;
	
	# l'idéee avec ce gauss est de diminuer les intensités au bord de l'image, là 
	# où elles n'ont pas trop d'intérêt.
	if(!defined $gauss) {
		$gauss = Image::Magick->new(size=>"${W}x${H}");
		$gauss->Read("xc:black");
		$gauss=$gauss->Fx(expression=>"exp(-8*(((i-$W/2)/$W)^2 + ((j-$H/2)/$H)^2))", channel=>"All");
		# $gauss->Write("gauss.png");
	}
	$z->Composite(image=>$gauss, Compose=>"Multiply", channel=>"All");
	
	
	$z->Blur(sigma=>2);
	$z->Set(colorspace=>$LINEAR_SPACE);
	$z->OrderedDither($dither);
	# $z->Write("gauss.png");
	
	my @h = $z->Histogram();
	my @H = (0)x8;
	for(my $i=$#h+1; ($i-=5)>=0;) {
		$H[($h[$i]&$RED)|($h[$i+1]&$GRN)|($h[$i+2]&$BLU)] = 
		$h[$i+4];
	}
	my @sf;
	for my $i (0..7) {
		my $t = int($c[$i] = ($c[$i]*0 + 32*$H[$i])/32);
		my $x = $t;	$x |= $x>>1; $x |= $x>>2; $x |= $x>>4; $x |= $x>>8; $x |= $x>>16; ++$x;
		my $y = $x>>4; $x -= $y?$y:1; $t &= $x;
		$sf[$i]=$t*16 + ($i==0?8:$i);
	}
	@h = sort {$sf[$b]<=>$sf[$a]} (0..7);
	# print "\n";
	# for my $i (@h) {
		# print $i, "=>", $sf[$i]>>4, " ",int($c[$i]), " (",$H[$i],")\n";
	# }
	
	
	# construction d'une palette avec ces 4 couleurs
	my $k = join(',', @h[0..3]);
	my $m = $pal{$k};
	if(!defined $m) {
		my(@px);
		for my $c (@h[0..3]) {
			push(@px, ($c&$RED)?255:0, ($c&$GRN)?255:0, ($c&$BLU)?255:0);
		}
		my $tmp = $ENV{'HOME'}."/.toto.pnm";
		open(ZZ,">$tmp");
		print ZZ "P6\n4 1\n255\n", pack('C*',@px),"\n";
		close(ZZ);
		$m = Image::Magick->new();
		$m->Read($tmp);
		unlink($tmp);
		$pal{$k} = $m;
	}
	# remping de l'image tramée sur ces 4 couleurs
	$img->Remap(image=>$m, 'dither-method'=>'none');
		
	# ajout de l'image au gif animé
	$img->Set(dispose=>"None");
	$img->Set(delay=>int(100/$fps));
	push(@$gif, $img);
		
	# pas plus de 4200 imgs (6mins)
	last if $cpt==4200;
}
unlink(<tmp/img*.bmp>);

# ecriture du fichier gif
$gif->Set(dispose=>"None");
$gif->Set(Layers=>"optimize-trans");
$gif->Set(delay=>int(100/$fps));
$gif->Write($out);

sub init_magick {
	# chargement image-magick la 1ere fois
	my($home) = "tmp";
	$ENV{'HOME'} = $home;
	mkdir($home);
	mkdir("$home/.magick");
	open(THR, ">$home/.magick/thresholds.xml");
		
	print THR <<EOF;
		<thresholds>
			<threshold map="2x2">
				<description>2x2 dither matrix</description>
				<levels width="2" height="2" divisor="5">
				1 2
				3 4
				</levels>
			</threshold>
			<threshold map="3x3">
				<description>3x3 dither matrix</description>
				<levels width="3" height="3" divisor="10">
				7 8 2
				6 9 4
				3 5 1
				</levels>
			</threshold>
			<threshold map="5x3">
				<description>5x3 dither matrix</description>
				<levels width="3" height="5" divisor="16">
				 3  9  4
				 8 14 10	
				13 15 11
				 7 12  5
				 2  6  1
				</levels>
			</threshold>			
			<threshold map="vac8">
				<description>void and cluster 65 niveaux</description>
				<levels width="8" height="8" divisor="65">
				35 57 19 55  7 51  4 21
				29  6 41 27 37 17 59 45
				61 15 53 12 62 25 33  9
				23 39 31 49  2 47 13 43
				 3 52  8 22 36 58 20 56
				38 18 60 46 30  5 42 28
				63 26 34 11 64 16 54 10
				14 48  1 44 24 40 32 50
				</levels>
			</threshold>
		</thresholds>
EOF
	close(THR);
		
	eval 'use Image::Magick;';
	
	# determination de l'espace RGB lineaire
	my $img = Image::Magick->new(size=>"256x1", depth=>16);
	$img->Read('gradient:black-white');
	$img->Set(colorspace=>'RGB');
	#$img->Set(colorspace=>"Gray") unless $coul;
	my @px1 = $img->GetPixel(x=>128, y=>0);
	$img->Read('gradient:black-white');
	$img->Set(colorspace=>'sRGB');
	#$img->Set(colorspace=>"Gray") unless $coul;
	my @px2 = $img->GetPixel(x=>128, y=>0);
	my $d1 = $px1[0]-0.5; $d1=-$d1 if $d1<0;
	my $d2 = $px2[0]-0.5; $d2=-$d2 if $d2<0;
	$LINEAR_SPACE = $d1>=$d2 ? "RGB" : "sRGB";
	#print $px1[0], "   ",$px2[0],"    $LINEAR_SPACE\n";
}
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Avatar de l’utilisateur
yo_fr
Messages : 1336
Inscription : 13 août 2009 18:24
Localisation : 78...
Contact :

Re: HectorDuino

Message par yo_fr »

ok, mais effectivement la méthode du pipe me semble très largement plus classe !
(maintenant que je comprends ! :D )
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: HectorDuino

Message par __sam__ »

Petits exemples de conversion pour hector (4 couls maxi à l'écran). Saurez vous deviner les clips dont il s'agit :?:
Image Image Image
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Avatar de l’utilisateur
gleike
Messages : 1341
Inscription : 16 oct. 2014 11:12
Localisation : Ludres (54710) Meurthe & Moselle

Re: HectorDuino

Message par gleike »

Peter Gabriel - Sledgehammer
https://www.youtube.com/watch?v=g93mz_eZ5N4
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: HectorDuino

Message par __sam__ »

oui 1/3 :D
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Avatar de l’utilisateur
gleike
Messages : 1341
Inscription : 16 oct. 2014 11:12
Localisation : Ludres (54710) Meurthe & Moselle

Re: HectorDuino

Message par gleike »

Dire Straits - Calling Elvis
https://www.youtube.com/watch?v=BUavFsfbFv8

The Cure - Close To Me
https://www.youtube.com/watch?v=BjvfIJstWeg

Encore, encore, j'adore les quiz :lol:
Répondre