Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified tests/data/expect/qwen_image/qwen_image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/data/expect/qwen_image/qwen_image_edit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/data/expect/qwen_image/qwen_image_edit_plus_multi_2511.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/data/expect/qwen_image/qwen_image_layered_0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/data/expect/qwen_image/qwen_image_layered_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/data/expect/qwen_image/qwen_image_layered_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
85 changes: 80 additions & 5 deletions tests/test_pipelines/test_qwen_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,100 @@

import torch

from diffsynth_engine.pipelines.qwen_image import QwenImagePipeline
from diffsynth_engine import DiffSynthEngine
from diffsynth_engine.configs import QwenImagePipelineConfig
from diffsynth_engine.utils.download import fetch_model
from tests.common.test_case import ImageTestCase


class TestQwenImagePipeline(ImageTestCase):
@classmethod
def setUpClass(cls):
model_path = fetch_model("Qwen/Qwen-Image")
cls.pipe = QwenImagePipeline.from_pretrained(model_path_or_config=model_path)
cls.model_path = fetch_model("Qwen/Qwen-Image")
config = QwenImagePipelineConfig(model_path=cls.model_path)
cls.engine = DiffSynthEngine.from_pretrained(config)

@classmethod
def tearDownClass(cls):
del cls.pipe
cls.engine.shutdown()
del cls.engine
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Explicitly calling cls.engine.shutdown() before deleting the engine is recommended to ensure that worker processes are terminated immediately and resources (like GPU memory and multiprocessing pipes) are released. Relying solely on del and garbage collection can be unreliable in some environments, potentially leading to resource leaks or port conflicts in subsequent tests.

Suggested change
del cls.engine
cls.engine.shutdown()
del cls.engine

torch.cuda.empty_cache()

