Compare commits

...
14 Commits
Author SHA1 Message Date
Dr.Lt.Data 0b94005b1b hotfix: impactswitch - frontend crash
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/970#issuecomment-2827153556
2025-04-25 08:03:20 +09:00
Dr.Lt.Data bfcb8674b3 feat: Select Nth Item (Any list)
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/971
2025-04-24 18:00:37 +09:00
Dr.Lt.Data 839e5a9f90 fixed: ImpactSwitch - slot was not shrinked when disconnecting
fixed: ImpactSwitch - invalid upper bounding of the select value

https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/972
2025-04-24 12:38:35 +09:00
Dr.Lt.Data 5a2adda580 fixed: impact switch - compatibility patch with new front
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/964
2025-04-22 02:06:08 +09:00
Dr.Lt.Data d05882a9e0 refactor: remove useless legacy code 2025-04-22 01:33:58 +09:00
Dr.Lt.Data d900939861 feat: Impact Sampler - support OSS scheduler 2025-04-13 10:24:35 +09:00
Jaquan cd2696f6fd fix #946 InversedSwitch(Any) was not append outputs after first connect (#956)
InversedSwitch(Any) not increasing number of outputs after connecting first output  (in )
# 953  #946
In the latest ComfyUI_frontend version, output is not appended as expected
2025-04-12 19:01:00 +09:00
Dr.Lt.Data 0b1ac0f1c5 bump version to v8.10 2025-03-24 00:06:42 +09:00
chutchatut 216d7fd60c Added sort=None to SEGS order filter (#944)
* added pick first N SEGs node

* remove take first N segs node and update segs order filter to add order=None

* add comments

* update for readibility

* format code

* remove match statement
2025-03-24 00:06:06 +09:00
chutchatut 28cd2f70b2 Update README.md (#943) 2025-03-21 19:06:24 +09:00
Dr.Lt.Data 2708eba825 bump version v8.9 2025-03-20 21:36:25 +09:00
chutchatut c19ab92172 add intersection and non max suppression SEGS filter (#940) 2025-03-20 21:21:15 +09:00
Robin Huangandsnomiao 6e3d07277b chore(publish): update workflow for node publishing with conditional execution and permissions (#935)
Co-authored-by: snomiao <snomiao+comfy-pr@gmail.com>
2025-03-17 00:19:33 +09:00
Dr.Lt.Data 782c6f439e fixed: controlbridge - better error message
- mute/bypass behavior cannot be used in api mode

https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/933
2025-03-12 18:10:42 +09:00
13 changed files with 278 additions and 92 deletions
+6 -2
View File
@@ -7,15 +7,19 @@ on:
paths:
- "pyproject.toml"
permissions:
issues: write
jobs:
publish-node:
name: Publish Custom Node to registry
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'ltdrdata' }}
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Publish Custom Node
uses: Comfy-Org/publish-node-action@main
uses: Comfy-Org/publish-node-action@v1
with:
## Add your own personal access token to your Github Repository secrets and reference it here.
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
+4 -1
View File
@@ -132,6 +132,8 @@ NOTE: The UltralyticsDetectorProvider node is not part of the ComfyUI-Impact-Pac
* `SEGS Filter (label)` - This node filters SEGS based on the label of the detected areas.
* `SEGS Filter (ordered)` - This node sorts SEGS based on size and position and retrieves SEGs within a certain range.
* `SEGS Filter (range)` - This node retrieves only SEGs from SEGS that have a size and position within a certain range.
* `SEGS Filter (non max suppression)` - This node filters SEGS by removing those with high overlap based on the Intersection over Union (IoU) threshold, keeping only the most confident detections.
* `SEGS Filter (intersection)` - This node filters segs1, keeping only the SEGS that do not significantly overlap with any SEGS in segs2, based on the Intersection over Area (IoA) threshold.
* `SEGS Assign (label)` - Assign labels sequentially to SEGS. This node is useful when used with `[LAB]` of FaceDetailer.
* `SEGSConcat` - Concatenate segs1 and segs2. If source shape of segs1 and segs2 are different from segs2 will be ignored.
* `SEGS Merge` - SEGS contains multiple SEGs. SEGS Merge integrates several SEGs into a single merged SEG. The label is changed to `merged` and the confidence becomes the minimum confidence. The applied controlnet and cropped_image are removed.
@@ -249,7 +251,7 @@ NOTE: The UltralyticsDetectorProvider node is not part of the ComfyUI-Impact-Pac
### Impact KSampler
* These samplers support basic_pipe and AYS scheduler
* These samplers support basic_pipe and AYS/OSS/GITS scheduler
* `KSampler (pipe)` - pipe version of KSampler
* `KSampler (advanced/pipe)` - pipe version of KSamplerAdvacned
* When converting the scheduler widget to input, refer to the `Impact Scheduler Adapter` node to resolve compatibility issues.
@@ -266,6 +268,7 @@ NOTE: The UltralyticsDetectorProvider node is not part of the ComfyUI-Impact-Pac
* `Masks to Mask List`, `Mask List to Masks`, `Make Mask List`, `Make Mask Batch` - It has the same functionality as the nodes above, but uses mask as input instead of image.
* `Flatten Mask Batch` - Flattens a Mask Batch into a single Mask. Normal operation is not guaranteed for non-binary masks.
* `Make List (Any)` - Create a list with arbitrary values.
* `Select Nth Item (Any list)` - Selects the Nth item from a list. If the index is out of range, it returns the last item in the list.
### Logics (experimental)
* These nodes are experimental nodes designed to implement the logic for loops and dynamic switching.
+6
View File
@@ -234,6 +234,7 @@ NODE_CLASS_MAPPINGS = {
"ImpactMakeAnyList": MakeAnyList,
"ImpactMakeMaskList": MakeMaskList,
"ImpactMakeMaskBatch": MakeMaskBatch,
"ImpactSelectNthItemOfAnyList": NthItemOfAnyList,
"RegionalSampler": RegionalSampler,
"RegionalSamplerAdvanced": RegionalSamplerAdvanced,
@@ -247,6 +248,8 @@ NODE_CLASS_MAPPINGS = {
"ImpactSEGSLabelFilter": SEGSLabelFilter,
"ImpactSEGSRangeFilter": SEGSRangeFilter,
"ImpactSEGSOrderedFilter": SEGSOrderedFilter,
"ImpactSEGSIntersectionFilter": SEGSIntersectionFilter,
"ImpactSEGSNMSFilter": SEGSNMSFilter,
"ImpactCompare": ImpactCompare,
"ImpactConditionalBranch": ImpactConditionalBranch,
@@ -362,6 +365,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"ImpactSEGSLabelFilter": "SEGS Filter (label)",
"ImpactSEGSRangeFilter": "SEGS Filter (range)",
"ImpactSEGSOrderedFilter": "SEGS Filter (ordered)",
"ImpactSEGSIntersectionFilter": "SEGS Filter (intersection)",
"ImpactSEGSNMSFilter": "SEGS Filter (non max suppression)",
"ImpactSEGSConcat": "SEGS Concat",
"ImpactSEGSToMaskList": "SEGS to Mask List",
"ImpactSEGSToMaskBatch": "SEGS to Mask Batch",
@@ -403,6 +408,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"ImpactMakeMaskList": "Make Mask List",
"ImpactMakeMaskBatch": "Make Mask Batch",
"ImpactMakeAnyList": "Make List (Any)",
"ImpactSelectNthItemOfAnyList": "Select Nth Item (Any list)",
"ImpactStringSelector": "String Selector",
"StringListToString": "String List to String",
+42
View File
@@ -3,6 +3,48 @@ import { app } from "../../scripts/app.js";
let original_show = app.ui.dialog.show;
export function customAlert(message) {
try {
app.extensionManager.toast.addAlert(message);
}
catch {
alert(message);
}
}
export function isBeforeFrontendVersion(compareVersion) {
try {
const frontendVersion = window['__COMFYUI_FRONTEND_VERSION__'];
if (typeof frontendVersion !== 'string') {
return false;
}
function parseVersion(versionString) {
const parts = versionString.split('.').map(Number);
return parts.length === 3 && parts.every(part => !isNaN(part)) ? parts : null;
}
const currentVersion = parseVersion(frontendVersion);
const comparisonVersion = parseVersion(compareVersion);
if (!currentVersion || !comparisonVersion) {
return false;
}
for (let i = 0; i < 3; i++) {
if (currentVersion[i] > comparisonVersion[i]) {
return false;
} else if (currentVersion[i] < comparisonVersion[i]) {
return true;
}
}
return false;
} catch {
return true;
}
}
function dialog_show_wrapper(html) {
if (typeof html === "string") {
if(html.includes("IMPACT-PACK-SIGNAL: STOP CONTROL BRIDGE")) {
+52 -20
View File
@@ -1,6 +1,13 @@
import { ComfyApp, app } from "../../scripts/app.js";
import { ComfyDialog, $el } from "../../scripts/ui.js";
import { api } from "../../scripts/api.js";
import { customAlert, isBeforeFrontendVersion } from "./common.js";
const is_legacy_front = () => isBeforeFrontendVersion('1.16.9');
if(is_legacy_front()) {
customAlert("An outdated version(<1.16.9) of the `comfyui-frontend-package` is installed. It is not compatible with the current version of the Impact Pack.");
}
let wildcards_list = [];
async function load_wildcards() {
@@ -324,6 +331,32 @@ app.registerExtension({
}
}
if(nodeData.name == "ImpactSelectNthItemOfAnyList") {
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) {
if(!link_info || this.inputs[0].type != '*')
return;
if(index >= 2)
return;
// assign type
let slot_type = '*';
if(type == 2) {
slot_type = link_info.type;
}
else {
const node = app.graph.getNodeById(link_info.origin_id);
slot_type = node.outputs[link_info.origin_slot].type;
}
this.inputs[0].type = slot_type;
this.outputs[0].type = slot_type;
this.outputs[0].label = slot_type;
}
}
if(nodeData.name === 'ImpactInversedSwitch') {
nodeData.output = ['*'];
nodeData.output_is_list = [false];
@@ -403,6 +436,9 @@ app.registerExtension({
let slot_i = 1;
for (let i = 0; i < this.outputs.length; i++) {
this.outputs[i].name = `output${slot_i}`
if (this.outputs[i].slot_index === undefined) {
this.outputs[i].slot_index = i;
}
slot_i++;
}
@@ -471,6 +507,15 @@ app.registerExtension({
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) {
const stackTrace = new Error().stack;
if(stackTrace.includes('LGraph.configure')) {
if(this.widgets) {
this.widgets[0].options.max = this.inputs.length-3;
this.widgets[0].value = Math.min(this.widgets[0].value, this.widgets[0].options.max);
}
return;
}
if(!link_info)
return;
@@ -513,7 +558,7 @@ app.registerExtension({
if(this.inputs[0].type == '*'){
const node = app.graph.getNodeById(link_info.origin_id);
let origin_type = node.outputs[link_info.origin_slot]?.type;
if(link_info.target_slot == 0 && this.inputs.length > 1) {
if(link_info.target_slot == 0 && this.inputs.length > 3) { // NOTE: widgets are regarded as input since new front
origin_type = this.inputs[1].type;
node.connect(link_info.origin_slot, node.id, 'input1');
}
@@ -536,15 +581,8 @@ app.registerExtension({
}
let select_slot = this.inputs.find(x => x.name == "select");
let mode_slot = this.inputs.find(x => x.name == "sel_mode");
let converted_count = 0;
converted_count += select_slot?1:0;
converted_count += mode_slot?1:0;
if (!connected && (this.inputs.length > 1+converted_count)) {
const stackTrace = new Error().stack;
if (!connected && (this.inputs.length > 3)) {
if(
!stackTrace.includes('LGraphNode.prototype.connect') && // for touch device
!stackTrace.includes('LGraphNode.connect') && // for mouse device
@@ -554,6 +592,7 @@ app.registerExtension({
}
}
let slot_i = 1;
for (let i = 0; i < this.inputs.length; i++) {
let input_i = this.inputs[i];
@@ -563,19 +602,12 @@ app.registerExtension({
}
}
let last_slot = this.inputs[this.inputs.length - 1];
if (
(last_slot.name == 'select' && last_slot.name != 'sel_mode' && this.inputs[this.inputs.length - 2].link != undefined)
|| (last_slot.name != 'select' && last_slot.name != 'sel_mode' && last_slot.link != undefined)) {
this.addInput(`${input_name}${slot_i}`, this.outputs[0].type);
if(connected) {
this.addInput(`${input_name}${slot_i}`, this.outputs[0].type);
}
if(this.widgets?.length) {
this.widgets[0].options.max = select_slot?this.inputs.length-1:this.inputs.length;
this.widgets[0].value = Math.min(this.widgets[0].value, this.widgets[0].options.max);
if(this.widgets[0].options.max > 0 && this.widgets[0].value == 0)
this.widgets[0].value = 1;
}
this.widgets[0].options.max = this.inputs.length-3;
this.widgets[0].value = Math.min(this.widgets[0].value, this.widgets[0].options.max);
}
}
},
+1 -1
View File
@@ -1,7 +1,7 @@
import configparser
import os
version_code = [8, 8, 4]
version_code = [8, 13, 1]
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
dependency_version = 24
+1 -1
View File
@@ -48,7 +48,7 @@ preview_bridge_last_mask_cache = {}
current_prompt = None
SCHEDULERS = comfy.samplers.KSampler.SCHEDULERS + ['AYS SDXL', 'AYS SD1', 'AYS SVD', 'GITS[coeff=1.2]', 'LTXV[default]']
SCHEDULERS = comfy.samplers.KSampler.SCHEDULERS + ['AYS SDXL', 'AYS SD1', 'AYS SVD', 'GITS[coeff=1.2]', 'LTXV[default]', 'OSS FLUX', 'OSS Wan']
def is_execution_model_version_supported():
+1 -1
View File
@@ -2435,7 +2435,7 @@ class ImpactSchedulerAdapter:
def INPUT_TYPES(s):
return {"required": {
"scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"defaultInput": True, }),
"extra_scheduler": (['None', 'AYS SDXL', 'AYS SD1', 'AYS SVD', 'GITS[coeff=1.2]', 'LTXV[default]'],),
"extra_scheduler": (['None', 'AYS SDXL', 'AYS SD1', 'AYS SVD', 'GITS[coeff=1.2]', 'LTXV[default]', 'OSS FLUX', 'OSS Wan'],),
}}
CATEGORY = "ImpactPack/Util"
+2
View File
@@ -29,6 +29,8 @@ def calculate_sigmas(model, sampler, scheduler, steps):
sigmas = nodes.NODE_CLASS_MAPPINGS['GITSScheduler']().get_sigmas(float(scheduler[11:-1]), steps, denoise=1.0)[0]
elif scheduler == 'LTXV[default]':
sigmas = nodes.NODE_CLASS_MAPPINGS['LTXVScheduler']().get_sigmas(20, 2.05, 0.95, True, 0.1)[0]
elif scheduler.startswith('OSS'):
sigmas = nodes.NODE_CLASS_MAPPINGS['OptimalStepsScheduler']().get_sigmas(scheduler[4:], steps, denoise=1.0)[0]
else:
sigmas = samplers.calculate_sigmas(model.get_model_object("model_sampling"), scheduler, steps)
+4 -22
View File
@@ -8,7 +8,7 @@ from impact.utils import any_typ
import impact.core as core
import re
import nodes
import traceback
class ImpactCompare:
@classmethod
@@ -574,27 +574,6 @@ class ImpactSleep:
return (signal,)
error_skip_flag = False
try:
import cm_global
def filter_message(str):
global error_skip_flag
if "IMPACT-PACK-SIGNAL: STOP CONTROL BRIDGE" in str:
return True
elif error_skip_flag and "ERROR:root:!!! Exception during processing !!!\n" == str:
error_skip_flag = False
return True
else:
return False
cm_global.try_call(api='cm.register_message_collapse', f=filter_message)
except Exception as e:
print(f"[WARN] ComfyUI-Impact-Pack: `ComfyUI` or `ComfyUI-Manager` is an outdated version.")
pass
def workflow_to_map(workflow):
nodes = {}
links = {}
@@ -701,6 +680,9 @@ class ImpactControlBridge:
return (value, )
else:
return (ExecutionBlocker(None), )
elif extra_pnginfo is None:
logging.warn(f"[Impact Pack] limitation: '{behavior}' behavior cannot be used in API execution.")
return (value,)
else:
workflow_nodes, links = workflow_to_map(extra_pnginfo['workflow'])
+133 -43
View File
@@ -13,6 +13,7 @@ from . import segs_upscaler
from comfy.cli_args import args
import math
from typing import Callable, Union
try:
from comfy_extras import nodes_differential_diffusion
@@ -504,7 +505,7 @@ class SEGSOrderedFilter:
def INPUT_TYPES(s):
return {"required": {
"segs": ("SEGS", ),
"target": (["area(=w*h)", "width", "height", "x1", "y1", "x2", "y2", "confidence"],),
"target": (["area(=w*h)", "width", "height", "x1", "y1", "x2", "y2", "confidence", "none"],),
"order": ("BOOLEAN", {"default": True, "label_on": "descending", "label_off": "ascending"}),
"take_start": ("INT", {"default": 0, "min": 0, "max": sys.maxsize, "step": 1}),
"take_count": ("INT", {"default": 1, "min": 0, "max": sys.maxsize, "step": 1}),
@@ -517,51 +518,35 @@ class SEGSOrderedFilter:
CATEGORY = "ImpactPack/Util"
@staticmethod
def get_sort_key_fn(target: str) -> Union[Callable, None]:
if target == "none":
return None
def sort_key_fn(seg):
x1, y1, x2, y2 = seg.crop_region
if target == "confidence": return seg.confidence
if target == "area(=w*h)": return (x2 - x1) * (y2 - y1)
if target == "width": return x2 - x1
if target == "height": return y2 - y1
if target == "x1": return x1
if target == "y1": return y1
if target == "x2": return x2
if target == "y2": return y2
raise Exception(f"[Impact Pack] SEGSOrderedFilter - Unexpected target '{target}'")
return sort_key_fn
def doit(self, segs, target, order, take_start, take_count):
segs_with_order = []
sort_key_fn = SEGSOrderedFilter.get_sort_key_fn(target)
for seg in segs[1]:
x1 = seg.crop_region[0]
y1 = seg.crop_region[1]
x2 = seg.crop_region[2]
y2 = seg.crop_region[3]
sorted_list = list(segs[1]) # make a shallow copy, so it does not mutate the original list when sort
if sort_key_fn is not None:
sorted_list.sort(key=sort_key_fn, reverse=order)
if target == "area(=w*h)":
value = (y2 - y1) * (x2 - x1)
elif target == "width":
value = x2 - x1
elif target == "height":
value = y2 - y1
elif target == "x1":
value = x1
elif target == "x2":
value = x2
elif target == "y1":
value = y1
elif target == "y2":
value = y2
elif target == "confidence":
value = seg.confidence
else:
raise Exception(f"[Impact Pack] SEGSOrderedFilter - Unexpected target '{target}'")
segs_with_order.append((value, seg))
if order:
sorted_list = sorted(segs_with_order, key=lambda x: x[0], reverse=True)
else:
sorted_list = sorted(segs_with_order, key=lambda x: x[0], reverse=False)
result_list = []
remained_list = []
for i, item in enumerate(sorted_list):
if take_start <= i < take_start + take_count:
result_list.append(item[1])
else:
remained_list.append(item[1])
return (segs[0], result_list), (segs[0], remained_list),
take_stop = take_start + take_count
return (segs[0], sorted_list[take_start:take_stop]), \
(segs[0], sorted_list[:take_start] + sorted_list[take_stop:]),
class SEGSRangeFilter:
@@ -629,6 +614,111 @@ class SEGSRangeFilter:
return (segs[0], new_segs), (segs[0], remained_segs),
class SEGSIntersectionFilter:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"segs1": ("SEGS", ),
"segs2": ("SEGS", ),
"ioa_threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
},
}
RETURN_TYPES = ("SEGS",)
RETURN_NAMES = ("filtered_SEGS",)
FUNCTION = "doit"
CATEGORY = "ImpactPack/Util"
def compute_ioa(self, mask1, mask2):
"""Compute Intersection over Area (IoA) between two boxes."""
inter_mask = utils.bitwise_and_masks(mask1, mask2)
inter_area = (inter_mask > 0).sum()
area1 = (mask1 > 0).sum()
return inter_area / area1 if area1 > 0 else 0
def doit(self, segs1, segs2, ioa_threshold):
"""Remove segments from segs1 if their IoA with any segment in segs2 exceeds the threshold."""
# Extract bounding boxes for all segments in segs1 and segs2
keep = []
# Iterate over all segments in segs1
for idx1, seg1 in enumerate(segs1[1]):
keep_segment = True # Assume the segment should be kept
mask1 = core.segs_to_combined_mask((segs1[0], [seg1]))
# Compare with every segment in segs2
for seg2 in segs2[1]:
mask2 = core.segs_to_combined_mask((segs2[0], [seg2]))
ioa = self.compute_ioa(mask1, mask2) # IoA between segment 1 and segment 2
if ioa > ioa_threshold: # If IoA exceeds the threshold, mark the segment for removal
keep_segment = False
break # If one overlap exceeds threshold, break early and mark for removal
# Keep the segment if it did not exceed the threshold with any other segment
if keep_segment:
keep.append(segs1[1][idx1])
return (segs1[0], keep), # Return the updated SEGS
class SEGSNMSFilter:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"segs": ("SEGS",),
"iou_threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
},
}
RETURN_TYPES = ("SEGS",)
RETURN_NAMES = ("filtered_SEGS",)
FUNCTION = "doit"
CATEGORY = "ImpactPack/Util"
def compute_iou(self, mask1, mask2):
"""Compute IoU between two bounding boxes (x1, y1, x2, y2)."""
inter_mask = utils.bitwise_and_masks(mask1, mask2)
union_mask = utils.add_masks(mask1, mask2)
inter_area = (inter_mask > 0).sum()
union_area = (union_mask > 0).sum()
return inter_area / union_area if union_area > 0 else 0
def doit(self, segs, iou_threshold):
"""Perform NMS to filter overlapping segments."""
confidences = np.ndarray.flatten(np.array([seg.confidence for seg in segs[1]]))
# Sort boxes by confidence (high to low)
sorted_indices = np.argsort(confidences)[::-1].tolist()
keep = []
while len(sorted_indices) > 0:
idx = sorted_indices[0]
mask1 = core.segs_to_combined_mask((segs[0], [segs[1][idx]]))
keep.append(idx)
sorted_indices = sorted_indices[1:]
# Filter indices only contain the indices where the bbox does not intersect
filtered_indices = []
for i in sorted_indices:
mask2 = core.segs_to_combined_mask((segs[0], [segs[1][i]]))
iou = self.compute_iou(mask1, mask2)
if iou < iou_threshold:
filtered_indices.append(i)
sorted_indices = np.array(filtered_indices)
filtered_segs = [segs[1][i] for i in keep]
return (segs[0], filtered_segs),
class SEGSToImageList:
@classmethod
def INPUT_TYPES(s):
+25
View File
@@ -446,6 +446,31 @@ class MakeMaskList:
return (masks, )
class NthItemOfAnyList:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"any_list": (any_typ,),
"index": ("INT", {"default": 0, "min": 0, "max": sys.maxsize, "step": 1, "tooltip": "The index of the item you want to select from the list."}),
}
}
RETURN_TYPES = (any_typ,)
INPUT_IS_LIST = True
FUNCTION = "doit"
CATEGORY = "ImpactPack/Util"
DESCRIPTION = "Selects the Nth item from a list. If the index is out of range, it returns the last item in the list."
def doit(self, any_list, index):
i = index[0]
if i >= len(any_list):
return (any_list[-1],)
else:
return (any_list[i],)
class MakeImageList:
@classmethod
def INPUT_TYPES(s):
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui-impact-pack"
description = "This node pack offers various detector nodes and detailer nodes that allow you to configure a workflow that automatically enhances facial details. And provide iterative upscaler."
version = "8.8.4"
version = "8.13.1"
license = { file = "LICENSE.txt" }
dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]