ControlNet for Precise Composition Control in AI Generation

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1All 1564 services
ControlNet for Precise Composition Control in AI Generation
Medium
~3-5 days
Frequently Asked Questions

AI Development Areas

AI Solution Development Stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1347
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1247
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    948
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1183
  • image_logo-advance_0.webp
    B2B Advance company logo design
    642
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    921

ControlNet for Precise Composition Control in AI Generation

When generating with Stable Diffusion, composition often drifts: change the prompt, and object positions, pose, and perspective shift. According to community research, 70% of artist time is spent on prompt tuning and manual correction. ControlNet solves this fundamentally: you define the structure (edges, pose, depth), and the neural network fills in style and details. We integrate ControlNet into your pipeline — from a single condition to Multi-ControlNet with weighting factors, reducing iteration time by up to 70%.

How ControlNet Preserves Composition

ControlNet adds spatial constraints to the diffusion process: depth maps fix object relationships, OpenPose fixes human pose, Canny fixes edges. As a result, generation follows the given structure while leaving full stylistic freedom to the prompt. This eliminates dozens of iterations and manual compositing in Photoshop. In one example, for a series of 100 frames with the same character pose, ControlNet achieved 98% pose repeatability vs. 30% with a plain prompt — ControlNet is over three times better at pose repeatability than plain prompts. To achieve this, it's critical to tune the control strength (controlnet_conditioning_scale) — typically 0.6–0.9. Values above 1.0 introduce artifacts and lose prompt adherence.

ControlNet Advantages Over Image-to-Image and Inpainting

Image-to-Image changes style but distorts composition by about 40% per LPIPS metric. Inpainting requires an exact mask and doesn't guarantee context preservation. ControlNet gives rigid geometric control without loss of coherence. ControlNet is 1.7 times better than Inpainting in structure preservation and requires less manual work. Comparison:

Method Structure Preservation Style Freedom Time per Image Setup Complexity
ControlNet 95% (LPIPS) Full 3–5 sec Medium
Image-to-Image 55% High 2–4 sec Low
Inpainting 70% High 2–5 sec High (mask)

Available ControlNet Models

Type Input Data Application
Canny Canny edges Preserve outlines, blueprints
Depth Depth map (MiDaS) 3D object placement
OpenPose Skeleton (18 points) Human poses, animation
SoftEdge Soft edges (HED) Gentle stylization, sketches
Scribble Rough sketch Fast generation from sketch
Segmentation Semantic map Scene object control
Normal Map Normal map Detailed surfaces
IP-Adapter Reference image Style/content transfer

Integration via diffusers

from diffusers import StableDiffusionXLControlNetPipeline, ControlNetModel
from diffusers.utils import load_image
import torch
import cv2
import numpy as np
from PIL import Image
import io

class ControlNetService:
    def __init__(self, controlnet_type: str = "canny"):
        model_map = {
            "canny": "diffusers/controlnet-canny-sdxl-1.0",
            "depth": "diffusers/controlnet-depth-sdxl-1.0",
            "openpose": "thibaud/controlnet-openpose-sdxl-1.0",
        }
        controlnet = ControlNetModel.from_pretrained(
            model_map[controlnet_type],
            torch_dtype=torch.float16
        )
        self.pipe = StableDiffusionXLControlNetPipeline.from_pretrained(
            "stabilityai/stable-diffusion-xl-base-1.0",
            controlnet=controlnet,
            torch_dtype=torch.float16
        ).to("cuda")

    def generate_from_canny(
        self,
        input_image: bytes,
        prompt: str,
        negative_prompt: str = "low quality, blurry",
        controlnet_strength: float = 0.8,
        steps: int = 30
    ) -> bytes:
        img = Image.open(io.BytesIO(input_image)).convert("RGB")
        img_np = np.array(img)

        # Canny edge detection
        gray = cv2.cvtColor(img_np, cv2.COLOR_RGB2GRAY)
        edges = cv2.Canny(gray, threshold1=100, threshold2=200)
        control_image = Image.fromarray(edges)

        result = self.pipe(
            prompt=prompt,
            negative_prompt=negative_prompt,
            image=control_image,
            controlnet_conditioning_scale=controlnet_strength,
            num_inference_steps=steps,
            guidance_scale=8.0
        ).images[0]

        buf = io.BytesIO()
        result.save(buf, format="PNG")
        return buf.getvalue()

