Compare commits
55
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55133b0910 | ||
|
|
da4c773cb7 | ||
|
|
fc1d1aff05 | ||
|
|
44adccf16d | ||
|
|
d465f55b3a | ||
|
|
fcce3cbfb3 | ||
|
|
fb1eed93ae | ||
|
|
2990e8f024 | ||
|
|
bb9b1d5755 | ||
|
|
4ac24cbd5a | ||
|
|
7a8276a8e4 | ||
|
|
68a83db7a5 | ||
|
|
cdc1e52076 | ||
|
|
449b6ebb84 | ||
|
|
02ead854bf | ||
|
|
eaca630863 | ||
|
|
c72754d15b | ||
|
|
731b03634a | ||
|
|
c5193119cf | ||
|
|
7a97620e78 | ||
|
|
8cf75db231 | ||
|
|
8c86560f23 | ||
|
|
3af0cc4341 | ||
|
|
bba67b3767 | ||
|
|
1d10365fe1 | ||
|
|
00c6822881 | ||
|
|
beadb83b5d | ||
|
|
f50bf3f2ed | ||
|
|
67f237b37c | ||
|
|
93971fc014 | ||
|
|
e4ea36e135 | ||
|
|
f42b100e1b | ||
|
|
9f3bfab023 | ||
|
|
424678c8e8 | ||
|
|
e4cf57f129 | ||
|
|
c45ad0647d | ||
|
|
158838158b | ||
|
|
6c1d953896 | ||
|
|
808721a995 | ||
|
|
7510ccca3a | ||
|
|
7d3edf6174 | ||
|
|
396d56eb2a | ||
|
|
e4a6238935 | ||
|
|
796013d91a | ||
|
|
0b935dcffc | ||
|
|
99155fb898 | ||
|
|
c266970425 | ||
|
|
83a1f760f4 | ||
|
|
7886ec31e2 | ||
|
|
cbd980ac92 | ||
|
|
0ef5288ff6 | ||
|
|
928091f7bb | ||
|
|
0a0e7eebaa | ||
|
|
6357dfccf0 | ||
|
|
9e6f75a7bf |
@@ -0,0 +1 @@
|
||||
*.pyc
|
||||
@@ -1,30 +1,63 @@
|
||||
# BRIA ComfyUI API Nodes
|
||||
|
||||
## Overview
|
||||
This repository contains custom nodes for ComfyUI that allow access to BRIA's API endpoints. You can find our API documentation [here](https://bria-ai-api-docs.redoc.ly/#operation//generation/bria-v2/text-to-image).
|
||||
<p align="center" style="background-color:black; padding:10px;">
|
||||
<img src="./images/Bria Logo.svg" alt="BRIA Logo" width="200"/>
|
||||
</p>
|
||||
|
||||
To use the nodes in the workflow, you need a valid BRIA API token. You can get one [here](https://bria.ai/api/) (with 1000 free calls)
|
||||
This repository provides custom nodes for ComfyUI, enabling direct access to **BRIA's API endpoints** for image generation workflows. **API documentation** is available [**here**](https://bria-ai-api-docs.redoc.ly/#operation//generation/bria-v2/text-to-image).
|
||||
|
||||
You can load the workflow, which includes all available nodes, by importing the [workflow.json](workflow.json) file in this repo.
|
||||
An API token is required to use the nodes in your workflows. Get started quickly here
|
||||
<a href="https://bria.ai/api/" style="text-decoration:none; vertical-align:middle;">
|
||||
<img src="https://img.shields.io/badge/GET%20YOUR%20TOKEN-1000%20Free%20Calls-blue?style=flat-square" alt="Get Your Token" height="20">
|
||||
</a>.
|
||||
|
||||
You can also download the following image and import it to comfyui:
|
||||
To load a workflow, import the compatible workflow.json files from this [folder](workflows).
|
||||
|
||||
<img src="./images/eraser_workflow.png" alt="Original image" width="500"/>
|
||||
|
||||
An illustration of the workflow:
|
||||
<!-- Placeholder image of cool workflows. -->
|
||||
|
||||
<img src="./images/eraser_workflow_diagram.jpg" alt="Eraser workflow example" width="650"/> <img src="./images/original_image.jpg" alt="Original image" width="150"/>
|
||||
|
||||
## Available Nodes
|
||||
<!-- <img src="./images/bria_api_nodes_workflow_diagram.png" alt="all workflows example" width="400"/> <img src="./images/bria_api_nodes_workflow_diagram_2.png" alt="all workflows example" width="400"/> -->
|
||||
|
||||
### Eraser
|
||||
The **Eraser** node allows users to remove specific objects or areas from an image by providing a mask.
|
||||
# Coming soon
|
||||
|
||||
This functionality is powered by BRIA's ControlNet inpainting, available on [this model card](https://huggingface.co/briaai/BRIA-2.3-ControlNet-Inpainting) on Hugging Face.
|
||||
- [ ] Video Editing
|
||||
|
||||
You can also try out BRIA's Eraser demo by visiting our Hugging Face space [here](https://huggingface.co/spaces/briaai/BRIA-Eraser-API).
|
||||
# Available Nodes
|
||||
|
||||
## Installation
|
||||
## Image Generation Nodes
|
||||
|
||||
### **Text2Image Base Node**
|
||||
This node generates images from text prompts, serving as the foundation for creating visuals based on descriptive input. [[🤗model card](https://huggingface.co/briaai/BRIA-2.3)]
|
||||
|
||||
## Tailored Generation Nodes
|
||||
These nodes use pre-trained tailored models to generate images that faithfully reproduce specific visual IP elements or guidelines. [[API docs](https://bria-ai-api-docs.redoc.ly/tag/Tailored-Generation)].
|
||||
|
||||
### **Tailored Gen**
|
||||
This node generates images using a trained tailored model, faithfully reproducing specific visual IP elements or guidelines established during model training.
|
||||
|
||||
### **Tailored Model Info**
|
||||
This node retrieves the **default settings** and **prompt prefix** of a **trained tailored model**. It provides the necessary information to configure and run the model in the **Tailored Gen node**, ensuring consistency with the model's intended behavior.
|
||||
|
||||
## Image Editing Nodes
|
||||
These nodes modify specific parts of images, enabling adjustments, while maintaining the integrity of the rest of the image.[[API docs](https://bria-ai-api-docs.redoc.ly/tag/Image-Editing)]
|
||||
|
||||
### **Eraser**
|
||||
This node is used to remove specific objects or areas from an image by providing a mask. Powered by BRIA's ControlNet inpainting [[🤗model card](https://huggingface.co/briaai/BRIA-2.3-ControlNet-Inpainting)] [[🤗HF demo](https://huggingface.co/spaces/briaai/BRIA-Eraser-API)].
|
||||
|
||||
### **GenFill**
|
||||
This node is used to generate objects by prompt in a specific region of an image. This functionality is powered by BRIA's ControlNet Generative Fill. [[🤗model card](https://huggingface.co/briaai/BRIA-2.3-ControlNet-Generative-Fill)] [[🤗HF demo](https://huggingface.co/spaces/briaai/BRIA-Generative-Fill-API)]
|
||||
|
||||
## Product Shot Generation Nodes
|
||||
These nodes create high-quality product images for eCommerce workflows. [[API docs](https://bria-ai-api-docs.redoc.ly/tag/Product-Shots-Generation)]
|
||||
|
||||
### **ShotByText**
|
||||
This node is used to modify the background in an image by providing a prompt, This functionality is powered by BRIA's ControlNet Background-Generation.[[🤗ContrlNet model card](https://huggingface.co/briaai/BRIA-2.3-ControlNet-BG-Gen)] [[🤗HF demo](https://huggingface.co/spaces/briaai/Product-Shot-Generation)].
|
||||
|
||||
### **ShotByImage**
|
||||
This node is used to modify the background in an image by providing a reference image. This functionality is powered by BRIA's ControlNet Background-Generation and BRIA's Image-Prompt. [[🤗ContrlNet model card](https://huggingface.co/briaai/BRIA-2.3-ControlNet-Inpainting)] [[🤗IP-Adapter model card](https://huggingface.co/briaai/Image-Prompt)] [[🤗HF demo](https://huggingface.co/spaces/briaai/Product-Shot-Generation)].
|
||||
|
||||
# Installation
|
||||
There are two methods to install the BRIA ComfyUI API nodes:
|
||||
|
||||
### Method 1: Using ComfyUI's Custom Node Manager
|
||||
@@ -43,3 +76,6 @@ There are two methods to install the BRIA ComfyUI API nodes:
|
||||
```
|
||||
|
||||
3. Restart ComfyUI and load the workflows.
|
||||
|
||||
<!-- ### Campaign generation
|
||||
Coming soon -->
|
||||
|
||||
+21
-3
@@ -1,11 +1,29 @@
|
||||
from .bria_api_node import EraserNode
|
||||
|
||||
from .nodes import (EraserNode, GenFillNode, ShotByTextNode, ShotByImageNode, TailoredGenNode,
|
||||
TailoredModelInfoNode, Text2ImageBaseNode, Text2ImageFastNode, Text2ImageHDNode,
|
||||
ReimagineNode)
|
||||
# Map the node class to a name used internally by ComfyUI
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"BriaEraser": EraserNode, # Return the class, not an instance
|
||||
"BriaGenFill": GenFillNode,
|
||||
"ShotByTextNode": ShotByTextNode,
|
||||
"ShotByImageNode": ShotByImageNode,
|
||||
"BriaTailoredGen": TailoredGenNode,
|
||||
"TailoredModelInfoNode": TailoredModelInfoNode,
|
||||
"Text2ImageBaseNode": Text2ImageBaseNode,
|
||||
"Text2ImageFastNode": Text2ImageFastNode,
|
||||
"Text2ImageHDNode": Text2ImageHDNode,
|
||||
"ReimagineNode": ReimagineNode,
|
||||
}
|
||||
|
||||
# Map the node display name to the one shown in the ComfyUI node interface
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"BriaEraser": "Bria Eraser",
|
||||
"BriaGenFill": "Bria GenFill",
|
||||
"ShotByTextNode": "Bria Shot By Text",
|
||||
"ShotByImageNode": "Bria Shot By Image",
|
||||
"BriaTailoredGen": "Bria Tailored Gen",
|
||||
"TailoredModelInfoNode": "Bria Tailored Model Info",
|
||||
"Text2ImageBaseNode": "Bria Text2Image Base",
|
||||
"Text2ImageFastNode": "Bria Text2Image Fast",
|
||||
"Text2ImageHDNode": "Bria Text2Image HD",
|
||||
"ReimagineNode": "Bria Reimagine",
|
||||
}
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
import numpy as np
|
||||
import requests
|
||||
from PIL import Image
|
||||
import io
|
||||
import base64
|
||||
from torchvision.transforms import ToPILImage, ToTensor
|
||||
import torch
|
||||
|
||||
# Base class for shared functionality between both nodes
|
||||
class BriaAPINode:
|
||||
def __init__(self, api_url):
|
||||
self.api_url = api_url
|
||||
|
||||
def preprocess_image(self, image):
|
||||
if isinstance(image, torch.Tensor):
|
||||
# Print image shape for debugging
|
||||
if image.dim() == 4: # (batch_size, height, width, channels)
|
||||
image = image.squeeze(0) # Remove the batch dimension (1)
|
||||
# Convert to PIL after permuting to (height, width, channels)
|
||||
image = ToPILImage()(image.permute(2, 0, 1)) # (height, width, channels)
|
||||
else:
|
||||
print("Unexpected image dimensions. Expected 4D tensor.")
|
||||
return image
|
||||
|
||||
def preprocess_mask(self, mask):
|
||||
if isinstance(mask, torch.Tensor):
|
||||
# Print mask shape for debugging
|
||||
if mask.dim() == 3: # (batch_size, height, width)
|
||||
mask = mask.squeeze(0) # Remove the batch dimension (1)
|
||||
# Convert to PIL (grayscale mask)
|
||||
mask = ToPILImage()(mask) # No permute needed for grayscale
|
||||
else:
|
||||
print("Unexpected mask dimensions. Expected 3D tensor.")
|
||||
return mask
|
||||
|
||||
def image_to_base64(self, pil_image):
|
||||
# Convert a PIL image to a base64-encoded string
|
||||
buffered = io.BytesIO()
|
||||
pil_image.save(buffered, format="PNG") # Save the image to the buffer in PNG format
|
||||
buffered.seek(0) # Rewind the buffer to the beginning
|
||||
return base64.b64encode(buffered.getvalue()).decode('utf-8')
|
||||
|
||||
def process_request(self, image, mask, api_key):
|
||||
if api_key.strip() == "" or api_key.strip() == "BRIA_API_TOKEN":
|
||||
raise Exception("Please insert a valid API key.")
|
||||
|
||||
# Check if image and mask are tensors, if so, convert to NumPy arrays
|
||||
if isinstance(image, torch.Tensor):
|
||||
image = self.preprocess_image(image)
|
||||
if isinstance(mask, torch.Tensor):
|
||||
mask = self.preprocess_mask(mask)
|
||||
|
||||
# Convert the image and mask directly to Base64 strings
|
||||
image_base64 = self.image_to_base64(image)
|
||||
mask_base64 = self.image_to_base64(mask)
|
||||
|
||||
# Prepare the API request payload
|
||||
payload = {
|
||||
"file": f"{image_base64}",
|
||||
"mask_file": f"{mask_base64}"
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"api_token": f"{api_key}"
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(self.api_url, json=payload, headers=headers)
|
||||
# Check for successful response
|
||||
if response.status_code == 200:
|
||||
print('response is 200')
|
||||
# Process the output image from API response
|
||||
response_dict = response.json()
|
||||
image_response = requests.get(response_dict['result_url'])
|
||||
result_image = Image.open(io.BytesIO(image_response.content))
|
||||
result_image = result_image.convert("RGBA")
|
||||
result_image = np.array(result_image).astype(np.float32) / 255.0
|
||||
result_image = torch.from_numpy(result_image)[None,]
|
||||
# image_tensor = image_tensor = ToTensor()(output_image)
|
||||
# image_tensor = image_tensor.permute(1, 2, 0) / 255.0 # Shape now becomes [1, 2200, 1548, 3]
|
||||
# print(f"output tensor shape is: {image_tensor.shape}")
|
||||
return (result_image,)
|
||||
else:
|
||||
raise Exception(f"Error: API request failed with status code {response.status_code}")
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"{e}")
|
||||
|
||||
# Eraser Node
|
||||
class EraserNode(BriaAPINode):
|
||||
@staticmethod
|
||||
def INPUT_TYPES():
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",), # Input image from another node
|
||||
"mask": ("MASK",), # Binary mask input
|
||||
"api_key": ("STRING", {"default": "BRIA_API_TOKEN"}) # API Key input with a default value
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("output_image",)
|
||||
CATEGORY = "API Nodes"
|
||||
FUNCTION = "execute" # This is the method that will be executed
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("https://engine.prod.bria-api.com/v1/eraser") # Eraser API URL
|
||||
|
||||
# Define the execute method as expected by ComfyUI
|
||||
def execute(self, image, mask, api_key):
|
||||
return self.process_request(image, mask, api_key)
|
||||
|
||||
|
||||
# Generative Fill Node
|
||||
class GenerativeFillNode(BriaAPINode):
|
||||
@staticmethod
|
||||
def INPUT_TYPES():
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",), # Input image from another node
|
||||
"mask": ("MASK",), # Binary mask input
|
||||
"prompt": ("STRING",),
|
||||
"api_key": ("STRING", {"default": "BRIA_API_TOKEN"}), # API Key input with a default value
|
||||
},
|
||||
"optional": {
|
||||
"negative_prompt": ("STRING", {"default": None}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("output_image",)
|
||||
CATEGORY = "API Nodes"
|
||||
FUNCTION = "execute" # This is the method that will be executed
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("https://engine.prod.bria-api.com/v1/gen_fill") # Eraser API URL
|
||||
|
||||
# Define the execute method as expected by ComfyUI
|
||||
def execute(self, image, mask, prompt, negative_prompt, api_key):
|
||||
if api_key.strip() == "" or api_key.strip() == "BRIA_API_TOKEN":
|
||||
raise Exception("Please insert a valid API key.")
|
||||
|
||||
# Check if image and mask are tensors, if so, convert to NumPy arrays
|
||||
if isinstance(image, torch.Tensor):
|
||||
image = self.preprocess_image(image)
|
||||
if isinstance(mask, torch.Tensor):
|
||||
mask = self.preprocess_mask(mask)
|
||||
|
||||
# Convert the image and mask directly to Base64 strings
|
||||
image_base64 = self.image_to_base64(image)
|
||||
mask_base64 = self.image_to_base64(mask)
|
||||
|
||||
# Prepare the API request payload
|
||||
payload = {
|
||||
"file": f"{image_base64}",
|
||||
"mask_file": f"{mask_base64}",
|
||||
"prompt": prompt,
|
||||
"negative_prompt": negative_prompt,
|
||||
"sync": True
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"api_token": f"{api_key}"
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(self.api_url, json=payload, headers=headers)
|
||||
# Check for successful response
|
||||
if response.status_code == 200:
|
||||
print('response is 200')
|
||||
# Process the output image from API response
|
||||
response_dict = response.json()
|
||||
image_response = requests.get(response_dict['urls'][0])
|
||||
result_image = Image.open(io.BytesIO(image_response.content))
|
||||
result_image = result_image.convert("RGB")
|
||||
result_image = np.array(result_image).astype(np.float32) / 255.0
|
||||
result_image = torch.from_numpy(result_image)[None,]
|
||||
return (result_image,)
|
||||
else:
|
||||
raise Exception(f"Error: API request failed with status code {response.status_code}")
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"{e}")
|
||||
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="132" height="85.104" viewBox="0 0 132 85.104">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-2{fill:#5300c9}.cls-3{fill:#80f}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="Logo" transform="translate(-471.45 -246.19)">
|
||||
<circle id="Ellipse_1" data-name="Ellipse 1" cx="9.553" cy="9.553" r="9.553" transform="translate(560.271 310.103)" style="fill:#d80067"/>
|
||||
<g id="Group_56" data-name="Group 56" transform="translate(471.45 246.19)">
|
||||
<path id="Path_504" data-name="Path 504" class="cls-2" d="M690.915 569.28h-6.548a.077.077 0 0 0-.078.078v6.042a.077.077 0 0 0 .078.078h6.421c2.237 0 3.46-1.224 3.46-3.164a3.008 3.008 0 0 0-3.333-3.034z" transform="translate(-652.51 -521.037)"/>
|
||||
<path id="Path_505" data-name="Path 505" class="cls-2" d="M684.378 502.736h6a3.179 3.179 0 0 0 3.24-2.044 3.127 3.127 0 0 0-3.039-4.032h-6.2a.077.077 0 0 0-.078.078v5.922a.077.077 0 0 0 .077.076z" transform="translate(-652.518 -459.261)"/>
|
||||
<path id="Path_506" data-name="Path 506" class="cls-2" d="M877.962 498.92h-6.5a.077.077 0 0 0-.078.078v6.639a.077.077 0 0 0 .078.078h6.5c2.489 0 4.093-1.35 4.093-3.418 0-2.027-1.604-3.377-4.093-3.377z" transform="translate(-811.664 -461.183)"/>
|
||||
<path id="Path_507" data-name="Path 507" class="cls-2" d="M552.856 278.056a.077.077 0 0 1 .078-.078h2.244a42.566 42.566 0 1 0-2.322 28.112zm-41.9 28.191h-14.609a.077.077 0 0 1-.078-.078v-28.113a.077.077 0 0 1 .078-.078h14.564c5.19 0 8.565 3.08 8.565 7.721a5.646 5.646 0 0 1-4.641 5.949v.169c2.616.211 5.443 2.237 5.443 6.5 0 4.683-3.164 7.929-9.325 7.929zm30.8 0a.074.074 0 0 1-.058-.027l-8.477-9.652a.08.08 0 0 0-.058-.027h-1.913a.077.077 0 0 0-.078.078v9.55a.077.077 0 0 1-.078.078h-6.807a.077.077 0 0 1-.078-.078v-28.113a.077.077 0 0 1 .078-.078h14.227c6.117 0 10.168 3.712 10.168 9.24 0 4.6-2.818 7.914-7.3 8.959a.075.075 0 0 0-.04.124l8.658 9.817a.077.077 0 0 1-.058.128z" transform="translate(-471.45 -246.19)"/>
|
||||
</g>
|
||||
<g id="Group_57" data-name="Group 57" transform="translate(557.15 259.772)">
|
||||
<path id="Path_508" data-name="Path 508" class="cls-3" d="M1147.2 613.979a.078.078 0 0 0-.072-.049h-11.793a.076.076 0 0 0-.072.049l-1.327 3.446a23.417 23.417 0 0 0 15.136 1.414z" transform="translate(-1120.72 -572.602)"/>
|
||||
<path id="Path_509" data-name="Path 509" class="cls-3" d="M1068.251 337.15a23.451 23.451 0 0 0-22.851 18.206h2.591a.077.077 0 0 1 .078.078v17.094a23.542 23.542 0 0 0 4.712 5.68l9.529-22.8a.078.078 0 0 1 .072-.048h7.028a.079.079 0 0 1 .072.048l10.624 25.421a23.445 23.445 0 0 0-11.854-43.675z" transform="translate(-1045.4 -337.15)"/>
|
||||
<path id="Path_510" data-name="Path 510" class="cls-3" d="M1166.073 521.687a.077.077 0 0 0 .072-.106l-3.515-8.992a.078.078 0 0 0-.145 0l-3.475 8.992a.078.078 0 0 0 .072.106z" transform="translate(-1142.042 -486.351)"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 714 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 369 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 MiB |
@@ -0,0 +1,10 @@
|
||||
from .eraser_node import EraserNode
|
||||
from .generative_fill_node import GenFillNode
|
||||
from .shot_by_text_node import ShotByTextNode
|
||||
from .shot_by_image_node import ShotByImageNode
|
||||
from .tailored_gen_node import TailoredGenNode
|
||||
from .tailored_model_info_node import TailoredModelInfoNode
|
||||
from .text_2_image_base_node import Text2ImageBaseNode
|
||||
from .text_2_image_fast_node import Text2ImageFastNode
|
||||
from .text_2_image_hd_node import Text2ImageHDNode
|
||||
from .reimagine_node import ReimagineNode
|
||||
@@ -0,0 +1,92 @@
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import io
|
||||
import torch
|
||||
import base64
|
||||
from torchvision.transforms import ToPILImage
|
||||
import requests
|
||||
|
||||
def postprocess_image(image):
|
||||
result_image = Image.open(io.BytesIO(image))
|
||||
result_image = result_image.convert("RGB")
|
||||
result_image = np.array(result_image).astype(np.float32) / 255.0
|
||||
result_image = torch.from_numpy(result_image)[None,]
|
||||
return result_image
|
||||
|
||||
def image_to_base64(pil_image):
|
||||
# Convert a PIL image to a base64-encoded string
|
||||
buffered = io.BytesIO()
|
||||
pil_image.save(buffered, format="PNG") # Save the image to the buffer in PNG format
|
||||
buffered.seek(0) # Rewind the buffer to the beginning
|
||||
return base64.b64encode(buffered.getvalue()).decode('utf-8')
|
||||
|
||||
def preprocess_image(image):
|
||||
if isinstance(image, torch.Tensor):
|
||||
# Print image shape for debugging
|
||||
if image.dim() == 4: # (batch_size, height, width, channels)
|
||||
image = image.squeeze(0) # Remove the batch dimension (1)
|
||||
# Convert to PIL after permuting to (height, width, channels)
|
||||
image = ToPILImage()(image.permute(2, 0, 1)) # (height, width, channels)
|
||||
else:
|
||||
print("Unexpected image dimensions. Expected 4D tensor.")
|
||||
return image
|
||||
|
||||
|
||||
def preprocess_mask(mask):
|
||||
if isinstance(mask, torch.Tensor):
|
||||
# Print mask shape for debugging
|
||||
if mask.dim() == 3: # (batch_size, height, width)
|
||||
mask = mask.squeeze(0) # Remove the batch dimension (1)
|
||||
# Convert to PIL (grayscale mask)
|
||||
mask = ToPILImage()(mask) # No permute needed for grayscale
|
||||
else:
|
||||
print("Unexpected mask dimensions. Expected 3D tensor.")
|
||||
return mask
|
||||
|
||||
|
||||
def process_request(api_url, image, mask, api_key):
|
||||
if api_key.strip() == "" or api_key.strip() == "BRIA_API_TOKEN":
|
||||
raise Exception("Please insert a valid API key.")
|
||||
|
||||
# Check if image and mask are tensors, if so, convert to NumPy arrays
|
||||
if isinstance(image, torch.Tensor):
|
||||
image = preprocess_image(image)
|
||||
if isinstance(mask, torch.Tensor):
|
||||
mask = preprocess_mask(mask)
|
||||
|
||||
# Convert the image and mask directly to Base64 strings
|
||||
image_base64 = image_to_base64(image)
|
||||
mask_base64 = image_to_base64(mask)
|
||||
|
||||
# Prepare the API request payload
|
||||
payload = {
|
||||
"file": f"{image_base64}",
|
||||
"mask_file": f"{mask_base64}"
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"api_token": f"{api_key}"
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(api_url, json=payload, headers=headers)
|
||||
# Check for successful response
|
||||
if response.status_code == 200:
|
||||
print('response is 200')
|
||||
# Process the output image from API response
|
||||
response_dict = response.json()
|
||||
image_response = requests.get(response_dict['result_url'])
|
||||
result_image = Image.open(io.BytesIO(image_response.content))
|
||||
result_image = result_image.convert("RGBA")
|
||||
result_image = np.array(result_image).astype(np.float32) / 255.0
|
||||
result_image = torch.from_numpy(result_image)[None,]
|
||||
# image_tensor = image_tensor = ToTensor()(output_image)
|
||||
# image_tensor = image_tensor.permute(1, 2, 0) / 255.0 # Shape now becomes [1, 2200, 1548, 3]
|
||||
# print(f"output tensor shape is: {image_tensor.shape}")
|
||||
return (result_image,)
|
||||
else:
|
||||
raise Exception(f"Error: API request failed with status code {response.status_code}")
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"{e}")
|
||||
@@ -0,0 +1,25 @@
|
||||
from .common import process_request
|
||||
|
||||
class EraserNode():
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",), # Input image from another node
|
||||
"mask": ("MASK",), # Binary mask input
|
||||
"api_key": ("STRING", {"default": "BRIA_API_TOKEN"}) # API Key input with a default value
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("output_image",)
|
||||
CATEGORY = "API Nodes"
|
||||
FUNCTION = "execute" # This is the method that will be executed
|
||||
|
||||
def __init__(self):
|
||||
self.api_url = "https://engine.prod.bria-api.com/v1/eraser" # Eraser API URL
|
||||
|
||||
# Define the execute method as expected by ComfyUI
|
||||
def execute(self, image, mask, api_key):
|
||||
return process_request(self.api_url, image, mask, api_key)
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import numpy as np
|
||||
import requests
|
||||
from PIL import Image
|
||||
import io
|
||||
import torch
|
||||
|
||||
from .common import image_to_base64, preprocess_image, preprocess_mask
|
||||
|
||||
|
||||
class GenFillNode():
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",), # Input image from another node
|
||||
"mask": ("MASK",), # Binary mask input
|
||||
"prompt": ("STRING",),
|
||||
"api_key": ("STRING", {"default": "BRIA_API_TOKEN"}), # API Key input with a default value
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("output_image",)
|
||||
CATEGORY = "API Nodes"
|
||||
FUNCTION = "execute" # This is the method that will be executed
|
||||
|
||||
def __init__(self):
|
||||
self.api_url = "https://engine.prod.bria-api.com/v1/gen_fill" # Eraser API URL
|
||||
|
||||
# Define the execute method as expected by ComfyUI
|
||||
def execute(self, image, mask, prompt, api_key):
|
||||
if api_key.strip() == "" or api_key.strip() == "BRIA_API_TOKEN":
|
||||
raise Exception("Please insert a valid API key.")
|
||||
|
||||
# Check if image and mask are tensors, if so, convert to NumPy arrays
|
||||
if isinstance(image, torch.Tensor):
|
||||
image = preprocess_image(image)
|
||||
if isinstance(mask, torch.Tensor):
|
||||
mask = preprocess_mask(mask)
|
||||
|
||||
# Convert the image and mask directly to Base64 strings
|
||||
image_base64 = image_to_base64(image)
|
||||
mask_base64 = image_to_base64(mask)
|
||||
|
||||
# Prepare the API request payload
|
||||
payload = {
|
||||
"file": f"{image_base64}",
|
||||
"mask_file": f"{mask_base64}",
|
||||
"prompt": prompt,
|
||||
"negative_prompt": "blurry",
|
||||
"sync": True
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"api_token": f"{api_key}"
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(self.api_url, json=payload, headers=headers)
|
||||
# Check for successful response
|
||||
if response.status_code == 200:
|
||||
print('response is 200')
|
||||
# Process the output image from API response
|
||||
response_dict = response.json()
|
||||
image_response = requests.get(response_dict['urls'][0])
|
||||
result_image = Image.open(io.BytesIO(image_response.content))
|
||||
result_image = result_image.convert("RGB")
|
||||
result_image = np.array(result_image).astype(np.float32) / 255.0
|
||||
result_image = torch.from_numpy(result_image)[None,]
|
||||
return (result_image,)
|
||||
else:
|
||||
raise Exception(f"Error: API request failed with status code {response.status_code}")
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"{e}")
|
||||
@@ -0,0 +1,67 @@
|
||||
import requests
|
||||
|
||||
from .common import postprocess_image, preprocess_image, image_to_base64
|
||||
|
||||
|
||||
class ReimagineNode():
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"api_key": ("STRING", ),
|
||||
"prompt": ("STRING",),
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"steps_num": ("INT", {"default": 12}), # if used with tailored, possibly get this from the tailored model info node
|
||||
"structure_ref_influence": ("FLOAT", {"default": 0.75}),
|
||||
"fast": ("INT", {"default": 0}), # if used with tailored, possibly get this from the tailored model info node
|
||||
"structure_image": ("IMAGE", ),
|
||||
"tailored_model_id": ("STRING", ),
|
||||
"tailored_model_influence": ("FLOAT", {"default": 0.5}),
|
||||
"tailored_generation_prefix": ("STRING",), # if used with tailored, possibly get this from the tailored model info node
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("output_image",)
|
||||
CATEGORY = "API Nodes"
|
||||
FUNCTION = "execute" # This is the method that will be executed
|
||||
|
||||
def __init__(self):
|
||||
self.api_url = "https://engine.prod.bria-api.com/v1/reimagine" #"http://0.0.0.0:5000/v1/reimagine"
|
||||
|
||||
def execute(
|
||||
self, api_key, prompt, seed,
|
||||
steps_num, fast, structure_ref_influence, structure_image=None,
|
||||
tailored_model_id=None, tailored_model_influence=None, tailored_generation_prefix=None,
|
||||
):
|
||||
fast = bool(fast)
|
||||
payload = {
|
||||
"prompt": tailored_generation_prefix + prompt,
|
||||
"num_results": 1,
|
||||
"sync": True,
|
||||
"seed": seed,
|
||||
"steps_num": steps_num,
|
||||
"include_generation_prefix": False,
|
||||
}
|
||||
if structure_image is not None:
|
||||
structure_image = preprocess_image(structure_image)
|
||||
structure_image = image_to_base64(structure_image)
|
||||
payload["structure_image_file"] = structure_image
|
||||
payload["structure_ref_influence"] = structure_ref_influence
|
||||
if tailored_model_id is not None and tailored_model_id != "":
|
||||
payload["tailored_model_id"] = tailored_model_id
|
||||
payload["tailored_model_influence"] = tailored_model_influence
|
||||
response = requests.post(
|
||||
self.api_url,
|
||||
json=payload,
|
||||
headers={"api_token": api_key}
|
||||
)
|
||||
if response.status_code == 200:
|
||||
response_dict = response.json()
|
||||
image_response = requests.get(response_dict['result'][0]["urls"][0])
|
||||
result_image = postprocess_image(image_response.content)
|
||||
return (result_image,)
|
||||
else:
|
||||
raise Exception(f"Error: API request failed with status code {response.status_code} and text {response.text}")
|
||||
@@ -0,0 +1,68 @@
|
||||
import requests
|
||||
import torch
|
||||
|
||||
from .common import postprocess_image, preprocess_image, image_to_base64
|
||||
|
||||
class ShotByImageNode():
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",), # Input image from another node
|
||||
"ref_image": ("IMAGE",), # ref image from another node
|
||||
"enhance_ref_image": ("INT", {"default": 1}),
|
||||
"api_key": ("STRING", {"default": "BRIA_API_TOKEN"}) # API Key input with a default value
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("output_image",)
|
||||
CATEGORY = "API Nodes"
|
||||
FUNCTION = "execute" # This is the method that will be executed
|
||||
|
||||
def __init__(self):
|
||||
self.api_url = "https://engine.prod.bria-api.com/v1/product/lifestyle_shot_by_image" # Eraser API URL
|
||||
|
||||
# Define the execute method as expected by ComfyUI
|
||||
def execute(self, image, ref_image, api_key, enhance_ref_image, ):
|
||||
if api_key.strip() == "" or api_key.strip() == "BRIA_API_TOKEN":
|
||||
raise Exception("Please insert a valid API key.")
|
||||
|
||||
# Check if image and mask are tensors, if so, convert to NumPy arrays
|
||||
if isinstance(image, torch.Tensor):
|
||||
image = preprocess_image(image)
|
||||
if isinstance(ref_image, torch.Tensor):
|
||||
ref_image = preprocess_image(ref_image)
|
||||
|
||||
# Convert the image and mask directly to Base64 strings
|
||||
image_base64 = image_to_base64(image)
|
||||
ref_image_base64 = image_to_base64(ref_image)
|
||||
enhance_ref_image = bool(enhance_ref_image)
|
||||
|
||||
payload = {
|
||||
"file": image_base64,
|
||||
"ref_image_file": ref_image_base64,
|
||||
"enhance_ref_image": enhance_ref_image,
|
||||
"placement_type": "original",
|
||||
"original_quality": True,
|
||||
"sync": True
|
||||
}
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"api_token": f"{api_key}"
|
||||
}
|
||||
try:
|
||||
response = requests.post(self.api_url, json=payload, headers=headers)
|
||||
# Check for successful response
|
||||
if response.status_code == 200:
|
||||
print('response is 200')
|
||||
# Process the output image from API response
|
||||
response_dict = response.json()
|
||||
image_response = requests.get(response_dict['result'][0][0])
|
||||
result_image = postprocess_image(image_response.content)
|
||||
return (result_image,)
|
||||
else:
|
||||
raise Exception(f"Error: API request failed with status code {response.status_code}")
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"{e}")
|
||||
@@ -0,0 +1,64 @@
|
||||
import requests
|
||||
import torch
|
||||
|
||||
from .common import postprocess_image, preprocess_image, image_to_base64
|
||||
|
||||
class ShotByTextNode():
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",), # Input image from another node
|
||||
"scene_description": ("STRING",),
|
||||
"optimize_description": ("INT", {"default": 1}),
|
||||
"api_key": ("STRING", {"default": "BRIA_API_TOKEN"}) # API Key input with a default value
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("output_image",)
|
||||
CATEGORY = "API Nodes"
|
||||
FUNCTION = "execute" # This is the method that will be executed
|
||||
|
||||
def __init__(self):
|
||||
self.api_url = "https://engine.prod.bria-api.com/v1/product/lifestyle_shot_by_text" # Eraser API URL
|
||||
|
||||
# Define the execute method as expected by ComfyUI
|
||||
def execute(self, image, api_key, scene_description, optimize_description, ):
|
||||
if api_key.strip() == "" or api_key.strip() == "BRIA_API_TOKEN":
|
||||
raise Exception("Please insert a valid API key.")
|
||||
|
||||
# Check if image and mask are tensors, if so, convert to NumPy arrays
|
||||
if isinstance(image, torch.Tensor):
|
||||
image = preprocess_image(image)
|
||||
|
||||
optimize_description = bool(optimize_description)
|
||||
image_base64 = image_to_base64(image)
|
||||
payload = {
|
||||
"file": image_base64,
|
||||
"scene_description": scene_description,
|
||||
"optimize_description": optimize_description,
|
||||
"placement_type": "original",
|
||||
"original_quality": True,
|
||||
"sync": True
|
||||
}
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"api_token": f"{api_key}"
|
||||
}
|
||||
try:
|
||||
response = requests.post(self.api_url, json=payload, headers=headers)
|
||||
# Check for successful response
|
||||
if response.status_code == 200:
|
||||
print('response is 200')
|
||||
# Process the output image from API response
|
||||
response_dict = response.json()
|
||||
image_response = requests.get(response_dict['result'][0][0])
|
||||
result_image = postprocess_image(image_response.content)
|
||||
return (result_image,)
|
||||
else:
|
||||
raise Exception(f"Error: API request failed with status code {response.status_code}")
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"{e}")
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import requests
|
||||
|
||||
from .common import postprocess_image, preprocess_image, image_to_base64
|
||||
|
||||
|
||||
class TailoredGenNode():
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"model_id": ("STRING",),
|
||||
"api_key": ("STRING", ),
|
||||
},
|
||||
"optional": {
|
||||
"prompt": ("STRING",),
|
||||
"generation_prefix": ("STRING",), # possibly get this from the tailored model info node
|
||||
"aspect_ratio": (["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9"], {"default": "4:3"}),
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"model_influence": ("FLOAT", {"default": 1.0}),
|
||||
"negative_prompt": ("STRING", {"default": ""}),
|
||||
"fast": ("INT", {"default": 1}), # possibly get this from the tailored model info node
|
||||
"steps_num": ("INT", {"default": 8}), # possibly get this from the tailored model info node
|
||||
"guidance_method_1": (["controlnet_canny", "controlnet_depth", "controlnet_recoloring", "controlnet_color_grid"], {"default": "controlnet_canny"}),
|
||||
"guidance_method_1_scale": ("FLOAT", {"default": 1.0}),
|
||||
"guidance_method_1_image": ("IMAGE", ),
|
||||
"guidance_method_2": (["controlnet_canny", "controlnet_depth", "controlnet_recoloring", "controlnet_color_grid"], {"default": "controlnet_canny"}),
|
||||
"guidance_method_2_scale": ("FLOAT", {"default": 1.0}),
|
||||
"guidance_method_2_image": ("IMAGE", ),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("output_image",)
|
||||
CATEGORY = "API Nodes"
|
||||
FUNCTION = "execute" # This is the method that will be executed
|
||||
|
||||
def __init__(self):
|
||||
self.api_url = "https://engine.prod.bria-api.com/v1/text-to-image/tailored/" #"http://0.0.0.0:5000/v1/text-to-image/tailored/"
|
||||
|
||||
def execute(
|
||||
self, model_id, api_key, prompt, generation_prefix, aspect_ratio,
|
||||
seed, model_influence, negative_prompt, fast, steps_num,
|
||||
guidance_method_1=None, guidance_method_1_scale=None, guidance_method_1_image=None,
|
||||
guidance_method_2=None, guidance_method_2_scale=None, guidance_method_2_image=None,
|
||||
):
|
||||
fast = bool(fast)
|
||||
payload = {
|
||||
"prompt": generation_prefix + prompt,
|
||||
"num_results": 1,
|
||||
"aspect_ratio": aspect_ratio,
|
||||
"sync": True,
|
||||
"seed": seed,
|
||||
"model_influence": model_influence,
|
||||
"negative_prompt": negative_prompt,
|
||||
"fast": fast,
|
||||
"steps_num": steps_num,
|
||||
"include_generation_prefix": False,
|
||||
}
|
||||
if guidance_method_1_image is not None:
|
||||
guidance_method_1_image = preprocess_image(guidance_method_1_image)
|
||||
guidance_method_1_image = image_to_base64(guidance_method_1_image)
|
||||
payload["guidance_method_1"] = guidance_method_1
|
||||
payload["guidance_method_1_scale"] = guidance_method_1_scale
|
||||
payload["guidance_method_1_image_file"] = guidance_method_1_image
|
||||
if guidance_method_2_image is not None:
|
||||
guidance_method_2_image = preprocess_image(guidance_method_2_image)
|
||||
guidance_method_2_image = image_to_base64(guidance_method_2_image)
|
||||
payload["guidance_method_2"] = guidance_method_2
|
||||
payload["guidance_method_2_scale"] = guidance_method_2_scale
|
||||
payload["guidance_method_2_image_file"] = guidance_method_2_image
|
||||
response = requests.post(
|
||||
self.api_url + model_id,
|
||||
json=payload,
|
||||
headers={"api_token": api_key}
|
||||
)
|
||||
if response.status_code == 200:
|
||||
response_dict = response.json()
|
||||
image_response = requests.get(response_dict['result'][0]["urls"][0])
|
||||
result_image = postprocess_image(image_response.content)
|
||||
return (result_image,)
|
||||
else:
|
||||
raise Exception(f"Error: API request failed with status code {response.status_code} and text {response.text}")
|
||||
@@ -0,0 +1,35 @@
|
||||
import requests
|
||||
|
||||
|
||||
class TailoredModelInfoNode():
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"model_id": ("STRING",),
|
||||
"api_key": ("STRING", )
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING","INT", "INT", )
|
||||
RETURN_NAMES = ("generation_prefix", "model_id", "default_fast", "default_steps_num", )
|
||||
CATEGORY = "API Nodes"
|
||||
FUNCTION = "execute" # This is the method that will be executed
|
||||
|
||||
def __init__(self):
|
||||
self.api_url = "https://engine.prod.bria-api.com/v1/tailored-gen/models/"
|
||||
|
||||
# Define the execute method as expected by ComfyUI
|
||||
def execute(self, model_id, api_key):
|
||||
response = requests.get(
|
||||
self.api_url + model_id,
|
||||
headers={"api_token": api_key}
|
||||
)
|
||||
if response.status_code == 200:
|
||||
generation_prefix = response.json()["generation_prefix"]
|
||||
training_version = response.json()["training_version"]
|
||||
default_fast = 1 if training_version == "light" else 0
|
||||
default_steps_num = 8 if training_version == "light" else 30
|
||||
return (generation_prefix, model_id, default_fast, default_steps_num,)
|
||||
else:
|
||||
raise Exception(f"Error: API request failed with status code {response.status_code} and text {response.text}")
|
||||
@@ -0,0 +1,92 @@
|
||||
import requests
|
||||
|
||||
from .common import postprocess_image, preprocess_image, image_to_base64
|
||||
|
||||
|
||||
class Text2ImageBaseNode():
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"api_key": ("STRING", ),
|
||||
},
|
||||
"optional": {
|
||||
"prompt": ("STRING",),
|
||||
"aspect_ratio": (["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9"], {"default": "4:3"}),
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"negative_prompt": ("STRING", {"default": ""}),
|
||||
"steps_num": ("INT", {"default": 30}),
|
||||
"prompt_enhancement": ("INT", {"default": 0}),
|
||||
"text_guidance_scale": ("INT", {"default": 5}),
|
||||
"medium": (["photography", "art", "none"], {"default": "none"}),
|
||||
"guidance_method_1": (["controlnet_canny", "controlnet_depth", "controlnet_recoloring", "controlnet_color_grid"], {"default": "controlnet_canny"}),
|
||||
"guidance_method_1_scale": ("FLOAT", {"default": 1.0}),
|
||||
"guidance_method_1_image": ("IMAGE", ),
|
||||
"guidance_method_2": (["controlnet_canny", "controlnet_depth", "controlnet_recoloring", "controlnet_color_grid"], {"default": "controlnet_canny"}),
|
||||
"guidance_method_2_scale": ("FLOAT", {"default": 1.0}),
|
||||
"guidance_method_2_image": ("IMAGE", ),
|
||||
"image_prompt_mode": (["regular", "style_only"], {"default": "regular"}),
|
||||
"image_prompt_image": ("IMAGE", ),
|
||||
"image_prompt_scale": ("FLOAT", {"default": 1.0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("output_image",)
|
||||
CATEGORY = "API Nodes"
|
||||
FUNCTION = "execute" # This is the method that will be executed
|
||||
|
||||
def __init__(self):
|
||||
self.api_url = "https://engine.prod.bria-api.com/v1/text-to-image/base/2.3" #"http://0.0.0.0:5000/v1/text-to-image/base/2.3"
|
||||
|
||||
def execute(
|
||||
self, api_key, prompt, aspect_ratio, seed, negative_prompt,
|
||||
steps_num, prompt_enhancement, text_guidance_scale, medium,
|
||||
guidance_method_1=None, guidance_method_1_scale=None, guidance_method_1_image=None,
|
||||
guidance_method_2=None, guidance_method_2_scale=None, guidance_method_2_image=None,
|
||||
image_prompt_mode=None, image_prompt_image=None, image_prompt_scale=None,
|
||||
):
|
||||
prompt_enhancement = bool(prompt_enhancement)
|
||||
payload = {
|
||||
"prompt": prompt,
|
||||
"num_results": 1,
|
||||
"aspect_ratio": aspect_ratio,
|
||||
"sync": True,
|
||||
"seed": seed,
|
||||
"negative_prompt": negative_prompt,
|
||||
"steps_num": steps_num,
|
||||
"text_guidance_scale": text_guidance_scale,
|
||||
"prompt_enhancement": prompt_enhancement,
|
||||
}
|
||||
if medium != "none":
|
||||
payload["medium"] = medium
|
||||
if guidance_method_1_image is not None:
|
||||
guidance_method_1_image = preprocess_image(guidance_method_1_image)
|
||||
guidance_method_1_image = image_to_base64(guidance_method_1_image)
|
||||
payload["guidance_method_1"] = guidance_method_1
|
||||
payload["guidance_method_1_scale"] = guidance_method_1_scale
|
||||
payload["guidance_method_1_image_file"] = guidance_method_1_image
|
||||
if guidance_method_2_image is not None:
|
||||
guidance_method_2_image = preprocess_image(guidance_method_2_image)
|
||||
guidance_method_2_image = image_to_base64(guidance_method_2_image)
|
||||
payload["guidance_method_2"] = guidance_method_2
|
||||
payload["guidance_method_2_scale"] = guidance_method_2_scale
|
||||
payload["guidance_method_2_image_file"] = guidance_method_2_image
|
||||
if image_prompt_image is not None:
|
||||
image_prompt_image = preprocess_image(image_prompt_image)
|
||||
image_prompt_image = image_to_base64(image_prompt_image)
|
||||
payload["image_prompt_mode"] = image_prompt_mode
|
||||
payload["image_prompt_file"] = image_prompt_image
|
||||
payload["image_prompt_scale"] = image_prompt_scale
|
||||
response = requests.post(
|
||||
self.api_url,
|
||||
json=payload,
|
||||
headers={"api_token": api_key}
|
||||
)
|
||||
if response.status_code == 200:
|
||||
response_dict = response.json()
|
||||
image_response = requests.get(response_dict['result'][0]["urls"][0])
|
||||
result_image = postprocess_image(image_response.content)
|
||||
return (result_image,)
|
||||
else:
|
||||
raise Exception(f"Error: API request failed with status code {response.status_code} and text {response.text}")
|
||||
@@ -0,0 +1,85 @@
|
||||
import requests
|
||||
|
||||
from .common import postprocess_image, preprocess_image, image_to_base64
|
||||
|
||||
|
||||
class Text2ImageFastNode():
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"api_key": ("STRING", ),
|
||||
},
|
||||
"optional": {
|
||||
"prompt": ("STRING",),
|
||||
"aspect_ratio": (["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9"], {"default": "4:3"}),
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"steps_num": ("INT", {"default": 8}),
|
||||
"prompt_enhancement": ("INT", {"default": 0}),
|
||||
"guidance_method_1": (["controlnet_canny", "controlnet_depth", "controlnet_recoloring", "controlnet_color_grid"], {"default": "controlnet_canny"}),
|
||||
"guidance_method_1_scale": ("FLOAT", {"default": 1.0}),
|
||||
"guidance_method_1_image": ("IMAGE", ),
|
||||
"guidance_method_2": (["controlnet_canny", "controlnet_depth", "controlnet_recoloring", "controlnet_color_grid"], {"default": "controlnet_canny"}),
|
||||
"guidance_method_2_scale": ("FLOAT", {"default": 1.0}),
|
||||
"guidance_method_2_image": ("IMAGE", ),
|
||||
"image_prompt_mode": (["regular", "style_only"], {"default": "regular"}),
|
||||
"image_prompt_image": ("IMAGE", ),
|
||||
"image_prompt_scale": ("FLOAT", {"default": 1.0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("output_image",)
|
||||
CATEGORY = "API Nodes"
|
||||
FUNCTION = "execute"
|
||||
|
||||
def __init__(self):
|
||||
self.api_url = "https://engine.prod.bria-api.com/v1/text-to-image/fast/2.3" #"http://0.0.0.0:5000/v1/text-to-image/fast/2.3"
|
||||
|
||||
def execute(
|
||||
self, api_key, prompt, aspect_ratio, seed,
|
||||
steps_num, prompt_enhancement,
|
||||
guidance_method_1=None, guidance_method_1_scale=None, guidance_method_1_image=None,
|
||||
guidance_method_2=None, guidance_method_2_scale=None, guidance_method_2_image=None,
|
||||
image_prompt_mode=None, image_prompt_image=None, image_prompt_scale=None,
|
||||
):
|
||||
prompt_enhancement = bool(prompt_enhancement)
|
||||
payload = {
|
||||
"prompt": prompt,
|
||||
"num_results": 1,
|
||||
"aspect_ratio": aspect_ratio,
|
||||
"sync": True,
|
||||
"seed": seed,
|
||||
"steps_num": steps_num,
|
||||
"prompt_enhancement": prompt_enhancement,
|
||||
}
|
||||
if guidance_method_1_image is not None:
|
||||
guidance_method_1_image = preprocess_image(guidance_method_1_image)
|
||||
guidance_method_1_image = image_to_base64(guidance_method_1_image)
|
||||
payload["guidance_method_1"] = guidance_method_1
|
||||
payload["guidance_method_1_scale"] = guidance_method_1_scale
|
||||
payload["guidance_method_1_image_file"] = guidance_method_1_image
|
||||
if guidance_method_2_image is not None:
|
||||
guidance_method_2_image = preprocess_image(guidance_method_2_image)
|
||||
guidance_method_2_image = image_to_base64(guidance_method_2_image)
|
||||
payload["guidance_method_2"] = guidance_method_2
|
||||
payload["guidance_method_2_scale"] = guidance_method_2_scale
|
||||
payload["guidance_method_2_image_file"] = guidance_method_2_image
|
||||
if image_prompt_image is not None:
|
||||
image_prompt_image = preprocess_image(image_prompt_image)
|
||||
image_prompt_image = image_to_base64(image_prompt_image)
|
||||
payload["image_prompt_mode"] = image_prompt_mode
|
||||
payload["image_prompt_file"] = image_prompt_image
|
||||
payload["image_prompt_scale"] = image_prompt_scale
|
||||
response = requests.post(
|
||||
self.api_url,
|
||||
json=payload,
|
||||
headers={"api_token": api_key}
|
||||
)
|
||||
if response.status_code == 200:
|
||||
response_dict = response.json()
|
||||
image_response = requests.get(response_dict['result'][0]["urls"][0])
|
||||
result_image = postprocess_image(image_response.content)
|
||||
return (result_image,)
|
||||
else:
|
||||
raise Exception(f"Error: API request failed with status code {response.status_code} and text {response.text}")
|
||||
@@ -0,0 +1,62 @@
|
||||
import requests
|
||||
|
||||
from .common import postprocess_image
|
||||
|
||||
|
||||
class Text2ImageHDNode():
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"api_key": ("STRING", ),
|
||||
},
|
||||
"optional": {
|
||||
"prompt": ("STRING",),
|
||||
"aspect_ratio": (["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9"], {"default": "4:3"}),
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"negative_prompt": ("STRING", {"default": ""}),
|
||||
"steps_num": ("INT", {"default": 30}),
|
||||
"prompt_enhancement": ("INT", {"default": 0}),
|
||||
"text_guidance_scale": ("INT", {"default": 5}),
|
||||
"medium": (["photography", "art", "none"], {"default": "none"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("output_image",)
|
||||
CATEGORY = "API Nodes"
|
||||
FUNCTION = "execute"
|
||||
|
||||
def __init__(self):
|
||||
self.api_url = "https://engine.prod.bria-api.com/v1/text-to-image/hd/2.3" #"http://0.0.0.0:5000/v1/text-to-image/hd/2.3"
|
||||
|
||||
def execute(
|
||||
self, api_key, prompt, aspect_ratio, seed, negative_prompt,
|
||||
steps_num, prompt_enhancement, text_guidance_scale, medium,
|
||||
):
|
||||
prompt_enhancement = bool(prompt_enhancement)
|
||||
payload = {
|
||||
"prompt": prompt,
|
||||
"num_results": 1,
|
||||
"aspect_ratio": aspect_ratio,
|
||||
"sync": True,
|
||||
"seed": seed,
|
||||
"negative_prompt": negative_prompt,
|
||||
"steps_num": steps_num,
|
||||
"text_guidance_scale": text_guidance_scale,
|
||||
"prompt_enhancement": prompt_enhancement,
|
||||
}
|
||||
if medium != "none":
|
||||
payload["medium"] = medium
|
||||
response = requests.post(
|
||||
self.api_url,
|
||||
json=payload,
|
||||
headers={"api_token": api_key}
|
||||
)
|
||||
if response.status_code == 200:
|
||||
response_dict = response.json()
|
||||
image_response = requests.get(response_dict['result'][0]["urls"][0])
|
||||
result_image = postprocess_image(image_response.content)
|
||||
return (result_image,)
|
||||
else:
|
||||
raise Exception(f"Error: API request failed with status code {response.status_code} and text {response.text}")
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-bria-api"
|
||||
description = "Custom nodes for ComfyUI using BRIA's API."
|
||||
version = "1.0.0"
|
||||
version = "2.0.0"
|
||||
license = {file = "LICENSE"}
|
||||
|
||||
[project.urls]
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
{
|
||||
"last_node_id": 42,
|
||||
"last_link_id": 65,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 42,
|
||||
"type": "LoadImage",
|
||||
"pos": {
|
||||
"0": 591,
|
||||
"1": 593
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
64,
|
||||
65
|
||||
],
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"A_bottle_of_perfume.png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
"type": "LoadImage",
|
||||
"pos": {
|
||||
"0": 600,
|
||||
"1": 988
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
59
|
||||
],
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"A_red_studio_with_a_shelf__close_up.png",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"type": "Note",
|
||||
"pos": {
|
||||
"0": 995.4524536132812,
|
||||
"1": 601.5353393554688
|
||||
},
|
||||
"size": {
|
||||
"0": 306.28387451171875,
|
||||
"1": 58
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"You can get your BRIA API token at:\nhttps://bria.ai/api/"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 40,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 1408,
|
||||
"1": 623
|
||||
},
|
||||
"size": [
|
||||
210,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 62
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 41,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 1408,
|
||||
"1": 951
|
||||
},
|
||||
"size": [
|
||||
210,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 63
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 36,
|
||||
"type": "ShotByTextNode",
|
||||
"pos": {
|
||||
"0": 996,
|
||||
"1": 736
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 64
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "output_image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
62
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ShotByTextNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"a beautiful sunset",
|
||||
1,
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 37,
|
||||
"type": "ShotByImageNode",
|
||||
"pos": {
|
||||
"0": 999,
|
||||
"1": 932
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 102
|
||||
},
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 65
|
||||
},
|
||||
{
|
||||
"name": "ref_image",
|
||||
"type": "IMAGE",
|
||||
"link": 59
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "output_image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
63
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ShotByImageNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
0,
|
||||
""
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
59,
|
||||
39,
|
||||
0,
|
||||
37,
|
||||
1,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
62,
|
||||
36,
|
||||
0,
|
||||
40,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
63,
|
||||
37,
|
||||
0,
|
||||
41,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
64,
|
||||
42,
|
||||
0,
|
||||
36,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
65,
|
||||
42,
|
||||
0,
|
||||
37,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.9849732675807669,
|
||||
"offset": [
|
||||
-339.6686422794803,
|
||||
-496.4354678014682
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
{
|
||||
"last_node_id": 35,
|
||||
"last_link_id": 55,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 33,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 1420,
|
||||
"1": 574
|
||||
},
|
||||
"size": {
|
||||
"0": 433.29193115234375,
|
||||
"1": 357.1255187988281
|
||||
},
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 54
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
"type": "LoadImage",
|
||||
"pos": {
|
||||
"0": 479,
|
||||
"1": 572
|
||||
},
|
||||
"size": {
|
||||
"0": 395.7845153808594,
|
||||
"1": 352.8512268066406
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
53
|
||||
],
|
||||
"slot_index": 0,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": [
|
||||
55
|
||||
],
|
||||
"slot_index": 1,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"clipspace/clipspace-mask-1438488.400000006.png [input]",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"type": "Note",
|
||||
"pos": {
|
||||
"0": 478,
|
||||
"1": 444
|
||||
},
|
||||
"size": {
|
||||
"0": 396.80859375,
|
||||
"1": 61.8046875
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"Right click, and choose \"Open in Mask Editor\" to draw a mask of areas you want to erase."
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"type": "Note",
|
||||
"pos": {
|
||||
"0": 983,
|
||||
"1": 440
|
||||
},
|
||||
"size": {
|
||||
"0": 306.28387451171875,
|
||||
"1": 58
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"You can get your BRIA API token at:\nhttps://bria.ai/api/"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 34,
|
||||
"type": "BriaGenFill",
|
||||
"pos": {
|
||||
"0": 992,
|
||||
"1": 572
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 102
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 53
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 55
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "output_image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
54
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "BriaGenFill"
|
||||
},
|
||||
"widgets_values": [
|
||||
"a beautiful paint brush",
|
||||
"BRIA_API_TOKEN"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
53,
|
||||
30,
|
||||
0,
|
||||
34,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
54,
|
||||
34,
|
||||
0,
|
||||
33,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
55,
|
||||
30,
|
||||
1,
|
||||
34,
|
||||
1,
|
||||
"MASK"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.8264462809917364,
|
||||
"offset": [
|
||||
-266.96526103236687,
|
||||
114.47857424738714
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"last_node_id":21,"last_link_id":49,"nodes":[{"id":2,"type":"TailoredModelInfoNode","pos":[480.2208557128906,641.4290161132812],"size":[315,122],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"generation_prefix","type":"STRING","links":[2],"slot_index":0,"localized_name":"generation_prefix"},{"name":"default_fast","type":"INT","links":[46],"slot_index":1,"localized_name":"default_fast"},{"name":"default_steps_num","type":"INT","links":[40],"slot_index":2,"localized_name":"default_steps_num"}],"properties":{"Node name for S&R":"TailoredModelInfoNode"},"widgets_values":["",""]},{"id":3,"type":"PreviewImage","pos":[1728.9140625,688.2759399414062],"size":[210,246],"flags":{},"order":6,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":41,"localized_name":"images"}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":11,"type":"LoadImage","pos":[693.87060546875,831.6130981445312],"size":[315,314],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[48],"slot_index":0,"localized_name":"IMAGE"},{"name":"MASK","type":"MASK","links":null,"localized_name":"MASK"}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["example.png","image"]},{"id":5,"type":"JjkShowText","pos":[847.2867431640625,591.890625],"size":[315,76],"flags":{},"order":4,"mode":0,"inputs":[{"name":"text","type":"STRING","link":2,"widget":{"name":"text"}}],"outputs":[{"name":"text","type":"STRING","links":[49],"slot_index":0,"shape":6,"localized_name":"text"}],"properties":{"Node name for S&R":"JjkShowText"},"widgets_values":["A photo of a character named Sami, a siamese cat with blue eyes, "]},{"id":15,"type":"BriaTailoredGen","pos":[1207.595947265625,645.6781005859375],"size":[456,438],"flags":{},"order":5,"mode":0,"inputs":[{"name":"guidance_method_1_image","type":"IMAGE","link":48,"shape":7,"localized_name":"guidance_method_1_image"},{"name":"guidance_method_2_image","type":"IMAGE","link":null,"shape":7,"localized_name":"guidance_method_2_image"},{"name":"generation_prefix","type":"STRING","link":49,"widget":{"name":"generation_prefix"},"shape":7},{"name":"fast","type":"INT","link":46,"widget":{"name":"fast"},"shape":7},{"name":"steps_num","type":"INT","link":40,"widget":{"name":"steps_num"},"shape":7}],"outputs":[{"name":"output_image","type":"IMAGE","links":[41],"slot_index":0,"localized_name":"output_image"}],"properties":{"Node name for S&R":"BriaTailoredGen"},"widgets_values":["","","a cat","","4:3",-1,"randomize",1,"","",1,"controlnet_canny",1,"controlnet_canny",1]},{"id":21,"type":"Note","pos":[1215.2469482421875,522.4407348632812],"size":[449.75360107421875,58],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["You can get your BRIA API token at: https://bria.ai/api/"],"color":"#432","bgcolor":"#653"},{"id":19,"type":"Note","pos":[484.5993957519531,486.4328918457031],"size":[306.0655212402344,89.87609100341797],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["This node is used to retrieve default settings and prompt prefixes for the chosen tailored model."],"color":"#432","bgcolor":"#653"}],"links":[[2,2,0,5,0,"STRING"],[40,2,2,15,4,"INT"],[41,15,0,3,0,"IMAGE"],[46,2,1,15,3,"INT"],[48,11,0,15,0,"IMAGE"],[49,5,0,15,2,"STRING"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.7627768444385483,"offset":[-122.90473166350671,-300.2018923615813]},"node_versions":{"comfyui-bria-api":"c72754d15b53a13ee0c0419d70401232c56b7fdb","comfy-core":"v0.3.8-1-gc441048","ComfyUI-Jjk-Nodes":"b3c99bb78a99551776b5eab1a820e1cd58f84f31"}},"version":0.4}
|
||||
Reference in New Issue
Block a user