Initiliazing Runware ComfyUI Nodes Alpha 0.8

This commit is contained in:
Jack
2024-12-04 17:39:06 +00:00
parent e7e7417669
commit 08791ef84c
31 changed files with 1883 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
__pycache__/
*.py[cod]
*$py.class
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
cython_debug/
.pyre/
+51
View File
@@ -0,0 +1,51 @@
from .modules.imageInference import txt2img
from .modules.bgremoval import bgremoval
from .modules.photoMaker import photoMaker
from .modules.upscaler import upscaler
from .modules.modelSearch import modelSearch
from .modules.bridges import mainRoute
from .modules.controlNet import controlNet
from .modules.loraSearch import loraSearch
from .modules.loraCombine import loraCombine
from .modules.refiner import refiner
from .modules.imageMasking import imageMasking
from .modules.controlNetPreprocessor import controlNetPreprocessor
from .modules.apiManager import apiManager
from .modules.imageCaptioning import imageCaptioning
from .modules.controlNetCombine import controlNetCombine
RUNWARE_COMFYUI_VERSION = "Alpha 0.8"
RESET_COLOR = "\033[0m"
BLUE_COLOR = "\033[94m"
GREEN_COLOR = "\033[92m"
print(BLUE_COLOR + "##############################################################" + RESET_COLOR)
print(GREEN_COLOR + " Runware ComfyUI Inference Services Are Loaded Successfully" + RESET_COLOR)
print(GREEN_COLOR + " Version: " + RUNWARE_COMFYUI_VERSION + " | Maintained by: Runware Inc" + RESET_COLOR)
print(GREEN_COLOR + " Official Website: https://my.runware.ai" + RESET_COLOR)
print(BLUE_COLOR + "##############################################################" + RESET_COLOR)
NODE_CLASS_MAPPINGS = {
"Runware Image Inference": txt2img,
"Runware Background Removal": bgremoval,
"Runware PhotoMaker V2": photoMaker,
"Runware Image Upscaler": upscaler,
"Runware Model Search": modelSearch,
"Runware Lora Search": loraSearch,
"Runware ControlNet": controlNet,
"Runware Lora Combine": loraCombine,
"Runware Refiner": refiner,
"Runware Image Masking": imageMasking,
"Runware ControlNet PreProcessor": controlNetPreprocessor,
"Runware API Manager": apiManager,
"Runware Image Caption": imageCaptioning,
"Runware ControlNet Combine": controlNetCombine,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"Runware Model Search": "Runware Model",
"Runware Lora Search": "Runware Lora",
}
WEB_DIRECTORY = "./clientlibs"
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
+48
View File
@@ -0,0 +1,48 @@
import { app } from "../../scripts/app.js";
import { promptEnhanceHandler, syncDimensionsNodeHandler, searchNodeHandler, APIKeyHandler } from "./utils.js";
import { RUNWARE_NODE_TYPES, RUNWARE_NODE_PROPS, SEARCH_TERMS } from "./types.js";
app.registerExtension({
name: "runware.ai",
async setup() {
// pass
},
async nodeCreated(node) {
const nodeClass = node.comfyClass;
let crNodeProps = false;
if(typeof nodeClass === "string" && Object.values(RUNWARE_NODE_TYPES).includes(nodeClass)) {
crNodeProps = RUNWARE_NODE_PROPS[nodeClass];
} else {
return;
}
node.bgcolor = crNodeProps.bgColor;
if(nodeClass === RUNWARE_NODE_TYPES.APIMANAGER) {
APIKeyHandler(node);
}
if(crNodeProps.colorModeOnly === true) return;
const nodeWidgets = node.widgets;
if(nodeWidgets.length <= 0) return;
for(const nodeWidget of nodeWidgets) {
const widgetName = nodeWidget.name;
const widgetType = nodeWidget.type;
if(crNodeProps.promptEnhancer === true && widgetType === "customtext") {
if (widgetName === "positivePrompt" || widgetName === "negativePrompt") {
nodeWidget.inputEl.addEventListener('keydown', promptEnhanceHandler);
}
} else if(crNodeProps.liveDimensions === true && widgetType === "combo" && widgetName === "dimensions") {
syncDimensionsNodeHandler(node, nodeWidget);
}
if(crNodeProps.liveSearch === true) {
if(widgetType === "text" && SEARCH_TERMS.includes(widgetName)) {
searchNodeHandler(node, nodeWidget);
}
}
}
}
})
+141
View File
@@ -0,0 +1,141 @@
const DEFAULT_BGCOLOR = "#6c5ce7";
const DEFAULT_DIMENSIONS_LIST = {
"Square (512x512)": "512x512",
"Square HD (1024x1024)": "1024x1024",
"Portrait 3:4 (768x1024)": "768x1024",
"Portrait 9:16 (576x1024)": "576x1024",
"Landscape 4:3 (1024x768)": "1024x768",
"Landscape 16:9 (1024x576)": "1024x576"
};
const DEFAULT_MODELS_ARCH_LIST = {
"All": "all",
"FLUX.1-Schnell": "flux1s",
"FLUX.1-Dev": "flux1d",
"Pony": "pony",
"SD 1.5": "sd1x",
"SD 1.5 Hyper": "sdhyper",
"SD 1.5 LCM": "sd1xlcm",
"SD 3": "sd3",
"SDXL 1.0": "sdxl",
"SDXL 1.0 LCM": "sdxllcm",
"SDXL Distilled": "sdxldistilled",
"SDXL Hyper": "sdxlhyper",
"SDXL Lightning": "sdxllightning",
"SDXL Turbo": "sdxlturbo",
};
const DEFAULT_CONTROLNET_CONDITIONING_LIST = {
"All": "all",
"Canny": "canny",
"Depth": "depth",
"MLSD": "mlsd",
"Normal BAE": "normalbae",
"Open Pose": "openpose",
"Tile": "tile",
"Seg": "seg",
"Line Art": "lineart",
"Line Art Anime": "lineart_anime",
"Shuffle": "shuffle",
"Scribble": "scribble",
"Soft Edge": "softedge",
};
const SEARCH_TERMS = ["Model Search", "Lora Search", "ControlNet Search"];
const MODEL_TYPES_TERMS = ["ModelType", "LoraType", "ControlNetType"];
const MODEL_LIST_TERMS = ["ModelList", "LoraList", "ControlNetList"];
const RUNWARE_NODE_TYPES = {
IMAGEINFERENCE: "Runware Image Inference",
PHOTOMAKER: "Runware PhotoMaker V2",
MODELSEARCH: "Runware Model Search",
LORASEARCH: "Runware Lora Search",
CONTROLNET: "Runware ControlNet",
BGREMOVAL: "Runware Background Removal",
UPSCALER: "Runware Image Upscaler",
REFINER: "Runware Refiner",
LORACOMBINE: "Runware Lora Combine",
CONTROLNETCOMBINE: "Runware ControlNet Combine",
IMAGEMASKING: "Runware Image Masking",
CONTROLNETPREPROCESSING: "Runware ControlNet PreProcessor",
APIMANAGER: "Runware API Manager",
IMAGECAPTION: "Runware Image Caption",
};
const RUNWARE_NODE_PROPS = {
[RUNWARE_NODE_TYPES.IMAGEINFERENCE]: {
bgColor: DEFAULT_BGCOLOR,
liveDimensions: true,
promptEnhancer: true,
},
[RUNWARE_NODE_TYPES.PHOTOMAKER]: {
bgColor: DEFAULT_BGCOLOR,
liveDimensions: true,
promptEnhancer: true,
},
[RUNWARE_NODE_TYPES.MODELSEARCH]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.LORASEARCH]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.CONTROLNET]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.CONTROLNETCONDITIONING]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.BGREMOVAL]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.UPSCALER]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.REFINER]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.LORACOMBINE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.CONTROLNETCOMBINE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGEMASKING]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.CONTROLNETPREPROCESSING]: {
bgColor: DEFAULT_BGCOLOR,
liveDimensions: true,
},
[RUNWARE_NODE_TYPES.APIMANAGER]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGECAPTION]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
};
export {
DEFAULT_BGCOLOR,
DEFAULT_DIMENSIONS_LIST,
DEFAULT_MODELS_ARCH_LIST,
DEFAULT_CONTROLNET_CONDITIONING_LIST,
SEARCH_TERMS,
MODEL_TYPES_TERMS,
MODEL_LIST_TERMS,
RUNWARE_NODE_TYPES,
RUNWARE_NODE_PROPS
}
+249
View File
@@ -0,0 +1,249 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
import { DEFAULT_DIMENSIONS_LIST, DEFAULT_MODELS_ARCH_LIST, SEARCH_TERMS,
DEFAULT_CONTROLNET_CONDITIONING_LIST, RUNWARE_NODE_TYPES, MODEL_TYPES_TERMS, MODEL_LIST_TERMS
} from "./types.js";
async function enhancePrompt(userPrompt) {
const resp = await api.fetchApi('/promptEnhance', {
method: "POST",
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
userPrompt: userPrompt
})
});
const enhancePromptResults = await resp.json();
return enhancePromptResults;
}
async function setAPIKey(apiKey) {
const resp = await api.fetchApi('/setAPIKey', {
method: "POST",
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
apiKey: apiKey
})
});
const setAPIKeyResults = await resp.json();
return setAPIKeyResults;
}
async function modelSearch(modelQuery = "", modelArch = "all", modelType = "base", modelCat = "checkpoint", condtioning = "") {
const resp = await api.fetchApi('/modelSearch', {
method: "POST",
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
modelQuery: modelQuery,
modelArch: modelArch,
modelType: modelType,
modelCat: modelCat,
condtioning: condtioning
})
});
const modelSearchResults = await resp.json();
return modelSearchResults;
}
function notifyUser(message, type="info", title = "Runware", life = 4.5) {
app.extensionManager.toast.add({
severity: type, // 'info', 'success', 'warn', 'error' \\
summary: title,
detail: message,
life: Math.floor(life * 1000)
});
}
async function promptEnhanceHandler(e) {
if(e.altKey && e.shiftKey && e.key === 'E') {
const userPrompt = e.target.value.trim();
if(userPrompt.length <= 1) {
notifyUser("Prompt Is Too Short!", "error", "Runware Prompt Enhancer");
return;
} else if(userPrompt.length > 300) {
notifyUser("Prompt Must Not Exceed 300 Characters!", "error", "Runware Prompt Enhancer");
return;
}
const enhanceResults = await enhancePrompt(userPrompt);
if(enhanceResults.success) {
e.target.value = enhanceResults.enhancedPrompt;
} else {
notifyUser(enhanceResults.error, "error", "Runware Prompt Enhancer");
}
}
}
function remNode(node) {
app.graph.remove(node);
app.graph.setDirtyCanvas(true,true);
}
async function APIKeyHandler(apiManagerNode) {
const widgets = apiManagerNode.widgets;
for(const widget of widgets) {
if(widget.name === "API Key") {
appendWidgetCB(widget, async function(...args) {
const apiKey = args[0].trim();
if(apiKey.length < 30) {
notifyUser("Invalid API Key Set, Please Try Again!", "error", "Runware API Manager");
return;
} else {
const setAPIKeyResults = await setAPIKey(apiKey);
if(setAPIKeyResults.success) {
remNode(apiManagerNode);
notifyUser("API Key Set Successfully!", "success", "Runware API Manager");
} else {
notifyUser(setAPIKeyResults.error, "error", "Runware API Manager");
}
}
});
break;
}
}
}
function appendWidgetCB(node, newCB) {
const oldNodeCB = node.callback;
if(typeof oldNodeCB === "function") {
node.callback = function(...args) {
newCB?.apply(this, args);
oldNodeCB?.apply(this, args);
}
} else {
node.callback = newCB;
}
}
async function syncDimensionsNodeHandler(node, dimensionsWidget) {
const nodeWidgets = node.widgets;
let widthWidget = false, heightWidget = false;
for(const nodeWidget of nodeWidgets) {
const widgetName = nodeWidget.name;
const widgetType = nodeWidget.type;
if(widgetName === "width" && widgetType === "number") widthWidget = nodeWidget;
if(widgetName === "height" && widgetType === "number") heightWidget = nodeWidget;
}
appendWidgetCB(dimensionsWidget, function(...args) {
const chosenDimension = args[0];
if(chosenDimension === "Custom") return;
const dimensionValue = DEFAULT_DIMENSIONS_LIST[chosenDimension];
const [width, height] = dimensionValue.split("x");
widthWidget.callback(width, "customSetOperation");
heightWidget.callback(height, "customSetOperation");
});
if(widthWidget !== false) {
appendWidgetCB(widthWidget, function(...args) {
if(args[1] === "customSetOperation") return;
dimensionsWidget.value = "Custom";
});
}
if(heightWidget !== false) {
appendWidgetCB(heightWidget, function(...args) {
if(args[1] === "customSetOperation") return;
dimensionsWidget.value = "Custom";
});
}
}
async function searchNodeHandler(searchNode, searchInputWidget) {
const searchNodeWidgets = searchNode.widgets;
let modelArchWidget = false, modelTypeWidget = false, modelListWidget = false, defaultWidgetValues = {};
let isLora = false, isControlNet = false, modelTypeValue;
if(searchNode.comfyClass === RUNWARE_NODE_TYPES.LORASEARCH) isLora = true;
if(searchNode.comfyClass === RUNWARE_NODE_TYPES.CONTROLNET) isControlNet = true;
for(const searchWidget of searchNodeWidgets) {
const widgetName = searchWidget.name;
const widgetType = searchWidget.type;
if(widgetName === "Model Architecture" && widgetType === "combo") {
modelArchWidget = searchWidget;
defaultWidgetValues["modelArch"] = searchWidget.options.values;
}
if(MODEL_TYPES_TERMS.includes(widgetName) && widgetType === "combo") {
modelTypeWidget = searchWidget;
defaultWidgetValues["modelType"] = searchWidget.options.values;
}
if(MODEL_LIST_TERMS.includes(widgetName) && widgetType === "combo") {
modelListWidget = searchWidget;
defaultWidgetValues["modelList"] = searchWidget.options.values;
}
}
function resetValues() {
if(modelArchWidget) {
modelArchWidget.options.values = defaultWidgetValues["modelArch"];
modelArchWidget.value = defaultWidgetValues["modelArch"][0];
}
if(modelTypeWidget) {
modelTypeWidget.options.values = defaultWidgetValues["modelType"];
modelTypeWidget.value = defaultWidgetValues["modelType"][0];
}
if(modelListWidget) {
modelListWidget.options.values = defaultWidgetValues["modelList"];
modelListWidget.value = defaultWidgetValues["modelList"][0];
}
}
async function searchModels() {
const searchQuery = searchInputWidget.value.trim();
const modelArchValue = DEFAULT_MODELS_ARCH_LIST[modelArchWidget.value];
if(isControlNet) {
modelTypeValue = DEFAULT_CONTROLNET_CONDITIONING_LIST[modelTypeWidget.value];
} else {
modelTypeValue = modelTypeWidget.value.toLowerCase().replace("model", "").trim();
}
let modelSearchResults = null;
if(isControlNet) {
modelSearchResults = await modelSearch(searchQuery, modelArchValue, "", "controlnet", modelTypeValue);
} else if(isLora) {
modelSearchResults = await modelSearch(searchQuery, modelArchValue, "", modelTypeValue);
} else {
modelSearchResults = await modelSearch(searchQuery, modelArchValue, modelTypeValue);
}
if(modelSearchResults.success) {
const modelListArray = [];
modelSearchResults.modelList.forEach(modelObj => {
modelListArray.push(`${modelObj.air} (${modelObj.name} ${modelObj.version})`);
});
modelListWidget.options.values = modelListArray;
modelListWidget.value = modelListArray[0];
} else {
notifyUser(modelSearchResults.error, "error", "Runware Search");
}
}
appendWidgetCB(searchInputWidget, async function(...args) {
const searchQuery = args[0].trim();
if(searchQuery.length === 0) {
resetValues();
} else if(searchQuery.length < 2 || searchQuery.length > 32) {
notifyUser("Invalid Search Value, Search Query must be between 2 and 32 characters!", "error", "Runware Search");
return;
} else {
await searchModels();
}
});
appendWidgetCB(modelArchWidget, async function(...args) {
await searchModels();
});
if(!isLora){
appendWidgetCB(modelTypeWidget, async function(...args) {
await searchModels();
});
}
}
export {
enhancePrompt,
modelSearch,
notifyUser,
promptEnhanceHandler,
syncDimensionsNodeHandler,
searchNodeHandler,
APIKeyHandler,
};
+15
View File
@@ -0,0 +1,15 @@
class apiManager:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"API Key": ("STRING", {
"tooltip": "Set Your Runware API Key To Start Using Runware Image Inference Services.\n\n(eg: BcVr1JVQM8FGzrRwb3GsbCq5ww1QwabV)\n\nIf You Don't Have One, You Can Get It From: https://my.runware.ai/keys",
}),
},
}
DESCRIPTION = "API Managers is a Runware Utility That helps you set your API Key From The ComfyUI Interface Without having to adjust the env file locally."
CATEGORY = "Runware"
RETURN_TYPES = ()
OUTPUT_NODE = True
+86
View File
@@ -0,0 +1,86 @@
from .utils import runwareUtils as rwUtils
class bgremoval:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Image": ("IMAGE", {
"tooltip": "Specifies the input image to be processed."
}),
},
"optional": {
"Post Process Mask": ("BOOLEAN", {
"tooltip": "Controls whether the mask should undergo additional post-processing. This step can improve the accuracy and quality of the background removal mask.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"Return Only Mask": ("BOOLEAN", {
"tooltip": "Whether to return only the mask. The mask is the opposite of the image background removal.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"Alpha Matting": ("BOOLEAN", {
"tooltip": "Alpha matting is a post-processing technique that enhances the quality of the output by refining the edges of the foreground object.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"Alpha Matting Foreground Threshold": ("INT", {
"tooltip": "Threshold value used in alpha matting to distinguish the foreground from the background. Adjusting this parameter affects the sharpness and accuracy of the foreground object edges.",
"default": 240,
"min": 1,
"max": 255,
}),
"Alpha Matting Background Threshold": ("INT", {
"tooltip": "Threshold value used in alpha matting to refine the background areas. It influences how aggressively the algorithm removes the background while preserving image details. The higher the value, the more computation is needed and therefore the more expensive the operation is.",
"default": 10,
"min": 1,
"max": 255,
}),
"Alpha Matting Erode Size": ("INT", {
"tooltip": "Specifies the size of the erosion operation used in alpha matting. Erosion helps in smoothing the edges of the foreground object for a cleaner removal of the background.",
"default": 10,
"min": 1,
"max": 255,
}),
}
}
DESCRIPTION = "Remove backgrounds from images effortlessly using Runware's low-cost image editing Inference."
FUNCTION = "rembg"
RETURN_TYPES = ("IMAGE",)
CATEGORY = "Runware"
def rembg(self, **kwargs):
image = kwargs.get("Image")
postProcessMask = kwargs.get("Post Process Mask", False)
returnOnlyMask = kwargs.get("Return Only Mask", False)
alphaMatting = kwargs.get("Alpha Matting", False)
alphaMattingForegroundThreshold = kwargs.get("Alpha Matting Foreground Threshold", 240)
alphaMattingBackgroundThreshold = kwargs.get("Alpha Matting Background Threshold", 10)
alphaMattingErodeSize = kwargs.get("Alpha Matting Erode Size", 10)
genConfig = [
{
"taskType": "imageBackgroundRemoval",
"taskUUID": rwUtils.genRandUUID(),
"inputImage": rwUtils.convertTensor2IMG(image),
"postProcessMask": postProcessMask,
"returnOnlyMask": returnOnlyMask,
"alphaMatting": alphaMatting,
"outputFormat": "WEBP",
"outputType": "base64Data",
}
]
if(alphaMatting):
genConfig[0]["alphaMattingForegroundThreshold"] = alphaMattingForegroundThreshold
genConfig[0]["alphaMattingBackgroundThreshold"] = alphaMattingBackgroundThreshold
genConfig[0]["alphaMattingErodeSize"] = alphaMattingErodeSize
genResult = rwUtils.inferenecRequest(genConfig)
images = rwUtils.convertImageB64List(genResult)
return images
+73
View File
@@ -0,0 +1,73 @@
from ..utils import runwareUtils as rwUtils
from server import PromptServer
from aiohttp import web
routes = PromptServer.instance.routes
@routes.post('/setAPIKey')
async def setAPIKey(reqPayload):
reqData = await reqPayload.json()
apiKey = reqData.get('apiKey', None)
if(apiKey is None or apiKey == "" or len(apiKey) < 30):
return web.json_response({'success': False, 'error': 'API Key Not Provided!'})
try:
apiKey = apiKey.strip()
rwUtils.setAPIKey(apiKey)
except Exception as e:
return web.json_response({'success': False, 'error': str(e)})
return web.json_response({'success': True})
@routes.post('/promptEnhance')
async def promptEnhance(reqPayload):
reqData = await reqPayload.json()
userPrompt = reqData.get('userPrompt')
utilityConfig = [{
"taskType": "promptEnhance",
"taskUUID": rwUtils.genRandUUID(),
"prompt": userPrompt,
"promptMaxLength": 300,
"promptVersions": 1,
}]
try:
utilityResults = rwUtils.inferenecRequest(utilityConfig)
except Exception as e:
return web.json_response({'success': False, 'error': str(e)})
enhancedPrompt = utilityResults["data"][0]["text"]
return web.json_response({'success': True, 'enhancedPrompt': enhancedPrompt})
@routes.post('/modelSearch')
async def modelSearch(reqPayload):
reqData = await reqPayload.json()
modelQuery = reqData.get('modelQuery', "")
modelArch = reqData.get('modelArch', "all")
modelCategory = reqData.get('modelCat', "checkpoint")
modelType = reqData.get('modelType', "base")
controlNetConditioning = reqData.get('condtioning', "all")
utilityConfig = [{
"taskType": "modelSearch",
"taskUUID": rwUtils.genRandUUID(),
"category": modelCategory,
"limit": 15,
}]
if(modelCategory != "controlnet" and modelCategory != "lora"):
utilityConfig[0]["type"] = modelType
elif(modelCategory == "controlnet" and controlNetConditioning != "all"):
utilityConfig[0]["conditioning"] = controlNetConditioning
if(modelArch != "all"):
utilityConfig[0]["architecture"] = modelArch
if(modelQuery != ""):
utilityConfig[0]["search"] = modelQuery
try:
utilityResults = rwUtils.inferenecRequest(utilityConfig)
except Exception as e:
return web.json_response({'success': False, 'error': str(e)})
totalResults = utilityResults["data"][0]["totalResults"]
if(totalResults < 1):
return web.json_response({'success': False, 'error': 'No Results Found!'})
modelList = utilityResults["data"][0]["results"]
return web.json_response({'success': True, 'modelList': modelList})
+155
View File
@@ -0,0 +1,155 @@
from .utils import runwareUtils as rwUtils
class controlNet:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Guide Image": ("IMAGE", {
"tooltip": "Specifies the preprocessed image to be used as guide to control the image generation process."
}),
"ControlNet Search": ("STRING", {
"tooltip": "Searchg For Specific ControlNet By Name Or Civit AIR Code (eg: Canny).",
}),
"Model Architecture": ([
"All",
"FLUX.1-Schnell",
"FLUX.1-Dev",
"Pony",
"SD 1.5",
"SD 1.5 Hyper",
"SD 1.5 LCM",
"SD 3",
"SDXL 1.0",
"SDXL 1.0 LCM",
"SDXL Distilled",
"SDXL Hyper",
"SDXL Lightning",
"SDXL Turbo",
], {
"tooltip": "Choose ControlNet Model Architecture To Filter Results.",
"default": "All",
}),
"ControlNetType": ([
"All",
"Canny",
"Depth",
"MLSD",
"Normal BAE",
"Open Pose",
"Tile",
"Seg",
"Line Art",
"Line Art Anime",
"Shuffle",
"Scribble",
"Soft Edge",
], {
"tooltip": "Choose ControlNet Type To Filter Results.",
"default": "All",
}),
"ControlNetList": ([
"civitai:38784@44716 (SD1.5 Canny)",
"civitai:38784@44876 (SD1.5 Inpaint)",
"civitai:38784@44877 (SD1.5 Lineart)",
"civitai:38784@44795 (SD1.5 MLSD)",
"civitai:38784@44774 (SD1.5 NormalBAE)",
"runware:20@1 (SDXL Canny)",
"runware:25@1 (Flux Dev Canny)",
"runware:26@1 (Flux Dev Tile)",
"runware:28@1 (Flux Dev Blur)",
"runware:29@1 (Flux Dev OpenPose)",
"runware:30@1 (Flux Dev Gray)",
"runware:31@1 (Flux Dev Low Quality)",
], {
"tooltip": "ControlNet Results Will Show UP Here So You Could Choose From.",
"default": "civitai:38784@44716 (SD1.5 Canny)",
}),
"startStep": ("INT", {
"tooltip": "Represents the step number at which the ControlNet model starts to control the inference process. (Enter -1 To Disable)",
"min": -1,
"max": 99,
"default": -1,
}),
"startStepPercentage": ("INT", {
"tooltip": "Represents the percentage of steps at which the ControlNet model starts to control the inference process. (Enter -1 To Disable)",
"min": -1,
"max": 99,
"default": 0,
}),
"endStep": ("INT", {
"tooltip": "Represents the step number at which the ControlNet preprocessor ends to control the inference process. (Enter -1 To Disable)",
"min": -1,
"max": 100,
"default": -1,
}),
"endStepPercentage": ("INT", {
"tooltip": "Represents the percentage of steps at which the ControlNet model ends to control the inference process. (Enter -1 To Disable)",
"min": -1,
"max": 100,
"default": 80,
}),
"Control Mode": (["prompt", "controlnet", "balanced"], {
"tooltip": "Choose Control Mode To Control The Inference Process.",
"default": "balanced",
}),
"weight": ("FLOAT", {
"tooltip": "Represents the weight (strength) of the ControlNet model in the image.",
"default": 1.0,
"min": 0,
"max": 1.0,
"step": 0.1,
}),
},
}
DESCRIPTION = "Directly Search and Configure ControlNet Guidance to Connect It With Runware Image Inference Nodes In ComfyUI."
FUNCTION = "controlNet"
RETURN_TYPES = ("RUNWARECONTROLNET",)
RETURN_NAMES = ("Runware ControlNet",)
CATEGORY = "Runware"
@classmethod
def VALIDATE_INPUTS(cls, ControlNetList, startStep, startStepPercentage, endStep, endStepPercentage):
if((startStep == -1 and startStepPercentage == -1) or (startStep != -1 and startStepPercentage != -1)):
raise Exception("Error: Please Provide Either Start Step or Start Step Percentage!")
if((endStep == -1 and endStepPercentage == -1) or (endStep != -1 and endStepPercentage != -1)):
raise Exception("Error: Please Provide Either End Step or End Step Percentage!")
if((startStep != -1 and endStep != -1) and (startStep > endStep)):
raise Exception("Error: Start Step Cannot Be Greater Than End Step!")
elif((startStep != -1 and endStep != -1) and (startStep == endStep)):
raise Exception("Error: Start Step Cannot Be Equal To End Step!")
if((startStepPercentage != -1 and endStepPercentage != -1) and (startStepPercentage > endStepPercentage)):
raise Exception("Error: Start Step Percentage Cannot Be Greater Than End Step Percentage!")
elif((startStepPercentage != -1 and endStepPercentage != -1) and (startStepPercentage == endStepPercentage)):
raise Exception("Error: Start Step Percentage Cannot Be Equal To End Step Percentage!")
return True
def controlNet(self, **kwargs):
guideImage = kwargs.get("Guide Image")
currentModel = kwargs.get("ControlNetList")
controlNetAIRCode = currentModel.split(" ")[0]
startStep = kwargs.get("startStep")
startStepPercentage = kwargs.get("startStepPercentage")
endStep = kwargs.get("endStep")
endStepPercentage = kwargs.get("endStepPercentage")
controlMode = kwargs.get("Control Mode")
weight = kwargs.get("weight")
controlNetGuideOBJ = [{
"model": controlNetAIRCode,
"guideImage": rwUtils.convertTensor2IMG(guideImage),
"weight": round(weight, 2),
"controlMode": controlMode,
}]
if(startStep != -1):
controlNetGuideOBJ[0]["startStep"] = startStep
elif(startStepPercentage != -1):
controlNetGuideOBJ[0]["startStepPercentage"] = startStepPercentage
if(endStep != -1):
controlNetGuideOBJ[0]["endStep"] = endStep
elif(endStepPercentage != -1):
controlNetGuideOBJ[0]["endStepPercentage"] = endStepPercentage
return (controlNetGuideOBJ,)
+37
View File
@@ -0,0 +1,37 @@
class controlNetCombine:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"ControlNet 1": ("RUNWARECONTROLNET", {
"tooltip": "Connect a Runware ControlNet Node.",
}),
},
"optional": {
"ControlNet 2": ("RUNWARECONTROLNET", {
"tooltip": "Connect a Runware ControlNet Node.",
}),
"ControlNet 3": ("RUNWARECONTROLNET", {
"tooltip": "Connect a Runware ControlNet Node.",
}),
},
}
DESCRIPTION = "Combine One or More ControlNet's To Guide Image Generation Process in Runware Image Inference."
FUNCTION = "controlNetCombine"
RETURN_TYPES = ("RUNWARECONTROLNET",)
RETURN_NAMES = ("Runware ControlNet's",)
CATEGORY = "Runware"
def controlNetCombine(self, **kwargs):
controlNet1 = kwargs.get("ControlNet 1")
controlNet2 = kwargs.get("ControlNet 2", None)
controlNet3 = kwargs.get("ControlNet 3", None)
controlNetObjectArray = controlNet1
if(controlNet2 is not None):
controlNetObjectArray = controlNetObjectArray + controlNet2
if(controlNet3 is not None):
controlNetObjectArray = controlNetObjectArray + controlNet3
print(controlNetObjectArray)
return (controlNetObjectArray,)
+118
View File
@@ -0,0 +1,118 @@
from .utils import runwareUtils as rwUtils
class controlNetPreprocessor:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Image": ("IMAGE", {
"tooltip": "Specifies the input image to be preprocessed to generate a guide image. This guide image will be used as a reference for image generation processes, guiding the AI to generate images that are more aligned with the input image."
}),
"PreProcessor Type": ([
"Canny",
"Depth",
"MLSD",
"NormalBAE",
"OpenPose",
"Tile",
"Seg",
"LineArt",
"LineArtAnime",
"Shuffle",
"Scribble",
"SoftEdge",
], {
"tooltip": "Choose Preprocessor Type To Generate The Guide Image.",
"default": "Canny",
}),
"Include Hands And Faces [ OpenPose ]": ("BOOLEAN", {
"default": False,
"tooltip": "Include the hands and face in the pose outline when using the OpenPose preprocessor.",
"label_on": "Enabled",
"label_off": "Disabled",
}),
"Canny Low Threshold": ("INT", {
"tooltip": "Defines the lower threshold when using the Canny edge detection preprocessor.",
"default": 100,
"min": 0,
"max": 255,
}),
"Canny High Threshold": ("INT", {
"tooltip": "Defines the high threshold when using the Canny edge detection preprocessor.",
"default": 200,
"min": 0,
"max": 255,
}),
"Image Resizing": ("BOOLEAN", {
"default": False,
"tooltip": "It Allows Image Resizing if the dimension of the image is higher than a specific value it will be resized.",
"label_on": "Enabled",
"label_off": "Disabled",
}),
"dimensions": ([
"Square (512x512)", "Square HD (1024x1024)", "Portrait 3:4 (768x1024)",
"Portrait 9:16 (576x1024)", "Landscape 4:3 (1024x768)",
"Landscape 16:9 (1024x576)", "Custom"
], {
"default": "Square HD (1024x1024)",
"tooltip": "Resize the dimensions of the generated Guide image by specifying its width and height in pixels, or select from the predefined options. Image dimensions must be multiples of 64 (e.g., 512x512, 1024x768).",
}),
"width": ("INT", {
"tooltip": "If the Image height dimension is larger than this value, the output image will be resized to the specified height.",
"default": 1024,
"min": 512,
"max": 2048,
"step": 64,
}),
"height": ("INT", {
"tooltip": "If the Image width dimension is larger than this value, the output image will be resized to the specified width.",
"default": 1024,
"min": 512,
"max": 2048,
"step": 64,
}),
},
}
DESCRIPTION = "ControlNet offers advanced capabilities for precise image processing through the use of guide images in specific formats, known as preprocessed images. This powerful tool enhances the control and customization of image generation, enabling users to achieve desired artistic styles and detailed adjustments effectively."
FUNCTION = "controlNetPreProcess"
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("GUIDE IMAGE",)
CATEGORY = "Runware"
def controlNetPreProcess(self, **kwargs):
image = kwargs.get("Image")
preProcessorType = kwargs.get("PreProcessor Type")
includeHandsAndFaces = kwargs.get("Include Hands And Faces [ OpenPose ]")
cannyLowThreshold = kwargs.get("Canny Low Threshold")
cannyHighThreshold = kwargs.get("Canny High Threshold")
imageResizing = kwargs.get("Image Resizing")
width = kwargs.get("width")
height = kwargs.get("height")
genConfig = [
{
"taskType": "imageControlNetPreProcess",
"taskUUID": rwUtils.genRandUUID(),
"inputImage": rwUtils.convertTensor2IMG(image),
"preProcessorType": preProcessorType,
"outputFormat": "WEBP",
"outputType": "base64Data",
}
]
# For Debugging Purposes Only
print(f"[Debugging] Task UUID: {genConfig[0]['taskUUID']}")
if(imageResizing):
genConfig[0]["width"] = width
genConfig[0]["height"] = height
if(preProcessorType == "OpenPose" and includeHandsAndFaces):
genConfig[0]["includeHandsAndFaceOpenPose"] = includeHandsAndFaces
elif(preProcessorType == "Canny"):
genConfig[0]["lowThresholdCanny"] = cannyLowThreshold
genConfig[0]["highThresholdCanny"] = cannyHighThreshold
genResult = rwUtils.inferenecRequest(genConfig)
images = rwUtils.convertImageB64List(genResult)
return images
+42
View File
@@ -0,0 +1,42 @@
from .utils import runwareUtils as rwUtils
class imageCaptioning:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Image": ("IMAGE", {
"tooltip": "Specifies the input image to be processed."
}),
},
"optional": {
"Image Description": ("STRING", {
"multiline": True,
"placeholder": "Don't Write Anything Here, The Image Description Will Be Generated Automatically.",
}),
},
"hidden": { "node_id": "UNIQUE_ID" }
}
DESCRIPTION = "Image to text, also known as image captioning, allows you to obtain descriptive text prompts based on uploaded or previously generated images. This process is instrumental in generating textual descriptions that can be used to create additional images or provide detailed insights into visual content."
FUNCTION = "imageCaptioning"
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("IMAGE PROMPT",)
CATEGORY = "Runware"
OUTPUT_NODE = True
def imageCaptioning(self, **kwargs):
print(kwargs)
image = kwargs.get("Image")
kwargs.set("Image Description", "Don't Write Anything Here, The Image Description Will Be Generated Automatically.")
genConfig = [
{
"taskType": "imageCaption",
"taskUUID": rwUtils.genRandUUID(),
"inputImage": rwUtils.convertTensor2IMG(image)
}
]
genResult = rwUtils.inferenecRequest(genConfig)
genText = genResult["data"][0]["text"]
return (genText, )
+211
View File
@@ -0,0 +1,211 @@
from .utils import runwareUtils as rwUtils
class txt2img:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Model": ("RUNWAREMODEL", {
"tooltip": "Connect a Runware Model From Runware Model Node.",
}),
"positivePrompt": ("STRING", {
"multiline": True,
"placeholder": "Positive Prompt: a text instruction to guide the model on generating the image. It is usually a sentence or a paragraph that provides positive guidance for the task. This parameter is essential to shape the desired results.\n\nYou Can Press (Ctrl + Alt + E) To Enhance The Prompt!",
"tooltip": "Positive Prompt: a text instruction to guide the model on generating the image. You Can Also Press (Ctrl + Alt + E) To Enhance The Prompt!"
}),
"negativePrompt": ("STRING", {
"multiline": True,
"placeholder": "Negative Prompt: a text instruction to guide the model on generating the image. It is usually a sentence or a paragraph that provides negative guidance for the task. This parameter helps to avoid certain undesired results.",
"tooltip": "Negative Prompt: a text instruction to guide the model on generating the image."
}),
"Prompt Weighting": ("BOOLEAN", {
"default": False,
"tooltip": "Prompt weighting allows you to adjust how strongly different parts of your prompt influence the generated image.",
"label_on": "Enabled",
"label_off": "Disabled",
}),
"dimensions": ([
"Square (512x512)", "Square HD (1024x1024)", "Portrait 3:4 (768x1024)",
"Portrait 9:16 (576x1024)", "Landscape 4:3 (1024x768)",
"Landscape 16:9 (1024x576)", "Custom"
], {
"default": "Square (512x512)",
"tooltip": "Adjust the dimensions of the generated image by specifying its width and height in pixels, or select from the predefined options. Image dimensions must be multiples of 64 (e.g., 512x512, 1024x768).",
}),
"width": ("INT", {
"tooltip": "The Width of the image in pixels.",
"default": 512,
"min": 512,
"max": 2048,
"step": 64,
}),
"height": ("INT", {
"tooltip": "The Height of the image in pixels.",
"default": 512,
"min": 512,
"max": 2048,
"step": 64,
}),
"steps": ("INT", {
"tooltip": "Guidance scale represents how closely the images will resemble the prompt or how much freedom the AI model has. Higher values are closer to the prompt. Low values may reduce the quality of the results.",
"default": 4,
"min": 1,
"max": 100,
}),
"scheduler": ([
"Default", "DDIMScheduler", "DDIMInverseScheduler", "DDPMScheduler", "DEISMultistepScheduler",
"DPMSolverSinglestepScheduler", "DPMSolverMultistepScheduler", "DPMSolverMultistepInverse", "DPMSolverSDEScheduler", "HeunDiscreteScheduler", "KDPM2DiscreteScheduler", "KDPM2AncestralDiscreteScheduler", "KarrasVeScheduler", "LMSDiscreteScheduler", "PNDMScheduler", "ScoreSdeVeScheduler", "ScoreSdeVpScheduler", "IPNDMScheduler", "EulerDiscreteScheduler", "EulerAncestralDiscreteScheduler", "EDMEulerScheduler", "FlowMatchEulerDiscreteScheduler", "VQDiffusionScheduler", "UniPCMultistepScheduler", "RePaintScheduler", "DPM++ 2M Karras", "DPM++ 2M SDE Karras", "DPM++ 2M SDE", "DPM++ SDE Karras", "DPM++ SDE", "LCMScheduler", "EDMDPMSolverMultistepScheduler", "TCDScheduler"
], {
"tooltip": "An scheduler is a component that manages the inference process. Different schedulers can be used to achieve different results like more detailed images, faster inference, or more accurate results.",
"default": "Default",
}),
"cfgScale": ("FLOAT", {
"tooltip": "The scale of the image generation. The higher the scale, the more detailed the image will be.",
"default": 6.5,
"min": 1.0,
"max": 30.0,
"step": 0.5,
}),
"seed": ("INT", {
"tooltip": "A value used to randomize the image generation. If you want to make images reproducible (generate the same image multiple times), you can use the same seed value.",
"default": rwUtils.genRandSeed(),
"min": 1,
"max": 9223372036854776000,
}),
"clipSkip": ("INT", {
"tooltip": "Enables skipping layers of the CLIP embedding process, leading to quicker and more varied image generation.",
"default": 0,
"min": 0,
"max": 2,
}),
"strength": ("FLOAT", {
"tooltip": "When doing Image-to-Image or Inpainting, this parameter is used to determine the influence of the seedImage image in the generated output. A lower value results in more influence from the original image, while a higher value allows more creative deviation.",
"default": 0.80,
"min": 0.0,
"max": 1.0,
"step": 0.05,
}),
"Mask Margin": ("BOOLEAN", {
"tooltip": "Enables Or Disables Mask Margin Feature.",
"default": False,
"label_on": "Enabled",
"label_off": "Disabled",
}),
"maskMargin": ("INT", {
"tooltip": "Adds extra context pixels around the masked region during inpainting. When this parameter is present, the model will zoom into the masked area, considering these additional pixels to create more coherent and well-integrated details.",
"default": 32,
"min": 32,
"max": 128,
}),
"batchSize": ("INT", {
"tooltip": "The number of images to generate in a single request.",
"default": 1,
"min": 1,
"max": 10,
}),
},
"optional": {
"Lora": ("RUNWARELORA", {
"tooltip": "Connect a Runware Lora From Lora Search Node Or Lora Combine For Multiple Lora's Together.",
}),
"ControlNet": ("RUNWARECONTROLNET", {
"tooltip": "Connect a Runware ControlNet Guidance Node to help the model generate images that align with the desired structure.",
}),
"Refiner": ("RUNWAREREFINER", {
"tooltip": "Connect a Runware Refiner Node to help create higher quality image outputs by incorporating specialized models designed to enhance image details and overall coherence.",
}),
"seedImage": ("IMAGE", {
"tooltip": "Specifies the seed image to be used for the diffusion process, when doing Image-to-Image, Inpainting or Outpainting, this parameter is required.",
}),
"maskImage": ("MASK", {
"tooltip": "Specifies the mask image to be used for the inpainting process, when doing Inpainting, this parameter is required.",
}),
}
}
@classmethod
def VALIDATE_INPUTS(cls, positivePrompt, negativePrompt):
if (positivePrompt is None or len(positivePrompt) < 3 or len(positivePrompt) > 2000):
raise Exception("Positive Prompt Must Be Between 3 And 2000 characters!")
if (negativePrompt is not None and negativePrompt != "" and (len(negativePrompt) < 3 or len(negativePrompt) > 2000)):
raise Exception("Negative Prompt Must Be Between 3 And 2000 characters!")
return True
DESCRIPTION = "Generates Images Lightning Fast With Runware Image Inference Sonic Engine."
FUNCTION = "generateImage"
RETURN_TYPES = ("IMAGE",)
CATEGORY = "Runware"
def generateImage(self, **kwargs):
runwareModel = kwargs.get("Model")
positivePrompt = kwargs.get("positivePrompt")
negativePrompt = kwargs.get("negativePrompt", None)
promptWeighting = kwargs.get("Prompt Weighting", False)
runwareControlNet = kwargs.get("ControlNet", None)
runwareLora = kwargs.get("Lora", None)
runwareRefiner = kwargs.get("Refiner", None)
seedImage = kwargs.get("seedImage", None)
seedImageStrength = kwargs.get("strength", 0.8)
maskImage = kwargs.get("maskImage", None)
enableMaskMargin = kwargs.get("Mask Margin", False)
maskImageMargin = kwargs.get("maskMargin", 32)
clipSkip = kwargs.get("clipSkip", 0)
height = kwargs.get("height", 512)
width = kwargs.get("width", 512)
steps = kwargs.get("steps", 4)
scheduler = kwargs.get("scheduler", "Default")
cfgScale = kwargs.get("cfgScale", 6.5)
seed = kwargs.get("seed")
batchSize = kwargs.get("batchSize", 1)
if(maskImage is not None and seedImage is None):
raise Exception("Mask Image Requires Seed Image To Be Provided!")
genConfig = [
{
"taskType": "imageInference",
"taskUUID": rwUtils.genRandUUID(),
"positivePrompt": positivePrompt,
"height": height,
"width": width,
"model": runwareModel,
"steps": steps,
"CFGScale": cfgScale,
"scheduler": scheduler,
"clipSkip": clipSkip,
"seed": seed,
"outputFormat": "WEBP",
"outputType": "base64Data",
"numberResults": batchSize,
}
]
# For Debugging Purposes Only
print(f"[Debugging] Task UUID: {genConfig[0]['taskUUID']}")
if(negativePrompt is not None and negativePrompt != ""):
genConfig[0]["negativePrompt"] = negativePrompt
if(promptWeighting):
genConfig[0]["promptWeighting"] = "sdEmbeds"
if(runwareLora is not None):
if(isinstance(runwareLora, list)):
genConfig[0]["lora"] = runwareLora
elif(isinstance(runwareLora, dict)):
genConfig[0]["lora"] = [runwareLora]
if(runwareControlNet is not None):
genConfig[0]["controlNet"] = runwareControlNet
if(runwareRefiner is not None):
genConfig[0]["refiner"] = runwareRefiner
if(seedImage is not None):
seedImage = rwUtils.convertTensor2IMG(seedImage)
genConfig[0]["seedImage"] = seedImage
genConfig[0]["strength"] = seedImageStrength
if(maskImage is not None):
maskImage = rwUtils.convertTensor2IMG(maskImage)
genConfig[0]["maskImage"] = maskImage
if(enableMaskMargin):
genConfig[0]["maskMargin"] = maskImageMargin
genResult = rwUtils.inferenecRequest(genConfig)
images = rwUtils.convertImageB64List(genResult)
return images
+79
View File
@@ -0,0 +1,79 @@
from .utils import runwareUtils as rwUtils
class imageMasking:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Image": ("IMAGE", {
"tooltip": "Specifies the input image to be processed for mask generation."
}),
"Detection Model": ([
"face_yolov8n", "face_yolov8s", "hand_yolov8n", "person_yolov8n-seg", "person_yolov8s-seg",
"mediapipe_face_full", "mediapipe_face_short", "mediapipe_face_mesh"
], {
"tooltip": "Specifies the specialized detection model to use for mask generation.",
"default": "face_yolov8n",
}),
"Confidence": ("FLOAT", {
"tooltip": "Confidence threshold for detections. Only detections with confidence scores above this threshold will be included in the mask.",
"default": 0.25,
"min": 0,
"max": 1,
"step": 0.01,
}),
"Max Detections": ("INT", {
"tooltip": "Limits the maximum number of elements (faces, hands, or people) that will be detected and masked in the image. If there are more elements than this value, only the ones with highest confidence scores will be included.",
"default": 6,
"min": 1,
"max": 20,
}),
"Mask Padding": ("INT", {
"tooltip": "Extends or reduces the detected mask area by the specified number of pixels. Positive values create a larger masked region (useful when you want to ensure complete coverage of the element), while negative values shrink the mask (useful for tighter, more focused areas).",
"default": 4,
"min": -40,
"max": 40,
}),
"Mask Blur": ("INT", {
"tooltip": "Extends the mask by the specified number of pixels with a gradual fade-out effect, creating smooth transitions between masked and unmasked regions in the final result.",
"default": 4,
"min": 0,
"max": 20,
}),
},
}
DESCRIPTION = "Image Masking provides intelligent detection and mask generation for specific elements in images, particularly optimized for faces, hands, and people. Built on advanced detection models, this feature enhances the inpainting workflow by automatically creating precise masks around detected elements, enabling targeted enhancement and detailing."
FUNCTION = "imageMasking"
RETURN_TYPES = ("IMAGE", "IMAGE", "MASK")
RETURN_NAMES = ("Image", "Mask Preview", "Mask")
CATEGORY = "Runware"
def imageMasking(self, **kwargs):
image = kwargs.get("Image")
detectionModel = kwargs.get("Detection Model")
confidence = kwargs.get("Confidence")
maxDetections = kwargs.get("Max Detections")
maskPadding = kwargs.get("Mask Padding")
maskBlur = kwargs.get("Mask Blur")
genConfig = [
{
"taskType": "imageMasking",
"taskUUID": rwUtils.genRandUUID(),
"inputImage": rwUtils.convertTensor2IMG(image),
"model": detectionModel,
"confidence": confidence,
"maxDetections": maxDetections,
"outputFormat": "WEBP",
"outputType": "base64Data",
}
]
genConfig[0]["maskPadding"] = maskPadding
genConfig[0]["maskBlur"] = maskBlur
genResult = rwUtils.inferenecRequest(genConfig)
images = rwUtils.convertImageB64List(genResult)
return (image, images[0], images[0])
+37
View File
@@ -0,0 +1,37 @@
class loraCombine:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Lora 1": ("RUNWARELORA", {
"tooltip": "Connect a Runware Lora From Lora Search Node.",
}),
},
"optional": {
"Lora 2": ("RUNWARELORA", {
"tooltip": "Connect a Runware Lora From Lora Search Node.",
}),
"Lora 3": ("RUNWARELORA", {
"tooltip": "Connect a Runware Lora From Lora Search Node.",
}),
},
}
DESCRIPTION = "Combine One or More Lora's To Connect It With Runware Image Inference."
FUNCTION = "loraCombine"
RETURN_TYPES = ("RUNWARELORA",)
RETURN_NAMES = ("Runware Lora's",)
CATEGORY = "Runware"
def loraCombine(self, **kwargs):
runwareLora1 = kwargs.get("Lora 1")
runwareLora2 = kwargs.get("Lora 2", None)
runwareLora3 = kwargs.get("Lora 3", None)
loraObjectArray = []
loraObjectArray.append(runwareLora1)
if(runwareLora2):
loraObjectArray.append(runwareLora2)
if(runwareLora3):
loraObjectArray.append(runwareLora3)
return (loraObjectArray,)
+72
View File
@@ -0,0 +1,72 @@
class loraSearch:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Lora Search": ("STRING", {
"tooltip": "Searchg For Specific Lora By Name Or Civit AIR Code (eg: Cyberpunk).",
}),
"Model Architecture": ([
"All",
"FLUX.1-Schnell",
"FLUX.1-Dev",
"Pony",
"SD 1.5",
"SD 1.5 Hyper",
"SD 1.5 LCM",
"SD 3",
"SDXL 1.0",
"SDXL 1.0 LCM",
"SDXL Distilled",
"SDXL Hyper",
"SDXL Lightning",
"SDXL Turbo",
], {
"tooltip": "Choose Lora Model Architecture To Filter Results.",
"default": "All",
}),
"LoraType": ([
"Lora",
], {
"tooltip": "Choose Lora Type To Filter Results.",
"default": "Lora",
}),
"LoraList": ([
"civitai:58390@62833 (Detail Tweaker LoRA (细节调整LoRA))",
"civitai:82098@87153 (Add More Details - Detail Enhancer / Tweaker (细节调整) LoRA)",
"civitai:122359@135867 (Detail Tweaker XL)",
"civitai:14171@16677 (Cute_girl_mix4)",
"civitai:13941@16576 (epi_noiseoffset)",
"civitai:25995@32988 (blindbox/大概是盲盒)",
], {
"tooltip": "Lora Results Will Show UP Here So You Could Choose From.",
"default": "civitai:58390@62833 (Detail Tweaker LoRA (细节调整LoRA))",
}),
"weight": ("FLOAT", {
"tooltip": "Defines the strength or influence of the LoRA model in the generation process.",
"default": 1.0,
"min": -4.0,
"max": 4.0,
"step": 0.1,
}),
},
}
DESCRIPTION = "Directly Search and Connect Lora's to Runware Inference Nodes In ComfyUI."
FUNCTION = "loraSearch"
RETURN_TYPES = ("RUNWARELORA",)
RETURN_NAMES = ("Runware Lora",)
CATEGORY = "Runware"
@classmethod
def VALIDATE_INPUTS(cls, LoraList):
return True
def loraSearch(self, **kwargs):
currentModel = kwargs.get("LoraList")
loraWeight = kwargs.get("weight")
loraAIRCode = currentModel.split(" ")[0]
return ({
"model": loraAIRCode,
"weight": round(loraWeight, 2),
},)
+63
View File
@@ -0,0 +1,63 @@
class modelSearch:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Model Search": ("STRING", {
"tooltip": "Searchg For Specific Model By Name Or Civit AIR Code (eg: Juggernaut).",
}),
"Model Architecture": ([
"All",
"FLUX.1-Schnell",
"FLUX.1-Dev",
"Pony",
"SD 1.5",
"SD 1.5 Hyper",
"SD 1.5 LCM",
"SD 3",
"SDXL 1.0",
"SDXL 1.0 LCM",
"SDXL Distilled",
"SDXL Hyper",
"SDXL Lightning",
"SDXL Turbo",
], {
"tooltip": "Choose Model Architecture To Filter Results.",
"default": "All",
}),
"ModelType": ([
"Base Model",
"Inpainting Model",
], {
"tooltip": "Choose Model Type To Filter Results.",
"default": "Base Model",
}),
"ModelList": ([
"runware:100@1 (Flux Schnell)",
"runware:101@1 (Flux Dev)",
"runware:5@1 (SD3)",
"civitai:4384@128713 (SDXL 1.5 DreamShaper)",
"civitai:43331@176425 (SD 1.5 majicMIX realistic 麦橘写实)",
"civitai:101055@128078 (SDXL v1.0 VAE fix)",
"civitai:133005@288982 (SDXL Juggernaut XL V8)",
], {
"tooltip": "Model Results Will Show UP Here So You Could Choose From. If You didn't Search For Anything this will show featured Model List.",
"default": "runware:100@1 (Flux Schnell)",
}),
},
}
DESCRIPTION = "Directly Search and Connect Model Checkpoints to Runware Inference Nodes In ComfyUI."
FUNCTION = "modelSearch"
RETURN_TYPES = ("RUNWAREMODEL",)
RETURN_NAMES = ("Runware Model",)
CATEGORY = "Runware"
@classmethod
def VALIDATE_INPUTS(cls, ModelList):
return True
def modelSearch(self, **kwargs):
currentModel = kwargs.get("ModelList")
modelAIRCode = currentModel.split(" ")[0]
return (modelAIRCode,)
+179
View File
@@ -0,0 +1,179 @@
from .utils import runwareUtils as rwUtils
class photoMaker:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Model": ("RUNWAREMODEL", {
"tooltip": "Connect a Runware Model From Runware Model Node.",
}),
"Image 1": ("IMAGE", {
"tooltip": "Specifies Input Image 1 that will be used as reference for the subject. These reference images help the AI to maintain identity consistency during the generation process."
}),
"positivePrompt": ("STRING", {
"multiline": True,
"placeholder": "Positive Prompt: a text instruction to guide the model on generating the image. It is usually a sentence or a paragraph that provides positive guidance for the task. This parameter is essential to shape the desired results.\n\nThe PhotoMaker positive prompt must follow a specific format: the class word (like \"man\", \"woman\", \"girl\") must be followed by the trigger word \"img\".\nExample: man img, wearing a suit",
"tooltip": "Positive Prompt: a text instruction to guide the model on generating the image.\nDo not Forget To Add trigger word \"img\" after the class word (like \"man\", \"woman\", \"girl\")",
}),
"negativePrompt": ("STRING", {
"multiline": True,
"placeholder": "Negative Prompt: a text instruction to guide the model on generating the image. It is usually a sentence or a paragraph that provides negative guidance for the task. This parameter helps to avoid certain undesired results.",
"tooltip": "Negative Prompt: a text instruction to guide the model on generating the image."
}),
"style": ([
"No style", "Cinematic", "Disney Character", "Digital Art", "Photographic", "Fantasy art",
"Neonpunk", "Enhance", "Comic book", "Lowpoly", "Line art"
], {
"default": "No style",
"tooltip": "Specifies the artistic style to be applied to the generated images."
}),
"dimensions": ([
"Square (512x512)", "Square HD (1024x1024)", "Portrait 3:4 (768x1024)",
"Portrait 9:16 (576x1024)", "Landscape 4:3 (1024x768)",
"Landscape 16:9 (1024x576)", "Custom"
], {
"default": "Square HD (1024x1024)",
"tooltip": "Adjust the dimensions of the generated image by specifying its width and height in pixels, or select from the predefined options. Image dimensions must be multiples of 64 (e.g., 512x512, 1024x768).",
}),
"width": ("INT", {
"tooltip": "The Width of the image in pixels.",
"default": 1024,
"min": 512,
"max": 2048,
"step": 64,
}),
"height": ("INT", {
"tooltip": "The Height of the image in pixels.",
"default": 1024,
"min": 512,
"max": 2048,
"step": 64,
}),
"steps": ("INT", {
"tooltip": "Guidance scale represents how closely the images will resemble the prompt or how much freedom the AI model has. Higher values are closer to the prompt. Low values may reduce the quality of the results.",
"default": 25,
"min": 1,
"max": 100,
}),
"scheduler": ([
"Default", "DDIMScheduler", "DDIMInverseScheduler", "DDPMScheduler", "DEISMultistepScheduler",
"DPMSolverSinglestepScheduler", "DPMSolverMultistepScheduler", "DPMSolverMultistepInverse", "DPMSolverSDEScheduler", "HeunDiscreteScheduler", "KDPM2DiscreteScheduler", "KDPM2AncestralDiscreteScheduler", "KarrasVeScheduler", "LMSDiscreteScheduler", "PNDMScheduler", "ScoreSdeVeScheduler", "ScoreSdeVpScheduler", "IPNDMScheduler", "EulerDiscreteScheduler", "EulerAncestralDiscreteScheduler", "EDMEulerScheduler", "FlowMatchEulerDiscreteScheduler", "VQDiffusionScheduler", "UniPCMultistepScheduler", "RePaintScheduler", "DPM++ 2M Karras", "DPM++ 2M SDE Karras", "DPM++ 2M SDE", "DPM++ SDE Karras", "DPM++ SDE", "LCMScheduler", "EDMDPMSolverMultistepScheduler", "TCDScheduler"
], {
"tooltip": "An scheduler is a component that manages the inference process. Different schedulers can be used to achieve different results like more detailed images, faster inference, or more accurate results.",
"default": "Default",
}),
"cfgScale": ("FLOAT", {
"tooltip": "The scale of the image generation. The higher the scale, the more detailed the image will be.",
"default": 6.5,
"min": 1.0,
"max": 30.0,
"step": 0.5,
}),
"strength": ("INT", {
"default": 15,
"min": 15,
"max": 50,
"tooltip": "Controls the balance between preserving the subject's original features and the creative transformation specified in the prompt.\n- Lower values provide stronger subject fidelity.\n- Higher values allow more creative freedom in the transformation."
}),
"clipSkip": ("INT", {
"tooltip": "Enables skipping layers of the CLIP embedding process, leading to quicker and more varied image generation.",
"default": 0,
"min": 0,
"max": 2,
}),
"seed": ("INT", {
"tooltip": "A value used to randomize the image generation. If you want to make images reproducible (generate the same image multiple times), you can use the same seed value.",
"default": rwUtils.genRandSeed(),
"min": 1,
"max": 9223372036854776000,
}),
"batchSize": ("INT", {
"tooltip": "The number of images to generate in a single request.",
"default": 1,
"min": 1,
"max": 10,
}),
},
"optional": {
"Image 2": ("IMAGE", {
"tooltip": "Specifies Input Image 2 that will be used as reference for the subject. These reference images help the AI to maintain identity consistency during the generation process."
}),
"Image 3": ("IMAGE", {
"tooltip": "Specifies Input Image 3 that will be used as reference for the subject. These reference images help the AI to maintain identity consistency during the generation process."
}),
"Image 4": ("IMAGE", {
"tooltip": "Specifies Input Image 4 that will be used as reference for the subject. These reference images help the AI to maintain identity consistency during the generation process."
}),
}
}
DESCRIPTION = "Transform and style images using PhotoMaker's advanced personalization technology. Create consistent, high-quality image variations with precise subject fidelity and style control."
FUNCTION = "photoMaker"
RETURN_TYPES = ("IMAGE",)
CATEGORY = "Runware"
@classmethod
def VALIDATE_INPUTS(cls, positivePrompt, negativePrompt):
if (positivePrompt is None or len(positivePrompt) < 3 or len(positivePrompt) > 2000):
raise Exception("Positive Prompt Must Be Between 3 And 2000 characters!")
if (negativePrompt is not None and negativePrompt != "" and (len(negativePrompt) < 3 or len(negativePrompt) > 2000)):
raise Exception("Negative Prompt Must Be Between 3 And 2000 characters!")
return True
def photoMaker(self, **kwargs):
runwareModel = kwargs.get("Model")
image1 = kwargs.get("Image 1")
image2 = kwargs.get("Image 2", None)
image3 = kwargs.get("Image 3", None)
image4 = kwargs.get("Image 4", None)
positivePrompt = kwargs.get("positivePrompt")
negativePrompt = kwargs.get("negativePrompt", None)
style = kwargs.get("style", "No style")
dimensions = kwargs.get("dimensions", "Square HD (1024x1024)")
width = kwargs.get("width", 1024)
height = kwargs.get("height", 1024)
steps = kwargs.get("steps", 25)
scheduler = kwargs.get("scheduler", "Default")
cfgScale = kwargs.get("cfgScale", 6.5)
strength = kwargs.get("strength", 15)
clipSkip = kwargs.get("clipSkip", 0)
seed = kwargs.get("seed")
batchSize = kwargs.get("batchSize", 1)
imageList = [rwUtils.convertTensor2IMG(image1)]
if(image2 is not None):
imageList.append(rwUtils.convertTensor2IMG(image2))
if(image3 is not None):
imageList.append(rwUtils.convertTensor2IMG(image3))
if(image4 is not None):
imageList.append(rwUtils.convertTensor2IMG(image4))
genConfig = [
{
"taskType": "photoMaker",
"taskUUID": rwUtils.genRandUUID(),
"model": runwareModel,
"inputImages": imageList,
"positivePrompt": positivePrompt,
"style": style,
"strength": strength,
"height": height,
"width": width,
"steps": steps,
"scheduler": scheduler,
"seed": seed,
"CFGScale": cfgScale,
"clipSkip": clipSkip,
"numberResults": batchSize,
"outputFormat": "WEBP",
"outputType": "base64Data",
}
]
if(negativePrompt is not None and negativePrompt != ""):
genConfig[0]["negativePrompt"] = negativePrompt
genResult = rwUtils.inferenecRequest(genConfig)
images = rwUtils.convertImageB64List(genResult)
return images
+49
View File
@@ -0,0 +1,49 @@
class refiner:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Model": ("RUNWAREMODEL", {
"tooltip": "Connect a Runware Model From Model Search Node.",
}),
"startStep": ("INT", {
"tooltip": "Represents the step number at which the refinement process begins. The initial model will generate the image up to this step, after which the refiner model takes over to enhance the result.",
"min": 0,
"max": 99,
"default": 0,
}),
"startStepPercentage": ("INT", {
"tooltip": "Represents the percentage of total steps at which the refinement process begins. The initial model will generate the image up to this percentage of steps before the refiner takes over.",
"min": 0,
"max": 99,
"default": 0
}),
},
}
DESCRIPTION = "Refiner models help create higher quality image outputs by incorporating specialized models designed to enhance image details and overall coherence. This can be particularly useful when you need results with superior quality, photorealism, or specific aesthetic refinements. (Note that refiner models are only SDXL based)"
FUNCTION = "refiner"
RETURN_TYPES = ("RUNWAREREFINER",)
CATEGORY = "Runware"
@classmethod
def VALIDATE_INPUTS(cls, startStep, startStepPercentage):
if(startStep != 0 and startStepPercentage != 0):
raise Exception("Please provide either startStep or startStepPercentage, not both.")
elif(startStep == 0 and startStepPercentage == 0):
raise Exception("Please provide either startStep or startStepPercentage.")
else:
return True
def refiner(self, **kwargs):
runwareModel = kwargs.get("Model")
startStep = kwargs.get("startStep")
startStepPercentage = kwargs.get("startStepPercentage")
refinerResult = {
"model": runwareModel,
}
if(startStep != 0):
refinerResult["startStep"] = startStep
else:
refinerResult["startStepPercentage"] = startStepPercentage
return (refinerResult,)
+42
View File
@@ -0,0 +1,42 @@
from .utils import runwareUtils as rwUtils
class upscaler:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"Image": ("IMAGE", {
"tooltip": "Specifies the input image to be upscaled."
}),
"upscaleFactor": ("INT", {
"tooltip": "Each level will increase the size of the image by the corresponding factor.",
"default": 2,
"min": 2,
"max": 4,
}),
},
}
DESCRIPTION = "Enhance the resolution and quality of your images using Runware's advanced upscaling API. Transform low-resolution images into sharp, high-definition visuals."
FUNCTION = "upscale"
RETURN_TYPES = ("IMAGE",)
CATEGORY = "Runware"
def upscale(self, **kwargs):
image = kwargs.get("Image")
upscaleFactor = kwargs.get("upscaleFactor", 2)
genConfig = [
{
"taskType": "imageUpscale",
"taskUUID": rwUtils.genRandUUID(),
"inputImage": rwUtils.convertTensor2IMG(image),
"upscaleFactor": upscaleFactor,
"outputFormat": "WEBP",
"outputType": "base64Data",
}
]
genResult = rwUtils.inferenecRequest(genConfig)
images = rwUtils.convertImageB64List(genResult)
return images
+111
View File
@@ -0,0 +1,111 @@
from requests.adapters import HTTPAdapter
from dotenv import load_dotenv
from pathlib import Path
from PIL import Image
import numpy as np
import requests
import random
import base64
import torch
import uuid
import os
import io
load_dotenv()
SESSION_TIMEOUT = 60
RUNWARE_API_BASE_URL = "https://api.runware.ai/v1"
session = requests.Session()
adapter = HTTPAdapter(pool_connections=10, pool_maxsize=10)
session.mount("http://", adapter)
session.mount("https://", adapter)
def getAPIKey():
apiKey = os.getenv("RUNWARE_API_KEY")
if apiKey and isinstance(apiKey, str) and len(apiKey) > 24:
return apiKey
return False
RUNWARE_API_KEY = getAPIKey()
def setAPIKey(apiKey: str):
global RUNWARE_API_KEY
comfyNodeRoot = Path(__file__).parent.parent.parent
envFilePath = comfyNodeRoot / '.env'
if not envFilePath.exists():
envFilePath.touch()
with open(envFilePath, 'r') as f:
lines = f.readlines()
key_exists = False
new_lines = []
for line in lines:
if line.startswith('RUNWARE_API_KEY='):
key_exists = True
new_lines.append(f'RUNWARE_API_KEY={apiKey}\n')
else:
new_lines.append(line)
if not key_exists:
new_lines.append(f'RUNWARE_API_KEY={apiKey}\n')
with open(envFilePath, 'w') as f:
f.writelines(new_lines)
RUNWARE_API_KEY = apiKey
os.environ["RUNWARE_API_KEY"] = apiKey
return True
def genRandSeed(minSeed = 1000, maxSeed = 9223372036854776000):
return random.randint(minSeed, maxSeed)
def genRandUUID():
return str(uuid.uuid4())
def inferenecRequest(genConfig):
global RUNWARE_API_KEY
RUNWARE_API_KEY = os.getenv("RUNWARE_API_KEY")
headers = {
"Authorization": f"Bearer {RUNWARE_API_KEY}",
"Content-Type": "application/json",
"Accept-Encoding": "gzip, deflate, br, zstd",
}
try:
genResult = session.post(RUNWARE_API_BASE_URL, headers=headers, json=genConfig, timeout=SESSION_TIMEOUT, allow_redirects=False, stream=True)
genResult = genResult.json()
if("errors" in genResult):
raise Exception(genResult["errors"][0]["message"])
else:
return genResult
except requests.exceptions.Timeout:
raise Exception(f"Error: Request Timed Out After {SESSION_TIMEOUT} Seconds - Please Try Again!")
except requests.exceptions.RequestException:
raise Exception("Error: Runware Request Failed!")
except Exception as e:
if "invalid api key" in str(e).lower():
raise Exception("Invalid API Key - Please Make Sure you set your correctly API Key Either in the env file or through the API Manager Node!\n\nIf you don't have an API Key, you can get it from: https://my.runware.ai/keys")
else:
raise Exception(f"Error: {e}")
return False
def convertTensor2IMG(tensorImage):
imageNP = (tensorImage.squeeze().numpy() * 255).astype(np.uint8)
image = Image.fromarray(imageNP)
with io.BytesIO() as buffer:
image.save(buffer, format="webp")
imgb64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
imgDataURI = f'data:image/webp;base64,{imgb64}'
return imgDataURI
def convertIMG2Tensor(b64img):
imgbytes = base64.b64decode(b64img)
image = Image.open(io.BytesIO(imgbytes))
imageNP = np.array(image).astype(np.float32) / 255.0
tensorImage = torch.from_numpy(imageNP).squeeze()
return tensorImage
def convertImageB64List(imageDataObject):
images = ()
for result in imageDataObject["data"]:
generatedImage = result.get("imageBase64Data", result.get("maskImageBase64Data", result.get("guideImageBase64Data", False)))
generatedImage = convertIMG2Tensor(generatedImage)
images += (generatedImage,)
images = torch.stack(images, dim=0)
return (images,)
+1
View File
@@ -0,0 +1 @@
python-dotenv
+1
View File
@@ -0,0 +1 @@
{"last_node_id":7,"last_link_id":8,"nodes":[{"id":4,"type":"Runware Image Masking","pos":[351.7330627441406,132.90028381347656],"size":[315,194],"flags":{},"order":4,"mode":0,"inputs":[{"name":"Image","type":"IMAGE","link":3}],"outputs":[{"name":"Image","type":"IMAGE","links":[4],"slot_index":0},{"name":"Mask Preview","type":"IMAGE","links":null},{"name":"Mask","type":"MASK","links":[5],"slot_index":2}],"properties":{"Node name for S&R":"Runware Image Masking"},"widgets_values":["face_yolov8n",0.25,6,4,4],"bgcolor":"#6c5ce7"},{"id":2,"type":"Runware Model Search","pos":[-75.49443817138672,-288.33795166015625],"size":[315,130],"flags":{"collapsed":true},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"Runware Model","type":"RUNWAREMODEL","links":[2],"slot_index":0}],"properties":{"Node name for S&R":"Runware Model Search"},"widgets_values":["","All","Base Model","civitai:101055@128078 (SDXL v1.0 VAE fix)"],"bgcolor":"#6c5ce7"},{"id":6,"type":"PreviewImage","pos":[1151.2889404296875,-264.380859375],"size":[552.3806762695312,564.0267944335938],"flags":{},"order":6,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":7}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"}},{"id":1,"type":"PreviewImage","pos":[357.7220764160156,-258.3918762207031],"size":[307.82305908203125,331.447509765625],"flags":{},"order":3,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":1}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"}},{"id":7,"type":"Runware Model Search","pos":[691.1177978515625,-322.2765808105469],"size":[315,130],"flags":{"collapsed":true},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"Runware Model","type":"RUNWAREMODEL","links":[8],"slot_index":0}],"properties":{"Node name for S&R":"Runware Model Search"},"widgets_values":["","All","Base Model","runware:101@1 (Flux Dev)"],"bgcolor":"#6c5ce7"},{"id":3,"type":"Runware Image Inference","pos":[-77.49113464355469,-239.42645263671875],"size":[400,562],"flags":{},"order":2,"mode":0,"inputs":[{"name":"Model","type":"RUNWAREMODEL","link":2},{"name":"Lora","type":"RUNWARELORA","link":null,"shape":7},{"name":"ControlNet","type":"RUNWARECONTROLNET","link":null,"shape":7},{"name":"Refiner","type":"RUNWAREREFINER","link":null,"shape":7},{"name":"seedImage","type":"IMAGE","link":null,"shape":7},{"name":"maskImage","type":"MASK","link":null,"shape":7}],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[1,3],"slot_index":0}],"properties":{"Node name for S&R":"Runware Image Inference"},"widgets_values":["a man standing in the woods and looking serious, wide screen, zoomed out, realistic, HD, details","",false,"Square HD (1024x1024)",1024,1024,20,"Default",4.5,502840077979554,"randomize",0,0.8,false,32,1],"bgcolor":"#6c5ce7"},{"id":5,"type":"Runware Image Inference","pos":[700.1026611328125,-276.35955810546875],"size":[421.96026611328125,591.9458618164062],"flags":{},"order":5,"mode":0,"inputs":[{"name":"Model","type":"RUNWAREMODEL","link":8},{"name":"Lora","type":"RUNWARELORA","link":null,"shape":7},{"name":"ControlNet","type":"RUNWARECONTROLNET","link":null,"shape":7},{"name":"Refiner","type":"RUNWAREREFINER","link":null,"shape":7},{"name":"seedImage","type":"IMAGE","link":4,"shape":7},{"name":"maskImage","type":"MASK","link":5,"shape":7}],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[7],"slot_index":0}],"properties":{"Node name for S&R":"Runware Image Inference"},"widgets_values":["a man standing in the woods and looking serious, wide screen, zoomed out, realistic, HD, details","",false,"Square HD (1024x1024)",1024,1024,30,"Default",2,1108798832542204,"randomize",0,0.8,true,32,1],"bgcolor":"#6c5ce7"}],"links":[[1,3,0,1,0,"IMAGE"],[2,2,0,3,0,"RUNWAREMODEL"],[3,3,0,4,0,"IMAGE"],[4,4,0,5,4,"IMAGE"],[5,4,2,5,5,"MASK"],[7,5,0,6,0,"IMAGE"],[8,7,0,5,0,"RUNWAREMODEL"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.001808834538306,"offset":[117.41863536835024,390.59035778045654]}},"version":0.4}
@@ -0,0 +1 @@
{"last_node_id":5,"last_link_id":6,"nodes":[{"id":5,"type":"Runware Background Removal","pos":[481.94317626953125,-300.1940612792969],"size":[333.4461364746094,178],"flags":{},"order":1,"mode":0,"inputs":[{"name":"Image","type":"IMAGE","link":5}],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[6],"slot_index":0}],"properties":{"Node name for S&R":"Runware Background Removal"},"widgets_values":[false,false,false,240,10,10],"bgcolor":"#6c5ce7"},{"id":2,"type":"PreviewImage","pos":[479.01971435546875,-74.14266204833984],"size":[343.48712158203125,246],"flags":{},"order":2,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":6}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"}},{"id":1,"type":"LoadImage","pos":[91.22550201416016,-285.5789794921875],"size":[363.7179260253906,459.1794128417969],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[5],"slot_index":0},{"name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["r2rs1PrivateFLux.png","image"]}],"links":[[5,1,0,5,0,"IMAGE"],[6,5,0,2,0,"IMAGE"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.0263162364614136,"offset":[464.15888590882696,406.82531520554556]}},"version":0.4}
@@ -0,0 +1 @@
{"last_node_id":79,"last_link_id":52,"nodes":[{"id":76,"type":"PreviewImage","pos":[890.173095703125,-194.4432373046875],"size":[442.2727355957031,469.6363525390625],"flags":{},"order":3,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":51}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"}},{"id":77,"type":"Runware Image Inference","pos":[401.4457092285156,-337.6249694824219],"size":[420,608],"flags":{},"order":2,"mode":0,"inputs":[{"name":"Model","type":"RUNWAREMODEL","link":52},{"name":"Lora","type":"RUNWARELORA","link":null,"shape":7},{"name":"ControlNet","type":"RUNWARECONTROLNET","link":null,"shape":7},{"name":"Refiner","type":"RUNWAREREFINER","link":null,"shape":7},{"name":"seedImage","type":"IMAGE","link":null,"shape":7},{"name":"maskImage","type":"MASK","link":null,"shape":7}],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[51],"slot_index":0}],"properties":{"Node name for S&R":"Runware Image Inference"},"widgets_values":["A rebellious model with sharp features adorned with gold make-up and black eyeliner with lavander bob cut. A black off-shoulder top with a pink cats print pattern and choker. She posing in a vibrant, vivid color palette setting with ambient lighting, her expression calm and composed. The scene is painted in Patrick Nagel's painting style, with rich, smooth brushstrokes and a soft, warm color grading. The lighting is soft and diffused, creating a gentle glow around her. ArcaneFGTNR, Niji Stan Katayama Style, cyb3rp0p","",false,"Square (512x512)",512,512,4,"Default",6.5,697387220709399,"randomize",2,0.8,false,32,1],"bgcolor":"#6c5ce7"},{"id":79,"type":"Note","pos":[-46.554290771484375,-124.62498474121094],"size":[418,327],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Hello there! 🌟\n\nWelcome to the Runware Image Inference workflow. 🎉\n\nHere’s what you need to know to get started: \n\n- You can explore and search from a variety of models. 🧠✨\n \n- Remember to adjust the settings appropriately to achieve the desired results. ⚙️✅\n\n- Don’t forget to tweak the **\"steps\"** for optimal performance. 🛠️📈\n\nThe best part? We support a wide range of models, so dive in and have fun experimenting! 🚀❤️\n\nEnjoy your creative journey! 💡😊\n\n\ntip: Press \"Ctrl + Alt + E\" To Automatically Enhance Your Prompt\n\ntip: You can reset the model, lora search by clearing the search input and everything will get back to defaults."],"color":"#432","bgcolor":"#653"},{"id":78,"type":"Runware Model Search","pos":[5.445709228515625,-324.625],"size":[315,130],"flags":{"collapsed":false},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"Runware Model","type":"RUNWAREMODEL","links":[52],"slot_index":0}],"properties":{"Node name for S&R":"Runware Model Search"},"widgets_values":["","All","Base Model","runware:100@1 (Flux Schnell)"],"bgcolor":"#6c5ce7"}],"links":[[51,77,0,76,0,"IMAGE"],[52,78,0,77,0,"RUNWAREMODEL"]],"groups":[],"config":{},"extra":{"ds":{"scale":1,"offset":[260.5542915265911,467.0624812864706]}},"version":0.4}
@@ -0,0 +1 @@
{"last_node_id":4,"last_link_id":3,"nodes":[{"id":3,"type":"Runware Model Search","pos":[-170.87696838378906,-336.2454528808594],"size":[315,130],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"Runware Model","type":"RUNWAREMODEL","links":[2],"slot_index":0}],"properties":{"Node name for S&R":"Runware Model Search"},"widgets_values":["","All","Base Model","runware:100@1 (Flux Schnell)"],"bgcolor":"#6c5ce7"},{"id":2,"type":"Runware Image Inference","pos":[191.5845489501953,-370.3477478027344],"size":[410.71795654296875,598.05126953125],"flags":{},"order":2,"mode":0,"inputs":[{"name":"Model","type":"RUNWAREMODEL","link":2},{"name":"Lora","type":"RUNWARELORA","link":3,"shape":7},{"name":"ControlNet","type":"RUNWARECONTROLNET","link":null,"shape":7},{"name":"Refiner","type":"RUNWAREREFINER","link":null,"shape":7},{"name":"seedImage","type":"IMAGE","link":null,"shape":7},{"name":"maskImage","type":"MASK","link":null,"shape":7}],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[1],"slot_index":0}],"properties":{"Node name for S&R":"Runware Image Inference"},"widgets_values":["a guy wearing red glasses, anime, pixiv top monthly, trending on artstation, cinematic, danbooru, zerochan art, kyoto animation, niji","",false,"Square (512x512)",512,512,4,"Default",6.5,258656211183721,"randomize",0,0.8,false,32,1],"bgcolor":"#6c5ce7"},{"id":4,"type":"Runware Lora Search","pos":[-162.10772705078125,-143.322265625],"size":[315,154],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"Runware Lora","type":"RUNWARELORA","links":[3],"slot_index":0}],"properties":{"Node name for S&R":"Runware Lora Search"},"widgets_values":["niji","FLUX.1-Schnell","Lora","civitai:768440@859463 (Flux-Better-Art-With-Niji Schnell)",1],"bgcolor":"#6c5ce7"},{"id":1,"type":"PreviewImage","pos":[627.12255859375,-365.4764404296875],"size":[458.46142578125,484.9228515625],"flags":{},"order":3,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":1}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"}}],"links":[[1,2,0,1,0,"IMAGE"],[2,3,0,2,0,"RUNWAREMODEL"],[3,4,0,2,1,"RUNWARELORA"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.0263162364614136,"offset":[606.4152342088262,463.3381111055449]}},"version":0.4}
@@ -0,0 +1 @@
{"last_node_id":5,"last_link_id":3,"nodes":[{"id":4,"type":"Runware Image Upscaler","pos":[139.44570922851562,-279.625],"size":[315,58],"flags":{},"order":1,"mode":0,"inputs":[{"name":"Image","type":"IMAGE","link":3}],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[2],"slot_index":0}],"properties":{"Node name for S&R":"Runware Image Upscaler"},"widgets_values":[4],"bgcolor":"#6c5ce7"},{"id":1,"type":"PreviewImage","pos":[497.6885986328125,-288.6935729980469],"size":[593.5640869140625,614.8204345703125],"flags":{},"order":2,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":2}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"}},{"id":5,"type":"LoadImage","pos":[155.82870483398438,-114.90699005126953],"size":[320.84613037109375,411.43585205078125],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[3],"slot_index":0},{"name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["qwd2s.jpg","image"]}],"links":[[2,4,0,1,0,"IMAGE"],[3,5,0,4,0,"IMAGE"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.0263162364614136,"offset":[452.4665833088276,458.4663183555447]}},"version":0.4}
+1
View File
@@ -0,0 +1 @@
{"last_node_id":6,"last_link_id":6,"nodes":[{"id":4,"type":"LoadImage","pos":[0.36829668283462524,-416.1068420410156],"size":[401.6533203125,200.8212432861328],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[4],"slot_index":0},{"name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["qwd2qdasf.jpg","image"]},{"id":6,"type":"Runware Model Search","pos":[426.5971374511719,-462.02398681640625],"size":[315,130],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"Runware Model","type":"RUNWAREMODEL","links":[5],"slot_index":0}],"properties":{"Node name for S&R":"Runware Model Search"},"widgets_values":["","All","Base Model","runware:101@1 (Flux Dev)"],"bgcolor":"#6c5ce7"},{"id":5,"type":"Runware ControlNet PreProcessor","pos":[-3.6244616508483887,-163.56333923339844],"size":[390.5999755859375,226],"flags":{},"order":2,"mode":0,"inputs":[{"name":"Image","type":"IMAGE","link":4}],"outputs":[{"name":"GUIDE IMAGE","type":"IMAGE","links":[6],"slot_index":0}],"properties":{"Node name for S&R":"Runware ControlNet PreProcessor"},"widgets_values":["OpenPose",true,100,200,false,"Square HD (1024x1024)",1024,1024],"bgcolor":"#6c5ce7"},{"id":1,"type":"PreviewImage","pos":[843.8425903320312,-253.40106201171875],"size":[593.306640625,620.9238891601562],"flags":{},"order":5,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":1}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"}},{"id":2,"type":"Runware Image Inference","pos":[415.61724853515625,-281.35052490234375],"size":[413.9747009277344,664.8140258789062],"flags":{},"order":4,"mode":0,"inputs":[{"name":"Model","type":"RUNWAREMODEL","link":5},{"name":"Lora","type":"RUNWARELORA","link":null,"shape":7},{"name":"ControlNet","type":"RUNWARECONTROLNET","link":2,"shape":7},{"name":"Refiner","type":"RUNWAREREFINER","link":null,"shape":7},{"name":"seedImage","type":"IMAGE","link":null,"shape":7},{"name":"maskImage","type":"MASK","link":null,"shape":7}],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[1],"slot_index":0}],"properties":{"Node name for S&R":"Runware Image Inference"},"widgets_values":["a girl wearing a red dress, with curly hair, very anime!!! fine - face, pretty face, realistic shaded perfect face, fine details by stanley artgerm lau, wlop, rossdraws, james jean, andrei riabovitchev, marc simonetti, and sakimichan, trending on artstation","",false,"Square HD (1024x1024)",1024,1024,30,"Default",2.5,164154394413164,"randomize",0,0.8,false,32,1],"bgcolor":"#6c5ce7"},{"id":3,"type":"Runware ControlNet","pos":[4.361094951629639,107.9453125],"size":[380.4000244140625,274],"flags":{},"order":3,"mode":0,"inputs":[{"name":"Guide Image","type":"IMAGE","link":6}],"outputs":[{"name":"Runware ControlNet","type":"RUNWARECONTROLNET","links":[2],"slot_index":0}],"properties":{"Node name for S&R":"Runware ControlNet"},"widgets_values":["","FLUX.1-Dev","Open Pose","runware:29@1 (ControlNetFlux (CNFlux) instantx-union)",-1,0,-1,65,"balanced",0.8],"bgcolor":"#6c5ce7"}],"links":[[1,2,0,1,0,"IMAGE"],[2,3,0,2,2,"RUNWARECONTROLNET"],[4,4,0,5,0,"IMAGE"],[5,6,0,2,0,"RUNWAREMODEL"],[6,5,0,3,0,"IMAGE"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.001808834538306,"offset":[369.9618265217751,378.612024603221]}},"version":0.4}
@@ -0,0 +1 @@
{"last_node_id":6,"last_link_id":6,"nodes":[{"id":1,"type":"Runware Model Search","pos":[-180.6204376220703,-331.3739318847656],"size":[315,130],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"Runware Model","type":"RUNWAREMODEL","links":[1],"slot_index":0}],"properties":{"Node name for S&R":"Runware Model Search"},"widgets_values":["","All","Base Model","civitai:101055@128078 (SDXL v1.0 VAE fix)"],"bgcolor":"#6c5ce7"},{"id":4,"type":"PreviewImage","pos":[616.4048461914062,-342.0920104980469],"size":[572.4613647460938,603.7946166992188],"flags":{},"order":4,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":3}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"}},{"id":6,"type":"Runware Model Search","pos":[-169.90292358398438,-124.80946350097656],"size":[315,130],"flags":{"collapsed":false},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"Runware Model","type":"RUNWAREMODEL","links":[6],"slot_index":0}],"properties":{"Node name for S&R":"Runware Model Search"},"widgets_values":["","All","Base Model","civitai:133005@288982 (SDXL Juggernaut XL V8)"],"bgcolor":"#6c5ce7"},{"id":2,"type":"Runware Image Inference","pos":[183.7898406982422,-340.14276123046875],"size":[410.71795654296875,598.05126953125],"flags":{},"order":3,"mode":0,"inputs":[{"name":"Model","type":"RUNWAREMODEL","link":1},{"name":"Lora","type":"RUNWARELORA","link":null,"shape":7},{"name":"ControlNet","type":"RUNWARECONTROLNET","link":null,"shape":7},{"name":"Refiner","type":"RUNWAREREFINER","link":4,"shape":7},{"name":"seedImage","type":"IMAGE","link":null,"shape":7},{"name":"maskImage","type":"MASK","link":null,"shape":7}],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[3],"slot_index":0}],"properties":{"Node name for S&R":"Runware Image Inference"},"widgets_values":["A juicy burger on the lunar surface, an astronaut in a sleek white spacesuit gestures towards it, with the vibrant blue Earth rising behind him, soft golden light illuminating the burger's crispy bun and melted cheese, casting a warm glow on the desolate moon rocks.","",false,"Square HD (1024x1024)",1024,1024,50,"Default",6.5,362638305241772,"randomize",0,0.8,false,32,1],"bgcolor":"#6c5ce7"},{"id":5,"type":"Runware Refiner","pos":[-175.74874877929688,74.93402099609375],"size":[315,82],"flags":{},"order":2,"mode":0,"inputs":[{"name":"Model","type":"RUNWAREMODEL","link":6}],"outputs":[{"name":"RUNWAREREFINER","type":"RUNWAREREFINER","links":[4],"slot_index":0}],"properties":{"Node name for S&R":"Runware Refiner"},"widgets_values":[0,60],"bgcolor":"#6c5ce7"}],"links":[[1,1,0,2,0,"RUNWAREMODEL"],[3,2,0,4,0,"IMAGE"],[4,5,0,2,3,"RUNWAREREFINER"],[6,6,0,5,0,"RUNWAREMODEL"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.0263162364614136,"offset":[568.4152507588258,429.23556185554514]}},"version":0.4}
+1
View File
@@ -0,0 +1 @@
{"last_node_id":30,"last_link_id":36,"nodes":[{"id":26,"type":"LoadImage","pos":[-440.0780029296875,-294.6051025390625],"size":[371,462],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[31],"slot_index":0},{"name":"MASK","type":"MASK","links":[32],"slot_index":1}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["clipspace/clipspace-mask-6099630.100000024.png [input]","image"]},{"id":25,"type":"Runware Model Search","pos":[-427.0780029296875,-472.6051025390625],"size":[315,130],"flags":{"collapsed":false},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"Runware Model","type":"RUNWAREMODEL","links":[30],"slot_index":0}],"properties":{"Node name for S&R":"Runware Model Search"},"widgets_values":["","SDXL Lightning","Base Model","civitai:133005@357609 (Juggernaut XL from RunDiffusion V9+RDPhoto2-Lightning_4S)"],"bgcolor":"#6c5ce7"},{"id":23,"type":"PreviewImage","pos":[423.9219970703125,-451.6051025390625],"size":[553,581],"flags":{},"order":3,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":29}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":24,"type":"Runware Image Inference","pos":[-42.0780029296875,-489.6051025390625],"size":[443,656],"flags":{},"order":2,"mode":0,"inputs":[{"name":"Model","type":"RUNWAREMODEL","link":30},{"name":"Lora","type":"RUNWARELORA","link":null,"shape":7},{"name":"ControlNet","type":"RUNWARECONTROLNET","link":null,"shape":7},{"name":"Refiner","type":"RUNWAREREFINER","link":null,"shape":7},{"name":"seedImage","type":"IMAGE","link":31,"shape":7},{"name":"maskImage","type":"MASK","link":32,"shape":7}],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[29],"slot_index":0}],"properties":{"Node name for S&R":"Runware Image Inference"},"widgets_values":["a cat","",false,"Square HD (1024x1024)",1024,1024,8,"Default",2.5,696000855492372,"randomize",0,0.79,true,50,1],"bgcolor":"#6c5ce7"}],"links":[[29,24,0,23,0,"IMAGE"],[30,25,0,24,0,"RUNWAREMODEL"],[31,26,0,24,4,"IMAGE"],[32,26,1,24,5,"MASK"]],"groups":[],"config":{},"extra":{"ds":{"scale":1,"offset":[634.0779979367994,575.0425882124623]}},"version":0.4}
+1
View File
@@ -0,0 +1 @@
{"last_node_id":13,"last_link_id":13,"nodes":[{"id":10,"type":"LoadImage","pos":[-11.610055923461914,-225.45167541503906],"size":[324.9819641113281,419.80859375],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[11],"slot_index":0},{"name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ggasdw.jpg","image"]},{"id":11,"type":"LoadImage","pos":[28.317718505859375,-268.37384033203125],"size":[315,314],"flags":{"collapsed":true},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[12],"slot_index":0},{"name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["qwd2qdasf.jpg","image"]},{"id":12,"type":"LoadImage","pos":[172.05775451660156,-269.3723449707031],"size":[315,314],"flags":{"collapsed":true},"order":2,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[9]},{"name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["qwd2s.jpg","image"]},{"id":13,"type":"Runware Model Search","pos":[108.17327117919922,-327.2672424316406],"size":[315,130],"flags":{"collapsed":true},"order":3,"mode":0,"inputs":[],"outputs":[{"name":"Runware Model","type":"RUNWAREMODEL","links":[13],"slot_index":0}],"properties":{"Node name for S&R":"Runware Model Search"},"widgets_values":["","All","Base Model","civitai:133005@288982 (SDXL Juggernaut XL V8)"],"bgcolor":"#6c5ce7"},{"id":9,"type":"Runware PhotoMaker V2","pos":[333.765380859375,-334.2550964355469],"size":[411.97833251953125,527.9025268554688],"flags":{},"order":4,"mode":0,"inputs":[{"name":"Model","type":"RUNWAREMODEL","link":13},{"name":"Image 1","type":"IMAGE","link":9,"shape":7},{"name":"Image 2","type":"IMAGE","link":12,"shape":7},{"name":"Image 3","type":"IMAGE","link":11,"shape":7},{"name":"Image 4","type":"IMAGE","link":null,"shape":7}],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[5],"slot_index":0}],"properties":{"Node name for S&R":"Runware PhotoMaker V2"},"widgets_values":["a girl wearing a red dress, ilya kuvshinov, anime, pixiv top monthly, trending on artstation, cinematic, danbooru, zerochan art, kyoto animation","nsfw, bad quality, deformed, out of frame, watermark, text","Fantasy art","Square HD (1024x1024)",1024,1024,25,"Default",6.5,15,0,484392277852135,"randomize",1],"bgcolor":"#6c5ce7"},{"id":7,"type":"PreviewImage","pos":[772.9705200195312,-347.2317810058594],"size":[528.4240112304688,550.0520629882812],"flags":{},"order":5,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":5}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]}],"links":[[5,9,0,7,0,"IMAGE"],[9,12,0,9,1,"IMAGE"],[11,10,0,9,3,"IMAGE"],[12,11,0,9,2,"IMAGE"],[13,13,0,9,0,"RUNWAREMODEL"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.001808834538306,"offset":[394.91668730768725,442.4964682151535]}},"version":0.4}