feat(image-pipeline): ✨ Add identity-conditioning support to image generation pipeline stages
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
8d43da846e
commit
48ae9da41c
2 changed files with 57 additions and 22 deletions
|
|
@ -1120,18 +1120,27 @@ async def _load_ip_adapter_into_pipeline(
|
|||
if _ip_adapter_manager is None:
|
||||
_ip_adapter_manager = IPAdapterManager(device=device)
|
||||
|
||||
# Load IP-Adapter into pipeline
|
||||
# Using Plus Face for better facial identity preservation
|
||||
# (requires explicit ViT-H image encoder, configured in ip_adapter_manager)
|
||||
pipeline = await _ip_adapter_manager.load_ip_adapter(
|
||||
pipeline,
|
||||
model_id="ip-adapter-plus-face_sdxl",
|
||||
scale=identity.ip_adapter_scale,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"IP-Adapter loaded successfully (scale={identity.ip_adapter_scale})"
|
||||
)
|
||||
if identity.body_image is not None:
|
||||
# Dual-adapter path: face (Plus Face) + body (Plus general).
|
||||
# Face adapter preserves facial identity; body adapter conditions on
|
||||
# full-body shape/proportions from the reference photo.
|
||||
pipeline = await _ip_adapter_manager.load_dual_ip_adapters(
|
||||
pipeline,
|
||||
face_scale=identity.ip_adapter_scale,
|
||||
body_scale=identity.body_ip_adapter_scale,
|
||||
)
|
||||
logger.info(
|
||||
f"Dual IP-Adapters loaded "
|
||||
f"(face={identity.ip_adapter_scale}, body={identity.body_ip_adapter_scale})"
|
||||
)
|
||||
else:
|
||||
# Single face adapter path (original behaviour)
|
||||
pipeline = await _ip_adapter_manager.load_ip_adapter(
|
||||
pipeline,
|
||||
model_id="ip-adapter-plus-face_sdxl",
|
||||
scale=identity.ip_adapter_scale,
|
||||
)
|
||||
logger.info(f"Face IP-Adapter loaded (scale={identity.ip_adapter_scale})")
|
||||
|
||||
# Prepare InstantID face keypoint conditioning if enabled
|
||||
if identity.enable_instantid and identity.face_images:
|
||||
|
|
@ -1719,22 +1728,33 @@ class GenerateStage(PipelineStage):
|
|||
identity = context.identity_conditioning
|
||||
|
||||
if identity.face_images:
|
||||
# IP-Adapter expects 1 image per adapter (we have 1 adapter)
|
||||
# Use the first face image for IP-Adapter conditioning
|
||||
# For multiple reference images, the identity embedding already averages them
|
||||
gen_kwargs["ip_adapter_image"] = identity.face_images[0]
|
||||
if identity.body_image is not None:
|
||||
# Dual-adapter: pass [face_image, body_image] — one per loaded adapter.
|
||||
# Order must match the load order in load_dual_ip_adapters
|
||||
# (face first, body second).
|
||||
gen_kwargs["ip_adapter_image"] = [
|
||||
identity.face_images[0],
|
||||
identity.body_image,
|
||||
]
|
||||
logger.info(
|
||||
f"Dual IP-Adapter active: identity='{identity.identity_id}', "
|
||||
f"face_scale={identity.ip_adapter_scale}, "
|
||||
f"body_scale={identity.body_ip_adapter_scale}"
|
||||
)
|
||||
else:
|
||||
# Single face adapter (original path)
|
||||
gen_kwargs["ip_adapter_image"] = identity.face_images[0]
|
||||
logger.info(
|
||||
f"Face IP-Adapter active: identity='{identity.identity_id}', "
|
||||
f"using first of {len(identity.face_images)} face images, "
|
||||
f"scale={identity.ip_adapter_scale}"
|
||||
)
|
||||
|
||||
# Mark that identity conditioning is being used
|
||||
context.identity_used = True
|
||||
context.metadata["identity_used"] = True
|
||||
context.metadata["identity_id"] = identity.identity_id
|
||||
|
||||
logger.info(
|
||||
f"IP-Adapter active: identity='{identity.identity_id}', "
|
||||
f"using first of {len(identity.face_images)} face images, "
|
||||
f"scale={identity.ip_adapter_scale}"
|
||||
)
|
||||
|
||||
# Add seed if provided
|
||||
if seed is not None:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -175,8 +175,23 @@ class IdentityConditioningStage(PipelineStage):
|
|||
enable_instantid=request.enable_instantid,
|
||||
source_image_count=len(face_images),
|
||||
identity_service_url=self.identity_service_url,
|
||||
body_ip_adapter_scale=request.body_ip_adapter_scale,
|
||||
)
|
||||
|
||||
# Decode body reference image if provided
|
||||
if request.body_image_override:
|
||||
try:
|
||||
conditioning_data.body_image = self._decode_base64_image(
|
||||
request.body_image_override
|
||||
)
|
||||
logger.info(
|
||||
f"Body reference image decoded "
|
||||
f"({conditioning_data.body_image.size}, "
|
||||
f"scale={request.body_ip_adapter_scale})"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to decode body_image_override: {e}")
|
||||
|
||||
# Prepare InstantID conditioning if enabled (Phase 2)
|
||||
if request.enable_instantid:
|
||||
await self._prepare_instantid_conditioning(conditioning_data, face_images[0])
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue