Compare commits

..
9 Commits
Author SHA1 Message Date
shadowcz007 ea24f52b13 Update checkVersion_mixlab.js 2023-12-31 13:12:34 +08:00
shadowcz007 4abfc47346 ### Update 0.8.0
v0.8.0 🚀🚗🚚🏃‍ LaMaInpainting
- 新增 LaMaInpainting
- 优化color节点的输出
- 修复高清显示屏上定位节点不准的情况

- Add LaMaInpainting
- Optimize the output of the color node
- Fix the issue of inaccurate positioning node on high-definition display screens
2023-12-31 13:11:31 +08:00
shadowcz007 3b95010d06 新增LaMaInpainting & 优化color节点的输出 2023-12-31 13:04:28 +08:00
shadow b3c1b96088 Merge pull request #97 from shadowcz007/fix_hidpi_node_move_center
Fix:node can't move to center on HiDPI device
2023-12-31 10:07:07 +08:00
shadowcz007 a9f1326873 update 2023-12-30 23:52:50 +08:00
shadowcz007 75a696fb64 update 2023-12-30 23:39:36 +08:00
shadowcz007 24aaacba6d update 2023-12-30 23:38:52 +08:00
shadowcz007 e3cd7d5f91 更新示例 2023-12-30 21:05:07 +08:00
gold3bear 6359c3f70f Fix:node can't move to center on HiDPI device 2023-12-30 02:09:45 +08:00
16 changed files with 206 additions and 54 deletions
+38 -22
View File
@@ -1,7 +1,8 @@
##
v0.7.0 🚀🚗🚚🏃‍ Workflow-to-APP
- 支持多个web app 切换
### Workflow-to-APP 🚀🚗🚚🏃
- 新增AppInfo节点,可以通过简单的配置,把workflow转变为一个Web APP。
- 支持多个web app 切换
- Support multiple web app switching.
- Add the AppInfo node, which allows you to transform the workflow into a web app by simple configuration.
@@ -24,15 +25,9 @@ APP-JSON:
> 输出节点:PreviewImage 、SaveImage、ShowTextForGPT、VHS_VideoCombine
### 3D
![](./assets/3dimage.png)
[workflow](./workflow/3D-workflow.json)
### Real-time Design
> ScreenShareNode & FloatingVideoNode. Now comfyui supports capturing screen pixel streams from any software and can be used for LCM-Lora integration. Let's get started with implementation and design! 💻🌐
### ScreenShareNode & FloatingVideoNode
> Now comfyui supports capturing screen pixel streams from any software and can be used for LCM-Lora integration. Let's get started with implementation and design! 💻🌐
>
![screenshare](./assets/screenshare.png)
https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43e-410a-ab3a-1952b7b4e7da
@@ -43,6 +38,7 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
!! Please use the address with HTTPS (https://127.0.0.1).
### SpeechRecognition & SpeechSynthesis
![f](./assets/audio-workflow.svg)
@@ -51,11 +47,23 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
### GPT
> Support for calling multiple GPTs.ChatGPT、ChatGLM3 , Some code provided by rui. If you are using OpenAI's service, fill in https://api.openai.com/v1 . If you are using a local LLM service, fill in http://127.0.0.1:xxxx/v1 . Azure OpenAI:https://xxxx.openai.azure.com
![gpt-workflow.svg](./assets/gpt-workflow.svg)
[workflow-5](./workflow/5-gpt-workflow.json)
### 3D
![](./assets/3dimage.png)
[workflow](./workflow/3D-workflow.json)
### Layers
> A new layer class node has been added, allowing you to separate the image into layers. After merging the images, you can input the controlnet for further processing.
![layers](./assets/layers-workflow.svg)
![poster](./assets/poster-workflow.svg)
### LoadImagesFromLocal
> Monitor changes to images in a local folder, and trigger real-time execution of workflows, supporting common image formats, especially PSD format, in conjunction with Photoshop.
@@ -67,13 +75,6 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
> Conveniently load images from a fixed address on the internet to ensure that default images in the workflow can be executed.
### Layers
> A new layer class node has been added, allowing you to separate the image into layers. After merging the images, you can input the controlnet for further processing.
![layers](./assets/layers-workflow.svg)
![poster](./assets/poster-workflow.svg)
## Utils
> The Color node provides a color picker for easy color selection, the Font node offers built-in font selection for use with TextImage to generate text images, and the DynamicDelayByText node allows delayed execution based on the length of the input text.
@@ -113,6 +114,10 @@ Add edges to an image.
![FeatheredMask](./assets/FlVou_Y6kaGWYoEj1Tn0aTd4AjMI.jpg)
> LaMaInpainting
from [simple-lama-inpainting](https://github.com/enesmsahin/simple-lama-inpainting)
### Improvement
@@ -126,14 +131,26 @@ An improvement has been made to directly redirect to GitHub to search for missin
![node-not-found](./assets/node-not-found.png)
### Update
v0.8.0 🚀🚗🚚🏃‍ LaMaInpainting
- 新增 LaMaInpainting
- 优化color节点的输出
- 修复高清显示屏上定位节点不准的情况
- Add LaMaInpainting
- Optimize the output of the color node
- Fix the issue of inaccurate positioning node on high-definition display screens
### Models
[Download CLIPSeg](https://huggingface.co/CIDAS/clipseg-rd64-refined/tree/main), move to : model/clipseg
[Download CLIPSeg](https://huggingface.co/CIDAS/clipseg-rd64-refined/tree/main), move to : models/clipseg
[Download lama](https://github.com/enesmsahin/simple-lama-inpainting/releases/download/v0.1.0/big-lama.pt), move to : models/lama
<!-- ### Workflow
[Workflow](./workflow.md) -->
## Installation
manually install, simply clone the repo into the custom_nodes directory with this command:
@@ -167,7 +184,6 @@ pip3 install -r requirements.txt
访问 [www.mixcomfy.com](https://www.mixcomfy.com),获得更多内测功能,关注微信公众号:Mixlab无界社区
#### Thanks:
[ComfyUI-CLIPSeg](https://github.com/biegert/ComfyUI-CLIPSeg/tree/main)
+5 -3
View File
@@ -445,7 +445,7 @@ from .nodes.Clipseg import CLIPSeg,CombineMasks
from .nodes.ChatGPT import ChatGPTNode,ShowTextForGPT,CharacterInText
from .nodes.Audio import GamePal,SpeechRecognition,SpeechSynthesis
from .nodes.Utils import AppInfo,IntNumber,FloatSlider,TextInput,ColorInput,FontInput,TextToNumber,DynamicDelayProcessor,LimitNumber,SwitchByIndex,GetImageSize_,MultiplicationNode
from .nodes.Lama import LaMaInpainting
# 要导出的所有节点及其名称的字典
# 注意:名称应全局唯一
@@ -492,6 +492,7 @@ NODE_CLASS_MAPPINGS = {
"GetImageSize_":GetImageSize_,
"SwitchByIndex":SwitchByIndex,
"LimitNumber":LimitNumber,
"LaMaInpainting":LaMaInpainting
# "GamePal":GamePal
}
@@ -511,7 +512,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"SpeechSynthesis":"SpeechSynthesis ♾️Mixlab",
"SpeechRecognition":"SpeechRecognition ♾️Mixlab",
"3DImage":"3DImage ♾️Mixlab",
"DynamicDelayProcessor":"DynamicDelayByText ♾️Mixlab"
"DynamicDelayProcessor":"DynamicDelayByText ♾️Mixlab",
"LaMaInpainting":"LaMaInpainting ♾️Mixlab"
# "GamePal":"GamePal ♾️Mixlab"
}
@@ -520,5 +522,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
WEB_DIRECTORY = "./web"
print('--------------')
print('\033[91mMixlab Nodes: \033[93mLoaded\033[0m')
print('\033[91m ### Mixlab Nodes: \033[93mLoaded\033[0m')
print('--------------')
+3 -1
View File
@@ -4793,12 +4793,14 @@
"SplitLongMask",
"SvgImage",
"TextImage",
"ResizeImageMixlab",
"TransparentImage",
"VAEDecodeConsistencyDecoder",
"VAELoaderConsistencyDecoder",
"TextToNumber",
"TextInput_",
"DynamicDelayProcessor"
"DynamicDelayProcessor",
"LaMaInpainting"
],
{
"title_aux": "comfyui-mixlab-nodes"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -101,7 +101,7 @@ class CLIPSeg:
return {"required":
{
"image": ("IMAGE",),
"text": ("STRING", {"multiline": False}),
"text": ("STRING", {"multiline": False,"dynamicPrompts": False}),
},
"optional":
@@ -246,7 +246,7 @@ class CombineMasks:
# Resize heatmap and binary mask to match the original image dimensions
dimensions = (image_np.shape[1], image_np.shape[0])
print('heatmap',heatmap)
# print('heatmap',heatmap)
if dimensions is None or dimensions[0] == 0 or dimensions[1] == 0:
raise ValueError("Invalid dimensions")
+7 -6
View File
@@ -506,6 +506,7 @@ def generate_text_image(text, font_path, font_size, text_color, vertical=True, s
y = 0
for line in lines:
for char in line:
#print('char',char)
char_coordinates.append((x, y))
x += font_size + spacing
y += font_size + spacing
@@ -514,10 +515,10 @@ def generate_text_image(text, font_path, font_size, text_color, vertical=True, s
# 3. Calculate image width and height
if layout == "vertical":
width = (len(lines) * (font_size + spacing)) - spacing
height = (len(max(lines, key=len)) * (font_size + spacing)) + spacing
height = ((len(max(lines, key=len))+1) * (font_size + spacing)) + spacing
else:
width = (len(max(lines, key=len)) * (font_size + spacing)) - spacing
height = (len(lines) * (font_size + spacing)) + spacing
height = ((len(lines)-1) * (font_size + spacing)) + font_size
# 4. Draw each character on the image
image = Image.new('RGBA', (width, height), (255, 255, 255,0))
@@ -990,8 +991,8 @@ class TextImage:
def INPUT_TYPES(s):
return {"required": {
"text": ("STRING",{"multiline": True,"default": "龍馬精神迎新歲"}),
"font_path": ("STRING",{"multiline": False,"default": FONT_PATH}),
"text": ("STRING",{"multiline": True,"default": "龍馬精神迎新歲","dynamicPrompts": False}),
"font_path": ("STRING",{"multiline": False,"default": FONT_PATH,"dynamicPrompts": False}),
"font_size": ("INT",{
"default":100,
"min": 100, #Minimum value
@@ -1001,12 +1002,12 @@ class TextImage:
}),
"spacing": ("INT",{
"default":12,
"min": 1, #Minimum value
"min": -200, #Minimum value
"max": 200, #Maximum value
"step": 1, #Slider's step
"display": "number" # Cosmetic only: display as "number" or "slider"
}),
"text_color":("STRING",{"multiline": False,"default": "#000000"}),
"text_color":("STRING",{"multiline": False,"default": "#000000","dynamicPrompts": False}),
"vertical":("BOOLEAN", {"default": True},),
},
}
+85
View File
@@ -0,0 +1,85 @@
import os
import folder_paths
from simple_lama_inpainting import SimpleLama
from PIL import Image
import numpy as np
import torch
os.environ['LAMA_MODEL'] = os.path.join(folder_paths.models_dir, "lama/big-lama.pt")
if os.environ.get("LAMA_MODEL"):
model_path=os.environ.get("LAMA_MODEL")
if not os.path.exists(model_path):
os.environ['LAMA_MODEL']=''
raise FileNotFoundError(
f"lama torchscript model not found: {model_path}"
)
# Tensor to PIL
def tensor2pil(image):
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
# Convert PIL to Tensor
def pil2tensor(image):
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
# simple_lama = SimpleLama()
# img_path = "image.png"
# mask_path = "mask.png"
# image = Image.open(img_path)
# mask = Image.open(mask_path).convert('L')
# result = simple_lama(image, mask)
# result.save("inpainted.png")
class LaMaInpainting:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"image": ("IMAGE",),
"mask": ("MASK",),
},
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "run"
CATEGORY = "♾️Mixlab/image"
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
global simple_lama
simple_lama = None
def run(self,image,mask):
global simple_lama
result=[]
if simple_lama==None:
simple_lama = SimpleLama()
else:
simple_lama.model.to("cuda" if torch.cuda.is_available() else "cpu")
for i in range(len(image)):
im=image[i]
ma=mask[i]
im=tensor2pil(im)
ma=tensor2pil(ma)
ma =ma.convert('L')
res = simple_lama(im, ma)
res=pil2tensor(res)
result.append(res)
# result.save("inpainted.png")
if simple_lama.device=='cuda':
simple_lama.model.to('cpu')
return (result,)
+9 -4
View File
@@ -88,18 +88,23 @@ class ColorInput:
},
}
RETURN_TYPES = ("STRING",)
# RETURN_NAMES = ("WIDTH","HEIGHT","X","Y",)
RETURN_TYPES = ("STRING","INT","INT","INT","FLOAT",)
RETURN_NAMES = ("hex","r","g","b","a",)
FUNCTION = "run"
CATEGORY = "♾️Mixlab/utils"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,False,)
OUTPUT_IS_LIST = (False,False,False,False,False,)
def run(self,color):
return (color,)
h=color['hex']
r=color['r']
g=color['g']
b=color['b']
a=color['a']
return (h,r,g,b,a,)
+2 -1
View File
@@ -3,4 +3,5 @@ pyOpenSSL
watchdog
opencv-python-headless
matplotlib
openai
openai
simple-lama-inpainting
+3 -1
View File
@@ -143,6 +143,7 @@
.input_card {
display: flex;
flex-direction: column;
width: 100%;
}
.output {
@@ -153,7 +154,7 @@
.output_card {
height: 100%;
width: 100%;
box-shadow: 0px 0px 20px 7px #e6e7e7;
margin-top: 24px;
display: flex;
flex-wrap: wrap;
@@ -168,6 +169,7 @@
max-height: 600px;
margin: 8px;
min-width: 200px;
box-shadow: 0px 0px 20px 7px #e6e7e7;
}
.card {
+1 -1
View File
@@ -3,7 +3,7 @@ import { app } from '../../../scripts/app.js'
const repoOwner = 'shadowcz007' // 替换为仓库的所有者
const repoName = 'comfyui-mixlab-nodes' // 替换为仓库的名称
const version = 'v0.7.0'
const version = 'v0.8.0'
fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
.then(response => response.json())
+15 -1
View File
@@ -4,6 +4,7 @@ import { ComfyWidgets } from '../../../scripts/widgets.js'
import { $el } from '../../../scripts/ui.js'
import { closeIcon } from './svg_icons.js'
import {
GroupNodeConfig,
GroupNodeHandler
@@ -667,10 +668,23 @@ app.registerExtension({
...options
] // and return the options
}
LGraphCanvas.prototype.centerOnNode = function(node) {
var dpr = window.devicePixelRatio || 1; // 获取设备像素比
this.ds.offset[0] =
-node.pos[0] -
node.size[0] * 0.5 +
(this.canvas.width * 0.5) / (this.ds.scale * dpr); // 考虑设备像素比
this.ds.offset[1] =
-node.pos[1] -
node.size[1] * 0.5 +
(this.canvas.height * 0.5) / (this.ds.scale * dpr); // 考虑设备像素比
this.setDirty(true, true);
};
},
async setup () {
// Add canvas menu options
const orig = LGraphCanvas.prototype.getCanvasMenuOptions
LGraphCanvas.prototype.getCanvasMenuOptions = function () {
const options = orig.apply(this, arguments)
@@ -795,7 +809,7 @@ app.registerExtension({
let d = document.createElement('button')
d.style = `text-align: left;margin:6px;color: var(--input-text);
background-color: var(--comfy-input-bg); border-color: var(--border-color);cursor: pointer;`
d.addEventListener('click', () => {
d.addEventListener('click', () => {
const node = app.graph.getNodeById(nodeId)
if (!node) return
app.canvas.centerOnNode(node)
+34 -10
View File
@@ -2,7 +2,7 @@ import { app } from '../../../scripts/app.js'
import { api } from '../../../scripts/api.js'
import { ComfyWidgets } from '../../../scripts/widgets.js'
import { $el } from '../../../scripts/ui.js'
import { addValueControlWidget } from "../../../scripts/widgets.js";
import { addValueControlWidget } from '../../../scripts/widgets.js'
const getLocalData = key => {
let data = {}
@@ -45,6 +45,21 @@ function get_position_style (ctx, widget_width, y, node_height) {
}
}
function hexToRGBA (hexColor) {
var hex = hexColor.replace('#', '')
var r = parseInt(hex.substring(0, 2), 16)
var g = parseInt(hex.substring(2, 4), 16)
var b = parseInt(hex.substring(4, 6), 16)
// 获取透明度的十六进制值
var alphaHex = hex.substring(6)
// 将透明度的十六进制值转换为十进制值
var alpha = parseInt(alphaHex, 16) / 255
return [r,g,b,alpha]
}
app.registerExtension({
name: 'Mixlab.utils.Color',
async getCustomWidgets (app) {
@@ -60,8 +75,16 @@ app.registerExtension({
return [128, 32] // a method to compute the current size of the widget
},
async serializeValue (nodeId, widgetIndex) {
let data = getLocalData('_mixlab_utils_color')
return data[node.id] || '#000000'
let data = getLocalData('_mixlab_utils_color');
let hex=data[node.id] || '#000000'
let [r,g,b,a]=hexToRGBA(hex)
return {
hex,
r,
g,
b,
a
}
}
}
// widget.something = something; // maybe adds stuff to it
@@ -109,7 +132,7 @@ app.registerExtension({
ip.style = `outline: none;
border: none;
padding: 4px;
width: 100%;cursor: pointer;
width: 70%;cursor: pointer;
height: 32px;`
const label = document.createElement('label')
label.style = 'font-size: 10px;min-width:32px'
@@ -164,16 +187,17 @@ app.registerExtension({
async beforeRegisterNodeDef (nodeType, nodeData, app) {
if (nodeType.comfyClass == 'TextToNumber') {
const onExecuted = nodeType.prototype.onExecuted
nodeType.prototype.onExecuted = function (message) {
onExecuted?.apply(this, arguments)
const random_number=this.widgets.filter(w=>w.name==='random_number')[0]
if(random_number.value==='enable'){
const n=this.widgets.filter(w=>w.name==='number')[0]
n.value=message.num[0]
const random_number = this.widgets.filter(
w => w.name === 'random_number'
)[0]
if (random_number.value === 'enable') {
const n = this.widgets.filter(w => w.name === 'number')[0]
n.value = message.num[0]
}
console.log('TextToNumber', random_number.value)
}
}