# JoyAI-Image-Edit

[JoyAI-Image](https://github.com/jd-opensource/JoyAI-Image) est un modèle de fondation multimodal unifié pour la compréhension d’images, la génération d’images à partir de texte et l’édition d’images guidée par des instructions. Il combine un grand modèle de langage multimodal (MLLM) de 8B avec un transformeur de diffusion multimodal (MMDiT) de 16B. Un principe central de JoyAI-Image est la collaboration en boucle fermée entre la compréhension, la génération et l’édition.

JoyAI-Image-Edit prend en charge l'édition d'images générale ainsi que des capacités d'édition spatiale, notamment le déplacement d'objets, la rotation d'objets et le contrôle de la caméra.

| Modèle | Description | Télécharger |
|:-----:|:-----------:|:--------:|
| JoyAI-Image-Edit | Édition d'images guidée par des instructions avec une manipulation spatiale précise et contrôlable | [Hugging Face](https://huggingface.co/jdopensource/JoyAI-Image-Edit-Diffusers) |

```python
import torch
from diffusers import JoyImageEditPipeline
from diffusers.utils import load_image

pipeline = JoyImageEditPipeline.from_pretrained(
    "jdopensource/JoyAI-Image-Edit-Diffusers", dtype=torch.bfloat16
)
pipeline.to("cuda")  # or "mps", "xpu", "cpu"

image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/astronaut.jpg")
prompt = "Add wings to the astronaut."

output = pipeline(
    image=image,
    prompt=prompt,
    num_inference_steps=40,
    guidance_scale=4.0,
    generator=torch.Generator("cuda").manual_seed(0),
).images[0]
output.save("joyimage_edit_output.png")
```

## Édition spatiale

JoyAI-Image prend en charge trois motifs de prompts d'édition spatiale : **Object Move**, **Object Rotation** et **Camera Control**. Pour de meilleurs résultats, suivez aussi fidèlement que possible les modèles de prompts ci-dessous. Pour plus d'informations, reportez-vous à [SpatialEdit](https://github.com/EasonXiao-888/SpatialEdit).

### Déplacement d'objet

Déplacer un objet cible vers une zone spécifiée, marquée par un rectangle rouge, dans l'image d'entrée.

```text
Move the <object> into the red box and finally remove the red box.
```

### Rotation d'objet

Faire pivoter un objet vers une vue canonique spécifique. Valeurs `<view>` prises en charge : `front`, `right`, `left`, `rear`, `front right`, `front left`, `rear right`, `rear left`.

```text
Rotate the <object> to show the <view> side view.
```

### Contrôle de la caméra

Modifiez le point de vue de la caméra tout en gardant la scène 3D inchangée.

```text
Move the camera.
- Camera rotation: Yaw {y_rotation}°, Pitch {p_rotation}°.
- Camera zoom: in/out/unchanged.
- Keep the 3D scene static; only change the viewpoint.
```

## JoyImageEditPipeline[[diffusers.JoyImageEditPipeline]]

#### diffusers.JoyImageEditPipeline[[diffusers.JoyImageEditPipeline]]

```python
diffusers.JoyImageEditPipeline(scheduler: FlowMatchEulerDiscreteScheduler, vae: AutoencoderKLWan, text_encoder: Qwen3VLForConditionalGeneration, tokenizer: Qwen2Tokenizer, transformer: JoyImageEditTransformer3DModel, processor: Qwen3VLProcessor, text_token_max_length: int = 2048)
```

[Source](https://github.com/huggingface/diffusers/blob/v0.43.0/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L100)

Pipeline de diffusion pour l'édition d'images utilisant l'architecture JoyImage.

Le pipeline encode le conditionnement texte et image via un encodeur de texte Qwen3-VL, débruite les latents avec un transformateur 3-D et décode le résultat avec un VAE WAN.

Ordre de déchargement du modèle : text_encoder -> transformer -> vae.

#### __appel__[[diffusers.JoyImageEditPipeline.__call__]]

```python
__call__(image: typing.Union[PIL.Image.Image, numpy.ndarray, torch.Tensor, list[PIL.Image.Image], list[numpy.ndarray], list[torch.Tensor], NoneType] = None, prompt: str | list[str] = None, height: int | None = None, width: int | None = None, num_inference_steps: int = 40, timesteps: typing.List[int] = None, sigmas: typing.List[float] = None, guidance_scale: float = 4.0, negative_prompt: typing.Union[str, typing.List[str], NoneType] = None, num_images_per_prompt: typing.Optional[int] = 1, generator: typing.Union[torch.Generator, typing.List[torch.Generator], NoneType] = None, latents: typing.Optional[torch.Tensor] = None, prompt_embeds: typing.Optional[torch.Tensor] = None, prompt_embeds_mask: typing.Optional[torch.Tensor] = None, negative_prompt_embeds: typing.Optional[torch.Tensor] = None, negative_prompt_embeds_mask: typing.Optional[torch.Tensor] = None, output_type: typing.Optional[str] = 'pil', return_dict: bool = True, callback_on_step_end: typing.Union[typing.Callable[[int, int, typing.Dict], NoneType], diffusers.callbacks.PipelineCallback, diffusers.callbacks.MultiPipelineCallbacks, NoneType] = None, callback_on_step_end_tensor_inputs: typing.List[str] = ['latents'], max_sequence_length: int = 4096, enable_denormalization: bool = True)
```

[Source](https://github.com/huggingface/diffusers/blob/v0.43.0/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L600)

**Paramètres :**

prompt (*str* ou *List[str]*) : Le prompt ou les prompts pour guider la génération.

height (*int*) : Hauteur de la sortie générée en pixels.

width (*int*) : Largeur de la sortie générée en pixels.

image (*PipelineImageInput*, *facultatif*) : Image de référence utilisée pour le conditionnement. Si elle est fournie, le pipeline fonctionne en mode d'édition d'image avec `num_items=2`.

num_inference_steps (*int*, *optionnel*, valeur par défaut 40) : Nombre d'étapes de débruitage. Un nombre plus élevé d'étapes améliore généralement la qualité, au prix d'une inférence plus lente.

timesteps (*List[int]*, *facultatif*) : Étapes de temps personnalisées pour le processus de débruitage. Si fournies, `num_inference_steps` est déduit de la longueur de la liste.

sigmas (*List[float]*, *facultatif*) : Sigmas personnalisés pour le processus de débruitage. Mutuellement exclusif avec `timesteps`.

guidance_scale (*float*, *optionnel*, valeur par défaut 4.0) : Échelle du guidage sans classifieur.

negative_prompt (*str* ou *List[str]*, *facultatif*) : Prompt(s) négatif(s) utilisé(s) pour supprimer le contenu indésirable.

num_images_per_prompt (*int*, *optionnel*, valeur par défaut 1) : Nombre d'échantillons générés par invite.

generator (*torch.Generator* ou *List[torch.Generator]*, *facultatif*) : générateur(s) de nombres aléatoires pour un échantillonnage déterministe.

latents (*torch.Tensor*, *facultatif*) : Latents bruités pré-générés pour le slot cible. Échantillonnés à partir d'une distribution gaussienne lorsqu'ils ne sont pas fournis. Peut être utilisé pour amorcer la génération à partir d'un tenseur de bruit de départ spécifique.

prompt_embeds (*torch.Tensor*, *facultatif*) : Incrustations de prompt précalculées. Si fournies, `prompt` peut être omis.

prompt_embeds_mask (*torch.Tensor*, *facultatif*) : Masque d'attention pour `prompt_embeds`.

negative_prompt_embeds (*torch.Tensor*, *facultatif*) : Incrustations de prompt négatif précalculées.

negative_prompt_embeds_mask (*torch.Tensor*, *facultatif*) : Masque d'attention pour `negative_prompt_embeds`.

output_type (*str*, *facultatif*, par défaut `"pil"`) : Format de sortie. Passez `"latent"` pour retourner les latents bruts.

return_dict (*bool*, *facultatif*, par défaut *True*) : Indique s'il faut retourner un [JoyImageEditPipelineOutput](/docs/diffusers/v0.43.0/fr/api/pipelines/joyimage_edit#diffusers.JoyImageEditPipelineOutput) ou un simple tenseur.

callback_on_step_end (*Callable*, *PipelineCallback*, *MultiPipelineCallbacks*, *optionnel*) : Callback appelé à la fin de chaque étape de débruitage avec la signature `(self, step: int, timestep: int, callback_kwargs: Dict)`.

callback_on_step_end_tensor_inputs (*List[str]*, *facultatif*, par défaut `["latents"]`) : Clés de tenseurs incluses dans `callback_kwargs` pour `callback_on_step_end`.

max_sequence_length (*int*, *optionnel*, valeur par défaut 4096) : Longueur maximale de la séquence pour l'encodage du prompt.

enable_denormalization (*bool*, *facultatif*, par défaut *True*) : Dénormalise les latents avant le décodage VAE.

**Retourne :** [*~pipelines.joyimage.JoyImageEditPipelineOutput*] ou *torch.Tensor*

Si `return_dict` est `True`, renvoie un objet de sortie de pipeline contenant l’image ou les images générées. Sinon, renvoie directement le tenseur d’image.

Générer une image modifiée conditionnée par une image de référence et un prompt texte.

Exemples :
```python
>>> import torch
>>> from diffusers import JoyImageEditPipeline
>>> from diffusers.utils import load_image

>>> model_id = "jdopensource/JoyAI-Image-Edit-Diffusers"
>>> pipe = JoyImageEditPipeline.from_pretrained(model_id, torch_dtype=torch.bfloat16)
>>> pipe.to("cuda")

>>> image = load_image("https://huggingface.co/datasets/diffusers/docs-images/resolve/main/astronaut.jpg")
>>> output = pipe(
...     image=image,  # pass an image for editing; omit for text-to-image generation
...     prompt="Add wings to the astronaut.",
...     num_inference_steps=40,
...     guidance_scale=4.0,
...     generator=torch.manual_seed(0),
... )
>>> output.images[0].save("joyimage_edit.png")
```

#### check_inputs[[diffusers.JoyImageEditPipeline.check_inputs]]

```python
check_inputs(prompt, height, width, negative_prompt = None, prompt_embeds = None, negative_prompt_embeds = None, prompt_embeds_mask = None, negative_prompt_embeds_mask = None, callback_on_step_end_tensor_inputs = None)
```

[Source](https://github.com/huggingface/diffusers/blob/v0.43.0/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L409)

**Levée :** ``ValueError``

- ``ValueError`` -- En cas de combinaison d'arguments invalide.

Valider les entrées du pipeline avant le passage avant.

#### denormalize_latents[[diffusers.JoyImageEditPipeline.denormalize_latents]]

```python
denormalize_latents(latent: Tensor)
```

[Source](https://github.com/huggingface/diffusers/blob/v0.43.0/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L476)

**Paramètres :**

latent : Tenseur latent normalisé.

**Retourne :**

Tenseur latent à l'échelle attendue par `vae.decode`.

Inverser `normalize_latents` pour récupérer l'échelle latente d'origine.

#### encode_prompt[[diffusers.JoyImageEditPipeline.encode_prompt]]

```python
encode_prompt(prompt: typing.Union[str, typing.List[str]], device: typing.Optional[torch.device] = None, num_images_per_prompt: int = 1, prompt_embeds: typing.Optional[torch.Tensor] = None, prompt_embeds_mask: typing.Optional[torch.Tensor] = None, max_sequence_length: int = 1024, template_type: str = 'image')
```

[Source](https://github.com/huggingface/diffusers/blob/v0.43.0/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L364)

**Paramètres :**

prompt : Chaîne de prompt ou liste de chaînes de prompt.

device : Appareil cible.

num_images_per_prompt : Nombre de sorties à générer par invite.

prompt_embeds : Incrustations de prompt précalculées.

prompt_embeds_mask : Masque d'attention pour les embeddings précalculés.

max_sequence_length : Longueur maximale de la séquence de sortie.

template_type : Clé du modèle de prompt (`"image"` ou `"multiple_images"`).

**Retourne :**

Tuple de (prompt_embeds, prompt_embeds_mask).

Encoder un prompt de texte en embeddings (chemin texte uniquement).

Les `prompt_embeds` précalculés contournent entièrement l’encodage.

#### encode_prompt_multiple_images[[diffusers.JoyImageEditPipeline.encode_prompt_multiple_images]]

```python
encode_prompt_multiple_images(prompt: typing.Union[str, typing.List[str]], device: typing.Optional[torch.device] = None, num_images_per_prompt: int = 1, images: typing.Optional[torch.Tensor] = None, prompt_embeds: typing.Optional[torch.Tensor] = None, prompt_embeds_mask: typing.Optional[torch.Tensor] = None, template_type: typing.Optional[str] = 'multiple_images', max_sequence_length: typing.Optional[int] = None)
```

[Source](https://github.com/huggingface/diffusers/blob/v0.43.0/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L286)

**Paramètres :**

prompt : Chaîne(s) de prompt, pouvant éventuellement contenir des jetons `&amp;lt;image>\n`.

device : Appareil cible.

num_images_per_prompt : Nombre de sorties à générer par invite.

images : Tenseurs de pixels correspondant aux jetons d'image en ligne.

prompt_embeds : Incrustations de prompt précalculées.

prompt_embeds_mask : Masque d'attention pour les embeddings précalculés.

template_type : Doit être `"multiple_images"`.

max_sequence_length : Si défini, tronque la sortie à cette longueur (en conservant les `max_sequence_length` derniers jetons).

**Retourne :**

Tuple de (prompt_embeds, prompt_embeds_mask).

Encoder les invites contenant des jetons d’image en ligne via le processeur Qwen.

Les placeholders `&amp;lt;image>\n` dans chaque chaîne de prompt sont remplacés par les jetons spéciaux de vision de Qwen avant d’être transmis à l’encodeur multimodal.

#### normalize_latents[[diffusers.JoyImageEditPipeline.normalize_latents]]

```python
normalize_latents(latent: Tensor)
```

[Source](https://github.com/huggingface/diffusers/blob/v0.43.0/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L447)

**Paramètres :**

latent : Tenseur latent brut issu de `vae.encode`.

**Retourne :**

Tenseur latent normalisé.

Normalise les latents en utilisant les statistiques par canal de la configuration du VAE.

Uses (latent - mean) / std lorsque le VAE expose `latents_mean` et `latents_std` ; sinon, il bascule sur une mise à l'échelle par `scaling_factor`.

#### prepare_latents[[diffusers.JoyImageEditPipeline.prepare_latents]]

```python
prepare_latents(batch_size: int, num_channels_latents: int, height: int, width: int, video_length: int, dtype: dtype, device: device, generator: typing.Union[torch.Generator, typing.List[torch.Generator], NoneType], latents: typing.Optional[torch.Tensor] = None, image: typing.Optional[typing.List[PIL.Image.Image]] = None, enable_denormalization: bool = True)
```

[Source](https://github.com/huggingface/diffusers/blob/v0.43.0/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L502)

**Paramètres :**

batch_size : Nombre d'échantillons dans le lot.

num_channels_latents : Dimension du canal latent issue de la configuration du transformer.

height : Hauteur spatiale en pixels.

width : Largeur spatiale en pixels.

video_length : Nombre de trames (1 pour l'inférence d'image).

dtype : dtype à virgule flottante pour le tenseur latent.

device : Appareil cible.

generator : générateur(s) de nombres aléatoires pour un échantillonnage reproductible.

latents : Bruit initial optionnel fourni par l'utilisateur pour le slot cible. Lorsque `None`, un bruit aléatoire est échantillonné.

image : Liste facultative d'images de référence PIL à encoder avec le VAE en tant que slots de conditionnement.

enable_denormalization : Indique s'il faut normaliser les latents de référence encodés.

**Retourne :**

Tuple de `(latents, image_latents)` où `latents` a la forme `(B, 1, C, T, H', W')` et `image_latents` a la forme `(B, N_ref, C, T, H', W')` ou `None` si aucune image de référence n'est fournie.

**Levée :** `ValueError`

- `ValueError` -- Si `generator` est une liste dont la longueur diffère de `batch_size`.

Préparez le tenseur latent initial bruité pour la boucle de débruitage.

## JoyImageEditPipelineOutput[[diffusers.JoyImageEditPipelineOutput]]

#### diffusers.JoyImageEditPipelineOutput[[diffusers.JoyImageEditPipelineOutput]]

```python
diffusers.JoyImageEditPipelineOutput(images: typing.Union[typing.List[PIL.Image.Image], numpy.ndarray])
```

[Source](https://github.com/huggingface/diffusers/blob/v0.43.0/src/diffusers/pipelines/joyimage/pipeline_output.py#L11)

Classe de sortie pour les pipelines de génération JoyImageEdit.

