# JoyAI-Image-Edit

[JoyAI-Image](https://github.com/jd-opensource/JoyAI-Image) là một mô hình nền tảng đa phương thức thống nhất cho việc hiểu hình ảnh, tạo hình ảnh từ văn bản và chỉnh sửa hình ảnh theo hướng dẫn. Nó kết hợp một Mô hình Ngôn ngữ Lớn Đa phương thức (MLLM) 8B với một Transformer Khuếch tán Đa phương thức (MMDiT) 16B. Một nguyên tắc cốt lõi của JoyAI-Image là sự hợp tác khép kín giữa việc hiểu, tạo và chỉnh sửa.

JoyAI-Image-Edit hỗ trợ chỉnh sửa hình ảnh chung cũng như các khả năng chỉnh sửa không gian bao gồm di chuyển đối tượng, xoay đối tượng và kiểm soát camera.

| Mô hình | Mô tả | Tải xuống |
|:-----:|:-----------:|:--------:|
| JoyAI-Image-Edit | Chỉnh sửa hình ảnh theo hướng dẫn với thao tác không gian chính xác và có thể kiểm soát | [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")
```

## Chỉnh sửa không gian

JoyAI-Image hỗ trợ ba mẫu prompt chỉnh sửa không gian: **Di chuyển đối tượng**, **Xoay đối tượng** và **Điều khiển camera**. Để đạt kết quả tốt nhất, hãy tuân thủ các mẫu prompt bên dưới càng sát càng tốt. Để biết thêm thông tin, hãy tham khảo [SpatialEdit](https://github.com/EasonXiao-888/SpatialEdit).

### Di chuyển đối tượng

Di chuyển một đối tượng đích vào một vùng được chỉ định, đánh dấu bằng một hộp màu đỏ, trong hình ảnh đầu vào.

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

### Xoay Đối tượng

Xoay một đối tượng sang một góc nhìn chuẩn cụ thể. Các giá trị `<view>` được hỗ trợ: `front`, `right`, `left`, `rear`, `front right`, `front left`, `rear right`, `rear left`.

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

### Điều khiển Camera

Thay đổi góc nhìn của camera trong khi giữ nguyên cảnh 3D.

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

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

Pipeline khuếch tán để chỉnh sửa hình ảnh sử dụng kiến trúc JoyImage.

Pipeline mã hóa điều kiện văn bản và hình ảnh thông qua trình mã hóa văn bản Qwen3-VL, khử nhiễu các biến tiềm ẩn bằng một transformer 3-D, và giải mã kết quả bằng WAN VAE.

Thứ tự offloading mô hình: 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)
```

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

**Tham số:**

prompt (*str* hoặc *List[str]*) : Prompt hoặc các prompt để định hướng quá trình tạo.

height (*int*) : Chiều cao của đầu ra được tạo ra, tính bằng pixel.

width (*int*) : Độ rộng của đầu ra được tạo ra, tính bằng pixel.

image (*PipelineImageInput*, *tùy chọn*) : Ảnh tham chiếu được sử dụng để điều kiện hóa. Khi được cung cấp, pipeline sẽ hoạt động ở chế độ chỉnh sửa ảnh với `num_items=2`.

num_inference_steps (*int*, *optional*, mặc định là 40) : Số bước khử nhiễu. Nhiều bước hơn thường cải thiện chất lượng nhưng đổi lại suy luận sẽ chậm hơn.

timesteps (*List[int]*, *optional*) : Các bước thời gian tùy chỉnh cho quá trình khử nhiễu. Khi được cung cấp, `num_inference_steps` sẽ được suy ra từ độ dài của danh sách.

sigmas (*List[float]*, *optional*) : Các sigma tùy chỉnh cho quá trình khử nhiễu. Loại trừ lẫn nhau với `timesteps`.

guidance_scale (*float*, *optional*, mặc định là 4.0) : Thang độ hướng dẫn phân loại không có điều kiện (classifier-free guidance).

negative_prompt (*str* hoặc *List[str]*, *tùy chọn*) : Prompt âm được sử dụng để ngăn chặn nội dung không mong muốn.

num_images_per_prompt (*int*, *optional*, mặc định là 1) : Số lượng mẫu được tạo ra cho mỗi prompt.

generator (*torch.Generator* hoặc *List[torch.Generator]*, *tuỳ chọn*) : Bộ tạo RNG cho việc lấy mẫu xác định.

latents (*torch.Tensor*, *optional*) : Các latents nhiễu đã tạo sẵn cho slot đích. Được lấy mẫu từ phân phối Gaussian khi không được cung cấp. Có thể được sử dụng để khởi tạo quá trình sinh từ một tensor nhiễu bắt đầu cụ thể.

prompt_embeds (*torch.Tensor*, *tuỳ chọn*) : Các embedding của prompt đã được tính trước. Khi được cung cấp, `prompt` có thể được bỏ qua.

prompt_embeds_mask (*torch.Tensor*, *tuỳ chọn*) : Mặt nạ chú ý cho `prompt_embeds`.

negative_prompt_embeds (*torch.Tensor*, *optional*) : Các embedding của negative prompt đã được tính trước.

negative_prompt_embeds_mask (*torch.Tensor*, *tùy chọn*) : Mặt nạ chú ý cho `negative_prompt_embeds`.

output_type (*str*, *tuỳ chọn*, mặc định là `"pil"`) : Định dạng đầu ra. Truyền `"latent"` để trả về các latent thô.

return_dict (*bool*, *optional*, mặc định là *True*) : Xác định việc trả về một [JoyImageEditPipelineOutput](/docs/diffusers/v0.43.0/vi/api/pipelines/joyimage_edit#diffusers.JoyImageEditPipelineOutput) hay một tensor thông thường.

callback_on_step_end (*Callable*, *PipelineCallback*, *MultiPipelineCallbacks*, *optional*) : Callback được gọi vào cuối mỗi bước khử nhiễu với chữ ký `(self, step: int, timestep: int, callback_kwargs: Dict)`.

callback_on_step_end_tensor_inputs (*List[str]*, *optional*, mặc định là `["latents"]`) : Các khóa Tensor được bao gồm trong `callback_kwargs` cho `callback_on_step_end`.

max_sequence_length (*int*, *optional*, mặc định là 4096) : Độ dài tối đa của chuỗi cho việc mã hóa prompt.

enable_denormalization (*bool*, *optional*, mặc định là *True*) : Chuẩn hóa lại các biến tiềm năng trước khi giải mã VAE.

**Trả về:** [*~pipelines.joyimage.JoyImageEditPipelineOutput*] hoặc *torch.Tensor*

Nếu `return_dict` là `True`, trả về một đối tượng đầu ra của pipeline chứa hình ảnh đã được tạo. Ngược lại, trả về trực tiếp tensor hình ảnh.

Tạo một hình ảnh đã chỉnh sửa dựa trên một hình ảnh tham chiếu và một câu lệnh văn bản.

Ví dụ:
```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)
```

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

**Lỗi:** ``ValueError``

- ``ValueError`` -- Với bất kỳ tổ hợp tham số nào không hợp lệ.

Xác thực đầu vào của pipeline trước bước truyền thuận.

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

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

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

**Tham số:**

latent : Tensor ẩn đã được chuẩn hóa.

**Trả về:**

Tensor tiềm ẩn ở thang đo mà `vae.decode` mong đợi.

Đảo ngược `normalize_latents` để khôi phục thang đo latent ban đầu.

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

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

**Tham số:**

prompt : Chuỗi prompt hoặc danh sách các chuỗi prompt.

device : Thiết bị đích.

num_images_per_prompt : Số lượng đầu ra cần tạo cho mỗi prompt.

prompt_embeds : Nhúng (embeddings) của prompt đã được tính trước.

prompt_embeds_mask : Mặt nạ chú ý cho các nhúng đã tính toán trước.

max_sequence_length : Độ dài tối đa của chuỗi đầu ra.

template_type : Khóa mẫu prompt (`"image"` hoặc `"multiple_images"`).

**Trả về:**

Bộ ba (prompt_embeds, prompt_embeds_mask).

Mã hóa một prompt văn bản thành các embedding (đường dẫn chỉ văn bản).

`prompt_embeds` được tính trước sẽ bỏ qua hoàn toàn quá trình mã hóa.

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

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

**Tham số:**

prompt : Chuỗi prompt, có thể chứa các token `&amp;lt;image>\n`.

device : Thiết bị đích.

num_images_per_prompt : Số lượng đầu ra cần tạo cho mỗi prompt.

images : Các tensor pixel tương ứng với các token hình ảnh inline.

prompt_embeds : Nhúng (embeddings) của prompt đã được tính trước.

prompt_embeds_mask : Mặt nạ chú ý cho các nhúng đã tính toán trước.

template_type : Phải là `"multiple_images"`.

max_sequence_length : Nếu được đặt, cắt ngắn đầu ra xuống độ dài này (giữ lại `max_sequence_length` token cuối cùng).

**Trả về:**

Bộ ba (prompt_embeds, prompt_embeds_mask).

Mã hóa các prompt chứa các token hình ảnh inline thông qua bộ xử lý Qwen.

Các placeholder `&amp;lt;image>\n` trong mỗi chuỗi prompt sẽ được thay thế bằng các token đặc biệt của thị giác Qwen trước khi được đưa vào bộ mã hóa đa phương thức.

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

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

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

**Tham số:**

latent : Tensor latent thô từ `vae.encode`.

**Trả về:**

Tensor ẩn đã chuẩn hóa.

Chuẩn hóa các biến tiềm ẩn (latents) bằng cách sử dụng thống kê theo kênh (per-channel) từ cấu hình VAE.

Uses (latent - mean) / std khi VAE cung cấp `latents_mean` và `latents_std`; nếu không, sẽ dùng `scaling_factor` để nhân.

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

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

**Tham số:**

batch_size : Số lượng mẫu trong lô.

num_channels_latents : Kích thước chiều kênh tiềm ẩn từ cấu hình transformer.

height : Chiều cao không gian tính bằng pixel.

width : Độ rộng không gian tính bằng pixel.

video_length : Số lượng khung hình (1 cho suy luận ảnh).

dtype : Kiểu dữ liệu số dấu phẩy động cho tensor ẩn.

device : Thiết bị đích.

generator : Bộ tạo RNG cho việc lấy mẫu có thể tái lập.

latents : Nhiễu ban đầu tùy chọn do người dùng cung cấp cho slot đích. Khi `None`, nhiễu ngẫu nhiên sẽ được lấy mẫu.

image : Danh sách tùy chọn các hình ảnh tham chiếu PIL để mã hóa bằng VAE làm các vị trí điều kiện.

enable_denormalization : Có nên chuẩn hóa các latent tham chiếu đã mã hóa hay không.

**Trả về:**

Tuple của `(latents, image_latents)` trong đó `latents` có shape `(B, 1, C, T, H', W')` và `image_latents` có shape `(B, N_ref, C, T, H', W')` hoặc `None` khi không có hình ảnh tham chiếu nào được cung cấp.

**Lỗi:** `ValueError`

- `ValueError` -- Nếu `generator` là một danh sách có độ dài khác với `batch_size`.

Chuẩn bị tensor latent nhiễu ban đầu cho vòng lặp khử nhiễu.

## JoyImageEditPipelineOutput[[diffusers.JoyImageEditPipelineOutput]]

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

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

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

Lớp đầu ra cho các pipeline tạo JoyImageEdit.

