Diffusers documentation

Criando Blocos Personalizados

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Criando Blocos Personalizados

Os ModularPipelineBlocks são os blocos de construção fundamentais de um ModularPipeline. Você pode criar blocos personalizados definindo suas entradas, saídas e lógica de computação. Este guia demonstra como criar e usar um bloco personalizado.

Explore a coleção Modular Diffusers Custom Blocks para blocos personalizados oficiais.

Estrutura do Projeto

O projeto do seu bloco personalizado deve usar a seguinte estrutura:

.
├── block.py
└── modular_config.json
  • block.py contém a implementação do bloco personalizado
  • modular_config.json contém os metadados necessários para carregar o bloco

Início rápido com Template

A maneira mais rápida de criar um bloco personalizado é começar a partir do nosso modelo. O modelo fornece uma estrutura de projeto pré-configurada com os arquivos block.py e modular_config.json, além de exemplos comentados mostrando como definir componentes, entradas, saídas e o método __call__—para que você possa focar na sua lógica personalizada em vez da configuração padrão.

Baixe o modelo

from diffusers import ModularPipelineBlocks

model_id = "diffusers/custom-block-template"
local_dir = model_id.split("/")[-1]

blocks = ModularPipelineBlocks.from_pretrained(
    model_id, 
    trust_remote_code=True, 
    local_dir=local_dir
)

Isso salva os arquivos de modelo em custom-block-template/ localmente, ou você pode usar local_dir para salvar em um local específico.

Editar localmente

Abra block.py e implemente seu bloco personalizado. O modelo inclui exemplos comentados mostrando como definir cada propriedade. Veja o exemplo do Florence-2 abaixo para uma implementação completa.

Teste seu bloco

from diffusers import ModularPipelineBlocks

blocks = ModularPipelineBlocks.from_pretrained(local_dir, trust_remote_code=True)
pipeline = blocks.init_pipeline()
output = pipeline(...)  # your inputs here

Upload para o Hub

pipeline.save_pretrained(local_dir, repo_id="your-username/your-block-name", push_to_hub=True)

Exemplo: Anotador de Imagens Florence-2

Este exemplo cria um bloco personalizado com Florence-2 para processar uma imagem de entrada e gerar uma máscara para inpainting.

Definir componentes

Defina os componentes de que o bloco precisa, Florence2ForConditionalGeneration e seu processador. Ao definir os componentes, especifique o name (como você o acessará no código), o type_hint (a classe do modelo) e o pretrained_model_name_or_path (de onde carregar os pesos).

# Inside block.py
from diffusers.modular_pipelines import ModularPipelineBlocks, ComponentSpec
from transformers import AutoProcessor, Florence2ForConditionalGeneration


class Florence2ImageAnnotatorBlock(ModularPipelineBlocks):

    @property
    def expected_components(self):
        return [
            ComponentSpec(
                name="image_annotator",
                type_hint=Florence2ForConditionalGeneration,
                pretrained_model_name_or_path="florence-community/Florence-2-base-ft",
            ),
            ComponentSpec(
                name="image_annotator_processor",
                type_hint=AutoProcessor,
                pretrained_model_name_or_path="florence-community/Florence-2-base-ft",
            ),
        ]

Defina entradas e saídas

As entradas incluem a imagem, a tarefa de anotação e o prompt. As saídas incluem a máscara gerada e as anotações.

from typing import List, Union
from PIL import Image
from diffusers.modular_pipelines import InputParam, OutputParam


class Florence2ImageAnnotatorBlock(ModularPipelineBlocks):

    # ... expected_components from above ...

    @property
    def inputs(self) -> List[InputParam]:
        return [
            InputParam(
                "image",
                type_hint=Union[Image.Image, List[Image.Image]],
                required=True,
                description="Image(s) to annotate",
            ),
            InputParam(
                "annotation_task",
                type_hint=str,
                default="<REFERRING_EXPRESSION_SEGMENTATION>",
                description="Annotation task to perform (e.g., <OD>, <CAPTION>, <REFERRING_EXPRESSION_SEGMENTATION>)",
            ),
            InputParam(
                "annotation_prompt",
                type_hint=str,
                required=True,
                description="Prompt to provide context for the annotation task",
            ),
            InputParam(
                "annotation_output_type",
                type_hint=str,
                default="mask_image",
                description="Output type: 'mask_image', 'mask_overlay', or 'bounding_box'",
            ),
        ]

    @property
    def intermediate_outputs(self) -> List[OutputParam]:
        return [
            OutputParam(
                "mask_image",
                type_hint=Image.Image,
                description="Inpainting mask for the input image",
            ),
            OutputParam(
                "annotations",
                type_hint=dict,
                description="Raw annotation predictions",
            ),
            OutputParam(
                "image",
                type_hint=Image.Image,
                description="Annotated image",
            ),
        ]

Implemente o método __call__

O método __call__ contém a lógica do bloco. Acesse as entradas via block_state, execute seu cálculo e defina as saídas de volta em block_state.

import torch
from diffusers.modular_pipelines import PipelineState


