# JoyAI-Image-Edit

[JoyAI-Image](https://github.com/jd-opensource/JoyAI-Image) adalah model dasar multimodal terpadu untuk pemahaman gambar, pembuatan gambar dari teks, dan pengeditan gambar yang dipandu instruksi. Model ini menggabungkan Multimodal Large Language Model (MLLM) 8B dengan Multimodal Diffusion Transformer (MMDiT) 16B. Prinsip utama JoyAI-Image adalah kolaborasi loop tertutup antara pemahaman, pembuatan, dan pengeditan.

JoyAI-Image-Edit mendukung pengeditan gambar umum serta kemampuan pengeditan spasial, termasuk perpindahan objek, rotasi objek, dan kontrol kamera.

| Model | Deskripsi | Unduh |
|:-----:|:-----------:|:--------:|
| JoyAI-Image-Edit | Pengeditan gambar yang dipandu instruksi dengan manipulasi spasial yang presisi dan dapat dikendalikan | [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")
```

## Penyuntingan spasial

JoyAI-Image mendukung tiga pola prompt pengeditan spasial: **Object Move**, **Object Rotation**, dan **Camera Control**. Untuk hasil terbaik, ikuti template prompt di bawah ini sedekat mungkin. Untuk informasi lebih lanjut, lihat [SpatialEdit](https://github.com/EasonXiao-888/SpatialEdit).

### Gerak Objek

Pindahkan objek target ke area tertentu yang ditandai dengan kotak merah pada gambar input.

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

### Rotasi Objek

Putar objek ke tampilan kanonik tertentu. Nilai `<view>` yang didukung: `front`, `right`, `left`, `rear`, `front right`, `front left`, `rear right`, `rear left`.

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

### Kontrol Kamera

Ubah sudut pandang kamera sambil mempertahankan adegan 3D tetap tidak berubah.

```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)
```

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

Pipeline difusi untuk pengeditan gambar menggunakan arsitektur JoyImage.

Pipeline ini mengkodekan kondisi teks dan gambar melalui pengkode teks Qwen3-VL, melakukan denoising pada latens dengan transformer 3-D, dan mendekode hasilnya dengan WAN VAE.

Urutan offloading model: text_encoder -> transformer -> vae.

#### __call__[[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)
```

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

**Parameter:**

prompt (*str* atau *List[str]*) : Prompt atau prompt-prompt untuk mengarahkan generasi.

height (*int*) : Tinggi output yang dihasilkan dalam piksel.

width (*int*) : Lebar output yang dihasilkan dalam piksel.

image (*PipelineImageInput*, *opsional*) : Gambar referensi yang digunakan untuk kondisioning. Ketika diberikan, pipeline beroperasi dalam mode pengeditan gambar dengan `num_items=2`.

num_inference_steps (*int*, *optional*, defaultnya 40) : Jumlah langkah denoising. Semakin banyak langkah umumnya meningkatkan kualitas dengan biaya inferensi yang lebih lambat.

timesteps (*List[int]*, *opsional*) : Timesteps kustom untuk proses denoising. Ketika diberikan, `num_inference_steps` disimpulkan dari panjang daftar.

sigmas (*List[float]*, *opsional*) : Sigma kustom untuk proses denoising. Saling eksklusif dengan `timesteps`.

guidance_scale (*float*, *optional*, default 4.0) : Skala panduan klasifikasi bebas.

negative_prompt (*str* atau *List[str]*, *opsional*) : Prompt negatif yang digunakan untuk menekan konten yang tidak diinginkan.

num_images_per_prompt (*int*, *opsional*, default 1) : Jumlah sampel yang dihasilkan per prompt.

generator (*torch.Generator* atau *List[torch.Generator]*, *opsional*) : generator RNG untuk pengambilan sampel deterministik.

latents (*torch.Tensor*, *opsional*) : Laten berisik yang telah dibuat sebelumnya untuk slot target. Disampelkan dari distribusi Gaussian jika tidak disediakan. Dapat digunakan untuk memulai generasi dari tensor noise awal tertentu.

prompt_embeds (*torch.Tensor*, *opsional*) : Embedding prompt yang sudah dihitung sebelumnya. Jika diberikan, `prompt` dapat diabaikan.

prompt_embeds_mask (*torch.Tensor*, *opsional*) : Attention mask untuk `prompt_embeds`.

negative_prompt_embeds (*torch.Tensor*, *opsional*) : Embedding prompt negatif yang sudah dihitung sebelumnya.

negative_prompt_embeds_mask (*torch.Tensor*, *opsional*) : Attention mask untuk `negative_prompt_embeds`.

output_type (*str*, *opsional*, defaultnya `"pil"`) : Format output. Berikan `"latent"` untuk mengembalikan latens mentah.

return_dict (*bool*, *opsional*, defaultnya *True*) : Apakah akan mengembalikan [JoyImageEditPipelineOutput](/docs/diffusers/v0.43.0/id/api/pipelines/joyimage_edit#diffusers.JoyImageEditPipelineOutput) atau tensor biasa.

callback_on_step_end (*Callable*, *PipelineCallback*, *MultiPipelineCallbacks*, *opsional*) : Callback yang dipanggil di akhir setiap langkah denoising dengan tanda tangan `(self, step: int, timestep: int, callback_kwargs: Dict)`.

callback_on_step_end_tensor_inputs (*List[str]*, *opsional*, defaultnya `["latents"]`) : Kunci Tensor yang disertakan dalam `callback_kwargs` untuk `callback_on_step_end`.

max_sequence_length (*int*, *opsional*, default 4096) : Panjang maksimum urutan untuk enkoding prompt.

enable_denormalization (*bool*, *opsional*, defaultnya *True*) : Menormalisasi ulang latents sebelum decoding VAE.

**Mengembalikan:** [*~pipelines.joyimage.JoyImageEditPipelineOutput*] atau *torch.Tensor*

Jika `return_dict` adalah `True`, mengembalikan objek output pipeline yang berisi gambar yang dihasilkan. Jika tidak, mengembalikan tensor gambar secara langsung.

Buat gambar yang telah diedit berdasarkan gambar referensi dan prompt teks.

Contoh:
```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)
```

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

**Mengangkat:** ``ValueError``

- ``ValueError`` -- Pada kombinasi argumen yang tidak valid.

Validasi input pipeline sebelum forward pass.

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

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

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

**Parameter:**

latent : Tensor laten ternormalisasi.

**Mengembalikan:**

Tensor laten pada skala yang diharapkan oleh `vae.decode`.

Invert `normalize_latents` untuk memulihkan skala laten asli.

#### 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')
```

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

**Parameter:**

prompt : String prompt atau daftar string prompt.

device : Perangkat target.

num_images_per_prompt : Jumlah output yang akan dihasilkan per prompt.

prompt_embeds : Embedding prompt yang sudah dihitung sebelumnya.

prompt_embeds_mask : Attention mask untuk embedding yang sudah dihitung sebelumnya.

max_sequence_length : Panjang maksimum urutan output.

template_type : Kunci template prompt (`"image"` atau `"multiple_images"`).

**Mengembalikan:**

Tuple dari (prompt_embeds, prompt_embeds_mask).

Enkode prompt teks menjadi embeddings (jalur teks saja).

Pre-computed `prompt_embeds` melewati proses encoding sepenuhnya.

#### 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)
```

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

**Parameter:**

prompt : String prompt, opsional berisi token `&amp;lt;image>\n`.

device : Perangkat target.

num_images_per_prompt : Jumlah output yang akan dihasilkan per prompt.

images : Tensor piksel yang sesuai dengan token gambar inline.

prompt_embeds : Embedding prompt yang sudah dihitung sebelumnya.

prompt_embeds_mask : Attention mask untuk embedding yang sudah dihitung sebelumnya.

template_type : Harus berupa `"multiple_images"`.

max_sequence_length : Jika diatur, pangkas output ke panjang ini (menjaga `max_sequence_length` token terakhir).

**Mengembalikan:**

Tuple dari (prompt_embeds, prompt_embeds_mask).

Encode prompt yang berisi token gambar inline melalui prosesor Qwen.

Placeholder `&amp;lt;image>\n` dalam setiap string prompt diganti oleh token khusus visi Qwen sebelum diberikan ke encoder multimodal.

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

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

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

**Parameter:**

latent : Tensor laten mentah dari `vae.encode`.

**Mengembalikan:**

Tensor laten ternormalisasi.

Normalisasi latents menggunakan statistik per-channel dari konfigurasi VAE.

Uses (latent - mean) / std ketika VAE mengekspos `latents_mean` dan `latents_std`; jika tidak, kembali ke penskalaan dengan `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)
```

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

**Parameter:**

batch_size : Jumlah sampel dalam batch.

num_channels_latents : Dimensi kanal laten dari konfigurasi transformer.

height : Tinggi spasial dalam piksel.

width : Lebar spasial dalam piksel.

video_length : Jumlah frame (1 untuk inferensi gambar).

dtype : dtype bilangan pecahan untuk tensor laten.

device : Perangkat target.

generator : generator RNG untuk pengambilan sampel yang dapat direproduksi.

latents : Noise awal opsional yang disediakan pengguna untuk slot target. Ketika `None`, noise acak akan diambil.

image : Daftar opsional gambar referensi PIL untuk di-VAE-encode sebagai slot kondisioning.

enable_denormalization : Apakah akan menormalisasi latens referensi yang telah dikodekan.

**Mengembalikan:**

Tuple dari `(latents, image_latents)` di mana `latents` memiliki bentuk `(B, 1, C, T, H', W')` dan `image_latents` memiliki bentuk `(B, N_ref, C, T, H', W')` atau `None` ketika tidak ada gambar referensi yang diberikan.

**Mengangkat:** `ValueError`

- `ValueError` -- Jika `generator` adalah daftar yang panjangnya berbeda dari `batch_size`.

Siapkan tensor laten berisik awal untuk loop denoising.

## JoyImageEditPipelineOutput[[diffusers.JoyImageEditPipelineOutput]]

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

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

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

Kelas output untuk pipeline generasi JoyImageEdit.

