# JoyAI-Image-Edit

[JoyAI-Image](https://github.com/jd-opensource/JoyAI-Image)는 이미지 이해, 텍스트-이미지 생성, 지시 기반 이미지 편집을 위한 통합 멀티모달 파운데이션 모델입니다. 8B 멀티모달 대규모 언어 모델(MLLM)과 16B 멀티모달 디퓨전 트랜스포머(MMDiT)를 결합합니다. JoyAI-Image의 핵심 원칙은 이해, 생성, 편집 간의 폐쇄 루프 협업입니다.

JoyAI-Image-Edit는 일반 이미지 편집과 함께 객체 이동, 객체 회전, 카메라 제어와 같은 공간 편집 기능을 지원합니다.

| 모델 | 설명 | 다운로드 |
|:-----:|:-----------:|:--------:|
| JoyAI-Image-Edit | 정밀하고 제어 가능한 공간 조작을 통한 지시 기반 이미지 편집 | [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")
```

## 공간 편집

JoyAI-Image는 **Object Move**, **Object Rotation**, **Camera Control**의 세 가지 공간 편집 프롬프트 패턴을 지원합니다. 최적의 결과를 얻으려면 아래 프롬프트 템플릿을 최대한 준수하세요. 자세한 내용은 [SpatialEdit](https://github.com/EasonXiao-888/SpatialEdit)을 참고하세요.

### 객체 이동

입력 이미지의 빨간색 박스로 표시된 지정된 영역으로 대상 객체를 이동합니다.

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

### 객체 회전

특정 정준 뷰로 객체를 회전합니다. 지원되는 `<view>` 값: `front`, `right`, `left`, `rear`, `front right`, `front left`, `rear right`, `rear left`.

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

### 카메라 제어

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

[소스](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L100)

JoyImage 아키텍처를 사용하여 이미지 편집을 위한 Diffusion 파이프라인입니다.

파이프라인은 Qwen3-VL 텍스트 인코더를 통해 텍스트와 이미지 조건을 인코딩하고, 3-D 트랜스포머로 잠재 변수를 디노이즈한 후, WAN VAE로 결과를 디코딩합니다.

모델 오프로딩 순서: text_encoder -> transformer -> vae.

#### __호출__[[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)
```

[소스](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L600)

**매개변수:**

prompt (*str* 또는 *List[str]*) : 생성을 안내할 프롬프트입니다.

height (*int*) : 생성된 출력의 픽셀 단위 높이.

width (*int*) : 생성된 출력의 픽셀 단위 폭.

image (*PipelineImageInput*, *선택 사항*) : 조건부 생성에 사용되는 참조 이미지. 제공되면 파이프라인은 `num_items=2`로 이미지 편집 모드에서 작동합니다.

num_inference_steps (*int*, *optional*, 기본값 40) : 디노이징 단계 수. 단계가 많을수록 일반적으로 품질이 향상되지만 추론 속도가 느려집니다.

timesteps (*List[int]*, *선택 사항*) : 노이즈 제거 과정에 사용할 커스텀 timesteps입니다. 값이 제공되면 `num_inference_steps`는 리스트의 길이에서 추론됩니다.

sigmas (*List[float]*, *선택 사항*) : 노이즈 제거 과정에 사용할 커스텀 시그마입니다. `timesteps`와 상호 배타적입니다.

guidance_scale (*float*, *optional*, 기본값 4.0) : Classifier-free guidance scale입니다.

negative_prompt (*str* 또는 *List[str]*, *선택*) : 원치 않는 콘텐츠를 억제하는 데 사용되는 네거티브 프롬프트입니다.

num_images_per_prompt (*int*, *optional*, 기본값 1) : 프롬프트당 생성될 샘플의 수.

generator (*torch.Generator* 또는 *List[torch.Generator]*, *선택 사항*) : 결정론적 샘플링을 위한 RNG 생성기입니다.

latents (*torch.Tensor*, *선택 사항*) : 대상 슬롯에 사용할 사전 생성된 노이즈 레이턴트입니다. 제공되지 않으면 가우시안 분포에서 샘플링됩니다. 특정 시작 노이즈 텐서로부터 생성을 시작하기 위한 시드로 사용할 수 있습니다.

prompt_embeds (*torch.Tensor*, *선택*) : 미리 계산된 프롬프트 임베딩. 제공하면 `prompt`를 생략할 수 있습니다.

prompt_embeds_mask (*torch.Tensor*, *선택 사항*) : `prompt_embeds`에 대한 어텐션 마스크입니다.

negative_prompt_embeds (*torch.Tensor*, *선택*) : 사전 계산된 negative prompt 임베딩.

negative_prompt_embeds_mask (*torch.Tensor*, *선택*) : `negative_prompt_embeds`에 대한 어텐션 마스크입니다.

output_type (*str*, *선택 사항*, 기본값 `"pil"`) : 출력 형식. 원시 latent를 반환하려면 `"latent"`를 전달하세요.

return_dict (*bool*, *optional*, 기본값 *True*) : [JoyImageEditPipelineOutput](/docs/diffusers/main/ko/api/pipelines/joyimage_edit#diffusers.JoyImageEditPipelineOutput) 또는 일반 텐서를 반환할지 여부.

callback_on_step_end (*Callable*, *PipelineCallback*, *MultiPipelineCallbacks*, *optional*) : 각 노이즈 제거 단계의 끝에서 호출되는 콜백이며, 시그니처는 `(self, step: int, timestep: int, callback_kwargs: Dict)`입니다.

callback_on_step_end_tensor_inputs (*List[str]*, *선택 사항*, 기본값 `["latents"]`) : `callback_on_step_end`의 `callback_kwargs`에 포함되는 Tensor 키입니다.

max_sequence_length (*int*, *optional*, 기본값 4096) : 프롬프트 인코딩을 위한 최대 시퀀스 길이입니다.

enable_denormalization (*bool*, *optional*, 기본값 *True*) : VAE 디코딩 전에 latents를 비정규화합니다.

**반환값:** [*~pipelines.joyimage.JoyImageEditPipelineOutput*] 또는 *torch.Tensor*

`return_dict`가 `True`인 경우, 생성된 이미지를 포함하는 파이프라인 출력 객체를 반환합니다. 그렇지 않으면 이미지 텐서를 직접 반환합니다.

참조 이미지와 텍스트 프롬프트를 조건으로 편집된 이미지를 생성합니다.

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

[소스](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L409)

**예외:** ``ValueError``

- ``ValueError`` -- 인수의 잘못된 조합이 있는 경우.

Forward pass 전에 파이프라인 입력을 검증합니다.

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

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

[소스](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L476)

**매개변수:**

latent : 정규화된 잠재 텐서.

**반환값:**

`vae.decode`가 기대하는 스케일의 잠재 텐서.

`normalize_latents`를 역으로 적용하여 원래의 latent 스케일을 복원합니다.

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

[소스](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L364)

**매개변수:**

prompt : 프롬프트 문자열 또는 프롬프트 문자열 목록.

device : 대상 장치.

num_images_per_prompt : 프롬프트당 생성할 출력의 수.

prompt_embeds : 사전 계산된 프롬프트 임베딩.

prompt_embeds_mask : 사전 계산된 임베딩에 대한 어텐션 마스크.

max_sequence_length : 최대 출력 시퀀스 길이.

template_type : 프롬프트 템플릿 키(`"image"` 또는 `"multiple_images"`).

**반환값:**

(prompt_embeds, prompt_embeds_mask)의 Tuple입니다.

텍스트 프롬프트를 임베딩으로 인코딩합니다(텍스트 전용 경로).

`prompt_embeds`가 사전 계산되어 있으면 인코딩을 완전히 우회합니다.

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

[소스](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L286)

**매개변수:**

prompt : `&amp;lt;image>\n` 토큰을 선택적으로 포함할 수 있는 프롬프트 문자열입니다.

device : 대상 장치.

num_images_per_prompt : 프롬프트당 생성할 출력의 수.

images : 인라인 이미지 토큰에 해당하는 픽셀 텐서.

prompt_embeds : 사전 계산된 프롬프트 임베딩.

prompt_embeds_mask : 사전 계산된 임베딩에 대한 어텐션 마스크.

template_type : `"multiple_images"`여야 합니다.

max_sequence_length : 설정된 경우, 출력을 이 길이로 잘라냅니다(마지막 `max_sequence_length` 토큰을 유지).

**반환값:**

(prompt_embeds, prompt_embeds_mask)의 Tuple입니다.

Qwen 프로세서를 사용하여 인라인 이미지 토큰이 포함된 프롬프트를 인코딩합니다.

각 프롬프트 문자열의 `&amp;lt;image>\n` 플레이스홀더는 멀티모달 인코더에 전달되기 전에 Qwen 비전 특수 토큰으로 대체됩니다.

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

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

[소스](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L447)

**매개변수:**

latent : `vae.encode`에서 생성된 원시 잠재 텐서.

**반환값:**

정규화된 잠재 텐서.

VAE 구성에서 채널별 통계를 사용하여 latents를 정규화합니다.

Uses VAE가 `latents_mean`과 `latents_std`를 노출하는 경우 (latent - mean) / std를 계산하고, 그렇지 않으면 `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)
```

[소스](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/joyimage/pipeline_joyimage_edit.py#L502)

**매개변수:**

batch_size : 배치 내 샘플 수.

num_channels_latents : Transformer 설정에서 가져온 잠재 채널 차원.

height : 픽셀 단위의 공간적 높이.

width : 픽셀 단위의 공간적 폭.

video_length : 프레임 수(이미지 추론의 경우 1).

dtype : 잠재 텐서의 부동 소수점 dtype.

device : 대상 장치.

generator : 재현 가능한 샘플링을 위한 RNG 생성기.

latents : 대상 슬롯에 사용할 선택적 사용자 지정 초기 노이즈. `None`인 경우 무작위 노이즈가 샘플링됩니다.

image : VAE 인코딩하여 조건 슬롯으로 사용할 PIL 참조 이미지의 선택적 목록.

enable_denormalization : 인코딩된 참조 latents를 정규화할지 여부.

**반환값:**

`(latents, image_latents)`의 Tuple이며, `latents`의 shape은 `(B, 1, C, T, H', W')`이고 `image_latents`의 shape은 `(B, N_ref, C, T, H', W')`입니다. 참조 이미지가 제공되지 않으면 `None`입니다.

**예외:** `ValueError`

- `ValueError` -- `generator`가 `batch_size`와 길이가 다른 리스트인 경우.

디노이징 루프를 위한 초기 노이즈가 포함된 잠재 텐서를 준비합니다.

## JoyImageEditPipelineOutput[[diffusers.JoyImageEditPipelineOutput]]

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

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

[소스](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/joyimage/pipeline_output.py#L11)

JoyImageEdit 생성 파이프라인의 출력 클래스입니다.

