From e6d148e72945813d668aba092c8ece4e90a6782e Mon Sep 17 00:00:00 2001 From: lanvent Date: Tue, 14 Mar 2023 00:49:28 +0800 Subject: [PATCH] plugins: add sdwebui(stable diffusion) plugin --- plugins/sdwebui/__init__.py | 0 plugins/sdwebui/config.json.template | 67 +++++++++++++++++++++ plugins/sdwebui/readme.md | 63 ++++++++++++++++++++ plugins/sdwebui/sdwebui.py | 88 ++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+) create mode 100644 plugins/sdwebui/__init__.py create mode 100644 plugins/sdwebui/config.json.template create mode 100644 plugins/sdwebui/readme.md create mode 100644 plugins/sdwebui/sdwebui.py diff --git a/plugins/sdwebui/__init__.py b/plugins/sdwebui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/sdwebui/config.json.template b/plugins/sdwebui/config.json.template new file mode 100644 index 0000000..4fca569 --- /dev/null +++ b/plugins/sdwebui/config.json.template @@ -0,0 +1,67 @@ +{ + "start":{ + "host" : "127.0.0.1", + "port" : 7860 + }, + "defaults": { + "params": { + "sampler_name": "DPM++ 2M Karras", + "steps": 20, + "width": 512, + "height": 512, + "cfg_scale": 7, + "prompt":"masterpiece, best quality", + "negative_prompt": "(low quality, worst quality:1.4),(bad_prompt:0.8), (monochrome:1.1), (greyscale)", + "enable_hr": false, + "hr_scale": 2, + "hr_upscaler": "Latent", + "hr_second_pass_steps": 15, + "denoising_strength": 0.7 + }, + "options": { + "sd_model_checkpoint": "perfectWorld_v2Baked" + } + }, + "rules": [ + { + "keywords": [ + "横版", + "壁纸" + ], + "params": { + "width": 640, + "height": 384 + } + }, + { + "keywords": [ + "竖版" + ], + "params": { + "width": 384, + "height": 640 + } + }, + { + "keywords": [ + "高清" + ], + "params": { + "enable_hr": true, + "hr_scale": 1.6 + } + }, + { + "keywords": [ + "二次元" + ], + "params": { + "negative_prompt": "(low quality, worst quality:1.4),(bad_prompt:0.8), (monochrome:1.1), (greyscale)", + "prompt": "masterpiece, best quality" + }, + "options": { + "sd_model_checkpoint": "meinamix_meinaV8" + } + } + ] +} \ No newline at end of file diff --git a/plugins/sdwebui/readme.md b/plugins/sdwebui/readme.md new file mode 100644 index 0000000..ef325cd --- /dev/null +++ b/plugins/sdwebui/readme.md @@ -0,0 +1,63 @@ +本插件用于将画图请求转发给stable diffusion webui +使用前先安装stable diffusion webui,并在它的启动参数中添加 "--api" +具体参考(https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/API) + +请**安装**本插件的依赖包```webuiapi``` +``` + ```pip install webuiapi``` +``` +请将```config.json.template```复制为```config.json```,并修改其中的参数和规则 + +用户的画图请求格式为: +``` + <画图触发词><关键词1> <关键词2> ... <关键词n>: +``` +本插件会对画图触发词后的关键词进行逐个匹配,如果触发了规则中的关键词,则会在画图请求中重载对应的参数。 +规则会按顺序匹配,每个关键词最多匹配到1次,如果有重复的参数,则以最后一个为准: +第一个"**:**"号之后的内容会作为附加的**prompt**,接在最终的prompt后 + +例如: 画横版 高清 二次元:cat +会触发三个关键词 "横版", "高清", "二次元",prompt为"cat" +若默认参数是: +``` + "width": 512, + "height": 512, + "enable_hr": false, + "prompt": "8k" + "negative_prompt": "nsfw", + "sd_model_checkpoint": "perfectWorld_v2Baked" +``` + +"横版"触发的规则参数为: +``` + "width": 640, + "height": 384, +``` +"高清"触发的规则参数为: +``` + "enable_hr": true, + "hr_scale": 1.6, +``` +"二次元"触发的规则参数为: +``` + "negative_prompt": "(low quality, worst quality:1.4),(bad_prompt:0.8), (monochrome:1.1), (greyscale)", + "steps": 20, + "prompt": "masterpiece, best quality", + + "sd_model_checkpoint": "meinamix_meinaV8" +``` +最后将第一个":"后的内容cat连接在prompt后,得到最终参数为: +``` + "width": 640, + "height": 384, + "enable_hr": true, + "hr_scale": 1.6, + "negative_prompt": "(low quality, worst quality:1.4),(bad_prompt:0.8), (monochrome:1.1), (greyscale)", + "steps": 20, + "prompt": "masterpiece, best quality, cat", + + "sd_model_checkpoint": "meinamix_meinaV8" +``` +PS: 参数分为两部分, +一部分是params,为画画的参数;参数名**必须**与webuiapi包中[txt2img api](https://github.com/mix1009/sdwebuiapi/blob/fb2054e149c0a4e25125c0cd7e7dca06bda839d4/webuiapi/webuiapi.py#L163)的参数名一致 +另一部分是options,指sdwebui的设置,使用的模型和vae需要写在里面。它和http://127.0.0.1:7860/sdapi/v1/options所返回的键一致。 \ No newline at end of file diff --git a/plugins/sdwebui/sdwebui.py b/plugins/sdwebui/sdwebui.py new file mode 100644 index 0000000..f7da523 --- /dev/null +++ b/plugins/sdwebui/sdwebui.py @@ -0,0 +1,88 @@ +# encoding:utf-8 + +import json +import os +from bridge.context import ContextType +from bridge.reply import Reply, ReplyType +import plugins +from plugins import * +from common.log import logger +import webuiapi +import io + + +@plugins.register(name="sdwebui", desc="利用stable-diffusion webui来画图", version="2.0", author="lanvent") +class SDWebUI(Plugin): + def __init__(self): + super().__init__() + curdir = os.path.dirname(__file__) + config_path = os.path.join(curdir, "config.json") + try: + with open(config_path, "r", encoding="utf-8") as f: + config = json.load(f) + self.rules = config["rules"] + defaults = config["defaults"] + self.default_params = defaults["params"] + self.default_options = defaults["options"] + self.start_args = config["start"] + self.api = webuiapi.WebUIApi(**self.start_args) + self.handlers[Event.ON_HANDLE_CONTEXT] = self.on_handle_context + logger.info("[SD] inited") + except FileNotFoundError: + logger.error(f"[SD] init failed, {config_path} not found") + except Exception as e: + logger.error("[SD] init failed, exception: %s" % e) + + def on_handle_context(self, e_context: EventContext): + + if e_context['context'].type != ContextType.IMAGE_CREATE: + return + + logger.debug("[SD] on_handle_context. content: %s" %e_context['context'].content) + + logger.info("[SD] image_query={}".format(e_context['context'].content)) + reply = Reply() + try: + content = e_context['context'].content[:] + # 解析用户输入 如"横版 高清 二次元:cat" + keywords, prompt = content.split(":", 1) + keywords = keywords.split() + + rule_params = {} + rule_options = {} + for keyword in keywords: + matched = False + for rule in self.rules: + if keyword in rule["keywords"]: + for key in rule["params"]: + rule_params[key] = rule["params"][key] + if "options" in rule: + for key in rule["options"]: + rule_options[key] = rule["options"][key] + matched = True + break # 一个关键词只匹配一个规则 + if not matched: + logger.warning("[SD] keyword not matched: %s" % keyword) + + params = {**self.default_params, **rule_params} + options = {**self.default_options, **rule_options} + params["prompt"] = params.get("prompt", "")+f", {prompt}" + if len(options) > 0: + logger.info("[SD] cover rule_options={}".format(rule_options)) + self.api.set_options(options) + logger.info("[SD] params={}".format(params)) + result = self.api.txt2img( + **params + ) + reply.type = ReplyType.IMAGE + b_img = io.BytesIO() + result.image.save(b_img, format="PNG") + reply.content = b_img + e_context.action = EventAction.BREAK_PASS # 事件结束后,不跳过处理context的默认逻辑 + except Exception as e: + reply.type = ReplyType.ERROR + reply.content = "[SD] "+str(e) + logger.error("[SD] exception: %s" % e) + e_context.action = EventAction.CONTINUE # 事件继续,交付给下个插件或默认逻辑 + finally: + e_context['reply'] = reply