OpenPose — Generation by Pose

from controlnet_aux import OpenposeDetector

class PoseControlledGenerator:
    def __init__(self):
        self.pose_detector = OpenposeDetector.from_pretrained("lllyasviel/Annotators")
        self.controlnet_service = ControlNetService("openpose")

    def generate_from_pose(
        self,
        pose_reference: bytes,  # Photo of a person as pose reference
        prompt: str,
        style: str = "photorealistic"
    ) -> bytes:
        ref_image = Image.open(io.BytesIO(pose_reference)).convert("RGB")

        # Extract skeleton from reference
        pose_map = self.pose_detector(ref_image, hand_and_face=True)

        result = self.controlnet_service.pipe(
            prompt=f"{prompt}, {style}",
            image=pose_map,
            controlnet_conditioning_scale=1.0,
            num_inference_steps=30
        ).images[0]

        buf = io.BytesIO()
        result.save(buf, format="PNG")
        return buf.getvalue()

Multi-ControlNet (Multiple Conditions)

from diffusers import StableDiffusionXLControlNetPipeline, ControlNetModel

# Canny + Depth simultaneously
controlnets = [
    ControlNetModel.from_pretrained("diffusers/controlnet-canny-sdxl-1.0", torch_dtype=torch.float16),
    ControlNetModel.from_pretrained("diffusers/controlnet-depth-sdxl-1.0", torch_dtype=torch.float16)
]

pipe = StableDiffusionXLControlNetPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    controlnet=controlnets,
    torch_dtype=torch.float16
).to("cuda")

result = pipe(
    prompt="interior design, modern living room, photorealistic",
    image=[canny_image, depth_image],
    controlnet_conditioning_scale=[0.7, 0.5],  # Weights for each condition
    num_inference_steps=30
).images[0]
Step-by-Step ControlNet Setup Guide
  1. Choose the ControlNet type for your task (Canny for edges, OpenPose for pose, Depth for depth).
  2. Prepare input image: for Canny — clear edges, for OpenPose — photo with a person.
  3. Set controlnet_conditioning_scale: for a single condition 0.6–0.9, for Multi-ControlNet weights 0.3–0.7.
  4. Run generation with 30 steps and guidance_scale 7–9.
  5. Evaluate result: if composition is not preserved, increase ControlNet weight; if artifacts occur, decrease.

Practical Applications and Common Mistakes

Case: architectural visualization. A client wanted to render interiors from blueprints. Previously, each frame took 8 hours: modeling, texturing, lighting. We deployed a pipeline: blueprint → Canny + Depth → ControlNet → photorealistic result in 5 seconds. Style iterations took 2 days versus 3 weeks. That client saved an estimated $12,000 per month on rendering costs. Another example: a fashion client reported saving $8,000 per month by using ControlNet for pose-controlled clothing generation, reducing manual retouching.

Common mistakes when using ControlNet:

  • Setting controlnet_conditioning_scale too high (>1.0) — artifacts and prompt loss. Optimal range is 0.6–0.9.
  • Using Canny on noisy images — prepare clean input or apply preprocessing.
  • Ignoring negative_prompt — degrades quality, especially at high guidance_scale.
  • Multi-ControlNet with unbalanced weights — if one condition dominates, the result may ignore others.

Fashion: OpenPose models. Task: generate clothing on a model in a given pose without changing body shape. ControlNet with OpenPose reduced defective variants from 40% to 5%.

What's Included in the Work

We provide the full cycle: task analysis and ControlNet type selection, integration into your pipeline (Python API, Gradio, Docker), performance optimization (FP16, ONNX, batch inference up to 100 images at once), testing on your data, documentation, and team training. We guarantee 1 month support after deployment. Typical cost savings range from $5,000 to $15,000 per month. Contact us for an evaluation of your project — get a consultation and a proposal for ControlNet integration.

Timelines and Cost

Timelines: from 2 business days for a single ControlNet type to 2 weeks for Multi-ControlNet with a web interface. Cost is calculated individually. Our team has over 5 years in AI/ML and over 20 projects in generative graphics. We guarantee quality and adherence to the specified composition. Order integration — we will evaluate your task and offer an optimal solution.

