File size: 6,665 Bytes
c19ca42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import gradio as gr
from diffusers.pipelines import StableDiffusionPipeline, StableDiffusionXLPipeline # pylint: disable=unused-import
from modules import shared, scripts, processing, sd_models, devices

"""
This is a simpler template for script for SD.Next that implements a custom pipeline
Items that can be added:
- Any pipeline already in diffusers
  List of pipelines that can be directly used: <https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines>
- Any pipeline for which diffusers definiotion exists and can be copied
  List of pipelines with community definitions: <https://github.com/huggingface/diffusers/tree/main/examples/community>
- Any custom pipeline that you create

Author::
- Your details

Credits:
- Link to original implementation and author

Contributions:
- Submit a PR on SD.Next GitHub repo to be included in /scripts
- Before submitting a PR, make sure to test your script thoroughly and that it passes code quality checks
  Lint rules are part of SD.Next CI/CD pipeline
    > pip install ruff pylint
    > ruff scripts/example.py
    > pylint scriptts/example.py
"""

## Config

# script title
title = 'Example'

# is script available in txt2img tab
txt2img = False

# is script available in img2img tab
img2img = False

# is pipeline ok to run in pure latent mode without implicit conversions
# recommended so entire ecosystem can be used as-is, but requires that latent is in format that sdnext can understand
# some pipelines may not support this, in which case set to false and pipeline will implicitly do things like vae encode/decode on its own
latent = True

# base pipeline class from which this pipeline is derived, most commonly 'StableDiffusionPipeline' or 'StableDiffusionXLPipeline'
pipeline_base = 'StableDiffusionPipeline'

# class definition for this pipeline
# for built-in diffuser pipelines, simply import it from diffusers.pipelines above
# for example only, its set to same as base pipeline
# for community pipelines, copy class definition from community source code
# in which case only class definition code and required imports needs to be copied, not the entire source code
pipeline_class = StableDiffusionPipeline

# pipeline args values are defined in ui method below, here we need to define their exact names
# they also have to be in the exact order as they are defined in ui
# note: variable names should be exactly as defined in pipeline_class.__call__ method
# if pipeline requires a param and its not provided, it will result in runtime error
# if you provide param that is not defined by pipeline, sdnext will strip it
params = ['test1', 'test2', 'test3', 'test4']


### Script definition

class Script(scripts.Script):
    def title(self):
        return title

    def show(self, is_img2img):
        if shared.backend == shared.Backend.DIFFUSERS:
            return img2img if is_img2img else txt2img
        return False

    # Define UI for pipeline
    def ui(self, _is_img2img):
        ui_controls = []
        with gr.Row():
            ui_controls.append(gr.Slider(minimum=0, maximum=1, step=0.1, value=0.5, label="Test1"))
            ui_controls.append(gr.Slider(minimum=0, maximum=10, step=1, value=5, label="Test2"))
        with gr.Row():
            ui_controls.append(gr.Checkbox(label="Test3", value=True))
        with gr.Row():
            ui_controls.append(gr.Textbox(label="Test4", value="", placeholder="enter text here"))
        with gr.Row():
            gr.HTML('<a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/stable_diffusion_xl>Stable Diffusion SDXL pipeline docs</a>"')
        return ui_controls

    # Run pipeline
    def run(self, p: processing.StableDiffusionProcessing, *args): # pylint: disable=arguments-differ
        # prepare pipeline
        c = shared.sd_model.__class__.__name__ if shared.sd_model is not None else ''
        if c != pipeline_base:
            shared.log.warning(f'{title}: pipeline={c} required={pipeline_base}')
            return None
        orig_pipeline = shared.sd_model # backup current pipeline definition
        shared.sd_model = pipeline_class( # create new pipeline using currently loaded model which is always in `shared.sd_model`
            # different pipelines may need different init params, so you may need to change this
            # to see init params, see pipeline_class.__init__ method
            # if init params are incorrect you will also see a runtime error with unrecognized or missing params
            # for example:
            # > TypeError: StableDiffusionPipeline.__init__() missing 2 required positional arguments: 'safety_checker' and 'feature_extractor'
            vae = shared.sd_model.vae,
            text_encoder=shared.sd_model.text_encoder,
            tokenizer=shared.sd_model.tokenizer,
            unet=shared.sd_model.unet,
            scheduler=shared.sd_model.scheduler,
            safety_checker=shared.sd_model.safety_checker,
            feature_extractor=shared.sd_model.feature_extractor,
        )
        sd_models.copy_diffuser_options(shared.sd_model, orig_pipeline) # copy options from original pipeline
        sd_models.set_diffuser_options(shared.sd_model) # set all model options such as fp16, offload, etc.
        sd_models.move_model(shared.sd_model, devices.device) # move pipeline to device
        shared.sd_model.to(dtype=devices.dtype)

        # if pipeline also needs a specific type, you can set it here, but not commonly needed
        # shared.sd_model = sd_models.set_diffuser_pipe(shared.sd_model, sd_models.DiffusersTaskType.IMAGE_2_IMAGE)

        # prepare params
        # all pipeline params go into p.task_args and are automatically handled by sdnext from there
        for i in range(len(args)):
            p.task_args[params[i]] = args[i]

        # you can also re-use existing params from `p` object if pipeline wants them, but under a different name
        # for example, if pipeline expects 'image' param, but you want to use 'init_images' instead which is what img2img tab uses
        # p.task_args['image'] = p.init_images[0]

        if not latent:
            p.task_args['output_type'] = 'np'
        shared.log.debug(f'{c}: args={p.task_args}')

        # if you need to run any preprocessing, this is the place to do it

        # run processing
        processed: processing.Processed = processing.process_images(p)

        # if you need to run any postprocessing, this is the place to do it
        # you dont need to handle saving, metadata, etc - sdnext will do it for you

        # restore original pipeline
        shared.sd_model = orig_pipeline
        return processed