Add files via upload

This commit is contained in:
Yanick112
2024-06-13 21:01:43 +08:00
committed by GitHub
parent 25de3c92da
commit 23badc0725
6 changed files with 1301 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
from .svgnode import *
NODE_CLASS_MAPPINGS = {
"ConvertRasterToVector": ConvertRasterToVector,
"SaveSVG": SaveSVG
}
NODE_DISPLAY_NAME_MAPPINGS = {
"ConvertRasterToVector": "Raster to Vector (SVG)",
"SaveSVG": "Save SVG"
}
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
+665
View File
@@ -0,0 +1,665 @@
{
"last_node_id": 48,
"last_link_id": 77,
"nodes": [
{
"id": 26,
"type": "ModelSamplingSD3",
"pos": [
620,
380
],
"size": {
"0": 220,
"1": 60
},
"flags": {
"collapsed": false
},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 31,
"label": "model"
}
],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
41
],
"shape": 3,
"slot_index": 0,
"label": "MODEL"
}
],
"properties": {
"Node name for S&R": "ModelSamplingSD3"
},
"widgets_values": [
3
]
},
{
"id": 33,
"type": "VAEDecode",
"pos": [
1120,
380
],
"size": {
"0": 140,
"1": 60
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 38,
"label": "Latent"
},
{
"name": "vae",
"type": "VAE",
"link": 39,
"label": "VAE"
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
46
],
"shape": 3,
"slot_index": 0,
"label": "图像"
}
],
"properties": {
"Node name for S&R": "VAEDecode"
}
},
{
"id": 31,
"type": "CLIPTextEncode",
"pos": [
440,
760
],
"size": {
"0": 400,
"1": 200
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 49,
"label": "CLIP"
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
52
],
"shape": 3,
"slot_index": 0,
"label": "条件"
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"(worst quality:1.3), low quality, lowres, messy, ugly, disfigured, bad anatomy, draft, deformed hands, fused fingers, signature, text, multi views"
]
},
{
"id": 22,
"type": "SaveSVG",
"pos": [
2220,
380
],
"size": [
220,
100
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"name": "svg_strings",
"type": "LIST",
"link": 25,
"label": "svg_strings"
}
],
"properties": {
"Node name for S&R": "SaveSVG"
},
"widgets_values": [
"ComfyUI_SVG",
true,
""
]
},
{
"id": 35,
"type": "CheckpointLoaderSimple",
"pos": [
-20,
380
],
"size": [
400,
100
],
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
31
],
"shape": 3,
"slot_index": 0,
"label": "模型"
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
48,
49
],
"shape": 3,
"slot_index": 1,
"label": "CLIP"
},
{
"name": "VAE",
"type": "VAE",
"links": [
39
],
"shape": 3,
"slot_index": 2,
"label": "VAE"
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"SD3\\sd3_medium_incl_clips.safetensors"
]
},
{
"id": 38,
"type": "PreviewBridge",
"pos": [
1280,
380
],
"size": [
320,
300
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 46,
"label": "图像"
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
47
],
"shape": 3,
"label": "图像",
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3,
"label": "遮罩"
}
],
"properties": {
"Node name for S&R": "PreviewBridge"
},
"widgets_values": [
"$38-0"
]
},
{
"id": 21,
"type": "ConvertRasterToVector",
"pos": [
1980,
380
],
"size": [
220,
300
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 76,
"label": "image"
}
],
"outputs": [
{
"name": "LIST",
"type": "LIST",
"links": [
25
],
"shape": 3,
"label": "LIST",
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "ConvertRasterToVector"
},
"widgets_values": [
"color",
"stacked",
"spline",
4,
6,
16,
60,
4,
10,
45,
3
]
},
{
"id": 19,
"type": "Image Rembg (Remove Background)",
"pos": [
1640,
380
],
"size": [
320,
260
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 47,
"label": "图像"
}
],
"outputs": [
{
"name": "images",
"type": "IMAGE",
"links": [
76,
77
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Image Rembg (Remove Background)"
},
"widgets_values": [
true,
"isnet-anime",
false,
false,
false,
240,
10,
10,
"none"
]
},
{
"id": 48,
"type": "SaveImage",
"pos": [
1640,
680
],
"size": [
320,
280
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 77,
"label": "图像"
}
],
"properties": {
"Node name for S&R": "SaveImage"
},
"widgets_values": [
"ComfyUI"
]
},
{
"id": 32,
"type": "EmptySD3LatentImage",
"pos": [
160,
520
],
"size": {
"0": 220,
"1": 106
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
44
],
"shape": 3,
"slot_index": 0,
"label": "LATENT"
}
],
"properties": {
"Node name for S&R": "EmptySD3LatentImage"
},
"widgets_values": [
1024,
1024,
1
]
},
{
"id": 24,
"type": "CLIPTextEncode",
"pos": [
440,
500
],
"size": {
"0": 400,
"1": 220
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 48,
"label": "CLIP"
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
42
],
"shape": 3,
"slot_index": 0,
"label": "条件"
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"flat illustration, pair of shoes, peach color background, by Adrian Tomine"
]
},
{
"id": 36,
"type": "KSampler",
"pos": [
880,
380
],
"size": [
220,
260
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 41,
"label": "模型"
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 42,
"label": "正面条件"
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 52,
"label": "负面条件"
},
{
"name": "latent_image",
"type": "LATENT",
"link": 44,
"label": "Latent"
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
38
],
"shape": 3,
"slot_index": 0,
"label": "Latent"
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
763482007024129,
"fixed",
28,
4.5,
"dpmpp_2m",
"sgm_uniform",
1
]
}
],
"links": [
[
25,
21,
0,
22,
0,
"LIST"
],
[
31,
35,
0,
26,
0,
"MODEL"
],
[
38,
36,
0,
33,
0,
"LATENT"
],
[
39,
35,
2,
33,
1,
"VAE"
],
[
41,
26,
0,
36,
0,
"MODEL"
],
[
42,
24,
0,
36,
1,
"CONDITIONING"
],
[
44,
32,
0,
36,
3,
"LATENT"
],
[
46,
33,
0,
38,
0,
"IMAGE"
],
[
47,
38,
0,
19,
0,
"IMAGE"
],
[
48,
35,
1,
24,
0,
"CLIP"
],
[
49,
35,
1,
31,
0,
"CLIP"
],
[
52,
31,
0,
36,
2,
"CONDITIONING"
],
[
76,
19,
0,
21,
0,
"IMAGE"
],
[
77,
19,
0,
48,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.6588450000000093,
"offset": [
-311.4302663398564,
25.544650420863906
]
},
"groupNodes": {}
},
"version": 0.4
}
+255
View File
@@ -0,0 +1,255 @@
{
"last_node_id": 49,
"last_link_id": 80,
"nodes": [
{
"id": 22,
"type": "SaveSVG",
"pos": [
2220,
380
],
"size": [
220,
100
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "svg_strings",
"type": "LIST",
"link": 25,
"label": "svg_strings"
}
],
"properties": {
"Node name for S&R": "SaveSVG"
},
"widgets_values": [
"ComfyUI_SVG",
true,
""
]
},
{
"id": 21,
"type": "ConvertRasterToVector",
"pos": [
1980,
380
],
"size": [
220,
300
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 80,
"label": "image"
}
],
"outputs": [
{
"name": "LIST",
"type": "LIST",
"links": [
25
],
"shape": 3,
"label": "LIST",
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "ConvertRasterToVector"
},
"widgets_values": [
"color",
"stacked",
"spline",
4,
6,
16,
60,
4,
10,
45,
3
]
},
{
"id": 48,
"type": "SaveImage",
"pos": [
1640,
680
],
"size": [
320,
280
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 77,
"label": "图像"
}
],
"properties": {
"Node name for S&R": "SaveImage"
},
"widgets_values": [
"ComfyUI"
]
},
{
"id": 19,
"type": "Image Rembg (Remove Background)",
"pos": [
1640,
380
],
"size": [
320,
260
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 79,
"label": "图像",
"slot_index": 0
}
],
"outputs": [
{
"name": "images",
"type": "IMAGE",
"links": [
77
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "Image Rembg (Remove Background)"
},
"widgets_values": [
true,
"isnet-anime",
false,
false,
false,
240,
10,
10,
"none"
]
},
{
"id": 49,
"type": "LoadImage",
"pos": [
1280,
380
],
"size": [
320,
320
],
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
79,
80
],
"shape": 3,
"label": "图像",
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3,
"label": "遮罩"
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"cj.parklee_Retro_Ghibli_styleThe_magician_a_young_girl_pink_hai_34ed42e2-f958-45d9-bd57-46f34514b0cd.png",
"image"
]
}
],
"links": [
[
25,
21,
0,
22,
0,
"LIST"
],
[
77,
19,
0,
48,
0,
"IMAGE"
],
[
79,
49,
0,
19,
0,
"IMAGE"
],
[
80,
49,
0,
21,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1.1671841070450175,
"offset": [
-1031.970146842897,
-277.3179964416557
]
},
"groupNodes": {}
},
"version": 0.4
}
+223
View File
@@ -0,0 +1,223 @@
{
"last_node_id": 50,
"last_link_id": 84,
"nodes": [
{
"id": 22,
"type": "SaveSVG",
"pos": [
2220,
380
],
"size": {
"0": 220,
"1": 106
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "svg_strings",
"type": "LIST",
"link": 25,
"label": "svg_strings"
}
],
"properties": {
"Node name for S&R": "SaveSVG"
},
"widgets_values": [
"ComfyUI_SVG",
true,
""
]
},
{
"id": 21,
"type": "ConvertRasterToVector",
"pos": [
1980,
380
],
"size": {
"0": 220,
"1": 300
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 84,
"label": "image"
}
],
"outputs": [
{
"name": "LIST",
"type": "LIST",
"links": [
25
],
"shape": 3,
"label": "LIST",
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "ConvertRasterToVector"
},
"widgets_values": [
"color",
"stacked",
"spline",
4,
6,
16,
60,
4,
10,
45,
3
]
},
{
"id": 50,
"type": "JoinImageWithAlpha",
"pos": [
1760,
380
],
"size": {
"0": 210,
"1": 46
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 81,
"label": "图像"
},
{
"name": "alpha",
"type": "MASK",
"link": 82,
"label": "透明遮罩"
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
84
],
"shape": 3,
"label": "图像",
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "JoinImageWithAlpha"
}
},
{
"id": 49,
"type": "LoadImage",
"pos": [
1400,
380
],
"size": {
"0": 320,
"1": 320
},
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
81
],
"shape": 3,
"label": "图像",
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": [
82
],
"shape": 3,
"label": "遮罩",
"slot_index": 1
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"ComfyUI_temp_czplp_00006_ (2).png",
"image"
]
}
],
"links": [
[
25,
21,
0,
22,
0,
"LIST"
],
[
81,
49,
0,
50,
0,
"IMAGE"
],
[
82,
49,
1,
50,
1,
"MASK"
],
[
84,
50,
0,
21,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.8769226950000005,
"offset": [
-1013.2097887128057,
-18.536754881162064
]
},
"groupNodes": {}
},
"version": 0.4
}
+4
View File
@@ -0,0 +1,4 @@
numpy
Pillow
torch
vtracer
+141
View File
@@ -0,0 +1,141 @@
import vtracer
import os
import time
import folder_paths
import numpy as np
from PIL import Image
from typing import List, Tuple
import torch
def RGB2RGBA(image:Image, mask:Image) -> Image:
(R, G, B) = image.convert('RGB').split()
return Image.merge('RGBA', (R, G, B, mask.convert('L')))
def pil2tensor(image:Image) -> torch.Tensor:
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
def tensor2pil(t_image: torch.Tensor) -> Image:
return Image.fromarray(np.clip(255.0 * t_image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
class ConvertRasterToVector:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE",),
"colormode": (["color", "binary"], {"default": "color"}),
"hierarchical": (["stacked", "cutout"], {"default": "stacked"}),
"mode": (["spline", "polygon", "none"], {"default": "spline"}),
"filter_speckle": ("INT", {"default": 4, "min": 0, "max": 100}),
"color_precision": ("INT", {"default": 6, "min": 0, "max": 10}),
"layer_difference": ("INT", {"default": 16, "min": 0, "max": 256}),
"corner_threshold": ("INT", {"default": 60, "min": 0, "max": 180}),
"length_threshold": ("FLOAT", {"default": 4.0, "min": 0.0, "max": 10.0}),
"max_iterations": ("INT", {"default": 10, "min": 1, "max": 70}),
"splice_threshold": ("INT", {"default": 45, "min": 0, "max": 180}),
"path_precision": ("INT", {"default": 3, "min": 0, "max": 10}),
}
}
RETURN_TYPES = ("LIST",)
FUNCTION = "convert_to_svg"
CATEGORY = "💎TOSVG"
def convert_to_svg(self, image, colormode, hierarchical, mode, filter_speckle, color_precision, layer_difference, corner_threshold, length_threshold, max_iterations, splice_threshold, path_precision):
svg_strings = []
for i in image:
i = torch.unsqueeze(i, 0)
_image = tensor2pil(i)
if _image.mode != 'RGBA':
alpha = Image.new('L', _image.size, 255)
_image.putalpha(alpha)
pixels = list(_image.getdata())
size = _image.size
svg_str = vtracer.convert_pixels_to_svg(
pixels,
size=size,
colormode=colormode,
hierarchical=hierarchical,
mode=mode,
filter_speckle=filter_speckle,
color_precision=color_precision,
layer_difference=layer_difference,
corner_threshold=corner_threshold,
length_threshold=length_threshold,
max_iterations=max_iterations,
splice_threshold=splice_threshold,
path_precision=path_precision
)
svg_strings.append(svg_str)
return (svg_strings,)
class SaveSVG:
def __init__(self):
self.output_dir = folder_paths.get_output_directory()
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"svg_strings": ("LIST", {"forceInput": True}),
"filename_prefix": ("STRING", {"default": "ComfyUI_SVG"}),
},
"optional": {
"append_timestamp": ("BOOLEAN", {"default": True}),
"custom_output_path": ("STRING", {"default": "", "multiline": False}),
}
}
CATEGORY = "💎TOSVG"
DESCRIPTION = "Save SVG data to a file."
RETURN_TYPES = ()
OUTPUT_NODE = True
FUNCTION = "save_svg_file"
def generate_unique_filename(self, prefix, timestamp=False):
if timestamp:
timestamp_str = time.strftime("%Y%m%d%H%M%S")
return f"{prefix}_{timestamp_str}.svg"
else:
return f"{prefix}.svg"
def save_svg_file(self, svg_strings, filename_prefix="ComfyUI_SVG", append_timestamp=True, custom_output_path=""):
output_path = custom_output_path if custom_output_path else self.output_dir
os.makedirs(output_path, exist_ok=True)
ui_info_list = []
for index, svg_string in enumerate(svg_strings):
unique_filename = self.generate_unique_filename(f"{filename_prefix}_{index}", append_timestamp)
final_filepath = os.path.join(output_path, unique_filename)
with open(final_filepath, "w") as svg_file:
svg_file.write(svg_string)
ui_info = {"ui": {"saved_svg": unique_filename, "path": final_filepath}}
ui_info_list.append(ui_info)
return ui_info_list