Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acc0a8d1cc | ||
|
|
82e3f4dbec | ||
|
|
10d93e0823 | ||
|
|
2ded553837 | ||
|
|
00a7d5a26c | ||
|
|
980408b363 | ||
|
|
eaaab6e346 | ||
|
|
eccc4bca31 | ||
|
|
8ed66b3260 | ||
|
|
7d0cfb6e5b | ||
|
|
a9c3075b70 | ||
|
|
0f29e2c27d | ||
|
|
b3ee46fdf2 |
@@ -79,6 +79,7 @@ class AvatarMainOutput(blender_node.ObjectOps):
|
||||
"files": [{
|
||||
"filename": filepath.replace(f"{self.output_dir}/", ""),
|
||||
"content_type": "model/gltf+json",
|
||||
"type": "output"
|
||||
},],
|
||||
"SHAPE_FLOW": {SHAPE_FLOW},
|
||||
"auto_save": {'true' if auto_save else 'false'},
|
||||
|
||||
+11
-11
@@ -259,7 +259,7 @@ def get_avatar_file(output):
|
||||
|
||||
def upload_avatar_file(output):
|
||||
file = get_avatar_file(output)
|
||||
response = requests.get("https://labs.avatech.ai/api/share")
|
||||
response = requests.get("https://labs.avatech.ai/api/share?version=v2")
|
||||
labData = response.json()
|
||||
modelId = labData["modelId"]
|
||||
|
||||
@@ -272,15 +272,15 @@ def upload_avatar_file(output):
|
||||
requests.put(labData["url"], headers=headers, data=file)
|
||||
|
||||
# send notification
|
||||
webhook_url = os.getenv("DISCORD_WEBHOOK_URL")
|
||||
data = {
|
||||
"username": "Avabot",
|
||||
"avatar_url": "https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/avatechai.png",
|
||||
"content": "[API Call] New register!",
|
||||
}
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
response = requests.post(webhook_url, headers=headers, data=json.dumps(data))
|
||||
# webhook_url = os.getenv("DISCORD_WEBHOOK_URL")
|
||||
# data = {
|
||||
# "username": "Avabot",
|
||||
# "avatar_url": "https://avatech-avatar-dev1.nyc3.cdn.digitaloceanspaces.com/avatechai.png",
|
||||
# "content": "[API Call] New register!",
|
||||
# }
|
||||
# headers = {
|
||||
# "Content-Type": "application/json",
|
||||
# }
|
||||
# response = requests.post(webhook_url, headers=headers, data=json.dumps(data))
|
||||
|
||||
return modelId
|
||||
|
||||
@@ -41,7 +41,7 @@ class Object_CreateMeshLayer(blender_node.ObjectOps):
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
bpy.ops.mesh.edge_face_add()
|
||||
|
||||
bpy.ops.transform.resize(value=(scale_x, scale_y, 1))
|
||||
bpy.ops.transform.resize(value=(float(scale_x), float(scale_y), 1))
|
||||
|
||||
bpy.context.object.vertex_groups.new(name=mesh_layer_name)
|
||||
bpy.ops.object.vertex_group_assign()
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import blender_node
|
||||
|
||||
|
||||
class Mesh_SetShapeKeyValue(blender_node.ObjectOps):
|
||||
|
||||
CUSTOM_NAME = "Set Shape Key Value"
|
||||
|
||||
EXTRA_INPUT_TYPES = {
|
||||
"shape_key_name": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "my_shape_key",
|
||||
}),
|
||||
"value": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01, "display": "number"}),
|
||||
}
|
||||
|
||||
def blender_process(self, bpy, BPY_OBJ, shape_key_name, value):
|
||||
# Check if the object has shape keys
|
||||
if BPY_OBJ.data.shape_keys:
|
||||
# Check if the specified shape key exists
|
||||
if shape_key_name in BPY_OBJ.data.shape_keys.key_blocks:
|
||||
BPY_OBJ.data.shape_keys.key_blocks[shape_key_name].value = float(value)
|
||||
else:
|
||||
print(f"The shape key {shape_key_name} does not exist on the object.")
|
||||
else:
|
||||
print("The object does not have any shape keys.")
|
||||
return (BPY_OBJ,)
|
||||
@@ -0,0 +1,132 @@
|
||||
import blender_node
|
||||
import math
|
||||
import folder_paths
|
||||
import torch
|
||||
import numpy as np
|
||||
import os
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
def get_incremented_filename(folder_path, base_filename):
|
||||
# Initialize the counter and create the full initial path
|
||||
counter = 0
|
||||
output_path = f"{folder_path}/{base_filename}.png"
|
||||
|
||||
# Check if the file exists and increment the counter until the file does not exist
|
||||
while os.path.exists(output_path):
|
||||
counter += 1
|
||||
output_path = f"{folder_path}/{base_filename}_{counter}.png"
|
||||
|
||||
return output_path
|
||||
|
||||
class BlenderRenderImage(blender_node.ObjectOps):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
EXTRA_INPUT_TYPES = {
|
||||
}
|
||||
|
||||
# OUTPUT_NODE = True
|
||||
RETURN_TYPES = ("BPY_OBJ", "IMAGE")
|
||||
|
||||
def add_light(self, bpy):
|
||||
# Check if there is at least one light source in the scene
|
||||
light_exists = any(ob for ob in bpy.data.objects if ob.type == 'LIGHT')
|
||||
if not light_exists:
|
||||
# Create a new Area light datablock for ambient light
|
||||
light_data = bpy.data.lights.new(name='AmbientLight', type='AREA')
|
||||
light_object = bpy.data.objects.new(name='AmbientLight', object_data=light_data)
|
||||
bpy.context.collection.objects.link(light_object)
|
||||
# Position the light in the scene
|
||||
light_object.location = (0, 0, 10)
|
||||
# Set light size for soft shadows and ambient effect
|
||||
light_data.size = 10
|
||||
light_data.energy = 1000
|
||||
print("Added an ambient light source to the scene.")
|
||||
|
||||
def add_camera(self, bpy):
|
||||
# Check if there is a camera in the scene
|
||||
if bpy.context.scene.camera:
|
||||
return bpy.context.scene.camera
|
||||
|
||||
# If not, create a new camera
|
||||
cam_data = bpy.data.cameras.new(name='Camera')
|
||||
cam = bpy.data.objects.new(name='Camera', object_data=cam_data)
|
||||
bpy.context.collection.objects.link(cam)
|
||||
# Set the new camera to the active camera
|
||||
bpy.context.scene.camera = cam
|
||||
# Position the camera to a default view
|
||||
cam.location = (0, 0, 10)
|
||||
return cam
|
||||
|
||||
def get_texture_size(self, obj):
|
||||
# Get the first material slot
|
||||
mat = obj.data.materials[0]
|
||||
|
||||
# Check if the material has a node tree
|
||||
if mat.node_tree:
|
||||
nodes = mat.node_tree.nodes
|
||||
# Find an image texture node in the node tree
|
||||
for node in nodes:
|
||||
if node.type == 'TEX_IMAGE':
|
||||
texture = node.image
|
||||
if texture:
|
||||
return texture.size
|
||||
print("No image texture node found in the material's node tree.")
|
||||
else:
|
||||
print("Material has no node tree.")
|
||||
|
||||
|
||||
def blender_process(self, bpy, BPY_OBJ=None):
|
||||
cam = self.add_camera(bpy)
|
||||
|
||||
plane = BPY_OBJ
|
||||
if plane:
|
||||
tex_width, tex_height = self.get_texture_size(plane)
|
||||
# Calculate the aspect ratio of the plane
|
||||
aspect_ratio_plane = tex_width / tex_height
|
||||
|
||||
# Set the render resolution to match the plane's aspect ratio
|
||||
# Choose an arbitrary resolution for the longer side of the plane
|
||||
base_resolution = 512
|
||||
|
||||
if aspect_ratio_plane > 1:
|
||||
# Plane is wider than it is tall
|
||||
bpy.context.scene.render.resolution_x = base_resolution
|
||||
bpy.context.scene.render.resolution_y = int(base_resolution / aspect_ratio_plane)
|
||||
else:
|
||||
# Plane is taller than it is wide
|
||||
bpy.context.scene.render.resolution_x = int(base_resolution * aspect_ratio_plane)
|
||||
bpy.context.scene.render.resolution_y = base_resolution
|
||||
bpy.context.scene.render.resolution_percentage = 100
|
||||
|
||||
|
||||
ortho_scale = max(plane.dimensions.x, plane.dimensions.y)
|
||||
cam.data.type = 'ORTHO'
|
||||
cam.data.ortho_scale = ortho_scale
|
||||
|
||||
self.add_light(bpy)
|
||||
|
||||
# Update the scene to reflect changes
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
# Set render engine (e.g., 'BLENDER_EEVEE', 'CYCLES', 'BLENDER_WORKBENCH')
|
||||
bpy.context.scene.render.engine = "BLENDER_EEVEE"
|
||||
|
||||
# Specify the render output path
|
||||
|
||||
output_path = get_incremented_filename(folder_paths.get_output_directory(), "render")
|
||||
bpy.context.scene.render.filepath = output_path
|
||||
|
||||
# Render the image
|
||||
bpy.ops.render.render(write_still=True)
|
||||
|
||||
# Load the image
|
||||
i = Image.open(output_path)
|
||||
i = ImageOps.exif_transpose(i)
|
||||
image = i.convert("RGB")
|
||||
image = np.array(image).astype(np.float32) / 255.0
|
||||
image = torch.from_numpy(image)[None,]
|
||||
|
||||
# print(image.shape)
|
||||
|
||||
return (BPY_OBJ, image)
|
||||
+1
-1
@@ -706,7 +706,7 @@ export function AvatarPreview() {
|
||||
console.log(showPreview);
|
||||
|
||||
return (
|
||||
(showPreview.val ? "" : "hidden ") +
|
||||
(showPreview.val && !showEditor.val ? "" : "hidden ") +
|
||||
"absolute w-[360px] h-[360px] rounded-xl overflow-hidden right-0 top-0 z-[99] pointer-events-auto flex border-none bg-transparent"
|
||||
);
|
||||
},
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
import { combinePointsNode, samPrompts } from "./state.js";
|
||||
import { van } from "./van.js";
|
||||
const { div, dialog, form, button, h3, input, span } = van.tags;
|
||||
|
||||
van.derive(() => {
|
||||
if (
|
||||
combinePointsNode.val != undefined &&
|
||||
combinePointsNode.val.type === "Combine Points"
|
||||
) {
|
||||
const inputNames = combinePointsNode.val.inputs?.map((x) => x.name) || [];
|
||||
const record = Object.keys(samPrompts.val);
|
||||
|
||||
const diff = inputNames.filter((x) => !record.includes(x));
|
||||
const missingDiff = record.filter((x) => !inputNames.includes(x));
|
||||
|
||||
if (diff.length > 0) {
|
||||
diff.forEach((x) => {
|
||||
combinePointsNode.val.removeInput(
|
||||
combinePointsNode.val.findInputSlot(x)
|
||||
);
|
||||
});
|
||||
combinePointsNode.val.graph.change();
|
||||
}
|
||||
|
||||
if (missingDiff.length > 0) {
|
||||
missingDiff.forEach((x) => {
|
||||
combinePointsNode.val.addInput(x, "POINTS");
|
||||
});
|
||||
combinePointsNode.val.graph.change();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export function CombinePointsDialog() {
|
||||
const showAddLayer = van.state(false);
|
||||
|
||||
return div(
|
||||
{
|
||||
class: () =>
|
||||
"absolute z-[100] top-0 left-0 flex justify-center w-full h-full ",
|
||||
},
|
||||
() =>
|
||||
dialog(
|
||||
{ id: "combine_points_dialog", class: "modal" },
|
||||
div(
|
||||
{ class: "modal-box text-base-content" },
|
||||
form(
|
||||
{
|
||||
class: "gap-2 flex flex-col",
|
||||
method: "dialog",
|
||||
onsubmit: (e) => {
|
||||
e.preventDefault();
|
||||
combine_points_dialog.close();
|
||||
},
|
||||
},
|
||||
button(
|
||||
{
|
||||
type: "button",
|
||||
class: "btn btn-sm btn-circle btn-ghost absolute right-2 top-2",
|
||||
onclick: (e) => {
|
||||
e.stopPropagation();
|
||||
combine_points_dialog.close();
|
||||
},
|
||||
},
|
||||
"✕"
|
||||
),
|
||||
h3({ class: "font-bold text-lg text-base-content" }, "Edit points"),
|
||||
() =>
|
||||
div(
|
||||
{ class: "flex flex-col gap-2 mb-2" },
|
||||
...Object.keys(samPrompts.val).map((key) => {
|
||||
return span(key);
|
||||
})
|
||||
),
|
||||
|
||||
() =>
|
||||
showAddLayer.val
|
||||
? div(
|
||||
{
|
||||
class:
|
||||
"flex flex-row justify-center items-center border rounded-md pr-2",
|
||||
},
|
||||
input({
|
||||
type: "text",
|
||||
placeholder: "Type here",
|
||||
id: "layerName",
|
||||
class:
|
||||
"input input-ghost w-full focus:ring-0 focus:border-none focus:outline-none",
|
||||
autofocus: true,
|
||||
}),
|
||||
button(
|
||||
{
|
||||
onclick: (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
showAddLayer.val = false;
|
||||
},
|
||||
},
|
||||
span({
|
||||
class: "iconify text-2xl",
|
||||
"data-icon": "iconoir:cancel",
|
||||
})
|
||||
),
|
||||
button(
|
||||
{
|
||||
onclick: (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
showAddLayer.val = false;
|
||||
const inputText =
|
||||
document.getElementById("layerName").value;
|
||||
samPrompts.val = {
|
||||
...samPrompts.val,
|
||||
[inputText]: [],
|
||||
};
|
||||
},
|
||||
},
|
||||
span({
|
||||
class: "iconify text-2xl",
|
||||
"data-icon": "mdi:tick",
|
||||
})
|
||||
)
|
||||
)
|
||||
: button(
|
||||
{
|
||||
class: "btn btn-outline btn",
|
||||
onclick: (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
showAddLayer.val = true;
|
||||
},
|
||||
},
|
||||
"Add new layer"
|
||||
),
|
||||
button(
|
||||
{
|
||||
type: "submit",
|
||||
class: "btn btn-sm btn-ghost place-self-end",
|
||||
},
|
||||
"Confirm"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
+11
-9
@@ -1,18 +1,20 @@
|
||||
import { LayerEditor } from './LayerEditor.js';
|
||||
import { ShapeFlowEditor } from './ShapeFlowEditor.js';
|
||||
import { van } from './van.js';
|
||||
import { AvatarPreview } from './AvatarPreview.js';
|
||||
import { Loading } from './Loading.js';
|
||||
import { Alert } from './Alert.js';
|
||||
import { AppHeader } from './AppHeader.js';
|
||||
import { LayerEditor } from "./LayerEditor.js";
|
||||
import { ShapeFlowEditor } from "./ShapeFlowEditor.js";
|
||||
import { van } from "./van.js";
|
||||
import { AvatarPreview } from "./AvatarPreview.js";
|
||||
import { Loading } from "./Loading.js";
|
||||
import { Alert } from "./Alert.js";
|
||||
import { AppHeader } from "./AppHeader.js";
|
||||
import { CombinePointsDialog } from "./CombinePointsDialog.js";
|
||||
const { button, iframe, div, img } = van.tags;
|
||||
|
||||
export function Container() {
|
||||
return div(
|
||||
{
|
||||
class: 'fixed left-0 top-0 w-full h-full z-[1000] pointer-events-none',
|
||||
id: 'avatech-editor',
|
||||
class: "fixed left-0 top-0 w-full h-full z-[1000] pointer-events-none",
|
||||
id: "avatech-editor",
|
||||
},
|
||||
CombinePointsDialog(),
|
||||
ShapeFlowEditor(),
|
||||
LayerEditor(),
|
||||
AvatarPreview(),
|
||||
|
||||
+29
-24
@@ -6,6 +6,7 @@ import {
|
||||
targetNode,
|
||||
showImageEditor,
|
||||
allImagePrompts,
|
||||
samPrompts,
|
||||
} from "./state.js";
|
||||
import { van } from "./van.js";
|
||||
const {
|
||||
@@ -24,6 +25,33 @@ const {
|
||||
span,
|
||||
} = van.tags;
|
||||
|
||||
export const updateOutputs = () => {
|
||||
const outputNames = targetNode.val.outputs.map((x) => x.name).slice(1);
|
||||
const record = Object.keys(imagePromptsMulti.val);
|
||||
|
||||
const diff = outputNames.filter((x) => !record.includes(x));
|
||||
const missingDiff = record.filter((x) => !outputNames.includes(x));
|
||||
|
||||
if (diff.length > 0) {
|
||||
console.log("Cleaning up missing output slots", diff);
|
||||
diff.forEach((x) => {
|
||||
targetNode.val.removeOutput(targetNode.val.findOutputSlot(x));
|
||||
});
|
||||
targetNode.val.graph.change();
|
||||
}
|
||||
|
||||
if (missingDiff.length > 0) {
|
||||
console.log("Adding missing output slots", diff);
|
||||
missingDiff.forEach((x) => {
|
||||
targetNode.val.addOutput(
|
||||
x,
|
||||
targetNode.val.type === "SAM MultiLayer" ? "IMAGE" : "SAM_PROMPT"
|
||||
);
|
||||
});
|
||||
targetNode.val.graph.change();
|
||||
}
|
||||
};
|
||||
|
||||
van.derive(() => {
|
||||
if (
|
||||
showImageEditor.val &&
|
||||
@@ -31,30 +59,7 @@ van.derive(() => {
|
||||
targetNode.val.outputs &&
|
||||
targetNode.val.type === "SAM MultiLayer"
|
||||
) {
|
||||
const outputNames = targetNode.val.outputs.map((x) => x.name).slice(1);
|
||||
const record = Object.keys(imagePromptsMulti.val);
|
||||
|
||||
const diff = outputNames.filter((x) => !record.includes(x));
|
||||
const missingDiff = record.filter((x) => !outputNames.includes(x));
|
||||
|
||||
if (diff.length > 0) {
|
||||
console.log("Cleaning up missing output slots", diff);
|
||||
diff.forEach((x) => {
|
||||
targetNode.val.removeOutput(targetNode.val.findOutputSlot(x));
|
||||
});
|
||||
targetNode.val.graph.change();
|
||||
}
|
||||
|
||||
if (missingDiff.length > 0) {
|
||||
console.log("Adding missing output slots", diff);
|
||||
missingDiff.forEach((x) => {
|
||||
targetNode.val.addOutput(
|
||||
x,
|
||||
targetNode.val.type === "SAM MultiLayer" ? "IMAGE" : "SAM_PROMPT"
|
||||
);
|
||||
});
|
||||
targetNode.val.graph.change();
|
||||
}
|
||||
updateOutputs();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+46
-5
@@ -5,6 +5,7 @@ import {
|
||||
imageUrl,
|
||||
imagePrompts,
|
||||
targetNode,
|
||||
combinePointsNode,
|
||||
fileName,
|
||||
embeddings,
|
||||
imagePromptsMulti,
|
||||
@@ -17,6 +18,7 @@ import {
|
||||
previewModelId,
|
||||
embeddingID,
|
||||
enableAutoSegment,
|
||||
samPrompts,
|
||||
} from "./state.js";
|
||||
import { van } from "./van.js";
|
||||
import { app } from "./app.js";
|
||||
@@ -33,6 +35,7 @@ import {
|
||||
import { infoDialog } from "./dialog.js";
|
||||
import { sharedAvatarLink } from "./AvatarPreview.js";
|
||||
import { updateImagePrompts } from "./LayerEditor.js";
|
||||
import { updateOutputs } from "./SideBar.js";
|
||||
|
||||
export const generatedImages = {};
|
||||
|
||||
@@ -357,7 +360,6 @@ function showMyImageEditor(node) {
|
||||
drawSegment(getClicks());
|
||||
updateImagePrompts();
|
||||
});
|
||||
targetNode.val = node;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
@@ -385,6 +387,25 @@ const ext = {
|
||||
showMyImageEditor(node);
|
||||
btn.serialize = false;
|
||||
});
|
||||
|
||||
targetNode.val = node;
|
||||
node.onConnectInput = (node, slot, targetSlot) => {
|
||||
if (targetSlot.name === "SAM_PROMPTS") {
|
||||
imagePromptsMulti.val = samPrompts.val;
|
||||
updateOutputs();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
widget: btn,
|
||||
};
|
||||
},
|
||||
COMBINE_POINTS(node, inputName, inputData, app) {
|
||||
const btn = node.addWidget("button", "Edit points", "", () => {
|
||||
console.log("Edit points");
|
||||
combine_points_dialog.showModal();
|
||||
combinePointsNode.val = node;
|
||||
});
|
||||
return {
|
||||
widget: btn,
|
||||
};
|
||||
@@ -675,6 +696,18 @@ const ext = {
|
||||
nodeData.input.required.sam = ["SAM_PROMPTS"];
|
||||
// nodeData.input.required.upload = ['IMAGEUPLOAD'];
|
||||
// nodeData.input.required.prompts_points = ["IMAGEUPLOAD"];
|
||||
addMenuHandler(nodeType, function (_, options) {
|
||||
options.unshift({
|
||||
content: "Open In Points Editor (Local)",
|
||||
callback: () => {
|
||||
showMyImageEditor(this);
|
||||
},
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "Combine Points":
|
||||
nodeData.input.required.sam = ["COMBINE_POINTS"];
|
||||
|
||||
addMenuHandler(nodeType, function (_, options) {
|
||||
options.unshift({
|
||||
content: "Open In Points Editor (Local)",
|
||||
@@ -746,11 +779,19 @@ function injectUIComponentToComfyuimenu() {
|
||||
if (!filename.toLowerCase().endsWith(".json")) {
|
||||
filename += ".json";
|
||||
}
|
||||
app.graphToPrompt().then(p=>{
|
||||
console.log('fkfk');
|
||||
app.graphToPrompt().then((p) => {
|
||||
let json = JSON.stringify(p.output, null, 2); // convert the data to a JSON string
|
||||
json = json.replace(/"seed": (\d+)/g, `"seed": "SEED"`).replace(/"image": "(?!.*mask.*\.png).*"/g, '"image": "reference_image_avatech"').replace(/"embedding_id": ".*"/g, '"embedding_id": "embedding_id_avatech"');
|
||||
const blob = new Blob([json], {type: "application/json"});
|
||||
json = json
|
||||
.replace(/"seed": (\d+)/g, `"seed": "SEED"`)
|
||||
.replace(
|
||||
/"image": "(?!.*mask.*\.png).*"/g,
|
||||
'"image": "reference_image_avatech"'
|
||||
)
|
||||
.replace(
|
||||
/"embedding_id": ".*"/g,
|
||||
'"embedding_id": "embedding_id_avatech"'
|
||||
);
|
||||
const blob = new Blob([json], { type: "application/json" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
|
||||
+6
-4
@@ -1,16 +1,18 @@
|
||||
import "https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js";
|
||||
import "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.16.3/dist/ort.min.js";
|
||||
import npyjs from "https://esm.sh/npyjs";
|
||||
import { imageSize } from "./state.js";
|
||||
import { modelData, onnxMaskToImage } from "./onnx_helper.js";
|
||||
|
||||
ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/";
|
||||
ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.16.3/dist/";
|
||||
|
||||
export let model = null;
|
||||
let modelType = null;
|
||||
|
||||
// Initialize the ONNX model
|
||||
export const initModel = async (modelType) => {
|
||||
export const initModel = async (type) => {
|
||||
try {
|
||||
if (!model) {
|
||||
if (!model || modelType !== type) {
|
||||
modelType = type;
|
||||
model = await ort.InferenceSession.create(
|
||||
`${location.protocol}//${location.host}/sam_model?type=${modelType}`
|
||||
);
|
||||
|
||||
+8
-3
@@ -19,11 +19,13 @@
|
||||
*/
|
||||
|
||||
import { van } from "./van.js";
|
||||
|
||||
export const iframeSrc = van.state("https://editor.avatech.ai?comfyui=true");
|
||||
export const showEditor = van.state(false);
|
||||
// localStorage.getItem("showPreview") == 'true'
|
||||
export const showPreview = van.state(true);
|
||||
console.log(localStorage.getItem("showPreview"));
|
||||
if (localStorage.getItem("showPreview") == null)
|
||||
localStorage.setItem("showPreview", 'true')
|
||||
export const showPreview = van.state(localStorage.getItem("showPreview") == 'true');
|
||||
export const previewUrl = van.state(
|
||||
"https://editor.avatech.ai/viewer?avatarId=default&debug=true&width=350&height=350&hideTrigger=true&voiceSelection=true&hideUI=true"
|
||||
);
|
||||
@@ -63,7 +65,6 @@ export const imagePrompts = van.state([]);
|
||||
|
||||
export const allImagePrompts = van.state([{}]);
|
||||
|
||||
|
||||
/** @type {State<Record<string, Point[]>>} */
|
||||
export const imagePromptsMulti = van.state({});
|
||||
|
||||
@@ -76,3 +77,7 @@ export const targetNode = van.state();
|
||||
export const imageSize = van.state({ width: 0, height: 0, samScale: 0 });
|
||||
export const embeddings = van.state();
|
||||
export const embeddingID = van.state("Test");
|
||||
|
||||
/** @type {State<LGraphNode>} */
|
||||
export const combinePointsNode = van.state();
|
||||
export const samPrompts = van.state({});
|
||||
|
||||
+261
-88
@@ -1,7 +1,7 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Gabarito&display=swap');
|
||||
|
||||
/*
|
||||
! tailwindcss v3.4.0 | MIT License | https://tailwindcss.com
|
||||
! tailwindcss v3.3.5 | MIT License | https://tailwindcss.com
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -34,11 +34,9 @@
|
||||
4. Use the user's configured `sans` font-family by default.
|
||||
5. Use the user's configured `sans` font-feature-settings by default.
|
||||
6. Use the user's configured `sans` font-variation-settings by default.
|
||||
7. Disable tap highlights on iOS
|
||||
*/
|
||||
|
||||
html,
|
||||
:host {
|
||||
html {
|
||||
line-height: 1.5;
|
||||
/* 1 */
|
||||
-webkit-text-size-adjust: 100%;
|
||||
@@ -48,14 +46,12 @@ html,
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
/* 3 */
|
||||
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
/* 4 */
|
||||
font-feature-settings: normal;
|
||||
/* 5 */
|
||||
font-variation-settings: normal;
|
||||
/* 6 */
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
/* 7 */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -127,10 +123,8 @@ strong {
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use the user's configured `mono` font-family by default.
|
||||
2. Use the user's configured `mono` font-feature-settings by default.
|
||||
3. Use the user's configured `mono` font-variation-settings by default.
|
||||
4. Correct the odd `em` font sizing in all browsers.
|
||||
1. Use the user's configured `mono` font family by default.
|
||||
2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
@@ -139,12 +133,8 @@ samp,
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
/* 1 */
|
||||
font-feature-settings: normal;
|
||||
/* 2 */
|
||||
font-variation-settings: normal;
|
||||
/* 3 */
|
||||
font-size: 1em;
|
||||
/* 4 */
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1011,6 +1001,99 @@ html{
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline:hover{
|
||||
--tw-border-opacity: 1;
|
||||
border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-primary:hover{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
@supports (color: color-mix(in oklab, black, black)){
|
||||
.btn-outline.btn-primary:hover{
|
||||
background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline.btn-secondary:hover{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
@supports (color: color-mix(in oklab, black, black)){
|
||||
.btn-outline.btn-secondary:hover{
|
||||
background-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline.btn-accent:hover{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
@supports (color: color-mix(in oklab, black, black)){
|
||||
.btn-outline.btn-accent:hover{
|
||||
background-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline.btn-success:hover{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
@supports (color: color-mix(in oklab, black, black)){
|
||||
.btn-outline.btn-success:hover{
|
||||
background-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline.btn-info:hover{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
@supports (color: color-mix(in oklab, black, black)){
|
||||
.btn-outline.btn-info:hover{
|
||||
background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline.btn-warning:hover{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
@supports (color: color-mix(in oklab, black, black)){
|
||||
.btn-outline.btn-warning:hover{
|
||||
background-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-outline.btn-error:hover{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
@supports (color: color-mix(in oklab, black, black)){
|
||||
.btn-outline.btn-error:hover{
|
||||
background-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-disabled:hover,
|
||||
.btn[disabled]:hover,
|
||||
.btn:disabled:hover{
|
||||
@@ -1353,6 +1436,43 @@ html{
|
||||
}
|
||||
}
|
||||
|
||||
@supports (color: color-mix(in oklab, black, black)){
|
||||
.btn-outline.btn-primary.btn-active{
|
||||
background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black);
|
||||
}
|
||||
|
||||
.btn-outline.btn-secondary.btn-active{
|
||||
background-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-s,oklch(var(--s)/1)) 90%, black);
|
||||
}
|
||||
|
||||
.btn-outline.btn-accent.btn-active{
|
||||
background-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black);
|
||||
}
|
||||
|
||||
.btn-outline.btn-success.btn-active{
|
||||
background-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-su,oklch(var(--su)/1)) 90%, black);
|
||||
}
|
||||
|
||||
.btn-outline.btn-info.btn-active{
|
||||
background-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-in,oklch(var(--in)/1)) 90%, black);
|
||||
}
|
||||
|
||||
.btn-outline.btn-warning.btn-active{
|
||||
background-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-wa,oklch(var(--wa)/1)) 90%, black);
|
||||
}
|
||||
|
||||
.btn-outline.btn-error.btn-active{
|
||||
background-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black);
|
||||
border-color: color-mix(in oklab, var(--fallback-er,oklch(var(--er)/1)) 90%, black);
|
||||
}
|
||||
}
|
||||
|
||||
.btn:focus-visible{
|
||||
outline-style: solid;
|
||||
outline-width: 2px;
|
||||
@@ -1399,6 +1519,95 @@ html{
|
||||
background-color: var(--fallback-bc,oklch(var(--bc)/0.2));
|
||||
}
|
||||
|
||||
.btn-outline{
|
||||
border-color: currentColor;
|
||||
background-color: transparent;
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.btn-outline.btn-active{
|
||||
--tw-border-opacity: 1;
|
||||
border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity)));
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity)));
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-b1,oklch(var(--b1)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-primary{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-primary.btn-active{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-secondary{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-secondary.btn-active{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-sc,oklch(var(--sc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-accent{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-accent.btn-active{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-success{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-success.btn-active{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-suc,oklch(var(--suc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-info{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-info.btn-active{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-warning{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-warning.btn-active{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-wac,oklch(var(--wac)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-error{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn-outline.btn-error.btn-active{
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity)));
|
||||
}
|
||||
|
||||
.btn.btn-disabled,
|
||||
.btn[disabled],
|
||||
.btn:disabled{
|
||||
@@ -1570,6 +1779,18 @@ details.collapse summary::-webkit-details-marker{
|
||||
outline-color: var(--fallback-bc,oklch(var(--bc)/0.2));
|
||||
}
|
||||
|
||||
.input-ghost{
|
||||
--tw-bg-opacity: 0.05;
|
||||
}
|
||||
|
||||
.input-ghost:focus,
|
||||
.input-ghost:focus-within{
|
||||
--tw-bg-opacity: 1;
|
||||
--tw-text-opacity: 1;
|
||||
color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity)));
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.input-disabled,
|
||||
.input:disabled,
|
||||
.input[disabled]{
|
||||
@@ -2248,6 +2469,10 @@ details.collapse summary::-webkit-details-marker{
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
.z-\[999\]{
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.z-\[99\]{
|
||||
z-index: 99;
|
||||
}
|
||||
@@ -2314,6 +2539,10 @@ details.collapse summary::-webkit-details-marker{
|
||||
height: 24rem;
|
||||
}
|
||||
|
||||
.h-\[360px\]{
|
||||
height: 360px;
|
||||
}
|
||||
|
||||
.h-\[394px\]{
|
||||
height: 394px;
|
||||
}
|
||||
@@ -2322,35 +2551,6 @@ details.collapse summary::-webkit-details-marker{
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.h-fit{
|
||||
height: -moz-fit-content;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.h-\[400px\]{
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.h-\[420px\]{
|
||||
height: 420px;
|
||||
}
|
||||
|
||||
.h-\[410px\]{
|
||||
height: 410px;
|
||||
}
|
||||
|
||||
.h-\[360px\]{
|
||||
height: 360px;
|
||||
}
|
||||
|
||||
.min-h-\[400px\]{
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.min-h-\[380px\]{
|
||||
min-height: 380px;
|
||||
}
|
||||
|
||||
.min-h-\[350px\]{
|
||||
min-height: 350px;
|
||||
}
|
||||
@@ -2379,6 +2579,10 @@ details.collapse summary::-webkit-details-marker{
|
||||
width: 32rem;
|
||||
}
|
||||
|
||||
.w-\[360px\]{
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.w-fit{
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
@@ -2388,38 +2592,6 @@ details.collapse summary::-webkit-details-marker{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.w-\[\]{
|
||||
width: ;
|
||||
}
|
||||
|
||||
.w-\[400px\]{
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.w-\[420px\]{
|
||||
width: 420px;
|
||||
}
|
||||
|
||||
.w-\[410px\]{
|
||||
width: 410px;
|
||||
}
|
||||
|
||||
.w-\[360px\]{
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.min-w-\[400px\]{
|
||||
min-width: 400px;
|
||||
}
|
||||
|
||||
.min-w-\[380\]{
|
||||
min-width: 380;
|
||||
}
|
||||
|
||||
.min-w-\[380px\]{
|
||||
min-width: 380px;
|
||||
}
|
||||
|
||||
.min-w-\[350px\]{
|
||||
min-width: 350px;
|
||||
}
|
||||
@@ -2514,10 +2686,6 @@ details.collapse summary::-webkit-details-marker{
|
||||
border-radius: 0.125rem;
|
||||
}
|
||||
|
||||
.rounded-2xl{
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.rounded-xl{
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
@@ -2739,10 +2907,6 @@ details.collapse summary::-webkit-details-marker{
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.p-24{
|
||||
padding: 6rem;
|
||||
}
|
||||
|
||||
.\!px-0{
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
@@ -2763,9 +2927,8 @@ details.collapse summary::-webkit-details-marker{
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.py-16{
|
||||
padding-top: 4rem;
|
||||
padding-bottom: 4rem;
|
||||
.pr-2{
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.text-start{
|
||||
@@ -3092,11 +3255,21 @@ img[src] {
|
||||
color: rgb(239 68 68 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.focus\:border-none:focus{
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
.focus\:outline-none:focus{
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.focus\:ring-0:focus{
|
||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
||||
}
|
||||
|
||||
@media (min-width: 640px){
|
||||
.sm\:flex{
|
||||
display: flex;
|
||||
|
||||
@@ -285,6 +285,52 @@ async def post_prompt_block(request):
|
||||
return web.json_response({"id": modelId}, status=200)
|
||||
time.sleep(0.5)
|
||||
|
||||
# TODO: refactor the code
|
||||
@server.PromptServer.instance.routes.post("/rendering_generation")
|
||||
async def post_data_generation(request):
|
||||
prompt_server = server.PromptServer.instance
|
||||
post = await request.json()
|
||||
workflow_name = post.get("workflow_name")
|
||||
workflow = load_workflow(workflow_name)
|
||||
workflow = workflow.replace("SEED", str(randomSeed()))
|
||||
|
||||
inputs = post.get("inputs")
|
||||
|
||||
for key, value in inputs.items():
|
||||
workflow = workflow.replace(f'"{key}"', f'"{str(value)}"')
|
||||
|
||||
res = post_prompt({"prompt": json.loads(workflow)})
|
||||
prompt_id = json.loads(res.text)["prompt_id"]
|
||||
while True:
|
||||
history = prompt_server.prompt_queue.get_history(prompt_id=prompt_id)
|
||||
if history:
|
||||
outputs = history[prompt_id]["outputs"]
|
||||
for node_id, output in outputs.items():
|
||||
if "images" in output:
|
||||
filename = output["images"][0]["filename"]
|
||||
if filename.startswith("rendered"):
|
||||
return web.json_response({"image": filename}, status=200)
|
||||
time.sleep(0.5)
|
||||
|
||||
@server.PromptServer.instance.routes.post("/image_generation")
|
||||
async def post_image_generation(request):
|
||||
prompt_server = server.PromptServer.instance
|
||||
workflow = load_workflow("generation")
|
||||
workflow = workflow.replace("SEED", str(randomSeed()))
|
||||
|
||||
res = post_prompt({"prompt": json.loads(workflow)})
|
||||
prompt_id = json.loads(res.text)["prompt_id"]
|
||||
while True:
|
||||
history = prompt_server.prompt_queue.get_history(prompt_id=prompt_id)
|
||||
if history:
|
||||
outputs = history[prompt_id]["outputs"]
|
||||
for node_id, output in outputs.items():
|
||||
if "images" in output:
|
||||
filename = output["images"][0]["filename"]
|
||||
if filename.startswith("avatar"):
|
||||
return web.json_response({"image": filename}, status=200)
|
||||
time.sleep(0.5)
|
||||
|
||||
|
||||
# @server.PromptServer.instance.routes.get("/get_default_workflow")
|
||||
# async def get_default_workflow(request):
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import json
|
||||
|
||||
class CombinePoints:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_NAMES = ("SAM_PROMPTS",)
|
||||
RETURN_TYPES = ("STRING",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
# OUTPUT_NODE = True
|
||||
|
||||
def run(self, *args, **kwargs):
|
||||
sam_prompts = json.dumps(kwargs, default=str)
|
||||
return (sam_prompts,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {"Combine Points": CombinePoints}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {"Combine Points": "Combine Points"}
|
||||
@@ -0,0 +1,66 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
|
||||
class ExtractBoundaryPoints:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"n_points": ("INT", {"default": -1, "min": -1, "max": 100}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("POINTS", "IMAGE")
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def find_main_contour(self, image, n_points):
|
||||
image = np.copy(image[0].numpy())
|
||||
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
|
||||
gray = (gray * 255).astype(np.uint8)
|
||||
# Find contours
|
||||
contours, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
if len(contours) == 0:
|
||||
raise Exception(
|
||||
"No contours found. Please ensure that the image has the correct segments (e.g. when you click on the mouth, it should display a proper blue area over the mouth region)."
|
||||
)
|
||||
|
||||
# Get the largest contour
|
||||
areas = [cv2.contourArea(contour) for contour in contours]
|
||||
|
||||
max_area_index = areas.index(max(areas))
|
||||
largest_contour = contours[max_area_index]
|
||||
if n_points > 0:
|
||||
divided_by = int(largest_contour.shape[0] / n_points)
|
||||
divided_by = min(divided_by, largest_contour.shape[0])
|
||||
largest_contour = largest_contour[::divided_by].astype(int)
|
||||
contours = [largest_contour]
|
||||
|
||||
if not image.flags["C_CONTIGUOUS"]:
|
||||
image = np.ascontiguousarray(image)
|
||||
cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
|
||||
|
||||
points = []
|
||||
for point in largest_contour:
|
||||
points.append(
|
||||
{"x": point[0][0], "y": point[0][1], "label": 1, "isAuto": True}
|
||||
)
|
||||
|
||||
return image, points
|
||||
|
||||
def run(self, image, n_points):
|
||||
contour_image, points = self.find_main_contour(image, n_points)
|
||||
contour_image = torch.from_numpy(np.expand_dims(contour_image, axis=0))
|
||||
print(points)
|
||||
return (points, contour_image)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {"Extract Boundary Points": ExtractBoundaryPoints}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {"Extract Boundary Points": "Extract Boundary Points"}
|
||||
@@ -0,0 +1,31 @@
|
||||
class LoadValueFromRequest:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"name": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": "key_name"},
|
||||
),
|
||||
},
|
||||
"optional": {
|
||||
"value": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01, "display": "number"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
RETURN_NAMES = ("value",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "image"
|
||||
|
||||
def run(self, name, value=None):
|
||||
if name:
|
||||
value = name
|
||||
return (value,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {"LoadValueFromRequest": LoadValueFromRequest}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {"LoadValueFromRequest": "Load Value From Request"}
|
||||
+20
-7
@@ -4,6 +4,7 @@ import numpy as np
|
||||
import torch
|
||||
import re
|
||||
import json
|
||||
import uuid
|
||||
from segment_anything import sam_model_registry, SamPredictor
|
||||
from einops import rearrange, repeat
|
||||
from PIL import Image
|
||||
@@ -126,7 +127,7 @@ class SAMMultiLayer:
|
||||
global global_predictor, face_landmarker, pose_landmarker
|
||||
|
||||
ckpt = folder_paths.get_full_path("sams", ckpt)
|
||||
sam = sam_model_registry[model_type](checkpoint=ckpt) # .to("cuda")
|
||||
sam = sam_model_registry[model_type](checkpoint=ckpt) # .to("cuda")
|
||||
global_predictor = SamPredictor(sam)
|
||||
|
||||
face_landmarker_model_path = os.path.join(
|
||||
@@ -270,16 +271,24 @@ class SAMMultiLayer:
|
||||
image_format=mp.ImageFormat.SRGB, data=(np_image * 255).astype(np.uint8)
|
||||
)
|
||||
face_landmarks = face_landmarker.detect(mp_image).face_landmarks
|
||||
face_landmarks = face_landmarks[0] if len(face_landmarks) > 0 else None
|
||||
if len(face_landmarks) == 0:
|
||||
print("Warning: no face detected")
|
||||
return None, None
|
||||
|
||||
pose_landmarks = pose_landmarker.detect(mp_image).pose_landmarks
|
||||
pose_landmarks = pose_landmarks[0] if len(pose_landmarks) > 0 else None
|
||||
if len(pose_landmarks) == 0:
|
||||
print("Warning: no pose detected")
|
||||
return None, None
|
||||
|
||||
imagePromptsMulti, boxesMulti = self.auto_segment(
|
||||
np_image, face_landmarks, pose_landmarks
|
||||
np_image, face_landmarks[0], pose_landmarks[0]
|
||||
)
|
||||
|
||||
return imagePromptsMulti, boxesMulti
|
||||
|
||||
def load_image(self, image, ckpt, embedding_id, image_prompts_json):
|
||||
if 'COMFY_DEPLOY' in os.environ and os.getenv('COMFY_DEPLOY', 'FALSE') == "TRUE":
|
||||
embedding_id = str(uuid.uuid4())
|
||||
image_prompts = json.loads(image_prompts_json.replace("'", '"'))
|
||||
|
||||
order_file = f"{self.output_dir}/segments_{embedding_id}/order.json"
|
||||
@@ -338,7 +347,7 @@ class SAMMultiLayer:
|
||||
imagePromptsMulti, boxesMulti = self.detect_face(image[0].numpy())
|
||||
|
||||
image_prompts = json.loads(image_prompts_json.replace("'", '"'))
|
||||
result = [image_prompts]
|
||||
result = [image_prompts] # use imagePromptsMulti
|
||||
|
||||
if isinstance(image_prompts, list):
|
||||
pass
|
||||
@@ -350,7 +359,9 @@ class SAMMultiLayer:
|
||||
continue
|
||||
|
||||
points = (
|
||||
imagePromptsMulti[key] if key in imagePromptsMulti else item
|
||||
imagePromptsMulti[key]
|
||||
if imagePromptsMulti is not None and key in imagePromptsMulti
|
||||
else item
|
||||
)
|
||||
point_coords = np.array([[p["x"], p["y"]] for p in points])
|
||||
point_labels = np.array([p["label"] for p in points])
|
||||
@@ -358,7 +369,9 @@ class SAMMultiLayer:
|
||||
masks, _, _ = global_predictor.predict(
|
||||
point_coords=point_coords,
|
||||
point_labels=point_labels,
|
||||
box=boxesMulti[key] if key in boxesMulti else None,
|
||||
box=boxesMulti[key]
|
||||
if boxesMulti is not None and key in boxesMulti
|
||||
else None,
|
||||
)
|
||||
masks = torch.from_numpy(masks)
|
||||
masks = rearrange(masks[0], "h w -> 1 h w")
|
||||
|
||||
Reference in New Issue
Block a user