Author: Clovis R.

Tutoriels

Créer et s’inspirer avec MidJourney : Les meilleures fonctionnalités…

Dans ce tutoriel, nous vous guiderons étape par étape à travers les fonctionnalités de base de MidJourney pour une maîtrise efficace. Ensuite, nous explorerons les paramètres et fonctionnalités avancés qui vous permettront d’obtenir des résultats artistiques exceptionnels.

MidJourney (https://www.midjourney.com/) est l’une des innovations technologiques les plus marquantes de ces dernières années dans le domaine de l’intelligence artificielle. Aujourd’hui, l’art est plus accessible que jamais – les seules limites à l’art sont votre imagination et votre maîtrise des outils d’IA.

D’ici la fin de ce tutoriel, vous serez capable de générer des images comme celles-ci :

Le prix de l’art

Midjourney fonctionne sur des serveurs énergivores et nécessite des améliorations continues. Pour assurer la pérennité du projet, il est indispensable de proposer ces services moyennant un abonnement.

Si vous envisagez d’utiliser Midjourney régulièrement, il est recommandé de choisir le plan à 24 $ par mois. Ce forfait vous offre une priorité dans la file d’attente de génération d’images, permettant des créations plus rapides et illimitées.

En revanche, le plan à 8 $ par mois est une excellente option si vous souhaitez générer un nombre restreint d’images.

Prise en main de Midjourney

Lorsque vous vous connectez sur le site MidJourney, vous verrez les meilleures créations des utilisateurs du jour. Cela peut vous aider à vous inspirer.

A gauche de l’interface, observons les différents outils mis à notre disposition :

  • Explorer (La boussole) : Les meilleurs créations des utilisateurs.
  • Créer (Le pinceau) : L’interface principale pour la génération des images.
  • Editeur (Le stylo, reservé aux abonnements annuels) : Editer des images déjà existantes.
  • Personnalisation (Les tags) : Outil pour guider MidJourney vers des résultats qui suit vos goûts.
  • Gallerie (L’image) : Organiser et consulter l’ensemble de vos créations.
  • Chat (Bulles de texte) : Discuter avec d’autres utilisateurs.
  • Taches (Le pouce) : Récompensé en bonus d’heures de générations, les taches améliorent les modèles de MidJourney.

Notre tutoriel va se focaliser sur la page « Créer« , ou nous allons produire notre IA Art.

Formuler des prompts parfaits

Les prompts de génération sont la base de la création des images. Ceux-ci permettent de guider MidJourney vers les résultats que vous voulez. Plus le prompt sera précis et clair, plus l’image se rapprochera de votre idée.

Voici les éléments primaires pour que vous formuliez vos premières compositions :

  • Le style esthétique : realistic, cinematic, cute, elegant, drawing, digital, in the style of baroque oil painting, 1900s
  • La scène : stormy background, blank background, in a castle, on the beach, in the clouds.
  • Les couleurs : vivid, warm, cold, contrast, black and white, natural lighting.
  • Le point de vue : Side angle profile, close-up, 50mm lens, depth of field, wide angle
  • Les éléments additionnels : La marque des éléments (Mercedes, Ferrari, Apple…), les personnes célèbres (Elon Musk, MrBeast..), L’origine (Asian, African, American), Le lieu (Central Park, New York, Paris, Tokyo, Japan, China..).

Voici des exemples de prompt efficace

demon 2024 brand new mercedes AMG GT dark silver. A dark and stormy background with the reflection on the wet ground. driving away from an explosion. a purple glow emanating from under the car. HD, realistic, high detail, 64k, cinematic, portrait, front diagonal views only

A high-resolution, highly detailed texture,with realistic grain patterns and natural imperfections.The scene is filled with intricate architectural details, warm lighting, and realistic textures. Fantasy art style, ultra-realistic, 8K resolution. fantasy.medieval streets, magic store for grimoires, wands, bottles, fantasy worldview, looking up from below, city, city of seven colors

Grainy black-and-white silhouette of a person standing with their head slightly bowed and hands behind their back. The figure is dressed in a textured, patterned outfit with flared pants, and the chest area radiates outward with a starburst effect, creating a glowing or radiant focal point. To the left of the figure is a bold, black asterisk symbol, contrasting against the minimalistic white background. The overall style has a retro, abstract, and sketch-like feel, with a sense of simplicity and emphasis on texture and contrast

Une fois les prompts maitrisés, vous pourrez découvrir encore plus d’horizons avec les paramètres stylistiques. Découvrons maintenant comment manipuler ces paramètres.

Les paramètres stylistiques

Pour tirer le meilleur parti de Midjourney, il est essentiel de comprendre les différents paramètres stylistiques disponibles. Ces options vous permettent de personnaliser et d’affiner vos créations visuelles selon vos préférences. Explorons en détail chaque section pour optimiser vos images générées.

Cette fenêtre peut être déroulée en cliquant sur l’icone ici en haut à droite

Image Size

Ceci correspond aux proportions souhaitées.

1:1 Carré – 16:9 Paysage – 9:16 Portrait

Aesthetics

  • Stylization : Midjourney privilégie les couleurs, la composition et les formes artistiques. Le paramètre Stylization ajuste l’intensité de cette approche. Des valeurs faibles produisent des images fidèles au prompt mais moins artistiques, tandis que des valeurs élevées génèrent des images très artistiques, moins liées au prompt.
  • Weirdness : Avec le paramètre expérimental Weirdness, explorez des esthétiques non conventionnelles. Ce paramètre introduit des qualités excentriques et décalées, créant des créations uniques et inattendues.
  • Variety : Le paramètre Variety influence la diversité des grilles d’images initiales. Des valeurs élevées produisent des résultats et compositions plus inhabituels et inattendus, tandis que des valeurs faibles offrent des résultats plus fiables et répétables.

Model

  • Mode : Le mode Raw utilise un modèle alternatif adapté aux utilisateurs maîtrisant les commandes et souhaitant un contrôle accru sur leurs images. Les créations Raw comportent moins d’embellissements automatiques, facilitant une correspondance précise pour des styles spécifiques.
  • Version : Midjourney met régulièrement à jour ses modèles pour améliorer cohérence, efficacité, qualité et style. Changez de version avec le paramètre Version. Certains modèles sont spécialisés, par exemple v6.1 pour le réalisme et Niji 6 pour les créations digitales.
  • Personalize : La personnalisation agit comme un assistant de style personnel. En aimant ou classant des images, Midjourney apprend vos préférences et crée des images correspondantes lors de l’utilisation de la personnalisation dans le prompt. Compatible avec Midjourney Versions 6, 6.1 et Niji 6.

Vitesse de traitement :

  • Fast Mode utilise les GPU pour générer des images rapidement en consommant le temps GPU de votre abonnement.
  • Relax Mode permet une création illimitée sans utiliser de GPU, mais les tâches sont mises en file d’attente avec des temps d’attente plus longs.
  • Turbo Mode (expérimental) génère des images jusqu’à quatre fois plus vite en consommant deux fois plus de temps GPU, disponible uniquement avec certaines versions de modèles.

Une fois tous ces paramètres compris, vous pouvez booster votre créativité, surtout grâce aux paramètres stylistiques. Voici quelques exemples d’utilisation de ces paramètres :

Focus sur le paramètre Stylization Prompt : « child’s drawing of a cat« 

Focus sur le paramètre WeirdnessPrompt : « cyanotype cat« 

Focus sur le paramètre ChaosPrompt : « a silver seashell inlaid with pink and green accents« 

A votre tour de créer!

Vous avez désormais maîtrisé les bases et les fonctionnalitées avancées (Stylization) de Midjourney, et pouvez exprimer votre créativité et vos idées sans difficulté. N’hésitez pas à explorer de nouveaux horizons ! Voici quelques-unes de mes créations préférées 🙂

Projets

Une nuit étoilée

Pour notre premier projet de la spé NSI dans le lycée Louis Pasteur, on nous à demandé de réaliser une image déssinée en Python. Nous avions libre choix de réaliser le dessin que l’on desirait, on à donc décidé de réaliser un sujet qui nous passione – le Pixel Art. Seulement, déssiner un pixel art en python demande beaucoup de travail et de réflexion.

Pour valoriser encore plus notre travail, nous avons décidé de rendre cette image animée.

C’est pour cela que le prof nous à suggeré d’utiliser les fonctions, et dans la suite de cet article, nous vous présenterons les techniques qui nous ont permi d’optimiser et de réaliser notre animation représentant un Pixel Art d’une nuie étoilée.

Schéma Original

Tout d’abord, Léandre à commencé par schématiser notre idée sur un logiciel de déssin pixelart – Aseprite.

Nous avons décidé de transcrire seulement les rideaux et la fenêtre en python, sinon le travail était trop manuel, long, et ne demandait pas tant de réfléxion.

Reconstitution des rideaux en Python

Commençons par le commencement :

Nous avons défini les constantes suivantes, qui vont nous aider plus tard.

SCREEN_WIDTH = 1280 + 10
SCREEN_HEIGHT = 720 + 10

PIXEL_SIZE = 15.25

SNOWFLAKE_COUNT = 50
SNOWFLAKE_SIZE_RATIO = 0.1
SNOWFLAKE_MOVE_DOWN = -10

STAR_COUNT = 100

WAIT_TIME_MS = 100
TOTAL_GENERATIONS = 100

Puis, nous avons transcrit manuellement le schéma en python à l’aide de boucles for, et d’une fonction spéciale, conçue par Clovis, nommée draw_pixel_symmetric(couleur, x, y).

Pourquoi symmetric? Car cela nous permet de réaliser 2 rideaux en même temps, symétrique à l’axe vertical, en 2 fois moins de lignes !

D’ailleurs, fun fact. Nous avions pris plus d’une heure à trouver comment réaliser cette symétrie. Clovis était parti dans l’écriture d’un algorithme de dessin de pixels sous forme de dictionnaires {"y = 0": ["Couleur du pixel en x = 0", "Couleur du pixel en x = 1", "etc.."], "y 1":["..."]} avant de trouver une solution plus simple, efficace qui occupe un peu moins de 10 lignes.

def draw_pixel_symmetric(couleur, x, y):
    # OG PIXEL
    penup()
    goto(x, y)
    pendown()
    color(couleur)
    begin_fill()
    for _ in range(4):
        forward(PIXEL_SIZE)
        left(90)
    end_fill()
    penup()

    # SYMMETRIQUE PIXEL
    goto(-x, y)
    pendown()
    begin_fill()
    for _ in range(4):
        forward(PIXEL_SIZE)
        left(90)
    end_fill()
    penup()

C’est évident – au lieu d’avoir un seul pixel en x, y, nous l’avons dupliqué et placé le doublon en -x, y.

Seulement, si c’était si simple.. Nous avons crée la fonction rideaux() où se focalise la transcription manuelle. Merci à Léandre d’avoir transcrit l’entièreté du rideau (2h de travail : 1400 lignes de code).

def rideaux():
    x = -SCREEN_WIDTH//2
    y = -SCREEN_HEIGHT//2 + PIXEL_SIZE
    
    for _ in range(2):
        draw_pixel_symmetric('#9e1b1b',x,y)
        x = x + PIXEL_SIZE

    x = -SCREEN_WIDTH//2
    y = -SCREEN_HEIGHT//2 + PIXEL_SIZE * 2

    draw_pixel_symmetric('#9e2828',x,y)
    x = x + PIXEL_SIZE

    draw_pixel_symmetric('#ac3232',x,y)
    x = x + PIXEL_SIZE
    
    # ... Et la suite dont on vous fait part d'abstraction.
    

rideaux() sans système de symétrie

rideaux() avec draw_pixel_symmetric(). Et voici donc le résultat final. Pas mal non ? Ce n’est seulement que la première étape.

Système de gradient

Le système de gradient, développé par Clovis, prend 5 arguments :

  • colors (liste ["couleur 1", "couleur 2"])
  • x_start
  • y_start
  • x_end
  • y_end

Sans oublier une fonction qui converti l’héxadécimal en décimal, pour pouvoir manipuler des couleurs RGB, et assurer une transition des couleurs de manière fluide.

def hex_to_rgb(hex_color):
    hex_color = hex_color.lstrip('#')
    return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))

def draw_gradient(colors, x_start, y_start, x_end, y_end):
    penup()
    setheading(0) # tortue regarde à l'est
    current_y = y_start
    total_steps = y_end - y_start

    start_rgb = hex_to_rgb(colors[0])
    end_rgb = hex_to_rgb(colors[1])

    while current_y <= y_end:
        proportion = (current_y - y_start) / total_steps
        interpolated_rgb = tuple(
            int(start + (end - start) * proportion) 
            for start, end in zip(start_rgb, end_rgb)
        )
        pencolor(interpolated_rgb)
        penup()
        goto(x_start, current_y)
        pendown()
        forward(x_end - x_start)
        current_y += 1

Expliquons la fonction draw_gradient()plus en détail :

Celle-ci effectue une boucle while qui s’arrête lorsque current_y dépasse ou est égal aux coordonnées y de destination (y_end).

Pour calculer la valeur RGB correspondante à la position de current_y, nous effectuons une boucle for qui consiste à construire un tuple contenant les valeurs rouge, vert, et bleu en fonction de start_rgb, end_rgb et la valeur qui change selon current_y : proportion.

Résultat avec les valeurs hexadécimales ["#4a4c9b", "#050758"].

Appel de la fonction complète : draw_gradient(["#4a4c9b", "#050758"], -SCREEN_WIDTH//2, -SCREEN_HEIGHT//2, SCREEN_WIDTH//2, SCREEN_HEIGHT//2)

Les étoiles

Pour pimenter notre gradient, nous avons placé des étoiles à des emplacements aléatoires.

Voici l’algorithme que nous avions développé.

def initialize_positions(count, screen_width, screen_height):
    # liste avec des tuples dedans PRCQ ON ADORE LES TUPLES !!!!! TOUCHE PAS A MON TUPLE !!!
    positions = []
    for _ in range(count):
        positions.append((randint(-screen_width // 2, screen_width // 2), randint(-screen_height // 2, screen_height // 2)))
    
    return positions

def draw_stars(stars):
    for (x, y) in stars:
        draw_pixel(2, x, y, '#ffffff')

stars = initialize_positions(STAR_COUNT, SCREEN_WIDTH, SCREEN_HEIGHT)
draw_stars(stars)

Pour générer les positions aléatoires, on à décidé de créer une fonction initialize_positions(count, screen_width, screen_height) qui prend en compte les arguments correspondant au montant désiré, la largeur et la hauteur de l’écran. Cette fonction renvoie une liste [] remplie de tuples (x, y) contenant les positions aléatoires en x et y. Créer cette fonction nous permettra d’optimiser encore plus notre système de flocons dans la prochaine section de cet article.

Puis, une fonction draw_starts(stars) à été réalisée pour looper dans chaques tuples contenus dans la liste stars et placer un pixel de taille 2 à l’emplacement correspondant.

Gradient + Etoiles

Gradient + Etoiles + Rideaux

Les compléments des rideaux

Pour ajouter un peu plus de détails, nous avons décidé de rajouter un mur en bas de l’image et un cadre de fenêtre.

De plus, nous avons détaillé les couleurs en rajoutant un système de randomization pour altérner les couleurs marron (claires & foncées). Pour faire cela, il fallait inventer un système pour que chaques pixels ai une variation de marron propre à celui-ci. (Vu qu’il y a une animation plus tard, faire colour_variations_marron[randint(0, 1)] changerait la couleur du pixel à chaques générations).

colour_variations_marron = []
# initialisation des variations de couleurs une seule fois
colour_variations_marron = tuple(
    tuple("#63352d" if randint(0, 1) == 0 else "#5e3129" for _ in range(SCREEN_HEIGHT))
    for _ in range(SCREEN_WIDTH)
)
def supplement_rideaux():

    x = -SCREEN_WIDTH//2 
    y = SCREEN_HEIGHT//2

    # Barre horizontale

    for _ in range(44):
        draw_pixel_symmetric('#4e2b25',x,PIXEL_SIZE * 2.2)
        x = x + PIXEL_SIZE

    x = 0 - PIXEL_SIZE * 12
    y = SCREEN_HEIGHT//2

    # Double Barre Verticale

    for _ in range(45):
        color = colour_variations_marron[round(x)][round(y)]
        draw_pixel_symmetric(color, x, y)
        draw_pixel_symmetric(color, x - PIXEL_SIZE // 1.5, y)
        y -= PIXEL_SIZE

    x = -SCREEN_WIDTH//2
    y = -SCREEN_HEIGHT//2 + PIXEL_SIZE * 8
    
    # Barre horizontale au dessus du mur

    for _ in range(43):
        draw_pixel_symmetric('#4e2b25',x,y)
        x = x + PIXEL_SIZE
    
    x = -SCREEN_WIDTH//2
    y = -SCREEN_HEIGHT//2

    # Mur en bas

    for _ in range(7):
        for _ in range(22):
            for _ in range(3):
                color = colour_variations_marron[round(x)][round(y)]
                draw_pixel(PIXEL_SIZE,x,y,color)
                x = x + PIXEL_SIZE
            
            draw_pixel(PIXEL_SIZE,x,y,'#8f563b')
            x = x + PIXEL_SIZE
        x = -SCREEN_WIDTH//2
        y += PIXEL_SIZE
        

Et voici le résultat !

Les flocons animés

Cette dernière partie était la plus intéressante à faire. Beaucoup de travail et de recherches mais pour un résultat surprenant.

Voici la fonction draw_snowflake() qui déssine un flocon en x, y. Le choix d’une couleur est possible grâce à l’argument couleur. Et la taille de celui-ci est désirable grâce à l’argument size_ratio. Étonnement, Clovis était celui qui à désinné ce flocon, et à trouvé cela plus compliqué comparé aux autres fonctions développées dans tout le projet (ça à pris du temps et de l’imagination).

def draw_snowflake(size_ratio, og_x, og_y, couleur):
    color(couleur)
    x, y = og_x, og_y
    ps = 7 * size_ratio
    # pixel(ps, og_x, og_y)

    goto(og_x - 8*ps, og_y - 8*ps)
    x, y = pos()
    
    left(90)
    pendown()

    for _ in range(16):
        draw_pixel(ps, x, y)
        x, y = x + ps, y + ps
        goto(x, y)

    goto(og_x - 8*ps, og_y + 7*ps)
    x, y = pos()

    for _ in range(16):
        draw_pixel(ps, x, y)
        x, y = x + ps, y - ps
        goto(x, y)

    goto(og_x - ps, og_y + ps * 8)
    x, y = pos()
    og_x2, og_y2 = x, y

    for p in range(18):
        goto(og_x2, og_y2 - (p * ps))
        x, y = pos()
        for i in range(2):
            draw_pixel(ps, x + (i * ps), y)
            x, y = pos()

    goto(og_x - 8 * ps, og_y- ps)
    x, y = pos()
    og_x2, og_y2 = x, y

    for p in range(16):
        goto(og_x2 +  (p * ps), og_y2)
        x, y = pos()
        for i in range(2):
            draw_pixel(ps, x , y+ (i * ps))
            x, y = pos()
    penup()

Quel banger – Léandre quand Clovis à montré son flocon pour la premère fois

Ensuite, passons au système de mouvement des flocons.

def update_snowflake_positions(current_positions):
    updated_positions = []
    for (x, y) in current_positions:
        new_y = y + SNOWFLAKE_MOVE_DOWN
        # snowflake pos reset if goes too down
        if new_y < -SCREEN_HEIGHT // 2:
            new_y = SCREEN_HEIGHT // 2
            new_x = randint(-SCREEN_WIDTH // 2, SCREEN_WIDTH // 2)
            updated_positions.append((new_x, new_y))
        else:
            updated_positions.append((x, new_y))
    return updated_positions

snowflakes = initialize_positions(SNOWFLAKE_COUNT, SCREEN_WIDTH, SCREEN_HEIGHT)

Pour commencer, la fonction update_snowflake_positions(current_positions) prend en compte la liste [(x, y), (x, y)] contenant les tuples de positions x, y de chaques flocons. (réf. initialize_positions(count, screen_width, screen_height)).

Puis initialise une nouvelle liste updated_positions qui contient une nouvelle version de chaques tuples (x, y), cette fois-ci avec une valeur y différée selon la constante SNOWFLAKE_MOVE_DOWN (new_y = y + SNOWFLAKE_MOVE_DOWN).

En plus, nous avons ajouté un système qui replace le flocon en haut de l’écran si les coordonnées y de celui-ci dépasse le point le plus bas de l’écran.

  • Point le plus haut de l’écran : SCREEN_HEIGHT // 2
  • Point le plus bas de l’écran : -SCREEN_HEIGHT // 2

La valeur SCREEN_HEIGHT est divisée par 2, car le module turtle fait en sorte que la position (0, 0) correspond au centre absolu de l’écran.

Enfin, voici la boucle finale permettant une animation.

for generation in range(1, TOTAL_GENERATIONS + 1): # main loop
    clear()

    snowflakes = update_snowflake_positions(snowflakes)
    draw_snowflakes(snowflakes)

    update()

    print(f"Generation n'{generation} dessinée :D")

    time.sleep(WAIT_TIME_MS / 1000)  # Converti millisecondes en seconds

Explication des termes :

  • clear() permet d’effacer complètement tous les éléments de la fenêtre turtle.
  • update() permet de mettre à jour la fenêtre, utile pour réaliser des animations.
  • time.sleep(WAIT_TIME_MS / 1000) permet un temps d’attente entre chaques image exposée. Ici la constante WAIT_TIME_MS est de 100, et la fonction time.sleep() ne prend seulement comme argument des secondes. Il faut donc convertir les millisecondes en secondes, signifiant une division par 1000.

Si TOTAL_GENERATIONS = 1, alors turtle nous renverra une seule image, et non un animé.

Autrement, si TOTAL_GENERATIONS est au dessus de 1, on obtient une animation. Voici un exemple avec 100 :

Pas mal non ?

Sources

Pour réaliser toutes ces fonctions, nous avons utilisé nos propres connaissances, logique, et w3schools qui nous à beaucoup servi pour comprendre certaines opérations et fonctions integrées dans python (comme la fonction zip).

Image et animation finale

Télécharger le projet

Remarque des enseignants : les élèves n’ont pas respecté le nombre maximal de lignes imposé (404) ni la charte de nommage des variables. Cela pose de nombreux problèmes. L’article est néanmoins publié.