| import os |
| import requests |
| from typing import Optional |
| from PIL import Image |
| from io import BytesIO |
| from diffusers.utils import load_image |
| from diffusers.modular_pipelines import ( |
| PipelineState, |
| ModularPipelineBlocks, |
| InputParam, |
| OutputParam, |
| ) |
|
|
| IDEOGRAM_GENERATE_URL = "https://api.ideogram.ai/v1/ideogram-v3/generate" |
|
|
| class CreateCharacterImageBlock(ModularPipelineBlocks): |
| |
| @property |
| def inputs(self) -> list[InputParam]: |
| return [ |
| InputParam( |
| name="prompt", |
| type_hint=str, |
| description="Text prompt describing the desired image." |
| ), |
| InputParam( |
| name="character_image", |
| type_hint=Image.Image, |
| description="A single PIL Image to use as the character reference." |
| ), |
| ] |
|
|
| @property |
| def intermediate_outputs(self) -> list[OutputParam]: |
| return [ |
| OutputParam( |
| name="image", |
| type_hint=Image.Image, |
| description="Generated image." |
| ), |
| OutputParam( |
| name="image_url", |
| type_hint=str, |
| description="URL to the generated image." |
| ) |
| ] |
| |
| @staticmethod |
| def generate_image_with_character_reference( |
| prompt: str, |
| character_image: Image.Image, *, |
| style_type: str = "AUTO", |
| seed: Optional[int] = None, |
| resolution: Optional[str] = "1024x1024", |
| request_timeout: int = 60, |
| ) -> str: |
| """ |
| Generate an image using Ideogram's character reference feature. |
| |
| Args: |
| api_key: Your Ideogram API key. |
| prompt: Text prompt describing the desired image. |
| character_image: A single PIL Image to use as the character reference. |
| style_type: Ideogram style selection. "AUTO" lets the API decide. |
| seed: Random seed for reproducibility. |
| resolution: Image resolution. |
| request_timeout: Timeout for HTTP requests (seconds). |
| |
| Returns: |
| str: URL to the generated image (default) |
| |
| Raises: |
| RuntimeError on API errors or missing results. |
| """ |
| |
| api_key = os.getenv("IDEOGRAM_API_KEY") |
|
|
| if not api_key: |
| raise RuntimeError( |
| "IDEOGRAM_API_KEY is not set. Provide api_key param or set env var IDEOGRAM_API_KEY." |
| ) |
|
|
| headers = {"Api-Key": api_key} |
|
|
| |
| if not isinstance(character_image, Image.Image): |
| raise TypeError("character_image must be a PIL.Image.Image") |
|
|
| |
| if seed is not None: |
| if not (0 <= int(seed) <= 2147483647): |
| raise ValueError("seed must be between 0 and 2147483647 inclusive") |
|
|
| |
| with BytesIO() as buf: |
| character_image.save(buf, format="PNG") |
| buf.seek(0) |
| files = [ |
| ( |
| "character_reference_images", |
| ("character.png", buf, "image/png"), |
| ) |
| ] |
|
|
| data: dict = { |
| "prompt": prompt, |
| "style_type": style_type, |
| } |
| if seed is not None: |
| data["seed"] = str(int(seed)) |
| if resolution is not None: |
| data["resolution"] = resolution |
|
|
| resp = requests.post( |
| IDEOGRAM_GENERATE_URL, |
| headers=headers, |
| data=data, |
| files=files, |
| timeout=request_timeout, |
| ) |
| if not resp.ok: |
| raise RuntimeError(f"Ideogram API error {resp.status_code}: {resp.text}") |
|
|
| payload = resp.json() |
| |
| try: |
| image_url = payload["data"][0]["url"] |
| except Exception as e: |
| raise RuntimeError(f"Unexpected Ideogram response format: {payload}") from e |
|
|
| return image_url |
|
|
| def __call__(self, components: dict, state: PipelineState): |
| |
| block_state = self.get_block_state(state) |
|
|
| if isinstance(block_state.character_image, str): |
| character_image = load_image(block_state.character_image) |
| elif isinstance(block_state.character_image, Image.Image): |
| character_image = block_state.character_image |
| else: |
| raise ValueError(f"Invalid character image type: {type(block_state.character_image)}") |
|
|
| block_state.image_url = self.generate_image_with_character_reference( |
| prompt=block_state.prompt, |
| character_image=character_image, |
| ) |
| block_state.image = load_image(block_state.image_url) |
| self.set_block_state(state, block_state) |
| return components, state |