Spaces:
Sleeping
Sleeping
File size: 18,966 Bytes
c3649b4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 | """
Workflow Engine β Maps user requests to model pipelines.
Analyzes user intent via keyword matching and optional captioning,
then selects and executes the appropriate pipeline.
Usage:
engine = WorkflowEngine(registry, vram_manager)
result_image = engine.process_edit(image, "make me look like an anime character")
result_image = engine.process_generate("a futuristic city at sunset")
"""
import re
from typing import Optional
from PIL import Image
from .plugin_base import PluginCapability
from .plugin_registry import PluginRegistry
from .vram_manager import VRAMManager
from .pipeline import Pipeline, PipelineStep, PipelineContext
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Pre-defined Workflows
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
PIPELINES = {
# ββ Style Transfer (identity-preserving) ββ
"style_transfer": Pipeline("style_transfer", [
PipelineStep("nsfw_detection", optional=True),
PipelineStep("captioning", optional=True),
PipelineStep("face_detection", optional=True),
PipelineStep("face_recognition", optional=True),
PipelineStep("image_generation", config={"mode": "style_transfer"}),
PipelineStep("face_restoration", optional=True),
], description="Apply artistic styles while preserving identity"),
# ββ Anime / Cartoon Conversion ββ
"anime_conversion": Pipeline("anime_conversion", [
PipelineStep("nsfw_detection", optional=True),
PipelineStep("face_detection", optional=True),
PipelineStep("face_recognition", optional=True),
PipelineStep("anime_conversion"),
PipelineStep("face_restoration", optional=True),
PipelineStep("upscaling", config={"scale_factor": 1}, optional=True),
], description="Convert photo to anime/manga style"),
# ββ Background Swap ββ
"background_swap": Pipeline("background_swap", [
PipelineStep("nsfw_detection", optional=True),
PipelineStep("segmentation", config={"target": "foreground"}),
PipelineStep("depth_estimation", optional=True),
PipelineStep("image_generation", config={"mode": "background"}),
PipelineStep("compositing", optional=True),
], description="Replace image background"),
# ββ Outfit / Clothing Change ββ
"outfit_change": Pipeline("outfit_change", [
PipelineStep("nsfw_detection", optional=True),
PipelineStep("captioning", optional=True),
PipelineStep("face_detection", optional=True),
PipelineStep("face_recognition", optional=True),
PipelineStep("segmentation", config={"target": "clothing"}),
PipelineStep("inpainting"),
PipelineStep("face_restoration", optional=True),
], description="Change outfit or clothing"),
# ββ Age Transformation ββ
"age_change": Pipeline("age_change", [
PipelineStep("nsfw_detection", optional=True),
PipelineStep("face_detection"),
PipelineStep("face_recognition"),
PipelineStep("identity_preservation", config={"type": "age"}),
PipelineStep("face_restoration", optional=True),
], description="Age or de-age a person"),
# ββ Face Swap ββ
"face_swap": Pipeline("face_swap", [
PipelineStep("nsfw_detection", optional=True),
PipelineStep("face_detection"),
PipelineStep("face_recognition"),
PipelineStep("identity_preservation", config={"type": "swap"}),
PipelineStep("face_restoration", optional=True),
], description="Swap faces between images"),
# ββ Object Insertion ββ
"object_insertion": Pipeline("object_insertion", [
PipelineStep("nsfw_detection", optional=True),
PipelineStep("captioning", optional=True),
PipelineStep("depth_estimation", optional=True),
PipelineStep("inpainting", config={"mode": "insert"}),
], description="Insert objects into the scene"),
# ββ Relighting ββ
"relighting": Pipeline("relighting", [
PipelineStep("nsfw_detection", optional=True),
PipelineStep("segmentation", config={"target": "foreground"}, optional=True),
PipelineStep("depth_estimation", optional=True),
PipelineStep("image_generation", config={"mode": "relight"}),
], description="Change lighting conditions"),
# ββ Background Removal ββ
"background_removal": Pipeline("background_removal", [
PipelineStep("segmentation", config={"target": "foreground"}),
], description="Remove background (transparent)"),
# ββ Image Enhancement / Upscaling ββ
"enhance": Pipeline("enhance", [
PipelineStep("face_restoration", optional=True),
PipelineStep("upscaling"),
], description="Enhance and upscale image"),
# ββ General Edit (catch-all) ββ
"general_edit": Pipeline("general_edit", [
PipelineStep("nsfw_detection", optional=True),
PipelineStep("captioning", optional=True),
PipelineStep("image_generation", config={"mode": "edit"}),
], description="General-purpose image editing"),
# ββ Text-to-Image Generation ββ
"generate": Pipeline("generate", [
PipelineStep("nsfw_detection", config={"mode": "prompt"}, optional=True),
PipelineStep("image_generation", config={"mode": "txt2img"}),
], description="Generate image from text prompt"),
}
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Intent Detection β Keyword-based request classification
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Patterns: (regex_pattern, pipeline_name, priority)
# Higher priority = matched first
INTENT_PATTERNS = [
# Background operations
(r"\b(remove|delete|erase)\s*(the\s+)?(background|bg)\b", "background_removal", 100),
(r"\b(transparent|no\s*background|cut\s*out)\b", "background_removal", 95),
(r"\b(change|replace|swap|new)\s*(the\s+)?(background|bg|scene|place|location)\b", "background_swap", 90),
(r"\b(background|bg)\s*(to|with|into)\b", "background_swap", 85),
(r"\bput\s*(me|him|her|them|this)\s*(in|at|on)\b", "background_swap", 80),
# Anime / style conversion
(r"\b(anime|manga|japanese\s*anime)\b", "anime_conversion", 90),
(r"\b(chibi|kawaii)\b", "anime_conversion", 85),
(r"\b(cartoon|oil\s*paint|watercolor|pencil|sketch|pop\s*art|comic|cyberpunk|fantasy|3d\s*pixar|pixar|disney)\b", "style_transfer", 80),
(r"\b(style\s*of|in\s*the\s*style|artistic|art\s*style)\b", "style_transfer", 75),
# Outfit / clothing
(r"\b(change|replace|swap|new)\s*(the\s+)?(outfit|clothes|clothing|dress|shirt|suit|wear)\b", "outfit_change", 90),
(r"\b(wear|wearing|dressed\s*in|put\s*on)\b", "outfit_change", 80),
# Age transformation
(r"\b(older|younger|age|aged|aging|baby|child|elderly|teenager|teen|old\s*version|young\s*version)\b", "age_change", 85),
(r"\b(how\s*(would|will)\s*(i|they|he|she)\s*look\s*(when|at|in))\b", "age_change", 80),
# Face swap
(r"\b(face\s*swap|swap\s*face|swap\s*my\s*face|put\s*my\s*face)\b", "face_swap", 90),
# Object insertion
(r"\b(add|insert|place|put)\s+(a\s+|an\s+|the\s+)?\w+\s+(in|on|to|into|next\s*to|beside|near)\b", "object_insertion", 70),
# Relighting
(r"\b(relight|lighting|sunset\s*light|golden\s*hour|dramatic\s*light|studio\s*light|neon\s*light)\b", "relighting", 75),
(r"\b(change|add|make)\s*(the\s+)?(light|lighting|illumination)\b", "relighting", 70),
# Enhancement / upscaling
(r"\b(enhance|upscale|upres|super\s*resolution|sharpen|improve\s*quality|hd|4k|8k)\b", "enhance", 85),
(r"\b(fix|restore|clean|denoise|deblur)\s*(the\s+)?(face|image|photo|picture)\b", "enhance", 75),
# General edit (lowest priority catch-all)
(r"\b(edit|modify|transform|convert|make|turn)\b", "general_edit", 10),
]
# Compile regex patterns
_COMPILED_PATTERNS = [
(re.compile(pattern, re.IGNORECASE), pipeline_name, priority)
for pattern, pipeline_name, priority in INTENT_PATTERNS
]
def analyze_request(prompt: str, has_image: bool = True) -> str:
"""
Analyze a user's request and determine which pipeline to use.
Args:
prompt: The user's text instruction
has_image: Whether an input image was provided
Returns:
Pipeline name (e.g., "anime_conversion", "background_swap")
"""
if not prompt.strip():
return "enhance" if has_image else "generate"
if not has_image:
return "generate"
# Score each pattern
matches = []
for regex, pipeline_name, priority in _COMPILED_PATTERNS:
if regex.search(prompt):
matches.append((priority, pipeline_name))
if matches:
# Return highest priority match
matches.sort(reverse=True)
return matches[0][1]
# Default fallback
return "general_edit"
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Style Detection
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
STYLE_INSTRUCTIONS = {
"cartoon": "Transform this photo into a high-quality cartoon illustration with vibrant colors, clean outlines, and expressive features while preserving the person's identity and facial features exactly",
"oil_painting": "Transform this photo into a masterful oil painting with rich brush strokes, detailed textures, warm classical art tones, and canvas texture while preserving the person's identity exactly",
"pencil": "Transform this photo into a detailed pencil sketch with realistic graphite shading, crosshatching, fine lines, and paper texture while preserving the person's identity exactly",
"watercolor": "Transform this photo into a beautiful watercolor painting with soft brush strokes, color blending, fluid artistic washes, and delicate transparency while preserving the person's identity exactly",
"anime": "Transform this photo into a high-quality anime character illustration with vibrant colors, beautiful anime eyes, clean line art, and studio ghibli style while preserving the person's identity and facial structure exactly",
"pop_art": "Transform this photo into bold pop art style with vivid flat colors, screen print halftone aesthetic, high contrast, and Andy Warhol inspired design while preserving the person's identity exactly",
"sketch": "Transform this photo into a clean minimalist line sketch with precise thin lines, minimal shading, on a clean white background while preserving the person's identity exactly",
"3d_pixar": "Transform this photo into a 3D Pixar/Disney animation style character with smooth plastic-like skin, bright vibrant colors, cinematic lighting, and cute character design while preserving the person's identity exactly",
"comic": "Transform this photo into a comic book illustration with bold ink outlines, halftone dot shading, retro comic coloring, and dynamic superhero aesthetic while preserving the person's identity exactly",
"cyberpunk": "Transform this photo into cyberpunk style with neon glowing lights, futuristic high-tech aesthetic, dark synthwave atmosphere, and holographic effects while preserving the person's identity exactly",
"fantasy": "Transform this photo into fantasy art style with magical atmosphere, glowing ethereal particles, enchanted lighting, and mythical aesthetic while preserving the person's identity exactly",
"chibi": "Transform this photo into a cute chibi/kawaii character with oversized head, tiny body, cute expressions, bright colorful anime style while preserving the person's facial features",
}
STYLE_ALIASES = {
"cartoon": "cartoon", "oil painting": "oil_painting", "oil_painting": "oil_painting",
"pencil": "pencil", "watercolor": "watercolor", "anime": "anime",
"pop art": "pop_art", "pop_art": "pop_art", "sketch": "sketch",
"3d pixar": "3d_pixar", "3d_pixar": "3d_pixar", "pixar": "3d_pixar", "disney": "3d_pixar",
"comic": "comic", "cyberpunk": "cyberpunk", "fantasy": "fantasy", "chibi": "chibi",
}
def detect_style(prompt: str) -> Optional[str]:
"""Detect style name from prompt. Returns canonical style name or None."""
if not prompt:
return None
normalized = prompt.strip().lower()
# Direct match
if normalized in STYLE_INSTRUCTIONS:
return normalized
# Alias match
for keyword, canonical in STYLE_ALIASES.items():
if keyword in normalized:
return canonical
return None
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Workflow Engine β Main orchestrator
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class WorkflowEngine:
"""
High-level orchestrator that ties together request analysis,
pipeline selection, and execution.
"""
def __init__(self, registry: PluginRegistry, vram_manager: VRAMManager):
self.registry = registry
self.vram_manager = vram_manager
def process_edit(
self,
image: Image.Image,
instruction: str,
style: str = "",
pipeline_override: str = "",
**kwargs,
) -> Image.Image:
"""
Process an image edit request.
Args:
image: Input PIL Image
instruction: User's edit instruction
style: Optional style name override
pipeline_override: Force a specific pipeline (bypass auto-detection)
**kwargs: Extra context (seed, guidance_scale, etc.)
Returns:
Output PIL Image
"""
# Determine style
detected_style = detect_style(style or instruction)
# Determine pipeline
if pipeline_override and pipeline_override in PIPELINES:
pipeline_name = pipeline_override
elif detected_style and detected_style == "anime":
pipeline_name = "anime_conversion"
elif detected_style:
pipeline_name = "style_transfer"
else:
pipeline_name = analyze_request(instruction, has_image=True)
pipeline = PIPELINES.get(pipeline_name)
if pipeline is None:
pipeline = PIPELINES["general_edit"]
pipeline_name = "general_edit"
print(f"\nπ§ Request: \"{instruction}\"")
print(f" Detected style: {detected_style or 'none'}")
print(f" Selected pipeline: {pipeline_name}")
# Build context
# Construct the prompt for generation steps
if detected_style and detected_style in STYLE_INSTRUCTIONS:
generation_prompt = STYLE_INSTRUCTIONS[detected_style]
else:
generation_prompt = instruction
context = PipelineContext(
image=image,
prompt=generation_prompt,
style=detected_style or "",
**{k: v for k, v in kwargs.items() if hasattr(PipelineContext, k)},
)
# Execute pipeline
context = pipeline.execute(context, self.registry, self.vram_manager)
if context.nsfw_detected:
raise ValueError("Content blocked: NSFW content detected in the image or request.")
if context.output_image is not None:
return context.output_image
elif context.image is not None:
return context.image
else:
raise RuntimeError(f"Pipeline '{pipeline_name}' produced no output image. Errors: {context.errors}")
def process_generate(
self,
prompt: str,
**kwargs,
) -> Image.Image:
"""
Process a text-to-image generation request.
Args:
prompt: Text description of the image to generate
**kwargs: Extra context (seed, guidance_scale, num_steps, etc.)
Returns:
Generated PIL Image
"""
pipeline = PIPELINES["generate"]
print(f"\nπ§ Generate: \"{prompt}\"")
context = PipelineContext(
prompt=prompt,
**{k: v for k, v in kwargs.items() if hasattr(PipelineContext, k)},
)
context = pipeline.execute(context, self.registry, self.vram_manager)
if context.nsfw_detected:
raise ValueError("Content blocked: NSFW content detected in the prompt or generated image.")
if context.output_image is not None:
return context.output_image
else:
raise RuntimeError(f"Generation pipeline produced no output. Errors: {context.errors}")
def process_remove_background(self, image: Image.Image) -> Image.Image:
"""Remove background from image."""
pipeline = PIPELINES["background_removal"]
context = PipelineContext(image=image)
context = pipeline.execute(context, self.registry, self.vram_manager)
return context.output_image or context.image
def process_enhance(self, image: Image.Image, scale_factor: int = 2) -> Image.Image:
"""Enhance and upscale image."""
pipeline = PIPELINES["enhance"]
context = PipelineContext(image=image, scale_factor=scale_factor)
context = pipeline.execute(context, self.registry, self.vram_manager)
return context.output_image or context.image
def get_available_pipelines(self) -> dict:
"""Return all available pipelines and their descriptions."""
return {
name: {
"description": p.description,
"steps": [s.capability for s in p.steps],
}
for name, p in PIPELINES.items()
}
def get_status(self) -> dict:
"""Get engine status including VRAM and plugin info."""
return {
"vram": self.vram_manager.get_status(),
"plugins": [p.__dict__ for p in self.registry.list_plugins()],
"pipelines": list(PIPELINES.keys()),
"capabilities": self.registry.list_capabilities(),
}
|