# ControlNet

Maîtrisez ControlNet pour un contrôle précis de la génération d'images par IA.

{% hint style="success" %}
Tous les exemples peuvent être exécutés sur des serveurs GPU loués via [CLORE.AI Marketplace](https://clore.ai/marketplace).
{% endhint %}

## Location sur CLORE.AI

1. Visitez [CLORE.AI Marketplace](https://clore.ai/marketplace)
2. Filtrer par type de GPU, VRAM et prix
3. Choisir **À la demande** (tarif fixe) ou **Spot** (prix d'enchère)
4. Configurez votre commande :
   * Sélectionnez l'image Docker
   * Définissez les ports (TCP pour SSH, HTTP pour les interfaces web)
   * Ajoutez des variables d'environnement si nécessaire
   * Entrez la commande de démarrage
5. Sélectionnez le paiement : **CLORE**, **BTC**, ou **USDT/USDC**
6. Créez la commande et attendez le déploiement

### Accédez à votre serveur

* Trouvez les détails de connexion dans **Mes commandes**
* Interfaces Web : utilisez l'URL du port HTTP
* SSH : `ssh -p <port> root@<adresse-proxy>`

## Qu'est-ce que ControlNet ?

ControlNet ajoute un contrôle conditionnel à Stable Diffusion :

* **Canny** - Détection des contours
* **Profondeur** - Cartes de profondeur 3D
* **Pose** - Poses humaines
* **Gribouillis** - Croquis approximatifs
* **Segmentation** - Masques sémantiques
* **Line Art** - Lignes nettes
* **IP-Adapter** - Transfert de style

## Exigences

| Type de contrôle  | VRAM min | Recommandé |
| ----------------- | -------- | ---------- |
| ControlNet unique | 8 Go     | RTX 3070   |
| Multi ControlNet  | 12Go     | RTX 3090   |
| ControlNet SDXL   | 16Go     | RTX 4090   |

## Déploiement rapide avec A1111

**Commande :**

```bash
cd /workspace/stable-diffusion-webui && \
cd extensions && \
git clone https://github.com/Mikubill/sd-webui-controlnet && \
cd .. && \
python launch.py --listen --enable-insecure-extension-access
```

### Télécharger des modèles

```bash
cd /workspace/stable-diffusion-webui/extensions/sd-webui-controlnet/models

# ControlNets SD 1.5
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_canny.pth
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11f1p_sd15_depth.pth
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_openpose.pth
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_scribble.pth
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_lineart.pth
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_softedge.pth
wget https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_seg.pth

# ControlNets SDXL
wget https://huggingface.co/diffusers/controlnet-canny-sdxl-1.0/resolve/main/diffusion_pytorch_model.safetensors -O controlnet-canny-sdxl.safetensors
```

## Python avec Diffusers

### Contrôle Canny Edge

```python
import torch
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
from diffusers.utils import load_image
from controlnet_aux import CannyDetector
import cv2
import numpy as np

# Charger ControlNet
controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_canny",
    torch_dtype=torch.float16
)

# Charger le pipeline
pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
)
pipe.to("cuda")
pipe.enable_model_cpu_offload()

# Préparer l'image de contrôle
image = load_image("input.jpg")
canny = CannyDetector()
control_image = canny(image)

# Générer
output = pipe(
    prompt="une belle femme dans un jardin, haute qualité",
    negative_prompt="laid, flou",
    image=control_image,
    num_inference_steps=30,
    controlnet_conditioning_scale=1.0
).images[0]

output.save("canny_output.png")
```

### Contrôle de profondeur

```python
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
from controlnet_aux import MidasDetector
import torch

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11f1p_sd15_depth",
    torch_dtype=torch.float16
)

pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

# Obtenir la carte de profondeur
depth_estimator = MidasDetector.from_pretrained("lllyasviel/Annotators")
depth_image = depth_estimator(image)

# Générer avec la profondeur
output = pipe(
    prompt="une ville futuriste, science-fiction, détaillée",
    image=depth_image,
    num_inference_steps=30
).images[0]
```

### OpenPose (poses humaines)

```python
from controlnet_aux import OpenposeDetector

# Obtenir la pose
pose_detector = OpenposeDetector.from_pretrained("lllyasviel/Annotators")
pose_image = pose_detector(image)

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_openpose",
    torch_dtype=torch.float16
)

pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

output = pipe(
    prompt="une ballerine dansant, élégante, éclairage studio",
    image=pose_image,
    num_inference_steps=30
).images[0]
```

### Gribouillis/Croquis

```python
from controlnet_aux import HEDdetector

# Détecter les contours en tant que gribouillis
hed = HEDdetector.from_pretrained("lllyasviel/Annotators")
scribble_image = hed(image, scribble=True)

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_scribble",
    torch_dtype=torch.float16
)

pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

output = pipe(
    prompt="une peinture détaillée d'un paysage",
    image=scribble_image,
    num_inference_steps=30
).images[0]
```

## Multi-ControlNet

Combiner plusieurs contrôles :

```python
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
import torch

# Charger plusieurs ControlNets
controlnet_canny = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_canny",
    torch_dtype=torch.float16
)

controlnet_depth = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11f1p_sd15_depth",
    torch_dtype=torch.float16
)

# Créer un pipeline avec plusieurs ControlNets
pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=[controlnet_canny, controlnet_depth],
    torch_dtype=torch.float16
).to("cuda")

# Générer avec plusieurs contrôles
output = pipe(
    prompt="un beau portrait",
    image=[canny_image, depth_image],
    controlnet_conditioning_scale=[1.0, 0.8],  # Ajuster les poids
    num_inference_steps=30
).images[0]
```

## ControlNet SDXL

```python
from diffusers import StableDiffusionXLControlNetPipeline, ControlNetModel
from controlnet_aux import CannyDetector
import torch

# Charger ControlNet SDXL
controlnet = ControlNetModel.from_pretrained(
    "diffusers/controlnet-canny-sdxl-1.0",
    torch_dtype=torch.float16
)

pipe = StableDiffusionXLControlNetPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

# Préparer l'image canny
canny = CannyDetector()
control_image = canny(image, low_threshold=100, high_threshold=200)

output = pipe(
    prompt="une photographie professionnelle, détaillée, 8k",
    image=control_image,
    controlnet_conditioning_scale=0.5,
    num_inference_steps=30
).images[0]
```

## IP-Adapter (transfert de style)

```python
from diffusers import StableDiffusionPipeline
from transformers import CLIPVisionModelWithProjection
import torch

# Charger IP-Adapter
pipe = StableDiffusionPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    torch_dtype=torch.float16
).to("cuda")

pipe.load_ip_adapter(
    "h94/IP-Adapter",
    subfolder="models",
    weight_name="ip-adapter_sd15.bin"
)

pipe.set_ip_adapter_scale(0.6)

# Image de référence de style
style_image = load_image("style_reference.jpg")

output = pipe(
    prompt="un chat assis sur une chaise",
    ip_adapter_image=style_image,
    num_inference_steps=30
).images[0]
```

## Préprocesseurs

Tous les préprocesseurs disponibles :

```python
from controlnet_aux import (
    CannyDetector,           # Détection des contours
    HEDdetector,             # Bord doux/gribouillis
    MidasDetector,           # Estimation de la profondeur
    OpenposeDetector,        # Pose humaine
    MLSDdetector,            # Détection des lignes
    LineartDetector,         # Line art
    LineartAnimeDetector,    # Line art anime
    NormalBaeDetector,       # Cartes normales
    ContentShuffleDetector,  # Mélanger le contenu
    ZoeDetector,             # Meilleure profondeur
    MediapipeFaceDetector,   # Maillage du visage
)

# Exemple d'utilisation
canny = CannyDetector()
canny_image = canny(image, low_threshold=100, high_threshold=200)

depth = MidasDetector.from_pretrained("lllyasviel/Annotators")
depth_image = depth(image)

pose = OpenposeDetector.from_pretrained("lllyasviel/Annotators")
pose_image = pose(image, hand_and_face=True)
```

## Poids de contrôle

Ajuster l'influence par ControlNet :

```python

# Contrôle total
output = pipe(..., controlnet_conditioning_scale=1.0)

# Contrôle partiel (plus de liberté créative)
output = pipe(..., controlnet_conditioning_scale=0.5)

# Guidance très légère
output = pipe(..., controlnet_conditioning_scale=0.3)
```

### Contrôle par étape

```python

# Contrôler uniquement pendant certaines étapes
output = pipe(
    prompt="...",
    image=control_image,
    controlnet_conditioning_scale=1.0,
    control_guidance_start=0.0,  # Commencer au début
    control_guidance_end=0.5,    # Arrêter à 50 % des étapes
    num_inference_steps=30
).images[0]
```

## Inpaint avec ControlNet

```python
from diffusers import StableDiffusionControlNetInpaintPipeline, ControlNetModel
import torch

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_canny",
    torch_dtype=torch.float16
)

pipe = StableDiffusionControlNetInpaintPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

output = pipe(
    prompt="une voiture de sport rouge",
    image=init_image,
    mask_image=mask,
    control_image=canny_image,
    num_inference_steps=30
).images[0]
```

## Traitement par lots

```python
import os
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
from controlnet_aux import CannyDetector
from PIL import Image
import torch

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_canny",
    torch_dtype=torch.float16
)

pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

canny = CannyDetector()

input_dir = "./inputs"
output_dir = "./outputs"
os.makedirs(output_dir, exist_ok=True)

prompt = "belle peinture de paysage, détaillée, artistique"

for filename in os.listdir(input_dir):
    if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
        image = Image.open(os.path.join(input_dir, filename))
        control_image = canny(image)

        output = pipe(
            prompt=prompt,
            image=control_image,
            num_inference_steps=30
        ).images[0]

        output.save(os.path.join(output_dir, f"cn_{filename}"))
```

## Guide des types de contrôle

| Contrôle    | Idéal pour              | Atout   |
| ----------- | ----------------------- | ------- |
| Canny       | Architecture, objets    | 0.8-1.0 |
| Profondeur  | Scènes 3D, perspective  | 0.6-0.8 |
| Pose        | Personnes, personnages  | 0.8-1.0 |
| Gribouillis | Croquis, concepts       | 0.6-0.8 |
| Line Art    | Illustrations           | 0.7-0.9 |
| Softedge    | Guidance générale       | 0.5-0.7 |
| Seg         | Composition de la scène | 0.6-0.8 |

## Performances

| Configuration   | GPU      | Résolution | Temps |
| --------------- | -------- | ---------- | ----- |
| CN unique SD1.5 | RTX 3090 | 512x512    | \~3s  |
| Multi CN SD1.5  | RTX 3090 | 512x512    | \~5s  |
| CN unique SDXL  | RTX 4090 | 1024x1024  | \~8s  |

## Optimisation de la mémoire

```python

# Activer l'attention mémoire-efficiente
pipe.enable_xformers_memory_efficient_attention()

# Déchargement CPU
pipe.enable_model_cpu_offload()

# Découpage de l'attention
pipe.enable_attention_slicing()
```

## Dépannage

### Effet de contrôle faible

* Augmentez `controlnet_conditioning_scale`
* Vérifier la qualité de sortie du préprocesseur
* Utiliser une image de contrôle à plus haute résolution

### Artefacts

* Réduire l'échelle de contrôle
* Utiliser un préprocesseur plus doux (softedge vs canny)
* Ajouter un prompt négatif pour les artefacts

### Problèmes de VRAM

* Utiliser le déchargement CPU
* Reduce resolution
* Utiliser un ControlNet à la fois

## Estimation des coûts

Tarifs typiques du marché CLORE.AI (à partir de 2024) :

| GPU       | Tarif horaire | Tarif journalier | Session de 4 heures |
| --------- | ------------- | ---------------- | ------------------- |
| RTX 3060  | \~$0.03       | \~$0.70          | \~$0.12             |
| RTX 3090  | \~$0.06       | \~$1.50          | \~$0.25             |
| RTX 4090  | \~$0.10       | \~$2.30          | \~$0.40             |
| A100 40GB | \~$0.17       | \~$4.00          | \~$0.70             |
| A100 80GB | \~$0.25       | \~$6.00          | \~$1.00             |

*Les prix varient selon le fournisseur et la demande. Vérifiez* [*CLORE.AI Marketplace*](https://clore.ai/marketplace) *pour les tarifs actuels.*

**Économisez de l'argent :**

* Utilisez **Spot** market pour les charges de travail flexibles (souvent 30-50 % moins cher)
* Payer avec **CLORE** jetons
* Comparer les prix entre différents fournisseurs

## Prochaines étapes

* Stable Diffusion WebUI
* Flux de travail ComfyUI
* [Entraînement Kohya](https://docs.clore.ai/guides/guides_v2-fr/entrainement/kohya-training)
