Holocine Frames added

This commit is contained in:
JJ
2025-10-28 18:05:04 +00:00
parent 9ff0752ef1
commit 56feded6da
2 changed files with 137 additions and 0 deletions
+1
View File
@@ -12,6 +12,7 @@ from .nodes.power_spline_editor import *
from .nodes.lora_extract_v2 import *
from .nodes.lora_change import *
from .nodes.lora_combine import *
from .nodes.math_nodes import *
from .nodes.draw_shapes import *
from .nodes.draw_joints import *
+136
View File
@@ -0,0 +1,136 @@
class HolocineFrames:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"video_length": ("INT", {"default": 161, "min": 41, "max": 541, "step": 4}),
"shots": ("INT", {"default": 2, "min": 2, "max": 10, "step": 1}),
"distribution": ("FLOAT", {"default": 0.50, "min": 0.01, "max": 1.00, "step": 0.01}),
},
"optional": {
"shot_list": ("WANVID_HOLOCINE_SHOT_LIST",),
}
}
RETURN_TYPES = ("INT", "STRING")
RETURN_NAMES = ("video_length", "shots_string")
FUNCTION = "calculate_holocine_frames"
CATEGORY = "WanVideoWrapper_QQ"
DESCRIPTION = "Calculates video frame distribution for Holocine video generation"
def calculate_holocine_frames(self, video_length, shots, distribution, shot_list=None):
"""
Compute cut positions using exponent-based easing:
- distribution < 0.5 → front-loaded (gamma > 1)
- distribution = 0.5 → linear (gamma = 1)
- distribution > 0.5 → back-loaded (gamma < 1)
Guarantees strictly increasing intermediate cuts and reserves final frame.
"""
# Use shot_list length if provided and valid, otherwise use shots parameter
if shot_list is not None and hasattr(shot_list, '__len__') and len(shot_list) >= 2:
actual_shots = len(shot_list)
else:
actual_shots = shots
# Number of intermediate cuts between start (0) and final (video_length)
num_cuts = max(1, actual_shots - 1)
d = float(distribution)
# Map distribution to exponent gamma
gamma_min = 0.35
gamma_max = 4.0
if d < 0.5:
# Front-load: push more cuts earlier
gamma = 1.0 + (1.0 - (d / 0.5)) * (gamma_max - 1.0)
elif d > 0.5:
# Back-load: push more cuts later
gamma = 1.0 - (((d - 0.5) / 0.5) * (1.0 - gamma_min))
else:
gamma = 1.0
shot_positions = []
prev = 0
for i in range(1, num_cuts + 1):
# Uniform fraction in (0,1)
t = i / float(actual_shots)
# Exponent-based easing
f = t ** gamma
pos = int(round(f * video_length))
# Reserve space for remaining cuts and final frame
remaining = num_cuts - i
max_allowed = (video_length - 1) - remaining
# Clamp and enforce strict monotonic increase
pos = max(prev + 1, min(max_allowed, pos))
shot_positions.append(pos)
prev = pos
# Always end with video_length
shot_positions.append(video_length)
# Post-processing: enforce first cut >= 9 and min gap >= 8
min_first = 9
min_gap = 8
cuts = shot_positions[:-1]
L = video_length
n = len(cuts)
if n > 0:
# Compute feasible gap given first cut constraint
gap_eff = min_gap
if n > 1:
max_gap_for_min_first = (L - 1 - min_first) // (n - 1)
if max_gap_for_min_first < gap_eff:
gap_eff = max(1, max_gap_for_min_first)
# Clamp first cut within feasible bounds
max_first_allowed = L - 1 - (n - 1) * gap_eff
if max_first_allowed < 1:
max_first_allowed = 1
cuts[0] = max(cuts[0], min_first)
if cuts[0] > max_first_allowed:
cuts[0] = max_first_allowed
# Forward pass: enforce min gaps and feasibility
for i in range(1, n):
min_i = cuts[i - 1] + gap_eff
max_i = L - 1 - (n - 1 - i) * gap_eff
if cuts[i] < min_i:
cuts[i] = min_i
if cuts[i] > max_i:
cuts[i] = max_i
# Backward pass: tighten from the end while keeping feasibility
for i in range(n - 2, -1, -1):
max_from_next = cuts[i + 1] - gap_eff
bound = L - 1 - (n - 1 - i) * gap_eff
max_allowed = min(max_from_next, bound)
if cuts[i] > max_allowed:
cuts[i] = max_allowed
# Final forward pass to ensure min gaps after backward tightening
for i in range(1, n):
min_i = cuts[i - 1] + gap_eff
if cuts[i] < min_i:
cuts[i] = min_i
# Ensure last intermediate cut doesn't exceed L - 1
if cuts[-1] > L - 1:
cuts[-1] = L - 1
# Recombine with final frame
shot_positions = cuts + [L]
# Convert to comma-separated string
shots_string = ",".join(map(str, shot_positions))
return (video_length, shots_string)
NODE_CLASS_MAPPINGS = {
"HolocineFrames": HolocineFrames
}
NODE_DISPLAY_NAME_MAPPINGS = {
"HolocineFrames": "Holocine Frames"
}