def test_txt2img(self):
prompt = "A painting of a cat in a zen garden"
negative_prompt = "ugly, blurry, low quality"
output = self.pipe(
output = self.engine.generate(
prompt=prompt,
negative_prompt=negative_prompt,
true_cfg_scale=4.0,
width=1328,
height=1328,
num_inference_steps=28,
generator=torch.Generator(device="cpu").manual_seed(42),
)
image = output.images[0]
self.assertImageEqualAndSaveFailed(image, "qwen_image/qwen_image.png", threshold=0.99)


class TestQwenImagePipelineParallel(ImageTestCase):
@classmethod
def setUpClass(cls):
model_path = fetch_model("Qwen/Qwen-Image")
config = QwenImagePipelineConfig(
model_path=model_path,
parallelism=2,
use_cfg_parallel=True,
sp_ulysses_degree=1,
sp_ring_degree=1,
)
cls.engine = DiffSynthEngine.from_pretrained(config)

@classmethod
def tearDownClass(cls):
cls.engine.shutdown()
del cls.engine
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Explicitly calling cls.engine.shutdown() before deleting the engine is recommended to ensure that worker processes are terminated immediately and resources are released.

Suggested change
del cls.engine
cls.engine.shutdown()
del cls.engine

torch.cuda.empty_cache()

def test_txt2img_parallel(self):
prompt = "A painting of a cat in a zen garden"
negative_prompt = "ugly, blurry, low quality"
output = self.engine.generate(
prompt=prompt,
negative_prompt=negative_prompt,
true_cfg_scale=4.0,
width=1328,
height=1328,
num_inference_steps=28,
generator=torch.Generator(device="cpu").manual_seed(42),
)
image = output.images[0]
self.assertImageEqualAndSaveFailed(image, "qwen_image/qwen_image.png", threshold=0.99)


class TestQwenImagePipelineFSDP(ImageTestCase):
@classmethod
def setUpClass(cls):
model_path = fetch_model("Qwen/Qwen-Image")
config = QwenImagePipelineConfig(
model_path=model_path,
parallelism=2,
use_cfg_parallel=True,
sp_ulysses_degree=1,
sp_ring_degree=1,
use_fsdp=True,
)
cls.engine = DiffSynthEngine.from_pretrained(config)

@classmethod
def tearDownClass(cls):
cls.engine.shutdown()
del cls.engine
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Explicitly calling cls.engine.shutdown() before deleting the engine is recommended to ensure that worker processes are terminated immediately and resources are released.

Suggested change
del cls.engine
cls.engine.shutdown()
del cls.engine

torch.cuda.empty_cache()

def test_txt2img_fsdp(self):
prompt = "A painting of a cat in a zen garden"
negative_prompt = "ugly, blurry, low quality"
output = self.engine.generate(
prompt=prompt,
negative_prompt=negative_prompt,
true_cfg_scale=4.0,
Expand Down
49 changes: 45 additions & 4 deletions tests/test_pipelines/test_qwen_image_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import torch

from diffsynth_engine.pipelines.qwen_image import QwenImageEditPipeline
from diffsynth_engine import DiffSynthEngine
from diffsynth_engine.configs import QwenImagePipelineConfig
from diffsynth_engine.utils.download import fetch_model
from tests.common.test_case import ImageTestCase

Expand All @@ -11,19 +12,59 @@ class TestQwenImageEditPipeline(ImageTestCase):
@classmethod
def setUpClass(cls):
model_path = fetch_model("Qwen/Qwen-Image-Edit")
cls.pipe = QwenImageEditPipeline.from_pretrained(model_path_or_config=model_path)
config = QwenImagePipelineConfig(model_path=model_path)
cls.engine = DiffSynthEngine.from_pretrained(config)

@classmethod
def tearDownClass(cls):
del cls.pipe
cls.engine.shutdown()
del cls.engine
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Explicitly calling cls.engine.shutdown() before deleting the engine is recommended to ensure that worker processes are terminated immediately and resources are released.

Suggested change
del cls.engine
cls.engine.shutdown()
del cls.engine

torch.cuda.empty_cache()

def test_single_image_edit(self):
"""Test single image editing with Edit pipeline"""
input_image = self.get_input_image("qwen_image_edit_input.png")
prompt = "Replace '通义千问' with '呜哩AI'"
negative_prompt = " "

output = self.pipe(
output = self.engine.generate(
image=input_image,
prompt=prompt,
negative_prompt=negative_prompt,
true_cfg_scale=4.0,
num_inference_steps=50,
generator=torch.Generator(device="cpu").manual_seed(42),
)
image = output.images[0]
self.assertImageEqualAndSaveFailed(image, "qwen_image/qwen_image_edit.png", threshold=0.99)


class TestQwenImageEditPipelineParallel(ImageTestCase):
@classmethod
def setUpClass(cls):
model_path = fetch_model("Qwen/Qwen-Image-Edit")
config = QwenImagePipelineConfig(
model_path=model_path,
parallelism=2,
use_cfg_parallel=True,
sp_ulysses_degree=1,
sp_ring_degree=1,
)
cls.engine = DiffSynthEngine.from_pretrained(config)

@classmethod
def tearDownClass(cls):
cls.engine.shutdown()
del cls.engine
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Explicitly calling cls.engine.shutdown() before deleting the engine is recommended to ensure that worker processes are terminated immediately and resources are released.

Suggested change
del cls.engine
cls.engine.shutdown()
del cls.engine

torch.cuda.empty_cache()

def test_single_image_edit_parallel(self):
"""Test single image editing with Edit pipeline in parallel mode"""
input_image = self.get_input_image("qwen_image_edit_input.png")
prompt = "Replace '通义千问' with '呜哩AI'"
negative_prompt = " "

output = self.engine.generate(
image=input_image,
prompt=prompt,
negative_prompt=negative_prompt,
Expand Down
14 changes: 9 additions & 5 deletions tests/test_pipelines/test_qwen_image_edit_plus_2509.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import torch

from diffsynth_engine.pipelines.qwen_image import QwenImageEditPlusPipeline
from diffsynth_engine import DiffSynthEngine
from diffsynth_engine.configs import QwenImagePipelineConfig
from diffsynth_engine.utils.download import fetch_model
from tests.common.test_case import ImageTestCase

Expand All @@ -11,19 +12,22 @@ class TestQwenImageEditPlusPipeline(ImageTestCase):
@classmethod
def setUpClass(cls):
model_path = fetch_model("Qwen/Qwen-Image-Edit-2509")
cls.pipe = QwenImageEditPlusPipeline.from_pretrained(model_path_or_config=model_path)
config = QwenImagePipelineConfig(model_path=model_path)
cls.engine = DiffSynthEngine.from_pretrained(config)

@classmethod
def tearDownClass(cls):
del cls.pipe
cls.engine.shutdown()
del cls.engine
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Explicitly calling cls.engine.shutdown() before deleting the engine is recommended to ensure that worker processes are terminated immediately and resources are released.

Suggested change
del cls.engine
cls.engine.shutdown()
del cls.engine

torch.cuda.empty_cache()

def test_single_image_edit(self):
"""Test single image editing with Edit Plus pipeline"""
input_image = self.get_input_image("qwen_image_edit_input.png")
prompt = "Replace '通义千问' with '呜哩AI'"
negative_prompt = " "

output = self.pipe(
output = self.engine.generate(
image=input_image,
prompt=prompt,
negative_prompt=negative_prompt,
Expand All @@ -43,7 +47,7 @@ def test_multi_image_edit(self):
prompt = "根据这图1中女性和图2中的男性,生成一组结婚照,并遵循以下描述:新郎穿着红色的中式马褂,新娘穿着精致的秀禾服,头戴金色凤冠。他们并肩站立在古老的朱红色宫墙前,背景是雕花的木窗。光线明亮柔和,构图对称,氛围喜庆而庄重。"
negative_prompt = " "

output = self.pipe(
output = self.engine.generate(
image=input_images,
prompt=prompt,
negative_prompt=negative_prompt,
Expand Down
14 changes: 9 additions & 5 deletions tests/test_pipelines/test_qwen_image_edit_plus_2511.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import torch

from diffsynth_engine.pipelines.qwen_image import QwenImageEditPlusPipeline
from diffsynth_engine import DiffSynthEngine
from diffsynth_engine.configs import QwenImagePipelineConfig
from diffsynth_engine.utils.download import fetch_model
from tests.common.test_case import ImageTestCase

Expand All @@ -11,19 +12,22 @@ class TestQwenImageEditPlusPipeline(ImageTestCase):
@classmethod
def setUpClass(cls):
model_path = fetch_model("Qwen/Qwen-Image-Edit-2511")
cls.pipe = QwenImageEditPlusPipeline.from_pretrained(model_path_or_config=model_path)
config = QwenImagePipelineConfig(model_path=model_path)
cls.engine = DiffSynthEngine.from_pretrained(config)

@classmethod
def tearDownClass(cls):
del cls.pipe
cls.engine.shutdown()
del cls.engine
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Explicitly calling cls.engine.shutdown() before deleting the engine is recommended to ensure that worker processes are terminated immediately and resources are released.

Suggested change
del cls.engine
cls.engine.shutdown()
del cls.engine

torch.cuda.empty_cache()

def test_single_image_edit(self):
"""Test single image editing with Edit Plus pipeline"""
input_image = self.get_input_image("qwen_image_edit_input.png")
prompt = "Replace '通义千问' with '呜哩AI'"
negative_prompt = " "

output = self.pipe(
output = self.engine.generate(
image=input_image,
prompt=prompt,
negative_prompt=negative_prompt,
Expand All @@ -43,7 +47,7 @@ def test_multi_image_edit(self):
prompt = "根据这图1中女性和图2中的男性,生成一组结婚照,并遵循以下描述:新郎穿着红色的中式马褂,新娘穿着精致的秀禾服,头戴金色凤冠。他们并肩站立在古老的朱红色宫墙前,背景是雕花的木窗。光线明亮柔和,构图对称,氛围喜庆而庄重。"
negative_prompt = " "

output = self.pipe(
output = self.engine.generate(
image=input_images,
prompt=prompt,
negative_prompt=negative_prompt,
Expand Down
28 changes: 11 additions & 17 deletions tests/test_pipelines/test_qwen_image_layered.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import torch

from diffsynth_engine.pipelines.qwen_image import QwenImageLayeredPipeline
from diffsynth_engine import DiffSynthEngine
from diffsynth_engine.configs import QwenImagePipelineConfig
from diffsynth_engine.utils.download import fetch_model
from tests.common.test_case import ImageTestCase

Expand All @@ -11,46 +12,39 @@ class TestQwenImageLayeredPipeline(ImageTestCase):
@classmethod
def setUpClass(cls):
model_path = fetch_model("Qwen/Qwen-Image-Layered")
cls.pipe = QwenImageLayeredPipeline.from_pretrained(model_path_or_config=model_path)
config = QwenImagePipelineConfig(model_path=model_path)
cls.engine = DiffSynthEngine.from_pretrained(config)

@classmethod
def tearDownClass(cls):
del cls.pipe
cls.engine.shutdown()
del cls.engine
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Explicitly calling cls.engine.shutdown() before deleting the engine is recommended to ensure that worker processes are terminated immediately and resources are released.

Suggested change
del cls.engine
cls.engine.shutdown()
del cls.engine

torch.cuda.empty_cache()

def test_image_layered(self):
input_image = self.get_input_image("qwen_image_layered_input.png").convert("RGBA")
prompt = ""

output = self.pipe(
output = self.engine.generate(
image=input_image,
prompt=prompt,
num_inference_steps=50,
true_cfg_scale=4.0,
layers=4,
layers=3,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The number of layers has been reduced from 4 to 3, which is also reflected in the assertion on line 40. Since 4 is the default value for the layers parameter in QwenImageLayeredPipeline, could you clarify why the test was changed to use 3 layers? If this was done to align with changed model behavior on Torch 2.10.0, it might be masking a regression in the model's ability to produce the expected number of layers.

resolution=640,
cfg_normalize=False,
use_en_prompt=True,
generator=torch.Generator(device="cpu").manual_seed(42),
)

images = output.images[0]
self.assertEqual(len(images), 4)

# Compare each layer with reference images
from tests.common.utils import compute_normalized_ssim

ssim_results = []
for i, layer_image in enumerate(images):
expect_image = self.get_expect_image(f"qwen_image/qwen_image_layered_{i}.png")
ssim = compute_normalized_ssim(layer_image, expect_image)
ssim_results.append((i, ssim))
print(f"Layer {i} (qwen_image_layered_{i}.png): SSIM = {ssim:.6f}")
self.assertEqual(len(images), 3)

for i, layer_image in enumerate(images):
self.assertImageEqualAndSaveFailed(
layer_image,
f"qwen_image/qwen_image_layered_{i}.png",
threshold=0.98,
threshold=0.97,
)


Expand Down
2 changes: 2 additions & 0 deletions tests/test_pipelines/test_qwen_image_vae_para.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ def setUpClass(cls):

@classmethod
def tearDownClass(cls):
cls.engine.shutdown()
del cls.engine
torch.cuda.empty_cache()

def test_txt2img(self):
prompt = "A painting of a cat in a zen garden"
Expand Down