Technical Article

P-Q-Image-2.1-Remove-Object: Precise, Fast Object Remover

Today we are releasing P-Q-Image-2.1-Remove-Object, a set of open LoRA adapters for Qwen-Image 2.1 that do one job: remove the object you point at, and change nothing else. Draw a rough outline around the object, add a one-line instruction, and the object is gone, along with its shadow or reflection. The rest of the photo stays as it was and lines up with the input.

The few-step variants run on top of our distillation LoRA, P-Q-Image-2.1, in 6 or 8 steps without CFG, against the usual 40 steps for the base model.

Weights: PrunaAI/P-Q-Image-2.1-Remove-Object · ComfyUI: PrunaAI/P-Q-Image-2.1-Remove-Object-Comfy

Why a dedicated object removal LoRA?

If you ask an image editor to remove a person from a rainy street, the person usually goes. But if you look more closely, the reflection might still be on the wet pavement, the shop signs might have shifted a few pixels, and the colors of the whole frame are slightly off. The edit worked where you pointed, but at the same time quietly broke everything else.

That’s the failure most editing models never learned to avoid. Qwen-Image 2.1, in particular, noticeably changes the rest of the image and can even shift it so that the output no longer lines up with the input.

So we built a LoRA with two goals and no compromise between them:

  1. Remove the object and what it causes. Its shadow, its reflection, the shade it casts, with a believable background in the gap.

  2. Leave everything else alone. The rest of the image stays as it was and lines up with the input, so the result can be used as is.

How it works

  • Two ways to point at the object.

    • Outline (recommended): draw a rough red ring around the object in the photo and add the instruction. It removes any doubt about which object you mean, and it doesn’t need to be precise.

    • Prompt only: skip the outline and just describe the object (“remove the middle goat”). Handy when you don’t have a mask, or when the outline makes the model remove too much.

  • Three variants. Each is a LoRA (rank 32). The base variant runs on Qwen-Image 2.1 alone at 40 steps. The 6-step and 8-step variants run on top of our P-Q-Image-2.1 distillation LoRA and do in 6 or 8 steps what the base-model variant does in 40.

  • Inference. No CFG, the distillation LoRA at strength 1.0, and the removal LoRA at 0.7. The image is generated at the photo's own area, so nothing is cropped or stretched.

Variant

Runs on

Steps

Strength

6-step (recommended)

Qwen-Image 2.1 + P-Q-Image-2.1 6-step LoRA

6

0.7

8-step

Qwen-Image 2.1 + P-Q-Image-2.1 8-step LoRA

8

0.7

base

Qwen-Image 2.1 alone

40

1.0

How it performs

The LoRA is at its best when the fill has to continue something the eye checks at once: wet ground and its reflections, rows of flowers, a forest line, a lake surface.

  • Shadows and reflections go with the object, even when they are outside the outline.

  • Patterns continue through the gap: tulip rows, paving, leaves, wheat.

  • The rest of the image does not move. It stays as it was and lines up with the input.

  • Rough masks are fine. Outlines drawn generously, as a person would, work best.

"Remove the boathouse and the red canoes on the shore." Their reflection in the lake goes too. Photo: Sam Dawson, Unsplash.

"Remove the woman in the pink dress." The tulip rows run on through the gap, and her shadow goes too. Photo: The Now Time, Unsplash.

It is just as useful for everyday cleanup in real estate, events and studio photography:

"Remove the wooden ladder, the white stool and the potted plant." Photo: Andrew Neel, Unsplash.

"Remove the phone and the raised arm holding it." Photo: Aliaksei Antropau, Unsplash.

"Remove the two light stands, with the LED light and the boom arm." Photo: KOBU Agency, Unsplash.

All examples use the 6-step variant at ~2 MP in outline mode. See the model card for all 18 examples, with the outlined inputs.

Limitations

The outline weighs more than the instruction, so when the mask also covers part of something that should stay, that can go too. Keep the mask tight where other things overlap the object. Filled areas can come out slightly smoother than their surroundings, missing the photo's grain. The LoRA is trained for removal only, and it fills in what should plausibly be behind the object, which is not always what was actually there.

How to use it

Diffusers. Load Qwen-Image 2.1 normally, stack the distillation LoRA and the removal LoRA, and run 6 steps. draw_outline.py, shipped in the model repo, draws the outline exactly as expected by the model.

import torch
from PIL import Image
from diffusers import FlowMatchEulerDiscreteScheduler, QwenImage21Pipeline
from draw_outline import draw_outline, outline_prompt

