Compare commits

...
Author SHA1 Message Date
xenia-kra 21267d0995 Comfy tailored portrait - version (#19) 2025-03-10 12:54:27 +04:00
xenia-kra 07827ef34f tailored portrait (#18) 2025-03-10 12:45:44 +04:00
MishaFein 00dccbb17a Update Readme.md 2025-02-10 18:11:40 +02:00
MishaFein 1cb9ce4394 Update Readme.md 2025-02-10 18:10:57 +02:00
MishaFein b2f8b8d0e3 Update Readme.md 2025-02-10 18:05:56 +02:00
MishaFein 129bc599d0 Update Readme.md 2025-02-10 18:04:28 +02:00
MishaFein 28c2631581 Update Readme.md 2025-02-10 17:57:11 +02:00
ori-liberman baadbc02bc Add content moderation option to background removal and image generation nodes 2025-02-10 12:49:02 +00:00
or a73045f8e6 Added workflow sample to readme 2025-02-09 14:21:38 +02:00
tairBria dd74d03fd0 Merge pull request #17 from Bria-AI/t2i-tailored-content-moderation
content moderation t2i reimagime tailored
2025-02-09 13:12:30 +02:00
or bfc83da41f added text-to-image workflow and updated version 2025-02-04 17:40:08 +02:00
BriaOr 499ec5d104 Update Readme.md 2025-02-04 13:44:48 +02:00
BriaOr fef2e49902 Update Readme.md with new API URL 2025-02-04 13:39:36 +02:00
BriaOr 2eae6dae46 Merge pull request #16 from Bria-AI/feature/content-moderation-expansion-removefg
image expansion & remove fg- content moderation
2025-02-03 16:10:11 +02:00
israelweiss90 28c0cdf8d5 image expansion & remove fg- content moderation 2025-02-02 16:14:02 +00:00
Tair 54f380f1f0 content moderation t2i reimagime tailored 2025-02-02 14:52:18 +00:00
BriaOr 4f7691ff93 Merge pull request #14 from Bria-AI/bugfi/rmbg-temp-file
temp file to buffer to avoid OS dependency
2025-02-02 14:08:39 +02:00
israelweiss90 1781beff09 temp file to buffer to avoid OS dependency 2025-02-02 10:56:04 +00:00
BriaOr 502206f518 Merge pull request #13 from Bria-AI/DvirYBria-gen-fill-seed
Added seed go gen fill
2025-02-02 11:45:15 +02:00
DvirYBria da8adda281 Added seed go gen fill 2025-02-02 11:35:57 +02:00
BriaOr 1c02dea96b Update Readme.md 2025-01-30 17:08:42 +02:00
or 5b77fd90a8 updated genfill workflow 2025-01-30 16:52:21 +02:00
BriaOr 246e05ac3e Update Readme.md 2025-01-30 15:29:41 +02:00
or b6f2f8b297 Updated background workflow 2025-01-30 15:24:53 +02:00
BriaOr 5052a7bf94 Update Readme.md 2025-01-30 15:13:40 +02:00
BriaOr afc9599c63 Update Readme.md 2025-01-30 15:03:42 +02:00
BriaOr 13a3dfb2bb Update Readme.md 2025-01-30 15:02:07 +02:00
BriaOr 6573f2e43d Rename Product shot generation_workflow.json to product shot generation_workflow.json 2025-01-30 14:52:06 +02:00
or ac7e97c348 Updated workflows 2025-01-30 14:51:39 +02:00
BriaOr c6fe701115 Merge pull request #10 from Bria-AI/feature/new-comfyui-nodes
4 new nodes- rmbg, replace bg, expand, remove fg
2025-01-30 14:21:15 +02:00
israelweiss90 ab17ee227d pyproject version 2.0.1 2025-01-29 16:18:52 +00:00
israelweiss90 2fa966e887 4 new nodes- rmbg, replace bg, expand, remove fg 2025-01-29 16:08:05 +00:00
BriaOr 3b5cca2dc8 Update Readme.md 2025-01-29 16:07:42 +02:00
BriaOr aaf0729f78 Update Readme.md 2025-01-28 17:57:17 +02:00
BriaOr 1a37414fe4 Update Readme.md 2025-01-28 17:51:10 +02:00
BriaOr 2fbbce0d1b Update Readme.md 2025-01-28 17:47:33 +02:00
BriaOr 23f9e46eff Update Readme.md 2025-01-28 17:39:26 +02:00
BriaOr 709d16cb72 Merge pull request #9 from Bria-AI/ranges
return model id from TG info node
2025-01-21 10:22:52 +02:00
Tair 55133b0910 return model id from TG info node 2025-01-20 16:19:30 +00:00
tairBria da4c773cb7 Merge pull request #7 from Bria-AI/t2i-comfy
* base and hd

* image prompt

* reimagine
2025-01-20 14:42:27 +02:00
Tair fc1d1aff05 כ 2025-01-20 12:41:46 +00:00
Tair 44adccf16d fix 2025-01-20 09:42:52 +00:00
Tair d465f55b3a fix 2025-01-20 09:05:27 +00:00
Tair fcce3cbfb3 clean 2025-01-20 08:57:31 +00:00
Tair fb1eed93ae fix 2025-01-20 08:56:07 +00:00
Tair 2990e8f024 fix none clause 2025-01-20 08:54:15 +00:00
Tair bb9b1d5755 reimagine 2025-01-19 14:23:35 +00:00
Tair 4ac24cbd5a Merge branch 'main' into t2i-comfy 2025-01-19 11:56:13 +00:00
Tair 7a8276a8e4 image prompt 2025-01-19 11:54:02 +00:00
tairBria 68a83db7a5 Merge pull request #8 from movalex/fix/shot-by-image-get-api-url
fix api url handling
2025-01-16 14:01:09 +02:00
Alexey Bogomolov cdc1e52076 fix api url handling 2025-01-15 22:28:44 +03:00
Tair 449b6ebb84 base and hd 2025-01-13 14:06:25 +00:00
BriaOr 02ead854bf Update Readme.md 2025-01-09 16:41:28 +02:00
or eaca630863 updated tailored workflow 2025-01-09 14:48:48 +02:00
BriaOr c72754d15b Update Readme.md 2025-01-09 13:51:14 +02:00
or 731b03634a Added T2I node to documentation 2025-01-09 13:50:31 +02:00
BriaOr c5193119cf Update Readme.md 2025-01-09 13:30:31 +02:00
BriaOr 7a97620e78 Merge pull request #6 from Bria-AI/Docs-update
Docs update
2025-01-09 13:22:12 +02:00
BriaOr 8cf75db231 Update Readme.md 2025-01-09 11:30:25 +02:00
BriaOr 8c86560f23 Merge pull request #5 from Bria-AI/t2i-comfy
T2i comfy
2025-01-09 11:16:15 +02:00
or 3af0cc4341 new doc version updated 2025-01-09 11:02:26 +02:00
Tair bba67b3767 tailored workflow 2025-01-08 16:32:59 +00:00
or 1d10365fe1 V1 of the documentation 2025-01-08 18:22:02 +02:00
Tair 00c6822881 text to image base 2025-01-08 16:20:12 +00:00
tairBria beadb83b5d Merge pull request #4 from Bria-AI/t2i-comfy
include_generation_prefix always false
2025-01-08 18:19:29 +02:00
or f50bf3f2ed Added more changes 2025-01-08 18:08:37 +02:00
Tair 67f237b37c include_generation_prefix always false 2025-01-08 15:41:06 +00:00
BriaOr 93971fc014 Merge pull request #3 from Bria-AI/t2i-comfy
tailored and some code cleaning
2025-01-08 16:14:11 +02:00
or e4ea36e135 Added coming soon 2025-01-08 15:33:00 +02:00
or f42b100e1b Added modifications to the readme 2025-01-08 15:23:12 +02:00
Tair 9f3bfab023 tailored and some code cleaning 2025-01-08 13:14:17 +00:00
BriaOr 424678c8e8 Merge pull request #2 from Bria-AI/t2i-comfy
nodes folder
2025-01-07 15:10:10 +02:00
Tair e4cf57f129 nodes folder 2025-01-06 16:47:06 +00:00
BriaOr c45ad0647d Update Readme.md 2024-12-22 22:31:02 +02:00
or 158838158b Added product shot generation collaterals 2024-12-22 22:30:22 +02:00
ori-liberman 6c1d953896 Merge branch 'main' of https://github.com/Bria-AI/ComfyUI-BRIA-API into main 2024-12-22 14:21:17 +00:00
ori-liberman 808721a995 Change optimize_description and enhance_ref_image types from BOOLEAN to INT 2024-12-22 14:21:15 +00:00
OriL 7510ccca3a Update Readme.md 2024-12-22 15:21:03 +02:00
BriaOr 7d3edf6174 Update Readme.md 2024-12-22 11:42:51 +02:00
OriL 396d56eb2a Add files via upload 2024-12-19 15:19:45 +02:00
OriL e4a6238935 Update Readme.md 2024-12-19 14:40:40 +02:00
ori-liberman 796013d91a Bump version to 1.0.2 in pyproject.toml 2024-12-19 08:07:59 +00:00
ori-liberman 0b935dcffc Add ShotByTextNode and ShotByImageNode classes with API integration 2024-12-18 15:11:57 +00:00
BriaOr 99155fb898 Update Readme.md 2024-12-03 18:28:55 +02:00
or c266970425 Updated readme and new workflows 2024-12-03 18:26:49 +02:00
or 83a1f760f4 updated genfill node 2024-12-03 18:17:00 +02:00
DvirYBria 7886ec31e2 Update Readme.md 2024-12-03 15:17:16 +02:00
or cbd980ac92 Working GenFill node 2024-12-03 11:53:37 +02:00
DvirYBria 0ef5288ff6 Update pyproject.toml 2024-12-02 15:40:38 +02:00
DvirYBria 928091f7bb Update __init__.py 2024-12-02 15:35:48 +02:00
DvirYBria 0a0e7eebaa Update __init__.py 2024-12-02 15:33:52 +02:00
DvirYBria 6357dfccf0 Update __init__.py 2024-12-02 15:27:38 +02:00
BriaOr 9e6f75a7bf Merge pull request #1 from Bria-AI/generative_fill_node
Update and added GenFill node bria_api_node.py
2024-12-02 09:11:49 +01:00
33 changed files with 1782 additions and 406 deletions
+1
View File
@@ -0,0 +1 @@
*.pyc
+60 -14
View File
@@ -1,30 +1,73 @@
# 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 and editing workflows. **API documentation** is available [**here**](https://docs.bria.ai/).
You can load the workflow, which includes all available nodes, by importing the [workflow.json](workflow.json) file in this repo.
BRIA's APIs and models are built for commercial use and trained on 100% licensed data and does not contain copyrighted materials, such as fictional characters, logos, trademarks, public figures, harmful content, or privacy-infringing content.
You can also download the following image and import it to comfyui:
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>.
<img src="./images/eraser_workflow.png" alt="Original image" width="500"/>
For direct API Endpoint use, look for the endpoint in our of our API partners like: [**fal.ai**](https://fal.ai/models?keywords=bria).
For source code and weigths access, go to our [**Hugging Face**](https://huggingface.co/briaai) space.
An illustration of the workflow:
To load a workflow, import the compatible workflow.json files from this [folder](workflows).
<p align="center">
<img src="./images/background_workflow.png" width="1200"/>
</p>
<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
<!-- Placeholder image of cool workflows. -->
### Eraser
The **Eraser** node allows users to remove specific objects or areas from an image by providing a mask.
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.
<!-- <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"/> -->
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
These nodes create high-quality images from text or image prompts, generating photorealistic or artistic results with support for various aspect ratios.
| Node | Description |
|------------------------|--------------------------------------------------------------------|
| **Text2Image Base** | Generates images from text prompts, serving as the foundation for text-based image creation. |
| **Text2Image Fast** | Optimized for speed, this node generates images from text prompts with faster results while maintaining quality. |
| **Text2Image HD** | Optimized for high-resolution outputs, this node generates detailed and sharp visuals from text prompts. |
| **Reimagine** | Guides image generation using both prompts and an input image. Preserve the original structure and depth while introducing new materials, colors, and textures. |
## Tailored Generation Nodes
These nodes use pre-trained tailored models to generate images that faithfully reproduce specific visual IP elements or guidelines.
| Node | Description |
|------------------------|--------------------------------------------------------------------|
| **Tailored Gen** | Generates images using a trained tailored model, reproducing specific visual IP elements or guidelines. Use the Tailored Model Info node to load the model's default settings. |
| **Tailored Model Info** | Retrieves the default settings and prompt prefix of a trained tailored model, which can be used to configure the Tailored Gen node. |
## Image Editing Nodes
These nodes modify specific parts of images, enabling adjustments while maintaining the integrity of the rest of the image.
| Node | Description |
|------------------------|--------------------------------------------------------------------|
| **RMBG 2.0 (Remove Background)** | Removes the background from an image, isolating the foreground subject. |
| **Replace Background** | Replaces an image’s background with a new one, guided by either a reference image or a prompt. |
| **Expand Image** | Expands the dimensions of an image, generating new content to fill the extended areas. |
| **Eraser** | Removes specific objects or areas from an image by providing a mask. |
| **GenFill** | Generates objects by prompt in a specific region of an image. |
| **Erase Foreground** | Removes the foreground from an image, isolating the background. |
## Product Shot Editing Nodes
These nodes create high-quality product images for eCommerce workflows.
| Node | Description |
|------------------------|--------------------------------------------------------------------|
| **ShotByText** | Modifies an image's background by providing a text prompt. Powered by BRIA's ControlNet Background-Generation. |
| **ShotByImage** | Modifies an image's background by providing a reference image. Uses BRIA's ControlNet Background-Generation and Image-Prompt. |
# Installation
There are two methods to install the BRIA ComfyUI API nodes:
### Method 1: Using ComfyUI's Custom Node Manager
@@ -43,3 +86,6 @@ There are two methods to install the BRIA ComfyUI API nodes:
```
3. Restart ComfyUI and load the workflows.
<!-- ### Campaign generation
Coming soon -->
+31 -3
View File
@@ -1,11 +1,39 @@
from .bria_api_node import EraserNode
from nodes.tailored_portrait_node import TailoredPortraitNode
from .nodes import (EraserNode, GenFillNode, ImageExpansionNode, ReplaceBgNode, RmbgNode, RemoveForegroundNode, 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,
"ImageExpansionNode": ImageExpansionNode,
"ReplaceBgNode": ReplaceBgNode,
"RmbgNode": RmbgNode,
"RemoveForegroundNode": RemoveForegroundNode,
"ShotByTextNode": ShotByTextNode,
"ShotByImageNode": ShotByImageNode,
"BriaTailoredGen": TailoredGenNode,
"TailoredModelInfoNode": TailoredModelInfoNode,
"TailoredPortraitNode": TailoredPortraitNode,
"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",
"ImageExpansionNode": "Bria Image Expansion",
"ReplaceBgNode": "Bria Replace Background",
"RmbgNode": "Bria RMBG",
"RemoveForegroundNode": "Bria Remove Foreground",
"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",
}
-185
View File
@@ -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}")
+21
View File
@@ -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: 634 KiB

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

+15
View File
@@ -0,0 +1,15 @@
from .eraser_node import EraserNode
from .generative_fill_node import GenFillNode
from .image_expansion_node import ImageExpansionNode
from .replace_bg_node import ReplaceBgNode
from .rmbg_node import RmbgNode
from .remove_foreground_node import RemoveForegroundNode
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 .tailored_portrait_node import TailoredPortraitNode
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
+92
View File
@@ -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}")
+25
View File
@@ -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)
+80
View File
@@ -0,0 +1,80 @@
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
},
"optional": {
"seed": ("INT", {"default": 123456})
}
}
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, seed):
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,
"seed": seed,
}
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}")
+101
View File
@@ -0,0 +1,101 @@
import numpy as np
import requests
from PIL import Image
import io
import torch
from .common import image_to_base64, preprocess_image
class ImageExpansionNode():
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"image": ("IMAGE",), # Input image from another node
"original_image_size": ("STRING",),
"original_image_location": ("STRING",),
"api_key": ("STRING", {"default": "BRIA_API_TOKEN"}), # API Key input with a default value
},
"optional": {
"canvas_size": ("STRING", {"default": "1000, 1000"}),
"prompt": ("STRING", {"default": ""}),
"seed": ("INT", {"default": 681794}),
"negative_prompt": ("STRING", {"default": "Ugly, mutated"}),
"content_moderation": ("BOOLEAN", {"default": False}),
}
}
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/image_expansion" # Image Expansion API URL
# Define the execute method as expected by ComfyUI
def execute(self, image,
original_image_size,
original_image_location,
canvas_size,
prompt,
seed,
negative_prompt,
content_moderation,
api_key):
if api_key.strip() == "" or api_key.strip() == "BRIA_API_TOKEN":
raise Exception("Please insert a valid API key.")
original_image_size = [int(x.strip()) for x in original_image_size.split(",")]
original_image_location = [int(x.strip()) for x in original_image_location.split(",")]
canvas_size = [int(x.strip()) for x in canvas_size.split(",")]
if prompt == "":
prompt = None
if negative_prompt == "":
negative_prompt = " " # hack to avoid error in triton which expects non-empty string
# Check if image and mask are tensors, if so, convert to NumPy arrays
if isinstance(image, torch.Tensor):
image = preprocess_image(image)
# Convert the image directly to Base64 string
image_base64 = image_to_base64(image)
# Prepare the API request payload
payload = {
"file": f"{image_base64}",
"original_image_size": original_image_size,
"original_image_location": original_image_location,
"canvas_size": canvas_size,
"prompt": prompt,
"negative_prompt": negative_prompt,
"seed": seed,
"content_moderation": content_moderation
}
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("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}")
+69
View File
@@ -0,0 +1,69 @@
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
"content_moderation": ("INT", {"default": 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/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,
content_moderation=0,
):
payload = {
"prompt": tailored_generation_prefix + prompt,
"num_results": 1,
"sync": True,
"seed": seed,
"steps_num": steps_num,
"include_generation_prefix": False,
"content_moderation": content_moderation,
}
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}")
+70
View File
@@ -0,0 +1,70 @@
import numpy as np
import requests
from PIL import Image
import io
import torch
from .common import preprocess_image, image_to_base64
class RemoveForegroundNode():
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"image": ("IMAGE",), # Input image from another node
"api_key": ("STRING", {"default": "BRIA_API_TOKEN"}), # API Key input with a default value
},
"optional": {
"content_moderation": ("BOOLEAN", {"default": False}),
}
}
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.internal.prod.bria-api.com/v1/erase_foreground" # remove foreground API URL
# Define the execute method as expected by ComfyUI
def execute(self, image, content_moderation, api_key):
if api_key.strip() == "" or api_key.strip() == "BRIA_API_TOKEN":
raise Exception("Please insert a valid API key.")
# Check if image is tensor, if so, convert to NumPy array
if isinstance(image, torch.Tensor):
image = preprocess_image(image)
# Prepare the API request payload
# temporary save the image to /tmp
# temp_img_path = "/tmp/temp_img.jpeg"
# image.save(temp_img_path, format="JPEG")
# files=[('file',('temp_img.jpeg', open(temp_img_path, 'rb'),'image/jpeg'))
# ]
payload = {"file": image_to_base64(image), "content_moderation": content_moderation}
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 = 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}")
+105
View File
@@ -0,0 +1,105 @@
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 ReplaceBgNode():
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"image": ("IMAGE",), # Input image from another node
"api_key": ("STRING", {"default": "BRIA_API_TOKEN"}), # API Key input with a default value
},
"optional": {
"fast": ("BOOLEAN", {"default": True}),
"bg_prompt": ("STRING",),
"ref_image": ("IMAGE",), # Input ref image from another node
"refine_prompt": ("BOOLEAN", {"default": True}),
"enhance_ref_image": ("BOOLEAN", {"default": True}),
"original_quality": ("BOOLEAN", {"default": False}),
"force_rmbg": ("BOOLEAN", {"default": False}),
"negative_prompt": ("STRING", {"default": None}),
"seed": ("INT", {"default": 681794}),
"content_moderation": ("BOOLEAN", {"default": False}),
}
}
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/background/replace" # Replace BG API URL
# Define the execute method as expected by ComfyUI
def execute(self, image, fast,
refine_prompt,
enhance_ref_image,
original_quality,
force_rmbg,
negative_prompt,
seed,
api_key,
content_moderation,
bg_prompt=None,
ref_image=None,):
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)
# Convert the image and mask directly to Base64 strings
image_base64 = image_to_base64(image)
ref_image_file = None # initialization, will be updated if it is supplied
if ref_image is not None:
ref_image = preprocess_image(ref_image)
ref_image_file = image_to_base64(ref_image)
# Prepare the API request payload
payload = {
"file": f"{image_base64}",
"fast": fast,
"bg_prompt": bg_prompt,
"ref_image_file": ref_image_file,
"refine_prompt": refine_prompt,
"enhance_ref_image": enhance_ref_image,
"original_quality": original_quality,
"force_rmbg": force_rmbg,
"negative_prompt": negative_prompt,
"seed": seed,
"sync": True,
"num_results": 1,
"content_moderation": content_moderation
}
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]) # first indexing for batched, second for url
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}")
+67
View File
@@ -0,0 +1,67 @@
import numpy as np
import requests
from PIL import Image
import io
import torch
from .common import preprocess_image
from io import BytesIO
class RmbgNode():
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"image": ("IMAGE",), # Input image from another node
"api_key": ("STRING", {"default": "BRIA_API_TOKEN"}), # API Key input with a default value
},
"optional": {
"content_moderation": ("BOOLEAN", {"default": False}),
}
}
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/background/remove" # RMBG API URL
# Define the execute method as expected by ComfyUI
def execute(self, image, content_moderation, api_key):
if api_key.strip() == "" or api_key.strip() == "BRIA_API_TOKEN":
raise Exception("Please insert a valid API key.")
# Check if image is tensor, if so, convert to NumPy array
if isinstance(image, torch.Tensor):
image = preprocess_image(image)
# Prepare the API request payload
image_buffer = BytesIO()
image.save(image_buffer, format="JPEG")
# Get binary data from buffer
image_buffer.seek(0) # Move cursor to the start of the buffer
binary_data = image_buffer.read()
files=[('file',('temp_img.jpeg', BytesIO(binary_data),'image/jpeg'))]
payload = {"content_moderation": content_moderation}
try:
response = requests.post(self.api_url, data=payload, headers={"api_token": api_key}, files=files)
# 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 = 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}")
+72
View File
@@ -0,0 +1,72 @@
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
},
"optional": {
"content_moderation": ("BOOLEAN", {"default": False}),
}
}
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, content_moderation):
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,
"content_moderation": content_moderation
}
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}")
+69
View File
@@ -0,0 +1,69 @@
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
},
"optional": {
"content_moderation": ("BOOLEAN", {"default": False}),
}
}
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, content_moderation):
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,
"content_moderation": content_moderation
}
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}")
+84
View File
@@ -0,0 +1,84 @@
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", ),
"content_moderation": ("INT", {"default": 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/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,
content_moderation=0,
):
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,
"content_moderation": content_moderation,
}
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}")
+35
View File
@@ -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}")
+72
View File
@@ -0,0 +1,72 @@
import numpy as np
import requests
from PIL import Image
import io
import torch
from .common import image_to_base64
class TailoredPortraitNode():
@classmethod
def INPUT_TYPES(self):
return {
"required": {
"image": ("IMAGE",), # Input image from another node
"tailored_model_id": ("INT",),
"api_key": ("STRING", {"default": "BRIA_API_TOKEN"}), # API Key input with a default value
},
"optional": {
"seed": ("INT", {"default": 123456}),
"tailored_model_influence": ("FLOAT", {"default": 0.9}),
"id_strength": ("FLOAT", {"default": 0.7}),
}
}
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/tailored-gen/restyle_portrait" # Eraser API URL
# Define the execute method as expected by ComfyUI
def execute(self, image, tailored_model_id, api_key, seed, tailored_model_influence, id_strength):
if api_key.strip() == "" or api_key.strip() == "BRIA_API_TOKEN":
raise Exception("Please insert a valid API key.")
# Convert the image and mask directly to Base64 strings
image_base64 = image_to_base64(image)
# Prepare the API request payload
payload = {
"id_image_file": f"{image_base64}",
"tailored_model_id": tailored_model_id,
"tailored_model_influence": tailored_model_influence,
"id_strength": id_strength,
"seed": seed
}
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['image_res'])
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}")
+94
View File
@@ -0,0 +1,94 @@
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}),
"content_moderation": ("INT", {"default": 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,
content_moderation=0,
):
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,
"content_moderation": content_moderation,
}
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}")
+87
View File
@@ -0,0 +1,87 @@
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}),
"content_moderation": ("INT", {"default": 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,
content_moderation=0,
):
payload = {
"prompt": prompt,
"num_results": 1,
"aspect_ratio": aspect_ratio,
"sync": True,
"seed": seed,
"steps_num": steps_num,
"prompt_enhancement": prompt_enhancement,
"content_moderation": content_moderation,
}
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}")
+63
View File
@@ -0,0 +1,63 @@
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"}),
"content_moderation": ("INT", {"default": 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/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, content_moderation=0,
):
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,
"content_moderation": content_moderation,
}
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
View File
@@ -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.3"
license = {file = "LICENSE"}
[project.urls]
-203
View File
@@ -1,203 +0,0 @@
{
"last_node_id": 28,
"last_link_id": 42,
"nodes": [
{
"id": 15,
"type": "Note",
"pos": {
"0": 1021,
"1": 280
},
"size": {
"0": 311.8914794921875,
"1": 153.69827270507812
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [],
"properties": {},
"widgets_values": [
"The default BRIA API key for ComfyUI (BRIA_ComfyUI_Key) offers 10,000 API calls for the entire community. \n\nGet your own token at:\nhttps://bria.ai/api/"
],
"color": "#432",
"bgcolor": "#653"
},
{
"id": 13,
"type": "PreviewImage",
"pos": {
"0": 1410,
"1": 160
},
"size": {
"0": 474.7605895996094,
"1": 303.117919921875
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 42
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 21,
"type": "LoadImage",
"pos": {
"0": 477,
"1": 156
},
"size": {
"0": 408.4602355957031,
"1": 333.19830322265625
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
40
],
"slot_index": 0,
"shape": 3
},
{
"name": "MASK",
"type": "MASK",
"links": [
41
],
"slot_index": 1,
"shape": 3
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"clipspace/clipspace-mask-82245.69999998808.png [input]",
"image"
]
},
{
"id": 28,
"type": "BriaEraser",
"pos": {
"0": 1022,
"1": 159
},
"size": {
"0": 315,
"1": 78
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 40
},
{
"name": "mask",
"type": "MASK",
"link": 41
}
],
"outputs": [
{
"name": "output_image",
"type": "IMAGE",
"links": [
42
],
"slot_index": 0,
"shape": 3
}
],
"properties": {
"Node name for S&R": "BriaEraser"
},
"widgets_values": [
"BRIA_ComfyUI_Key"
]
},
{
"id": 14,
"type": "Note",
"pos": {
"0": 483,
"1": 39
},
"size": [
396.80859375,
61.8046875
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [],
"properties": {},
"widgets_values": [
"Right click, and choose \"Open in Mask Editor\" to draw a mask of areas you want animated more. "
],
"color": "#432",
"bgcolor": "#653"
}
],
"links": [
[
40,
21,
0,
28,
0,
"IMAGE"
],
[
41,
21,
1,
28,
1,
"MASK"
],
[
42,
28,
0,
13,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
-293.75,
167.65625
]
}
},
"version": 0.4
}
@@ -0,0 +1 @@
{"last_node_id":39,"last_link_id":65,"nodes":[{"id":34,"type":"LoadImage","pos":[19.94045066833496,1075.806640625],"size":[315,314],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[61],"slot_index":0,"localized_name":"IMAGE"},{"name":"MASK","type":"MASK","links":null,"localized_name":"MASK"}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["quirky-red-brick-brick-wallpaper.jpg","image"]},{"id":36,"type":"PreviewImage","pos":[468.7108154296875,1210.288818359375],"size":[210,246],"flags":{},"order":5,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":63,"localized_name":"images"}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":33,"type":"PreviewImage","pos":[1317.4083251953125,1118.4864501953125],"size":[210,246],"flags":{},"order":8,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":59,"localized_name":"images"}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":31,"type":"PreviewImage","pos":[1668.734619140625,796.7755737304688],"size":[210,246],"flags":{},"order":10,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":57,"localized_name":"images"}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":29,"type":"PreviewImage","pos":[872.8858032226562,679.9429321289062],"size":[210,246],"flags":{},"order":6,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":55,"localized_name":"images"}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":28,"type":"LoadImage","pos":[29.629886627197266,676.8370361328125],"size":[315,314],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[54],"slot_index":0,"localized_name":"IMAGE"},{"name":"MASK","type":"MASK","links":null,"localized_name":"MASK"}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["pexels-photo-1808399.jpeg","image"]},{"id":35,"type":"RemoveForegroundNode","pos":[414.2638854980469,1079.1705322265625],"size":[315,58],"flags":{},"order":3,"mode":0,"inputs":[{"name":"image","type":"IMAGE","link":61,"localized_name":"image"}],"outputs":[{"name":"output_image","type":"IMAGE","links":[62,63],"slot_index":0,"localized_name":"output_image"}],"properties":{"Node name for S&R":"RemoveForegroundNode"},"widgets_values":["BRIA_API_TOKEN"]},{"id":32,"type":"ReplaceBgNode","pos":[834.6115112304688,1063.0406494140625],"size":[315,294],"flags":{},"order":7,"mode":0,"inputs":[{"name":"image","type":"IMAGE","link":65,"localized_name":"image"},{"name":"ref_image","type":"IMAGE","link":62,"shape":7,"localized_name":"ref_image"}],"outputs":[{"name":"output_image","type":"IMAGE","links":[59,64],"slot_index":0,"localized_name":"output_image"}],"properties":{"Node name for S&R":"ReplaceBgNode"},"widgets_values":["BRIA_API_TOKEN",false,"",true,true,true,false,"",1978,"randomize"]},{"id":30,"type":"ImageExpansionNode","pos":[1265.001220703125,798.8323974609375],"size":[315,226],"flags":{},"order":9,"mode":0,"inputs":[{"name":"image","type":"IMAGE","link":64,"localized_name":"image"}],"outputs":[{"name":"output_image","type":"IMAGE","links":[57],"slot_index":0,"localized_name":"output_image"}],"properties":{"Node name for S&R":"ImageExpansionNode"},"widgets_values":["600,760","200, 0","BRIA_API_TOKEN","1200, 800","",1729,"randomize","Ugly, mutated"]},{"id":38,"type":"Note","pos":[436.7110900878906,566.2047119140625],"size":[306.28387451171875,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":27,"type":"RmbgNode","pos":[430.8815002441406,678.7727661132812],"size":[315,58],"flags":{},"order":4,"mode":0,"inputs":[{"name":"image","type":"IMAGE","link":54,"localized_name":"image"}],"outputs":[{"name":"output_image","type":"IMAGE","links":[55,65],"slot_index":0,"localized_name":"output_image"}],"properties":{"Node name for S&R":"RmbgNode"},"widgets_values":["BRIA_API_TOKEN"]}],"links":[[51,5,0,15,2,"STRING"],[54,28,0,27,0,"IMAGE"],[55,27,0,29,0,"IMAGE"],[57,30,0,31,0,"IMAGE"],[59,32,0,33,0,"IMAGE"],[61,34,0,35,0,"IMAGE"],[62,35,0,32,1,"IMAGE"],[63,35,0,36,0,"IMAGE"],[64,32,0,30,0,"IMAGE"],[65,27,0,32,0,"IMAGE"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.8140274938684037,"offset":[101.53311990208498,-477.0342684311694]},"node_versions":{"comfyui-bria-api":"c72754d15b53a13ee0c0419d70401232c56b7fdb","comfy-core":"v0.3.8-1-gc441048","ComfyUI-Jjk-Nodes":"b3c99bb78a99551776b5eab1a820e1cd58f84f31"}},"version":0.4}
+1
View File
@@ -0,0 +1 @@
{"last_node_id":41,"last_link_id":62,"nodes":[{"id":14,"type":"Note","pos":[478,444],"size":[396.80859375,61.8046875],"flags":{},"order":0,"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":[1080.3062744140625,445.9654541015625],"size":[306.28387451171875,58],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["You can get your BRIA API token at:\nhttps://bria.ai/api/"],"color":"#432","bgcolor":"#653"},{"id":30,"type":"LoadImage","pos":[479,572],"size":[395.7845153808594,352.8512268066406],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[56],"slot_index":0,"shape":3,"localized_name":"IMAGE"},{"name":"MASK","type":"MASK","links":[57],"slot_index":1,"shape":3,"localized_name":"MASK"}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["clipspace/clipspace-mask-4068974.800000012.png [input]","image"]},{"id":37,"type":"PreviewImage","pos":[1504.4755859375,568.9967651367188],"size":[438.50262451171875,376.8338317871094],"flags":{},"order":6,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":58,"localized_name":"images"}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":33,"type":"PreviewImage","pos":[1502.785888671875,1078.3564453125],"size":[433.29193115234375,357.1255187988281],"flags":{},"order":7,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":54,"localized_name":"images"}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":40,"type":"LoadImage","pos":[541.1226806640625,1079.39697265625],"size":[315,314],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[61],"slot_index":0,"localized_name":"IMAGE"},{"name":"MASK","type":"MASK","links":[62],"slot_index":1,"localized_name":"MASK"}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["clipspace/clipspace-mask-4411367.100000024.png [input]","image"]},{"id":34,"type":"BriaGenFill","pos":[1032.416748046875,1073.984619140625],"size":[315,102],"flags":{},"order":5,"mode":0,"inputs":[{"name":"image","type":"IMAGE","link":61,"localized_name":"image"},{"name":"mask","type":"MASK","link":62,"localized_name":"mask"}],"outputs":[{"name":"output_image","type":"IMAGE","links":[54],"slot_index":0,"shape":3,"localized_name":"output_image"}],"properties":{"Node name for S&R":"BriaGenFill"},"widgets_values":["a blue coffee mug","BRIA_API_TOKEN"]},{"id":36,"type":"BriaEraser","pos":[1068.6063232421875,574.6080322265625],"size":[315,78],"flags":{},"order":4,"mode":0,"inputs":[{"name":"image","type":"IMAGE","link":56,"localized_name":"image"},{"name":"mask","type":"MASK","link":57,"localized_name":"mask"}],"outputs":[{"name":"output_image","type":"IMAGE","links":[58],"slot_index":0,"localized_name":"output_image"}],"properties":{"Node name for S&R":"BriaEraser"},"widgets_values":["BRIA_API_TOKEN"]}],"links":[[54,34,0,33,0,"IMAGE"],[56,30,0,36,0,"IMAGE"],[57,30,1,36,1,"MASK"],[58,36,0,37,0,"IMAGE"],[61,40,0,34,0,"IMAGE"],[62,40,1,34,1,"MASK"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.6727499949325677,"offset":[131.53042816003972,-419.53430403204317]}},"version":0.4}
@@ -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
}
+1
View File
@@ -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}
+169
View File
@@ -0,0 +1,169 @@
{
"last_node_id": 13,
"last_link_id": 11,
"nodes": [
{
"id": 12,
"type": "LoadImage",
"pos": [
669.7035522460938,
136.97129821777344
],
"size": [
315,
314
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
10
],
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"pexels-photo-3246665.png",
"image"
]
},
{
"id": 11,
"type": "PreviewImage",
"pos": [
1587.27001953125,
97.39167022705078
],
"size": [
210,
246
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 11
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 10,
"type": "Text2ImageFastNode",
"pos": [
1058.320556640625,
96.64427185058594
],
"size": [
438.71258544921875,
394.27716064453125
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "guidance_method_1_image",
"type": "IMAGE",
"link": null,
"shape": 7
},
{
"name": "guidance_method_2_image",
"type": "IMAGE",
"link": null,
"shape": 7
},
{
"name": "image_prompt_image",
"type": "IMAGE",
"link": 10,
"shape": 7
}
],
"outputs": [
{
"name": "output_image",
"type": "IMAGE",
"links": [
11
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Text2ImageFastNode"
},
"widgets_values": [
"BRIA_API_TOKEN",
"A drawing of a lion on a table.\t",
"4:3",
990,
"randomize",
8,
0,
"controlnet_canny",
1,
"controlnet_canny",
1,
"regular",
1
]
}
],
"links": [
[
10,
12,
0,
10,
2,
"IMAGE"
],
[
11,
10,
0,
11,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.7513148009015777,
"offset": [
11.112206386364164,
66.47311795454547
]
},
"node_versions": {
"comfy-core": "v0.3.10-42-gff83865",
"comfyui-bria-api": "499ec5d104cc5110407eafce468ce1d47ac168b3"
},
"VHS_latentpreview": false,
"VHS_latentpreviewrate": 0
},
"version": 0.4
}