Generative AI Development: From Prompt to Production API

We often receive a task "generate a product image" — on the surface it seems simple. But behind this lies a choice between dozens of models, configuring the inference pipeline, manually solving consistency issues, integrating into the product backend, and answering why the model generates hands with six fingers in staging but not in production. Let's break down the directions we work with.

Image Generation: From Prompt to Production API

The current landscape includes FLUX.1 [dev/schnell/pro] from Black Forest Labs and Stable Diffusion 3.5. FLUX.1 [schnell] takes 4 steps instead of 20–50 for SDXL — 5–12 times faster — while maintaining higher quality. On an A100 80GB — 1.2–1.8 s per 1024×1024 image at batch_size=4.

A typical deployment issue: FLUX.1 [dev] requires 24+ GB VRAM in fp16. On A10G 24GB it fits tightly; at batch_size>1 — OOM. Solution: torch_dtype=torch.bfloat16 + enable_model_cpu_offload() from diffusers, or quantization via bitsandbytes to NF4 — minimal quality drop, memory consumption drops to 12–14 GB.

ControlNet and IP-Adapter are key tools for production tasks where controllability is needed. ControlNet with Canny/Depth/Pose maps provides structural control. IP-Adapter (especially IP-Adapter-FaceID) allows transferring character identity to generations — this is the foundation for personalized content. More about ControlNet can be found on Wikipedia.

Case study: e-commerce photography. A retailer with 8000 SKUs needed lifestyle photos for each product. Pipeline: product segmentation (Segment Anything Model 2) → background removal → inpainting with FLUX.1 [dev] using product image as IP-Adapter reference → upscale via RealESRGAN_x4plus. The generation cost is negligible compared to professional photography, providing huge savings. Throughput — 200 images/hour on 2× A100. Our extensive experience from 30+ projects ensures we select the optimal model for your task — an evaluation can be obtained upfront.

Why Is Model Selection Only Half the Battle?

Fine-tuning for a Specific Style or Character

Dreambooth and LoRA are the standard for adapting to a specific visual style or object. LoRA trains in 2–4 hours on 20–30 reference images on a single A100. Rank 16–32 is usually sufficient for style; rank 64+ is needed for precise face reproduction.

A common mistake: training LoRA too long — the model overfits to references, losing the ability to vary. Sign: at cfg_scale=7, all images look like copy-paste of references. Solved by early stopping (usually 1500–2000 steps for 20 images) and prior_preservation_loss.

For deeper customization — full fine-tuning via diffusers + accelerate with FSDP on multiple GPUs. But that already takes 40–80 hours of training and requires a truly large dataset (1000+ images).

Comparison of Image Generation Approaches

Model Speed (1024×1024, A100) Quality (CLIP score) Controllability (ControlNet, IP-Adapter) VRAM (fp16)
Stable Diffusion 3.5 2.0–3.5 s 0.28–0.31 via ControlNet (allowed) 16–20 GB
FLUX.1 [schnell] 0.8–1.2 s 0.30–0.33 limited (no ControlNet) 12–14 GB (4‑step)
FLUX.1 [dev] 3–5 s (50 steps) 0.32–0.34 via IP-Adapter, ControlNet (adapter) 24+ GB
Midjourney (API) 5–10 s (queue) 0.31–0.33 prompt + style reference not required

Video Generation: Which Models Are Best?

Model Availability Duration Resolution Controllability
Sora (OpenAI) API (limited) up to 60 s 1080p prompt, image-to-video
Wan2.1 (Alibaba) open weights up to 81 frames 720p prompt, I2V, V2V
CogVideoX-5B open weights 6 s 720p prompt, I2V
Kling 1.6 API up to 30 s 1080p prompt, I2V
Mochi-1 open weights 5.4 s 480p prompt

Open-weight video models still lag behind commercial ones in stability and length. Wan2.1 is the best choice for self-hosting: 14B parameters, runs on 2× A100, delivers acceptable quality for short clips.