VARIANT = "6-step"  # or "8-step", or "base"
DISTILL, LORA, SIGMAS, DYNAMIC_SHIFT, STRENGTH = {
    "6-step": ("p_q_image_2.1_6_steps_v0.2.safetensors", "p_q_image_2.1_remove_object_6_steps_v0.1.safetensors",
               [1.0, 0.925, 0.85, 0.625, 0.375, 0.125], True, 0.7),
    "8-step": ("p_q_image_2.1_8_steps_v0.1.safetensors", "p_q_image_2.1_remove_object_8_steps_v0.1.safetensors",
               [1.0, 14 / 15, 6 / 7, 10 / 13, 2 / 3, 6 / 11, 0.4, 2 / 9], False, 0.7),
    "base": (None, "p_q_image_2.1_remove_object_base_v0.1.safetensors", None, None, 1.0),
}[VARIANT]

pipe = QwenImage21Pipeline.from_pretrained("Qwen/Qwen-Image-2.1", torch_dtype=torch.bfloat16).to("cuda")
pipe.load_lora_weights("PrunaAI/P-Q-Image-2.1-Remove-Object", weight_name=LORA, adapter_name="removal")
if DISTILL:
    pipe.load_lora_weights("PrunaAI/P-Q-Image-2.1", weight_name=DISTILL, adapter_name="distill")
    pipe.set_adapters(["distill", "removal"], adapter_weights=[1.0, STRENGTH])
    pipe.scheduler = FlowMatchEulerDiscreteScheduler.from_config(
        pipe.scheduler.config, use_dynamic_shifting=DYNAMIC_SHIFT, shift=1.0, shift_terminal=None)
else:
    pipe.set_adapters(["removal"], adapter_weights=[STRENGTH])

photo = Image.open("photo.jpg").convert("RGB")
mask = Image.open("mask.png")  # white = the object to remove

edited = pipe(
    prompt=outline_prompt("Remove the red car on the left."),
    image=[draw_outline(photo, mask)],
    sigmas=SIGMAS,
    num_inference_steps=len(SIGMAS) if SIGMAS else 40,
    true_cfg_scale=1.0,
    output_resolution=round((photo.width * photo.height) ** 0.5),
    generator=torch.Generator("cuda").manual_seed(0),
).images[0].resize(photo.size, Image.LANCZOS)
edited.save("edited.png")
import torch
from PIL import Image
from diffusers import FlowMatchEulerDiscreteScheduler, QwenImage21Pipeline
from draw_outline import draw_outline, outline_prompt

VARIANT = "6-step"  # or "8-step", or "base"
DISTILL, LORA, SIGMAS, DYNAMIC_SHIFT, STRENGTH = {
    "6-step": ("p_q_image_2.1_6_steps_v0.2.safetensors", "p_q_image_2.1_remove_object_6_steps_v0.1.safetensors",
               [1.0, 0.925, 0.85, 0.625, 0.375, 0.125], True, 0.7),
    "8-step": ("p_q_image_2.1_8_steps_v0.1.safetensors", "p_q_image_2.1_remove_object_8_steps_v0.1.safetensors",
               [1.0, 14 / 15, 6 / 7, 10 / 13, 2 / 3, 6 / 11, 0.4, 2 / 9], False, 0.7),
    "base": (None, "p_q_image_2.1_remove_object_base_v0.1.safetensors", None, None, 1.0),
}[VARIANT]

pipe = QwenImage21Pipeline.from_pretrained("Qwen/Qwen-Image-2.1", torch_dtype=torch.bfloat16).to("cuda")
pipe.load_lora_weights("PrunaAI/P-Q-Image-2.1-Remove-Object", weight_name=LORA, adapter_name="removal")
if DISTILL:
    pipe.load_lora_weights("PrunaAI/P-Q-Image-2.1", weight_name=DISTILL, adapter_name="distill")
    pipe.set_adapters(["distill", "removal"], adapter_weights=[1.0, STRENGTH])
    pipe.scheduler = FlowMatchEulerDiscreteScheduler.from_config(
        pipe.scheduler.config, use_dynamic_shifting=DYNAMIC_SHIFT, shift=1.0, shift_terminal=None)
else:
    pipe.set_adapters(["removal"], adapter_weights=[STRENGTH])

photo = Image.open("photo.jpg").convert("RGB")
mask = Image.open("mask.png")  # white = the object to remove

edited = pipe(
    prompt=outline_prompt("Remove the red car on the left."),
    image=[draw_outline(photo, mask)],
    sigmas=SIGMAS,
    num_inference_steps=len(SIGMAS) if SIGMAS else 40,
    true_cfg_scale=1.0,
    output_resolution=round((photo.width * photo.height) ** 0.5),
    generator=torch.Generator("cuda").manual_seed(0),
).images[0].resize(photo.size, Image.LANCZOS)
edited.save("edited.png")
import torch
from PIL import Image
from diffusers import FlowMatchEulerDiscreteScheduler, QwenImage21Pipeline
from draw_outline import draw_outline, outline_prompt

