Python et ImageMagick

Bonjour,

je cherche à créer un gif à partir en utilisant le code sur cette page :
http://python.physique.free.fr/animations.html

L'auteur de la page veut tracer le mouvement d'une particule se baladant sur une cercle. Dans le détail :
1 - il fixe la position de la particule au temps $t$ ; sa position est $(cos(t),sin(t))$ à des coefficients près
2 - pour chaque valeur de $t$ entre $0$ et $2$ (je le dis mal, mais il prend évidemment un échantillonnage de l'intervalle $[0,2]$), il construit et sauvegarde une image où la particule est en position $(cos(t),sin(t))$.
3 - On a alors une centaine d'images avec la particule à une position donnée et on anime en utilisant ImageMagick.

Le point qui me pose problème est le point 3, précisément la commande :
cmd = 'convert fichierTemp*.pdf Modele_animation_001a.gif'
os.system(cmd)
La deuxième ligne produit l'erreur :
Paramtre non valide - Modele_animation_001a.gif

J'ai vérifié que mes images sont bien créés dans le bon dossier par le script. Le problème vient du fait que je ne connais pas du tout ImageMagick. C'est apparemment une bonne solution pour faire ce que je veux faire, d'où ma tentative. Je l'ai installé sur ma machine, mais ce qui me paraît bizarre, c'est que je n'ai pas dit à Python où, donc ça ne me surprend pas tellement qu'il ne sache pas exécuter ma commande. Que faut-il faire pour faire fonctionner ce logiciel ?

Réponses

  • Tu as vraiment besoin de Python pour exécuter convert ? Pourquoi ne pas le faire directement en ligne de commande ?
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • J'avais un peur de cette réponse en tapant ma question :-D Je ne sais jamais bien ce qu'on entend par "le faire directement en ligne de commande". Dans mon idée, Python disait à ImageMagick d’exécuter la commande convert. Sinon, je ne sais pas du tout où taper cette commande : j'ai ouvert ImageMagick mais je n'ai que ce qu'il y a dans la capture jointe. Si je fais New, il ne se passe rien et si je lui demande d'ouvrir les pdf crées, il ne les voit pas.92714
  • Tu ouvres une console (xterm, gnome terminal, kterm…) et tu tapes la commande.
    Elle est néanmoins fausse vu la syntaxe de convert. Visiblement, il lui manque une option pour lui dire que les premiers fichiers servent à construire la dernière.
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Je suis sous Windows, donc j'imagine qu'il faut que j'aille dans l'invite de commande Windows (OK), que je change le répertoire courant (OK) et je copie/colle ma commande. Mais il me dit que 'os.system' n'est pas reconnu comme commande interne ou externe, un exécutable ou un fichier de commandes...
    Mais ce que je ne comprends pas avec ce genre de chose, c'est qu'à aucun moment je demande d'utiliser ImageMagick, donc je ne comprends pas bien comment l'ordinateur peut savoir que la commande "convert" se réfère à ce programme (sans même parler de l'erreur de syntaxe que tu signales).
  • En cherchant un peu mieux, je tombe sur https://imagemagick.org/script/convert.php où il donne quelques commandes et dans un sens, ça répond à ma gêne sur le fait que je ne vois pas comment ImageMagick peut savoir qu'on lui demande quelque chose... en ajoutant magick devant ma commande. Et ça marche, le gif n'est pas très beau (j'ai un point qui "clignote") mais en ajoutant du délai, ça ira peut-être mieux.
    J'ai tapé ça dans mon éditeur Python, je ne doute pas que ça marche dans l'invite de commande, mais je trouve ça finalement plus pratique de tout faire au même endroit
  • Il faut que tu utilises quelques options bien choisies pour régler le délai entre deux images.
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Sur Windows, il y a une commande convert qui n'est pas celle d'ImageMagick, elle est dans le dossier C:Windows\System32 :
    > where convert
    C:\Windows\System32\convert.exe
    C:\PortableApps\ImageMagick\convert.exe
    
    C'est cette commande qui est exécutée par ton code. Pour exécuter la commande convert d'ImageMagick, il faut faire magick convert.

    ___
    [size=large]EDIT[/size]
    Ah mince je n'avais pas vu que tu avais trouvé:
    en ajoutant magick devant ma commande

    Pour le point qui clignote, tu peux essayer l'option -layers Optimize.
  • Merci de votre aide. J'avais bricolé le délai entre 2 images comme me l'avait dit Nicolas, mais j'ai toujours ce point qui "clignote". Précisément, le point initial de ma boucle réapparaît de temps en temps sans que j'y vois une raison.

    J'ai essayé :
    magick convert fichierTemp*.pdf -set delay 50 -layers Optimize Modele_animation_001a.gif
    
    Mais j'ai toujours mon point parasite, mais peut-être que je ne mets pas la commande au bon endroit ?

    Au passage, ce logiciel est sans doute très puissant mais me paraît incroyablement difficile à utiliser pour un novice. La page http://www.imagemagick.org/Usage/ recense bien des exemples mais il y en a en fait tellement que je ne sais pas par quoi commencer. Bon, je suis bien conscient que mon avis n'est pas vraiment pertinent vu que je n'ai pas passé bien longtemps dessus.
  • Je pense que c’est un problème dans l’ordre de tes fichierTemp*.pdf.
    Essaie dir fichierTemp*.pdf pour voir dans quel ordre ils apparaissent et donc dans quel ordre convert les traite.
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Qu'est-ce que ça donne si tu convertis les fichiers pdf en png :
    mogrify -format png fichierTemp*.pdf
    
    Est-ce que les png obtenus sont corrects ?
  • Bien vu, il les voit dans l'ordre :
    $$0<1<10<11<12<...19<2<20<21<...<29<3<31....$$
    C'est pour ça que j'avais l'impression de voir la trajectoire normale (le saut entre 1 et 10 n'est pas clair à l’œil nu) et un retour à l'origine (en fait à la quasi origine) de temps en temps.

    J'avais testé les png qui me paraissent bon. En les ouvrant et en faisant défiler à la main, je les ai dans le bon ordre. Par contre, on revient à l'ordre lexicographique quand je les fais défiler avec ImageMagick

    Comment faites-vous pour régler ce souci ? Ce n'est pas la première fois que j'ai ce genre de problème. Évidemment, je peux faire ma boucle sur
    range(100, 200)
    
    au lieu de
    range(100)
    
    ou je peux me débrouiller pour numéroter mes images en 00, 01, 02, etc.
    Mais dans les deux cas, je trouve qu'il faut bien anticiper le comportement de la machine qui n'est pas très logique pour nous, ce genre de trucs a déjà dû générer quelques bugs :-X
  • Tu as dix images à renuméroter, préfère cette méthode, ça sera moins prise de tête.
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • ImageMagick prend l'ordre lexicographique, tu dois numéroter 00, 01, etc. Il y a certainement une commande Python équivalente au printf du C, dans ce cas tu fais printf("fichierTemp%02d.pdf", i), où i va de 0 à 99.

    S'il y a encore le point qui clignote, utilise gifski pour faire l'animation à partir des png. Il fait des gif de meilleur qualité.
  • Je ne pense pas que ce soit ImageMagick qui prend les images dans l’ordre lexicographique, c’est le shell de Windows.
    En effet, un moyen de contourner ça serait d’aller chercher la liste des fichiers de la forme fichierTemp*.pdf avec le module scandir. Ensuite tu tries les fichiers à l’aide de Python.
    Sans expression régulière, tu peux le faire comme ça :
    from os import scandir
    
    liste=list()
    for fichier in scandir("tonrépertoire"):
      nom=fichier.name
      if nom.startswith("fichierTemp") and nom.endswith(".pdf"):
        numéro=int(nom[11:-4])
        liste.append((numéro,nom))
    
    liste.sort()
    
    commande="magick convert "
    for _,fichier in liste:
      commande+=fichier+" "
    commande+=" -set delay 50 -layers Optimize Modele_animation_001a.gif"
    
    # Et hop, un coup d’os.system pour finir…
    
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Merci, je teste cela dans la semaine. Je pense aussi que c'est Windows qui me fait ça et pas ImageMagick, j'ai déjà eu le souci dans d'autres contextes. Du coup, je vais bien garder ton script sous le coude au cas où.

    Merci encore pour votre aide à tous les deux.
  • Tu peux écrire la fin de cette manière plus élégante :
    commande="magick convert "+" ".join(fichier for _,fichier in liste)+" -set delay 50 -layers Optimize Modele_animation_001a.gif"
    
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Le mieux, c'est quand même d'utiliser subprocess.run() ou une fonction du même acabit (subprocess.check_call() par exemple, mais elle est dépréciée en faveur de subprocess.run()). On passe alors la liste des arguments et aucun shell n'entre en jeu. Pas besoin de str.join() si l'on a bien préparé la liste (voir ci-dessous). Pas de shell lancé pour rien, donc pas de shell globbing : pas d'astérisque interprété, pas de problèmes avec les noms de fichiers contenant des espaces, des sauts de ligne (!), des crochets, des parenthèses, des points d'interrogation, etc. Et pas de trou de sécurité lié au fait qu'os.system() fait beaucoup, beaucoup plus de choses que ce que ses utilisateurs veulent, la plupart du temps.

    Note : le trou de sécurité, c'est si quelqu'un te fournit les fichiers avec des noms bien choisis et que tu lances le script utilisant os.system(). A priori, il n'y a donc pas de véritable risque si tu choisis toi-même les noms des fichiers, mais quand on prend l'habitude de quelque chose, on a souvent tendance à le réutiliser dans d'autres contextes (p. ex., pour un script CGI), et là, les conséquences peuvent être fâcheuses... Ce genre de problème de sécurité est aussi typiquement introduit lorsque quelqu'un copie/colle un bout de code trouvé sur Internet sans en comprendre les subtilités.

    Voici une modification du code de nicolas.patrois utilisant subprocess.run() comme je viens de le proposer. J'utilise une façon un peu différente de préparer la liste triée et fais en sorte de ne pas prendre un répertoire pour un fichier. Pour plus de souplesse, le chemin du dossier à scanner doit être passé en argument au script ; si cela n'est pas souhaité, remplacer sys.argv[1] par le chemin en question ('import sys' n'est alors plus nécessaire).
    #! /usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    import os
    import subprocess
    import sys
    
    liste = []
    
    for entrée in os.scandir(sys.argv[1]):
        if entrée.is_file():
            nom = entrée.name
            if nom.startswith("fichierTemp") and nom.endswith(".pdf"):
                liste.append(nom)
    
    liste.sort(key=lambda nom: int(nom[11:-4]))
    
    # Ou bien : "-set delay 50 -layers Optimize Modele_animation_001a.gif".split()
    args_supplémentaires = ["-set", "delay", "50", "-layers", "Optimize",
                            "Modele_animation_001a.gif"]
    commande = ["magick", "convert"] + liste + args_supplémentaires
    subprocess.run(commande)
    
    Edits: code ajouté et clarification.
  • Chouette, je ne connaissais subprocess.run. Merci du tuyau.
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Il n'y a pas de quoi. ;-) Avant l'existence du module subprocess, on pouvait faire l'équivalent avec un appel à os.fork() suivi, dans le processus fils, de la fermeture des descripteurs de fichiers non nécessaires pour lui, puis d'un appel à une fonction de la famille os.exec*(), par exemple os.execvp(). Mais subprocess s'occupe de tout cela et simplifie pas mal la gestion des erreurs, il est très pratique.
  • J'ai eu le temps de tester tout ça et cela marche parfaitement. Je comprends bien le script qui récupère les fichiers, je ne connaissais pas les fonctions startswith et endswith sur les chaînes qui sont extrêmement pratiques (j'ai déjà eu besoin de récupérer des fichiers selon l'extension et je bricolais ça à la main). Et la construction de la commande ImageMagick me va aussi sans problème.

    Maintenant, il y a deux choses qui me posent plus de problème :
    - j'ai remplacé sys.argv[1] par le chemin du répertoire pour que ça fonctionne facilement mais je n'ai pas du tout compris cette commande : selon ce que j'ai trouvé, sys.argv est une liste qui contient le nom de la fonction invoquée et d'autres arguments mais je dois dire que je suis perplexe.
    - comme je suis assez peu au fait des subtilités sur le shell, j'utilisais os.system sans trop me poser de question (dans mon idée, ça permet d'éviter d'ouvrir une invite de commande et ça s'arrête à peu près là). Je vois sur le lien de brian que sa commande subprocess.run va la remplacer, donc je veux bien l'utiliser :-D Mais elle fait la même chose avec moins de problème de sécurité ? La notion de sécurité est quelque chose de très vague pour moi, même si je suis entièrement d'accord sur le fait que si j'ai l'habitude d'utiliser une commande "non sécurisé" dans un environnement qui ne pose pas de problème, je finirai par l'utiliser ailleurs.

    Enfin, j'ai utilisé la commande subprocess.run (et avant la commande os.system) dans un autre contexte et dans les deux cas, la console me renvoie une erreur que je trouve incompréhensible. La première ligne me dit :
      File "c:...\anaconda3\lib\runpy.py", line 193, in _run_module_as_main
        "__main__", mod_spec)
    
    La dernière me dit :
      File "c:...\anaconda3\lib\subprocess.py", line 1178, in _execute_child
    CompletedProcess(args='spleeter separate -i audio_example.mp3 -o audio_output', returncode=1)
        startupinfo)
    FileNotFoundError: [WinError 2] Le fichier spcifi est introuvable
    

    Je comprends qu'il me dit qu'il manque un fichier et qu'il s'agit justement de subprocess.py et précisément de la fonction_execute_child. Je suis allé voir dans ce script à la ligne incriminée et je n'ai évidemment rien compris au problème.Une idée d'où vient le souci ?
  • sys.argv contient la ligne de commande complète sous forme d’une liste.
    Par exemple si tu tapes bidule.py -h (typiquement pour afficher l’aide de bidule.py), argv=["bidule.py","-h"].
    argv[0] est toujours la commande, les autres éléments de la liste sont les arguments.
    Pour l’erreur, vérifie que ton fichier se trouve bien là où ton script s’attend à le trouver. Pense aux chemins relatifs et absolus.
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • D'accord pour sys.argv.

    Pour l'erreur, j'ai l'impression que mon souci vient du fait que je croyais avoir installé ce que je voulais et je ne suis pas sûr que ce soit le cas : je voulais m'amuser avec l'IA de Deezer sur https://github.com/deezer/spleeter/

    Ils disent d'utiliser la commande conda install -c conda-forge spleeter pour installer le package spleeter. Comme cela ne fonctionne pas chez moi (Python me dit "The environment is inconsistent, please check the package plan carefully" avant de me lister tout un tas d'incompatibilités), j'ai essayé d'installer leur package avec pip et ça a l'air d'être bon (si je le relance, il me dit que le package est déjà là en me donnant le chemin).

    Je me place ensuite dans le répertoire indiqué et je lance la commande qu'ils me donnent avec subprocess.run et j'obtiens le message d'erreur :-X
  • Les joies de Windows. :-D
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • @Alban_

    Nicolas à répondu au sujet de sys.argv. L'erreur que tu as rapportée provient probablement du fait qu'il n'y a d'exécutable 'spleeter' (tel que spleeter.exe, spleeter.com ou spleeter.bat[1]) dans aucun répertoire de ton PATH.

    Il faut procéder méthodiquement. subprocess.run() lance le programme spécifié par le premier élément de la liste passée en argument, en le cherchant dans le PATH sauf si tu as passé un chemin au lieu d'un simple nom de fichier (par exemple, "machin\truc\spleeter.exe" ou "C:\machin\truc\spleeter.exe" : le premier est un chemin relatif, le second un chemin absolu). Il faut donc vérifier que tu peux effectivement lancer un tel programme (spleeter.exe, spleeter.com ou spleeter.bat) de cette manière. Essaie donc en ligne de commande avec cmd.exe.

    La variable d'environnement PATH contient une liste de dossiers (= répertoires) séparés par des ';' sous Windows (':' sous Unix). Dans un terminal tel que celui ouvert par cmd.exe, tu peux afficher sa valeur avec :
    echo %PATH%
    
    Il est aussi possible de la consulter et la modifier à partir d'un menu Windows, je te laisse chercher avec ton moteur de recherche préféré (p. ex. "Windows modify PATH environment variable"). C'est probablement dans Paramètres système ou quelque chose comme ça.

    Si tu n'arrives à lancer la commande spleeter que depuis le répertoire où se situe l'exécutable (spleeter.exe, spleeter.com ou spleeter.bat), cela suggère que tu peux régler le problème :
    • soit en ajoutant ce répertoire à ton PATH ;
    • soit en mettant dans ton script[2] le chemin complet menant au répertoire contenant l'exécutable 'spleeter' (quelque chose comme "C:\machin\truc\spleeter.exe").
    La deuxième solution est moins invasive mais a pour conséquence que si tu veux réutiliser ton script sur une autre installation de Windows, il faut vérifier que le chemin utilisé dans le script est bien celui où est installé l'exécutable 'spleeter'.

    Note : dans la fenêtre crée par cmd.exe, la commande 'cd' permet de changer de répertoire. Le répertoire courant est affiché au début de chaque ligne avant le signe '>', en principe — il fait partie du « prompt ».

    Comme Microsoft a choisi de rendre ses utilisateurs... dépendants d'autrui et vulnérables aux attaques les plus grossières (genre : 'chat-trop-mignon.exe' affiché comme 'chat-trop-mignon' <- double-clique sur moi en croyant que je suis un fichier image et tu es infecté), il se peut que sur ton système, l'explorateur Windows n'affiche pas les extensions des fichiers telles que .exe, .bat, etc. Dans ce cas, pour les voir, il faut décocher une option de l'explorateur qui doit s'intituler Masquer les extensions de fichiers, ou quelque chose comme ça.

    [1] Il y a probablement d'autres extensions qui peuvent intervenir, du genre .lnk.
    [2] Précisément, dans le premier élément de la liste passée à subprocess.run().

    Edit : ajout de la note sur la commande 'cd'.
  • Je tais le plus longtemps possible mon OS quand je pose une question d'informatique :-D J'avais réactivé l'affichage des extensions, pour m'éviter l'attaque "chat_mignon.exe" décrite par brian, donc pas de souci là-dessus.

    Dans le détail, dans le répertoire "anaconda3", j'ai un dossier appelé "Scripts" dans lequel il y a un éxécutable spleeter.exe. Mais dans le répertoire "anaconda3", j'ai aussi un dossier "Lib" qui contient un dossier "site-packages" qui contient (entre autres) deux dossiers : "spleeter" et "spleeter-1.4.7.dist-info"

    Je me suis placé dans le répertoire qui contient le dossier spleeter et j'ai lancé la commande avec subprocess.run :
    spleeter separate -i audio_example.mp3 -o audio_output
    
    Et j'ai eu le message d'erreur que j'ai copié en partie dans mon message précédent. J'ai aussi tenté de lancer cette commande dans cmd.exe : la commande spleeter n'est pas reconnu dans le répertoire site-package/spleeter mais elle est reconnue dans le repertoire anaconda3/Scripts mais produit une erreur. Donc pour le moment, j'en suis à l'étape 0 : je n'arrive pas à lancer cette commande quel que ce soit le répertoire dans lequel je me place.

    J'ai quand même regardé dans PATH et il n'y a effectivement pas de répertoire utile pour lancer mon script. Je ne me suis pas encore lancé à tenter d'ajouter un répertoire, j'essaie d'abord de tenter à lancer mon script en me plaçant dans le bon répertoire.
  • Tout à fait d'accord avec ton dernier paragraphe : il est a priori inutile de modifier le PATH tant que tu n'arrives pas à lancer spleeter à la main, après t'être rendu dans le bon répertoire (= dossier).

    Je n'ai jamais utilisé anaconda, mais il y a des choses qui ont l'air assez standard dans ce que tu as mentionné. Dans .../Lib/site-packages/spleeter, ce n'est pas très surprenant que cela ne fonctionne pas : il doit y avoir là les modules Python composant spleeter : ils sont faits pour être importés depuis un (autre) module Python avec la commande Python 'import'. Ce sont donc des bibliothèques Python, en quelque sorte, pas des exécutables à lancer directement (directement signifiant ici « hors contexte Python » : depuis cmd.exe, avec subprocess.run() qui lance un programme quelconque sans se soucier du langage dans lequel il est écrit, etc.).

    La bonne piste est à mon avis ton Scripts/spleeter.exe. Il faudrait voir le message d'erreur. Il y a peut-être des choses spéciales à faire pour pouvoir l'utiliser s'il a été installé par anaconda, mais comme ça, je ne saurais dire lesquelles. Peut-être que le message d'erreur nous donnera des idées.

    Note : il est possible de lancer un exécutable sans être dans le répertoire qui le contient, et le résultat n'est pas équivalent. Si tu es dans C:\...\machin\Scripts avec cmd.exe et que tu lances :
    spleeter argument1 ... argumentn
    
    spleeter.exe va voir qu'il est lancé depuis le répertoire C:\...\machin\Scripts (pour le processus exécutant le programme, c'est le répertoire courant tant qu'il ne le change pas en utilisant un truc comme os.chdir()). Mais tu peux aussi te placer dans C:\...\machin et lancer :
    Scripts\spleeter argument1 ... argumentn
    
    Dans ce cas, le répertoire courant au lancement de spleeter.exe est C:\...\machin. Ce n'est donc pas la même chose que dans le cas précédent. Par exemple, si le programme essaie d'ouvrir un fichier en donnant un chemin relatif, ce sera relatif au répertoire courant. Cas extrême : on peut aussi se placer « n'importe où » et lancer
    C:\...\machin\Scripts\spleeter argument1 ... argumentn
    
    Le répertoire courant au lancement de spleeter.exe sera alors le « n'importe où » choisi.

    Le répertoire depuis lequel on lance le programme peut donc avoir une influence sur le bon déroulement des choses. Je ne dis pas que c'est ça qui pose problème ici(*), mais il peut être utile de l'avoir en tête.

    (*) À moins qu'il y ait une bonne raison, on ne fait généralement pas dépendre le bon déroulement d'un programme de son répertoire de lancement, car ça n'est pas très convivial...
  • Après voir tapé ma commande dans l'invite de commande Windows, j'obtiens une dizaine de lignes que je recopie en partie :
    Traceback (most recent call last)
    File "C:....\anaconda3\lib\runpy.py" line 193, in _run_module_as_main 
    "__main__", mod_spec)
    File "C:....\anaconda3\lib\runpy.py" line 85, in run_code 
    exec(code, run_globals)
    File "C:....\anaconda3\Scripts\spleeter.exe\__main__.py", line 9, in <module>
    File "C:....\anaconda3\lib\site-packages\spleeter\__main__.py, line 54, in entrypoint 
    main(sys.argv)
    File "C:....\anaconda3\lib\site-packages\spleeter\__main__.py, line 36, in main 
    enable.logging()
    
    
    File "C:....\anaconda3\lib\site-packages\spleeter\numpy\_distributor_init.py', line 34, in <module> 
    from . import _mklinit
    ImportError : DLL load failed: Le module spécifié est introuvable. 
    
    J'ai sauté quelques lignes où il fait un appel à la bibliothèque TensorFlow

    J'ai voulu tester spleeter avec un autre ordinateur au cas où mon installation de Python ait eu un souci quelconque. Sur cet ordinateur, j'ai la distribution Miniconda et non Anaconda. Avec chdir, je me suis placé dans le répertoire où se trouve spleeter.exe (j'ai fait l'installation avant avec pip comme indiqué sur https://github.com/deezer/spleeter/) et depuis la console Python, j'ai lancé la commande :
    subprocess.run('spleeter separate -i audio_example.mp3 -o audio_output')
    
    J'obtiens :
    CompletedProcess(args='spleeter separate -i audio_example.mp3 -o audio_output', returncode=1)
    

    A priori, processus complété est une bonne nouvelle, mais je ne vois pas le dossier que le script doit créer ni les 2 fichiers obtenus. J'ai évidemment fait une recherche et cherché à la main dans le dossier où se trouve spleeter.exe ainsi que le dossier site-packages, mais la commande getcwd() me renvoie de toute façon au dossier dans lequel se trouve spleeter.exe. Et je vois que returncode vaut plutôt 0 dans le cas d'un succès apparemment.

    J'ai tenté de faire la même chose sur cet ordinateur en lançant la commande depuis l'invite de commande Windows et j'obtiens "Fatal error in launcher : unable to create process using '"d:\bld\spleeter_1574775900394\_h_env\python.exe" suivi du chemin absolu que j'avais indiqué et la commande que j'ai lancé.


    Pour le fait de lancer un exécutable sans se placer dans le répertoire qui le contient, je me disais bien que c'était possible. Je ne m'étais par contre jamais posé la question de la non-équivalence. Et évidemment pour le moment, je préfère limiter les erreurs potentielles en me plaçant directement dans le "bon répertoire".

    Dire que je voulais juste utiliser ce truc en boite noire pendant 15 minutes histoire de m'amuser un peu 8-)
  • Concernant cette erreur :
    from . import _mklinit
    ImportError : DLL load failed: Le module spécifié est introuvable.
    
    _mklinit est certainement un module Python écrit en C, C++ ou autre langage et compilé sous forme de DLL. Les DLL utilisent également le PATH pour se charger, donc il se peut que le fichier correspondant, _mklinit.dll ou quelque chose du genre, ne soit pas dans le PATH, ou encore qu'une dépendance — directe ou indirecte — de ce fichier ne le soit pas (si la DLL est dynamiquement liée à d'autres DLLs). Cela s'explique sans doute par le fait que tu dois probablement « activer un environnement » pour utiliser certaines choses installées par *conda hors contexte (p. ex., depuis cmd.exe). Voir par exemple ce qui est écrit ici.

    Le lien précédent vient de cette page. Comme tu peux le voir, il y a pas mal de solutions proposées, dont certaines consistent à récupérer le « bonne valeur » de PATH avec 'echo %PATH%' depuis le prompt Anaconda, puis à ajouter au PATH global de Windows les chemins additionnels présents dans le PATH récupéré depuis le prompt Anaconda.

    En fait, comme tu travailles en Python, tu n'es pas rigoureusement obligé de modifier ton PATH global si le problème est bien celui-ci. Tu peux :
    • récupérer l'environnement courant avec os.environ.copy() (c'est un object de type 'dict') ;
    • ajouter les répertoires manquants au PATH dans la valeur récupérée (pas dans os.environ lui-même) ;
    • transmettre le nouveau 'dict' représentant l'environnement souhaité à subprocess.run() via son argument 'env'.
    Avantage : tu ne modifies pas ta config Windows et tu changes le PATH uniquement à l'endroit nécessaire. Le reste de ton programme tourne avec le PATH normal (c'est pour ça que j'utilise dict.copy() ici). Inconvénient : ce n'est pas portable car les chemins à ajouter seront potentiellement différents sur un autre ordi.

    [ Note par rapport à ta réponse et à mon message précédent : si tu souhaites lancer spleeter.exe dans un répertoire particulier (c'est-à-dire choisir le répertoire courant qu'il aura à son lancement), il y a mieux que os.chdir(). Tu peux utiliser à la place l'argument 'cwd' de subprocess.run(). De cette façon, le répertoire courant ne change pas pour ton programme, il change uniquement pour le processus qui exécute spleeter.exe (lequel est un processus fils de celui qui exécute ton programme). ]

    De toute manière, il est sans doute plus facile de valider d'abord la solution en ligne de commande en modifiant le PATH soit temporairement dans l'invite de commandes cmd.exe, soit globalement via Paramètres système, quitte à le rétablir après le test de validation.

    Quant à l'erreur sur l'autre ordi :
    "Fatal error in launcher : unable to create process using '"d:\bld\spleeter_1574775900394\_h_env\python.exe"
    
    le mot “launcher” évoque ce que Python fait pour lancer un script sous Windows, mais je ne saurais dire pourquoi cela ne marche pas ici (le message est trop vague).

    Concernant le :
    CompletedProcess(args='spleeter separate -i audio_example.mp3 -o audio_output', returncode=1)
    
    le mot “completed” ne signifie pas (ici) une fin d'exécution avec succès. Par convention, le code de retour 0 quand on lance un programme (fonctions os.exec*() en Python) signifie « pas d'erreur ». Un code non nul peut également signifier « pas d'erreur » (c'est par exemple le cas lorsque grep retourne le code 1), mais disons que ce n'est pas le cas le plus courant. De manière générale, pour pouvoir interpréter le code de retour d'un programme, il faut voir soit le code, soit la documentation dudit programme. En général, lorsqu'il y a une erreur, les messages écrits sur stdout ou stderr sont bien plus utiles que le code de retour du processus (je ne parle pas d'un appel de fonction), d'où l'utilité de travailler en ligne de commande ici. Autrement, ton script peut récupérer et afficher — ou écrire dans un fichier — tout ce que le processus lancé a écrit sur stdout et stderr :
    • par exemple en passant capture_output=True à subprocess.run() puis en lisant les attributs 'stdout' et 'stderr' de l'objet retourné ;
    • ou bien en appelant subprocess.Popen() avec les arguments stdout=subprocess.PIPE et stderr=subprocess.STDOUT (il faut alors lire intégralement p.stdout, où p est la valeur de retour de subprocess.Popen(), avant d'appeler p.wait() qui donne le code de retour du processus et fait le ménage).
    (pour la première méthode, voir l'exemple donné dans la documentation de subprocess.run()).

    Edit: correction dans le dernier point (deuxième méthode) : utiliser subprocess.run() n'est pas la meilleure idée lorsqu'on a connecté stdout ou stderr du processus fils à un PIPE (tube), car vu que cette fonction attend que le processus fils se termine, elle ne permet pas de lire le tube en même temps (sauf à le faire dans un fil d'exécution séparé, hum !), ce qui peut causer un deadlock dès lors que le tube en question a été rempli par le processus fils. En effet, lorsque cela arrive, ce dernier va dans le meilleur des cas s'endormir tant qu'il ne peut pas écrire, et dans le pire des cas boucler « en attendant que ça se libère », donc ne pas se terminer, donc empêcher subprocess.run() de se terminer elle aussi.
  • Merci de ta patience, j'ai commencé à lire ton message qui est très dense pour moi et j'ai regardé ton lien et tenté conda update -c defaults python.

    J'ai relancé ma commande spleeter dans cmd.exe et j'ai eu un message d'erreur. Le début est le même mais pas la fin. Et surtout j'ai un message qui me dit de le lire pour résoudre mon problème. Il me dit de désinstaller puis réinstaller numpy (c'est fait). Puis, si nécessaire, de vérifier si je n'ai pas répertoire dans mon PATH ou PYTHONPATH qui interfère avec la version 1.17.3 de numpy que j'essaie d'utiliser.

    Le problème, c'est que je suis bien incapable de savoir si un répertoire pourrait faire ça. J'ai quand même utilisé ta commande echo %PATH% dans cmd.exe qui me renvoie :
    ImageMagick ; javapath, system32 ; Windows ; Wbem ; WindowsPowerShell\v1.0 ; OpenSSH ; Miktex ; Matlab ; WindowsApp (évidemment, il donne les chemins mais je m'épargne le fait de l'écrire).
  • Je ne vois pas grand-chose dans ton PATH qui pourrait interférer avec numpy, mais je n'ai pas d'éléments très solides pour affirmer cela — je ne sais pas ce qu'il y a dans WindowsApp, par exemple. Je vais faire l'hypothèse que le message d'erreur n'est pas très précis. Tu peux aussi lancer 'echo %PYTHONPATH%' pour suivre gentiment les instructions du message d'erreur dont tu viens de parler.

    Pour le moment, je vois plus d'espoir de ce côté-ci : est-ce que tu peux lancer un truc genre « Anaconda Prompt » comme mentionné ici ?
  • En fait, il faut sans doute aussi prendre en compte le fait que, sauf erreur, tu dois avoir deux installations de Python indépendantes (une « normale » et une avec ou faite par anaconda). Si NumPy est installé pour l'une, il n'est sans doute pas visible par l'autre. Il faut t'assurer que ton script a tout ce qu'il lui faut (NumPy, spleeter...) dans l'installation utilisée pour le lancer.
  • Dans le dossier WindowsApp, il n'y a rien, je viens d'aller voir (et les fichiers cachés sont activés).

    Pour la suite, j'ai lancé echo %PYTHONPATH% dans cmd et ça me renvoit '%PYTHONPATH%'. Je vois que je n'ai pas compris ce qu'il fallait faire ici.

    J'ai aussi lancé Anaconda PowerShell Prompt et j'ai tapé echo %PATH%, ce qui me renvoit '%PATH%' et '%PYTHONPATH%' pour echo %PYTHONPATH%. Désolé de ce message qui doit te sembler navrant...

    Je ne comprends pas pourquoi j'ai forcément deux installations de Python (il n'y a pas Python sous Windows directement). Mais je viens de me rappeler que j'avais installé Miniconda et en me plantant (j'ai la version 2.7) avant d'installer Anaconda. C'est effectivement peut-être ça, je vais désinstaller Miniconda, d'autant plus que je ne l'utilise jamais.
  • Pour cmd.exe, le fait de récupérer %PYTHONPATH% signifie peut-être simplement que la variable PYTHONPATH n'est pas définie (ce qui serait tout à fait normal). Tu peux essayer 'echo %nimportenawak%' pour t'en assurer (je n'ai pas de Windows pour vérifier).

    Pour Anaconda PowerShell Prompt, l'explication tient sans doute dans le mot “PowerShell”. Le PowerShell de Windows est un « nouveau » shell, sans doute beaucoup plus puissant que cmd.exe pour programmer. Je ne l'ai jamais utilisé, mais la syntaxe a l'air assez différente de celle de cmd.exe. À supposer que Anaconda PowerShell Prompt utilise le même type de syntaxe et d'après ce que je vois sur Internet, tu devrais peut-être essayer quelque chose comme ça :
    gci env:PATH
    
    commande qui est a priori équivalente à :
    Get-ChildItem env:PATH
    
    Il y aurait aussi :
    Write-Host $Env:PATH
    
    (à vérifier). Le Env et le PATH semblent pouvoir être écrits avec n'importe quelle casse, toujours d'après ce que je vois sur Internet (concernant PATH, je ne suis pas étonné sous Windows ; pour le Env : Powershell n'est pas dans mon domaine de compétences).
  • Effectivement, la variable PYTHONPATH n'est pas définie dans cmd.exe

    La commande gci env:PATH dans Anaconda Powershell Prompt me renvoie effectivement la valeur de PATH : il contient les chemins vers Anaconda, Anaconda\Library\mingw-w64.bin (aucune idée de ce que c'est) et il y d'autres choses mais que je ne peux pas voir car cela sort de l'écran :-S Et comme je ne l'utilise jamais, je n'ai aucune idée de comment régler ça et mes quelques tentatives sur Google n'ont rien donné.

    La commande Write-Host $Env:PATH me renvoie effectivement la même chose mais avec un retour lisible : il y a en plus dans Anaconda : \Library\usr\bin, \Library\bin, \Scripts (qui contenait spleeter.exe), \bin, \condabin.
    Il y a enfin les chemins vers les mêmes éléments que ce que m'avait donné la commande echo %PATH% dans cmd.exe :
    ImageMagick ; javapath, system32 ; Windows ; Wbem ; WindowsPowerShell\v1.0 ; OpenSSH ; Miktex ; Matlab ; WindowsApp
  • Pour le résultat qui dépasse, tu n'as qu'à rediriger vers un fichier :
    gci env:PATH >C:\Où-tu-veux\res.txt
    
    ou bien :
    gci env:PATH | Out-File C:\Où-tu-veux\res.txt
    
    Ensuite, tu ouvres le fichier avec un éditeur de texte (notepad, Emacs, vim...).

    MinGW-w64 est une réimplémentation (a priori indépendante) de MinGW. Les deux sont décrits sur wikipédia (en). Il s'agit en gros d'un environnement de développement sous Windows comportant notamment un port du compilateur GCC et des outils binutils du projet GNU, afin de produire des exécutables Windows natifs. Il faut peut-être paramétrer ton Google différemment (ne pas préférer le français ?) car je n'ai aucune difficulté à trouver ces infos...

    Bon, tu as confirmé que lorsque l'on travaille dans l'Anaconda Powershell Prompt, il y a des choses en plus dans le PATH. Est-ce que tu peux faire fonctionner spleeter là-dedans ? Si oui, tu peux sans doute lancer ton script depuis l'Anaconda Powershell Prompt ou ajouter les chemins supplémentaires dans ton script comme je l'ai indiqué plus haut (avec os.environ.copy() et le paramètre 'env' de subprocess.run()). La méthode « complétion de PATH par le script » pourrait ne pas marcher si l'exécutable Python qui interprète le script n'est pas lancé « comme il faut » pour Anaconda mais à mon avis, en pareil cas, ça foirerait dès que tu fais 'import numpy' dans ton script.
  • Désolé, je n'étais même pas allé voir ce \mingw-w64.bin sur Google, je dois commencer à saturer de voir ce message d'erreur (et je n'imagine pas pour toi)

    J'ai ajouté anaconda3\\Scripts au PATH (en utilisant les menus de Windows), j'ai ensuite vérifié en appelant os.environ.copy() est le chemin est bien présent.

    Mais ma commande subprocess.run('spleeter separate -i spleeter/audio_example.mp3 -p spleeter:2stems -o output', env = os.environ.copy()) dans Python me sort toujours ce message d'erreur. Et la commande 'spleeter separate -i spleeter/audio_example.mp3 -p spleeter:2stems -o output' dans Anaconda Power Shell me sort aussi un message d'erreur.

    Le seul truc qui marche est d'appeler l'aide (dans Anaconda ou avec subprocess) avec spleeter separate -h

    Enfin, j'ai toujours pu utiliser numpy sans souci
  • Pour ajouter un chemin comportant des antislashs (\) en Python, tu peux utiliser une raw string :
    r"anaconda3\Scripts"
    
    mais attention, il faut des chemins absolus dans le PATH : chaque chemin doit commencer par C:\ ou D:\ ou E:\, etc.

    Cela dit, ce n'est pas vraiment le problème. On ne pourra discuter sérieusement du script Python que lorsque tu arriveras à utiliser spleeter en ligne de commande. subprocess.run() ne fait pas de magie : il se contente de créer un processus fils (via os.fork()) et d'exécuter le programme indiqué dans ce processus fils. Si le programme en question ne marche pas comme tu veux, il ne faut pas attendre de subprocess.run() ou d'os.system() qu'ils règlent le problème...
  • Je vais attendre d'avoir un peu plus de temps à consacrer à ça et je reprendrai tes messages depuis le début parce que j'ai dû mal comprendre quelque chose que tu m'as dit de faire. Merci de ta patience.
  • De rien, pas de problème si tu n'as pas le temps de creuser maintenant.
Connectez-vous ou Inscrivez-vous pour répondre.