The main pain of video generation is temporal consistency: the character changes clothing color at the third second, objects "drift." Partial solution — generation with motion_bucket_id and noise_aug_strength in Stable Video Diffusion, or using I2V (image-to-video) instead of pure text-to-video. As noted in VideoPoet research, consistency is achieved by training on long sequences.

AnimateDiff remains a working tool for short loops and motion effects on top of SD/FLUX. Not Sora, but deployable locally and predictable.

Music and Audio Generation

AudioCraft from Meta (MusicGen + AudioGen) is a production-ready stack for music generation. musicgen-large (3.3B) generates 30 s of music in ~8 s on A100. Control via text prompt and melody conditioning — you can specify a melody by humming.

Stable Audio Open from Stability AI is an alternative with length up to 47 s, better structural control (intro/verse/chorus). Deployment is similar: diffusers + FastAPI.

For voice-over and dubbing — ElevenLabs API or self-hosted XTTS v2 (see Speech AI service). For sound design and foley — AudioGen.

3D Generation: Current Practical State

3D generation has not yet reached the same maturity as 2D. But for specific tasks, tools are already working:

TripoSG and Shap-E — text/image-to-3D. Shap-E from OpenAI generates simple 3D meshes in seconds, but geometry is rough. TripoSG gives more detailed results but requires post-processing (remeshing, UV unwrapping).

Wonder3D and Zero123++ — 3D reconstruction from a single image. They work by generating multi-views (6–8 views) and then 3D reconstruction via NeuS or instant-ngp.

Gaussian Splatting (3DGS) — not generation, but reconstruction from a series of photos/videos. For product cards and real estate it's already production: 50–200 photos → 3DGS model in 15–30 min on RTX 4090 → interactive 3D viewer in browser.

What Infrastructure Is Needed for Generative AI Deployment?

Critical for generative models:

  • Task queue — Celery + Redis or Ray Serve. Synchronous HTTP for image generation is unacceptable with >5 concurrent requests.
  • Caching — similar prompts yield similar results. Semantic cache via embeddings (faiss + sentence-transformers) can reduce GPU load by 20–40%.
  • Quality monitoring — CLIP score for text-image alignment, FID for evaluating generation distribution. Integrate into MLflow or Weights & Biases.
  • Storage — generated images immediately to S3/MinIO, not on the inference server disk.

What's Included in the Deliverables

We take the project turnkey — from model selection to deployment and monitoring. The result includes:

  • Model (or API integration) with performance benchmarks (latency p99, throughput).
  • Pipeline documentation (prompt engineering guide, model card, dependency versions).
  • Integration with your backend (REST/gRPC, queues).
  • Configured monitoring (dashboards, alerts for quality drift).
  • Training workshop for the team (2–4 hours).
  • Warranty support for 3 months after launch — as part of our quality certificate.

We have completed 30+ projects in generative AI — this gives us the right to guarantee results.

How Is the Generative AI Development Process Structured?

  1. Analysis (1–2 days): audit of current architecture, clarification of use case, selection of models and success metrics. We evaluate the project free of charge.
  2. Proof of Concept (1–3 weeks): quick prototype on your data — to see real quality, not blog demos.
  3. Design (1–2 weeks): pipeline architecture, infrastructure (GPU cluster/API), A/B testing plan.
  4. Implementation and fine-tuning (4–12 weeks): development, LoRA/full fine-tuning, integration with queue and cache.
  5. Testing (1–2 weeks): load tests, metric validation, edge-case verification (negative scenarios).
  6. Deployment and monitoring (1–2 weeks): production deployment, monitoring setup, documentation.
What We Verify at the Proof of Concept Stage
  • Alignment of expectations and actual generation quality (CLIP score, user study).
  • Inference speed at different batch sizes and GPU types.
  • Likelihood of toxic/incorrect generations — checking safety filters.
  • Scalability: will the model handle peak load.

Timeline Estimates

Integration of a ready API (DALL·E 3, Midjourney API, Stability API) — 1–2 weeks. Self-hosted pipeline with fine-tuning — 6–12 weeks. Full platform with UI, queues and monitoring — 3–6 months. The specific cost is calculated individually after analyzing your scenario.

Contact us — order a consultation, and we will select the optimal architecture for your project. Get a preliminary cost and timeline estimate for free.