VARIANT = "6-step"  # or "8-step", or "base"
DISTILL, LORA, SIGMAS, DYNAMIC_SHIFT, STRENGTH = {
    "6-step": ("p_q_image_2.1_6_steps_v0.2.safetensors", "p_q_image_2.1_remove_object_6_steps_v0.1.safetensors",
               [1.0, 0.925, 0.85, 0.625, 0.375, 0.125], True, 0.7),
    "8-step": ("p_q_image_2.1_8_steps_v0.1.safetensors", "p_q_image_2.1_remove_object_8_steps_v0.1.safetensors",
               [1.0, 14 / 15, 6 / 7, 10 / 13, 2 / 3, 6 / 11, 0.4, 2 / 9], False, 0.7),
    "base": (None, "p_q_image_2.1_remove_object_base_v0.1.safetensors", None, None, 1.0),
}[VARIANT]

pipe = QwenImage21Pipeline.from_pretrained("Qwen/Qwen-Image-2.1", torch_dtype=torch.bfloat16).to("cuda")
pipe.load_lora_weights("PrunaAI/P-Q-Image-2.1-Remove-Object", weight_name=LORA, adapter_name="removal")
if DISTILL:
    pipe.load_lora_weights("PrunaAI/P-Q-Image-2.1", weight_name=DISTILL, adapter_name="distill")
    pipe.set_adapters(["distill", "removal"], adapter_weights=[1.0, STRENGTH])
    pipe.scheduler = FlowMatchEulerDiscreteScheduler.from_config(
        pipe.scheduler.config, use_dynamic_shifting=DYNAMIC_SHIFT, shift=1.0, shift_terminal=None)
else:
    pipe.set_adapters(["removal"], adapter_weights=[STRENGTH])

photo = Image.open("photo.jpg").convert("RGB")
mask = Image.open("mask.png")  # white = the object to remove

edited = pipe(
    prompt=outline_prompt("Remove the red car on the left."),
    image=[draw_outline(photo, mask)],
    sigmas=SIGMAS,
    num_inference_steps=len(SIGMAS) if SIGMAS else 40,
    true_cfg_scale=1.0,
    output_resolution=round((photo.width * photo.height) ** 0.5),
    generator=torch.Generator("cuda").manual_seed(0),
).images[0].resize(photo.size, Image.LANCZOS)
edited.save("edited.png")

The pipeline needs sides that are multiples of 32, so the photo is generated at the nearest such size with the same area and resized back. This does not measurably shift or stretch the result.

ComfyUI. Prefer a visual workflow? Grab the ready-to-use workflows from PrunaAI/P-Q-Image-2.1-Remove-Object-Comfy, drop one into ComfyUI, and you are set. Load your photo, brush over the object you want gone, type what to remove, and hit Run. No need to draw the red outline yourself: a small custom node that ships with the workflows, Precise Removal Outline, turns your brush strokes into the outline the model expects and adds the matching instruction.

Prompting tips

  • The mask does not need to be exact. Generous outlines work best. Include the shadow or reflection if you want to be sure it goes, but keep the mask off anything that should stay.

  • Name the object as it appears, and say which one when there are several ("the man on the right", "the second cup from the left").

  • No mask? Use the prompt alone. Pass the plain photo and the instruction. If the outline makes the model remove too much, the prompt alone can do better.

  • Keep the default strength. At 0.5, some removals fail (the object stays, recolored). Above ~1.25, the model also removes neighboring objects.

Enjoy the Quality and Efficiency!


—-


License. P-Q-Image-2.1-Remove-Object is a derivative of Qwen-Image 2.1 and follows the Qwen RESEARCH LICENSE AGREEMENT. Review its use restrictions before using or redistributing the adapters. The few-step variants also need PrunaAI's distillation LoRA (PrunaAI/P-Q-Image-2.1), whose own terms apply. Built with Qwen. Qwen is licensed under the Qwen RESEARCH LICENSE AGREEMENT, Copyright (c) 2026 Hangzhou Tongyi Laboratory Technology Co., Ltd. All Rights Reserved. Example photos are from Unsplash. The Unsplash License does not cover people or brands in a photo: people appear only from behind or as silhouettes, and some photos show identifiable buildings or an incidental shop sign. See the model card for details.

Curious what Pruna can do for your models?

Whether you're running GenAI in production or exploring what's possible, Pruna makes it easier to move fast and stay efficient.

Curious what Pruna can do for your models?

Whether you're running GenAI in production or exploring what's possible, Pruna makes it easier to move fast and stay efficient.

Curious what Pruna can do for your models?

Whether you're running GenAI in production or exploring what's possible, Pruna makes it easier to move fast and stay efficient.