83 lines
3.1 KiB
Python
83 lines
3.1 KiB
Python
import torch
|
|
import numpy as np
|
|
import cv2
|
|
|
|
# --- NODE 1: ANALYZER (Calculates the scores) ---
|
|
class SharpnessAnalyzer:
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"images": ("IMAGE",),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("SHARPNESS_SCORES",)
|
|
RETURN_NAMES = ("scores",)
|
|
FUNCTION = "analyze_sharpness"
|
|
CATEGORY = "SharpFrames"
|
|
|
|
def analyze_sharpness(self, images):
|
|
print(f"[SharpAnalyzer] Calculating scores for {len(images)} frames...")
|
|
scores = []
|
|
|
|
# This loop is fast if 'images' are small (resized)
|
|
for i in range(len(images)):
|
|
img_np = (images[i].cpu().numpy() * 255).astype(np.uint8)
|
|
gray = cv2.cvtColor(img_np, cv2.COLOR_RGB2GRAY)
|
|
score = cv2.Laplacian(gray, cv2.CV_64F).var()
|
|
scores.append(score)
|
|
|
|
# We pass the list of scores to the next node
|
|
return (scores,)
|
|
|
|
# --- NODE 2: SELECTOR (Uses scores to filter high-res images) ---
|
|
class SharpFrameSelector:
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"images": ("IMAGE",), # Connect High-Res images here
|
|
"scores": ("SHARPNESS_SCORES",), # Connect output of Analyzer here
|
|
"selection_method": (["batched", "best_n"],),
|
|
"batch_size": ("INT", {"default": 24, "min": 1, "max": 10000, "step": 1}),
|
|
"num_frames": ("INT", {"default": 10, "min": 1, "max": 10000, "step": 1}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("IMAGE", "INT")
|
|
RETURN_NAMES = ("selected_images", "count")
|
|
FUNCTION = "select_frames"
|
|
CATEGORY = "SharpFrames"
|
|
|
|
def select_frames(self, images, scores, selection_method, batch_size, num_frames):
|
|
# Validation
|
|
if len(images) != len(scores):
|
|
print(f"[SharpSelector] WARNING: Frame count mismatch! Images: {len(images)}, Scores: {len(scores)}")
|
|
# If mismatch (e.g. latent optimization), we truncate to the shorter length
|
|
min_len = min(len(images), len(scores))
|
|
images = images[:min_len]
|
|
scores = scores[:min_len]
|
|
|
|
selected_indices = []
|
|
|
|
# --- SELECTION LOGIC (Same as before, but using pre-calculated scores) ---
|
|
if selection_method == "batched":
|
|
total_frames = len(scores)
|
|
for i in range(0, total_frames, batch_size):
|
|
chunk_end = min(i + batch_size, total_frames)
|
|
chunk_scores = scores[i : chunk_end]
|
|
|
|
# Find best in batch
|
|
best_in_chunk_idx = np.argmax(chunk_scores)
|
|
selected_indices.append(i + best_in_chunk_idx)
|
|
|
|
elif selection_method == "best_n":
|
|
target_count = min(num_frames, len(scores))
|
|
top_indices = np.argsort(scores)[-target_count:]
|
|
selected_indices = sorted(top_indices)
|
|
|
|
print(f"[SharpSelector] Selected {len(selected_indices)} frames.")
|
|
result_images = images[selected_indices]
|
|
|
|
return (result_images, len(selected_indices)) |