class Florence2ImageAnnotatorBlock(ModularPipelineBlocks):

    # ... expected_components, inputs, intermediate_outputs from above ...

    @torch.no_grad()
    def __call__(self, components, state: PipelineState) -> PipelineState:
        block_state = self.get_block_state(state)
        
        images, annotation_task_prompt = self.prepare_inputs(
            block_state.image, block_state.annotation_prompt
        )
        task = block_state.annotation_task
        fill = block_state.fill
        
        annotations = self.get_annotations(
            components, images, annotation_task_prompt, task
        )
        block_state.annotations = annotations
        if block_state.annotation_output_type == "mask_image":
            block_state.mask_image = self.prepare_mask(images, annotations)
        else:
            block_state.mask_image = None

        if block_state.annotation_output_type == "mask_overlay":
            block_state.image = self.prepare_mask(images, annotations, overlay=True, fill=fill)

        elif block_state.annotation_output_type == "bounding_box":
            block_state.image = self.prepare_bounding_boxes(images, annotations)

        self.set_block_state(state, block_state)

        return components, state
    
    # Helper methods for mask/bounding box generation...

Veja a implementação completa em diffusers/Florence2-image-Annotator.

Usando Blocos Personalizados

Carregue um bloco personalizado com from_pretrained() e defina trust_remote_code=True.

import torch
from diffusers import ModularPipeline
from diffusers.utils import load_image

# Load the Florence-2 annotator pipeline
image_annotator = ModularPipeline.from_pretrained(
    "diffusers/Florence2-image-Annotator",
    trust_remote_code=True
)

# Check the docstring to see inputs/outputs
print(image_annotator.blocks.doc)

Use o bloco para gerar uma máscara:

image_annotator.load_components(dtype=torch.bfloat16)
image_annotator.to("cuda")  # or "mps", "xpu", "cpu"

image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg")
image = image.resize((1024, 1024))
prompt = ["A red car"]
annotation_task = "<REFERRING_EXPRESSION_SEGMENTATION>"
annotation_prompt = ["the car"]

mask_image = image_annotator_node(
    prompt=prompt,
    image=image,
    annotation_task=annotation_task,
    annotation_prompt=annotation_prompt,
    annotation_output_type="mask_image",
).images
mask_image[0].save("car-mask.png")

Componha-o com outros blocos para criar um novo pipeline:

# Get the annotator block
annotator_block = image_annotator.blocks

# Get an inpainting workflow and insert the annotator at the beginning
inpaint_blocks = ModularPipeline.from_pretrained("Qwen/Qwen-Image").blocks.get_workflow("inpainting")
inpaint_blocks.sub_blocks.insert("image_annotator", annotator_block, 0)

# Initialize the combined pipeline
pipe = inpaint_blocks.init_pipeline()
pipe.load_components(dtype=torch.float16, device="cuda")  # or "mps", "xpu", "cpu"

# Now the pipeline automatically generates masks from prompts
output = pipe(
    prompt=prompt,
    image=image,
    annotation_task=annotation_task,
    annotation_prompt=annotation_prompt,
    annotation_output_type="mask_image",
    num_inference_steps=35,
    guidance_scale=7.5,
    strength=0.95,
    output="images"
)
output[0].save("florence-inpainting.png")

Editando blocos personalizados

Edite blocos personalizados baixando-os localmente. Este é o mesmo fluxo de trabalho do Início rápido com Template, mas partindo de um bloco existente em vez do template.

Use o argumento local_dir para baixar um bloco personalizado para uma pasta específica:

from diffusers import ModularPipelineBlocks

# Download to a local folder for editing
annotator_block = ModularPipelineBlocks.from_pretrained(
    "diffusers/Florence2-image-Annotator",
    trust_remote_code=True,
    local_dir="./my-florence-block"
)

Qualquer alteração feita nos arquivos de bloco nesta pasta será refletida quando você carregar o bloco novamente. Quando estiver pronto para compartilhar suas alterações, faça o upload para um novo repositório:

pipeline = annotator_block.init_pipeline()
pipeline.save_pretrained("./my-florence-block", repo_id="your-username/my-custom-florence", push_to_hub=True)

Próximas etapas

Conheça os tipos de bloco
Use no Mellon
Explore blocos existentes

Este guia abordou a criação de um único bloco personalizado. Aprenda a compor vários blocos juntos:

Dependências

Declarar as dependências de pacotes em blocos personalizados evita erros de importação em tempo de execução. O Diffusers valida as dependências e retorna um aviso se um pacote estiver ausente ou incompatível.

Defina um atributo _requirements na sua classe de bloco, mapeando nomes de pacotes para especificadores de versão.

from diffusers.modular_pipelines import PipelineBlock

class MyCustomBlock(PipelineBlock):
    _requirements = {
        "transformers": ">=4.44.0",
        "sentencepiece": ">=0.2.0"
    }

Quando há blocos com requisitos diferentes, o Diffusers mescla seus requisitos.

from diffusers.modular_pipelines import SequentialPipelineBlocks

class BlockA(PipelineBlock):
    _requirements = {"transformers": ">=4.44.0"}
    # ...

class BlockB(PipelineBlock):
    _requirements = {"sentencepiece": ">=0.2.0"}
    # ...

pipe = SequentialPipelineBlocks.from_blocks_dict({
    "block_a": BlockA,
    "block_b": BlockB,
})

Quando este bloco é salvo com save_pretrained(), os requisitos são salvos no arquivo modular_config.json. Quando este bloco é carregado, o Diffusers verifica cada requisito em relação ao ambiente atual. Se houver uma incompatibilidade ou um pacote não for encontrado, o Diffusers retorna o seguinte aviso.

# missing package
xyz-package was specified in the requirements but wasn't found in the current environment.

# version mismatch
xyz requirement 'specific-version' is not satisfied by the installed version 'actual-version'. Things might work unexpected.
Update on GitHub