fixed server side logics for trimming, etc.
This commit is contained in:
+18
-24
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
import sys
|
||||
import torch
|
||||
import numpy as np
|
||||
from PIL import Image, ImageOps
|
||||
@@ -9,11 +10,11 @@ class LoadImagesFromDirInpaint:
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"images_id": ("STRING", {"frames_upload": True}),
|
||||
"sequence_id": ("STRING", {"frames_upload": True}),
|
||||
},
|
||||
"optional": {
|
||||
"image_load_cap": ("INT", {"default": 0, "min": 0, "step": 1}),
|
||||
"start_index": ("INT", {"default": 0, "min": 0, "step": 1}),
|
||||
"end_index": ("INT", {"default": 0, "min": 0, "step": 1}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,11 +25,16 @@ class LoadImagesFromDirInpaint:
|
||||
CATEGORY = "image"
|
||||
|
||||
|
||||
def load_images(self, images_id: str, image_load_cap: int = 0, start_index: int = 0):
|
||||
def load_images(self, sequence_id: str, start_index: int = 0, end_index: int = 0):
|
||||
if start_index != 0:
|
||||
start_index -= 1
|
||||
if end_index == 0:
|
||||
end_index = sys.maxsize
|
||||
|
||||
comfy_custom_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
comfy_root_dir = os.path.join(comfy_custom_dir, "..", "..")
|
||||
|
||||
pathParts = images_id.split(":")
|
||||
pathParts = sequence_id.split(":")
|
||||
vframeIds = pathParts[len(pathParts) - 1].split("/")
|
||||
filenames = pathParts[:-1]
|
||||
|
||||
@@ -37,7 +43,7 @@ class LoadImagesFromDirInpaint:
|
||||
if len(vframeIds) == 1:
|
||||
imageDir = os.path.join(comfy_root_dir, "input", "extVideoFrame" + vframeIds[0])
|
||||
|
||||
images = self.load_from_dir(imageDir, image_load_cap, start_index)
|
||||
images = self.load_from_dir(imageDir, end_index, start_index)
|
||||
masks = [image.clone() for image in images]
|
||||
for mask in masks:
|
||||
mask.fill_(0)
|
||||
@@ -46,30 +52,24 @@ class LoadImagesFromDirInpaint:
|
||||
maskDir = os.path.join(comfy_root_dir, "input", "extVideoFrame" + vframeIds[1])
|
||||
sketchDir = os.path.join(comfy_root_dir, "input", "extVideoFrame" + vframeIds[2])
|
||||
|
||||
images = self.load_from_dir_sketch(imageDir, sketchDir, image_load_cap, start_index)
|
||||
masks = self.load_from_dir(maskDir, image_load_cap, start_index)
|
||||
images = self.load_from_dir_sketch(imageDir, sketchDir, end_index, start_index)
|
||||
masks = self.load_from_dir(maskDir, end_index, start_index)
|
||||
|
||||
|
||||
return (torch.cat(images, dim=0), torch.cat(masks, dim=0), len(images))
|
||||
|
||||
|
||||
def load_from_dir(self, imageDir, image_load_cap, start_index):
|
||||
def load_from_dir(self, imageDir, end_index, start_index):
|
||||
|
||||
img_files = self.getImagePaths(imageDir)
|
||||
img_files = img_files[start_index:]
|
||||
img_files = img_files[start_index:end_index]
|
||||
|
||||
images = []
|
||||
|
||||
limit_images = False
|
||||
if image_load_cap > 0:
|
||||
limit_images = True
|
||||
image_count = 0
|
||||
|
||||
for image_path in img_files:
|
||||
if os.path.isdir(image_path):
|
||||
continue
|
||||
if limit_images and image_count >= image_load_cap:
|
||||
break
|
||||
i = Image.open(image_path)
|
||||
i = ImageOps.exif_transpose(i)
|
||||
image = i.convert("RGB")
|
||||
@@ -85,25 +85,19 @@ class LoadImagesFromDirInpaint:
|
||||
return images
|
||||
|
||||
|
||||
def load_from_dir_sketch(self, imageDir, sketchDir, image_load_cap, start_index):
|
||||
def load_from_dir_sketch(self, imageDir, sketchDir, end_index, start_index):
|
||||
|
||||
img_files = self.getImagePaths(imageDir)
|
||||
img_files = img_files[start_index:]
|
||||
img_files = img_files[start_index:end_index]
|
||||
skt_files = self.getImagePaths(sketchDir)
|
||||
skt_files = skt_files[start_index:]
|
||||
skt_files = skt_files[start_index:end_index]
|
||||
|
||||
images = []
|
||||
|
||||
limit_images = False
|
||||
if image_load_cap > 0:
|
||||
limit_images = True
|
||||
image_count = 0
|
||||
|
||||
for idx, image_path in enumerate(img_files):
|
||||
if os.path.isdir(image_path):
|
||||
continue
|
||||
if limit_images and image_count >= image_load_cap:
|
||||
break
|
||||
i = Image.open(image_path)
|
||||
i = ImageOps.exif_transpose(i).convert("RGBA")
|
||||
s = Image.open(skt_files[idx])
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) <year> Adam Veldhousen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -126,7 +126,7 @@ class MaskEditorDialog extends ComfyDialog {
|
||||
text.style.top = "0";
|
||||
text.style.right = "0";
|
||||
text.style.fontFamily = "sans-serif";
|
||||
text.style.fontSize = "15px";
|
||||
text.style.fontSize = "20px";
|
||||
text.style.color = "#fff";
|
||||
text.innerText = content;
|
||||
return text;
|
||||
|
||||
+2
-2
@@ -110,7 +110,7 @@ app.registerExtension({
|
||||
|
||||
// widget setting
|
||||
// Adds an upload button to the nodes
|
||||
if (nodeData?.input?.required?.images_id?.[1]?.frames_upload === true) {
|
||||
if (nodeData?.input?.required?.sequence_id?.[1]?.frames_upload === true) {
|
||||
nodeData.input.required.upload = ["VFRAMESUPLOAD"];
|
||||
}
|
||||
},
|
||||
@@ -118,7 +118,7 @@ app.registerExtension({
|
||||
{
|
||||
return {
|
||||
VFRAMESUPLOAD(node, inputName, inputData, app) {
|
||||
const idWidget = node.widgets.find((w) => w.name === "images_id");
|
||||
const idWidget = node.widgets.find((w) => w.name === "sequence_id");
|
||||
|
||||
function showImage(name) {
|
||||
const img = new Image();
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# ComfyUI Sequential Image Loader
|
||||
|
||||
## Overview
|
||||
これはGUI上でフレーム毎にマスキング(とスケッチ)を行うためのComfyUI用の拡張ノードです。
|
||||
|
||||
## Usage
|
||||
|
||||
### ノードについて
|
||||
#### input
|
||||
* sequence_id: 無視してください (内部処理でのみ使います)。
|
||||
* upload button: ダイアログから動画のフレームを含むディレクトリ世指定します。
|
||||
* start_index: 開始フレーム番号を指定します。0で無効。
|
||||
* end_index: 終了フレーム番号を指定します。0で無効。
|
||||
#### output
|
||||
* images: 読み込んだフレームデータです。スケッチした場合、その内容が適用されます。
|
||||
* mask_images: 各フレームのマスクが画像として出力されます。Mask To Imageノード等でマスクデータに変換が必要です。
|
||||
* image_count: 処理フレーム数。
|
||||
|
||||
### マスクエディターについて
|
||||
1. フレームを読み込んだ後にノードを右クリックして、"Open In MaskEditor"を選択します。
|
||||
2. 出現したエディタ上でマスキングと必要に応じてスケッチを行います。
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
## Other
|
||||
input/ディレクトリに作業中の一時フレームデータが溜まっていくので、適当なタイミングで使わなくなったデータを削除してください。
|
||||
|
||||
Reference in New Issue
Block a user