updated for more flexibility when selecting what metadata to show

This commit is contained in:
RenderRift
2023-12-29 13:00:24 +00:00
parent 36b7be728a
commit e2ba7e9ad9
6 changed files with 165 additions and 26 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

+50 -5
View File
@@ -1,7 +1,52 @@
# ComfyUI-RenderRiftNodes
# RenderRift Node Pack
## Description
Hey everyone! I've whipped up a cool little node pack for ComfyUI. It's just a few nodes I made for myself, but I think they're pretty neat. We've got three main players here. First, there’s the Custom Output Folder String Generator for keeping your files tidy. Next, the LoadImageWithMetadata node – it’s a real game changer because it lets you pull up image metadata, which is perfect for the third node, the Image Metadata Overlay Node. This one lets you overlay that metadata right onto your images. LoadImageWithMetadata was actually made specifically to get the Metadata Overlay Node working.
Welcome to the RenderRift Custom Node Pack for the ComfyUI. This was created as I need a couple of extra nodes to help in my Animatediff workflow. Below you'll find detailed explanations of each node and their respective functionalities.
## Examples
TBC
## Nodes Description
### RR_VideoPathMetaExtraction
- **Purpose**: Works in conjunction with VHS_LoadVideoPath to set the file_path for a video and extracts the metadata from the file to send to RR_Image_metadata_overlay.
- **Functionality**:
- Provides metadata to RR_Image_Metadata_Overlay
- Provides file path to VHS_LoadVideoPath
### RR_Image_Metadata_Overlay
- **Purpose**: Main node for displaying video and metadata in a grid format.
- **Features**:
- Supports 2-6 videos & their corresponding metadatas.
- Optional support for an "original video" for comparison in vid2vid workflows.
- Grid format display with data overlay options.
- **Predefined Options for Overlay**:
- checkpoint, ksampler, controlnets, animatediff, ipadapter, loras.
- `search_query`: Enter a class_type from metadata to output all related values.
- **Usage**: Central to creating comparative video overlays with metadata insights.
### RR_Date_Folder_Format Node
- **Purpose**: Outputs a string for saving images/videos in a specific format. To be used as an input to the filename_prefix
- **Output Format**:
- `{todays_date}/1lq_`
- `{todays_date}/1_lq/lqimg_`
- `{todays_date}/1hq_`
- `{todays_date}/1_hq/hqimg_`
- `{todays_date}/1facedetailer_`
- `{todays_date}/1_facedetailer/facedetailerimg_`
## Workflows
### Workflow 1: Node Type Listing
![Workflow part 1](.github/images/workflowpt1.png)
- **Description**: Retrieves a list of node types from your video.
- **Steps**:
- Run the first group in the workflow to obtain the node types.
### Workflow 2: Video Comparison Setup
![Workflow part 2](.github/images/workflowpt2.png)
- **Description**: Sets up video paths for comparison and overlay selection in RR_Image_Metadata_Overlay.
- **Requirements**: Ensure videos have been saved using VHS_VideoCombine with metadata enabled.
- **Steps**:
- Bypass the first group if node types are known.
- Select video paths for comparison.
- Choose overlays for the RR_Image_Metadata_Overlay.
- Save the output as a comparison video.
+8 -4
View File
@@ -1,16 +1,20 @@
from .date_integer_node import DateIntegerNode, MetadataOverlayNode, AnalyseMetadata, LoadImageWithMeta
from .date_integer_node import DateIntegerNode, MetadataOverlayNode, AnalyseMetadata, LoadImageWithMeta, VideoPathMetaExtraction, DisplayMetaOptions
NODE_CLASS_MAPPINGS = {
"DateIntegerNode": DateIntegerNode,
"AnalyseMetadata": AnalyseMetadata,
"MetadataOverlayNode": MetadataOverlayNode,
"LoadImageWithMeta": LoadImageWithMeta,
"VideoPathMetaExtraction": VideoPathMetaExtraction,
"DisplayMetaOptions": DisplayMetaOptions,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"DateIntegerNode": "Customer Date Folder Format",
"MetadataOverlayNode": "Image Metadata Overlay Node",
"LoadImageWithMeta": "LoadImageWithMeta",
"DateIntegerNode": "RR_Date_Folder_Format",
"MetadataOverlayNode": "RR_Image_Metadata_Overlay",
"LoadImageWithMeta": "RR_LoadImageWithMeta (Depreciated)",
"VideoPathMetaExtraction": "RR_VideoPathMetaExtraction",
"DisplayMetaOptions": "RR_DisplayMetaOptions",
}
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
+105 -16
View File
@@ -1,4 +1,5 @@
from datetime import datetime
from cmd import PROMPT
from datetime import datetime
from pickle import APPEND
import os
import torch
@@ -7,6 +8,7 @@ import json
import textwrap
from PIL import Image, ImageOps, ImageDraw, ImageFont
import folder_paths
from tinytag import TinyTag
def process_text(anything):
return {"text": [str(anything)]}
@@ -40,6 +42,7 @@ class MetadataOverlayNode:
"metadata5": ("METADATA",),
"image6": ("IMAGE",),
"metadata6": ("METADATA",),
"search_query": ("STRING", {"default": ''}),
},
}
@@ -47,11 +50,10 @@ class MetadataOverlayNode:
pass
def extract_metadata(self, metadata, checkpoint, ksampler, controlnets, animdiff, ipadpter, loras):
def extract_metadata(self, metadata, checkpoint, ksampler, controlnets, animdiff, ipadpter, loras, search_query=None):
if metadata == "Original Video":
return "Original Video"
metadata = metadata.get('prompt')
if metadata:
try:
# Parse the JSON metadata
@@ -65,8 +67,15 @@ class MetadataOverlayNode:
for node_id, node in prompt_data.items():
node_type = node.get('class_type')
inputs = node.get('inputs', {})
if search_query and node_type == search_query:
# Format and return the inputs of the matching class type
details = search_query + "\n" + '\n'.join([f"{key}: {value}" for key, value in inputs.items()])
class_type_details.setdefault(node_type, []).append(details)
# Collect the details for each class_type of interest
if animdiff and node_type == "ADE_AnimateDiffLoaderWithContext":
elif animdiff and node_type == "ADE_AnimateDiffLoaderWithContext":
# Extract the context_options ID from the inputs
context_options_id = inputs.get('context_options', [])[0]
context_details = ""
@@ -96,7 +105,7 @@ class MetadataOverlayNode:
class_type_details.setdefault(node_type, []).append(details)
# Prepare the output string
output = '\n'.join(['\n'.join(details) for details in class_type_details.values()])
return output
except json.JSONDecodeError:
@@ -127,6 +136,7 @@ class MetadataOverlayNode:
metadata5=None,
image6=None,
metadata6=None,
search_query=None
):
images_and_metadata = []
@@ -157,12 +167,12 @@ class MetadataOverlayNode:
max_text_width = max_image_width - 20 # Consistent text width
all_texts = [self.extract_metadata(meta, checkpoint, ksampler, controlnets, animdiff, ipadpter, loras) for _, meta in images_and_metadata]
all_texts = [self.extract_metadata(meta, checkpoint, ksampler, controlnets, animdiff, ipadpter, loras, search_query) for _, meta in images_and_metadata]
max_border_height = self.calculate_max_border_height(all_texts, max_text_width)
# Now process each image with the standardized border height
for img, meta in images_and_metadata:
text = self.extract_metadata(meta, checkpoint, ksampler, controlnets, animdiff, ipadpter, loras)
text = self.extract_metadata(meta, checkpoint, ksampler, controlnets, animdiff, ipadpter, loras, search_query)
overlay_img = self.overlayText(img, text, max_border_height, max_image_width )
overlay_images.append(overlay_img[0] if isinstance(overlay_img, tuple) else overlay_img)
@@ -322,13 +332,8 @@ class DateIntegerNode:
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {"default": '', "multiline": False}),
"int_1": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff})
},
"optional": {
"text_c": ("STRING", {"default": '', "multiline": False}),
"text_d": ("STRING", {"default": '', "multiline": False}),
}
}
TEXT_TYPE = "STRING"
BOOLEAN_TYPE = "BOOLEAN"
@@ -339,7 +344,7 @@ class DateIntegerNode:
CATEGORY = "RenderRiftNodes"
def generate_date_string(self, text='', enable_date_folder=False, text_c='', text_d='', int_1=0):
def generate_date_string(self, int_1=0):
tokens = TextTokens()
@@ -364,8 +369,8 @@ class DateIntegerNode:
hq_video_path = append_text(hq_video_path, str(int_1) + "hq_")
hq_img_path = append_text(hq_img_path, str(int_1) + "_hq/hqimg_")
fd_video_path = append_text(fd_video_path, str(int_1) + "hq_")
fd_img_path = append_text(fd_img_path, str(int_1) + "_hq/hqimg_")
fd_video_path = append_text(fd_video_path, str(int_1) + "facedetailer_")
fd_img_path = append_text(fd_img_path, str(int_1) + "_facedetailer/facedetailerimg_")
hq_video_path = tokens.parseTokens(hq_video_path)
hq_img_path = tokens.parseTokens(hq_img_path)
@@ -472,4 +477,88 @@ class LoadImageWithMeta:
if not folder_paths.exists_annotated_filepath(image):
return "Invalid image file: {}".format(image)
return True
return True
class DisplayMetaOptions:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"metadata": ("METADATA",),
},
}
RETURN_TYPES = ("STRING",)
FUNCTION = "process_metadata"
def process_metadata(self, metadata):
try:
json_data = json.loads(metadata)
prompt_data = json_data.get("prompt", json_data)
class_types = [(key, value['class_type']) for key, value in prompt_data.items() if 'class_type' in value]
sorted_class_types_string = '\n'.join([f"{class_type}" for id, class_type in class_types])
# Convert the list of strings to a JSON string
print(sorted_class_types_string)
return [sorted_class_types_string]
except json.JSONDecodeError:
return "Error parsing metadata. Ensure it is in valid JSON format."
class VideoPathMetaExtraction:
CATEGORY = "RenderRiftNodes"
RETURN_TYPES = ("STRING", "METADATA")
FUNCTION = "process_video_file"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"video_file_path": ("STRING", {"default": ''})
}
}
def __init__(self):
pass
def process_video_file(self, video_file_path):
# Check if the file exists
if not os.path.exists(video_file_path):
return ("File not found", {})
# Extract metadata from the video
metadata = self.extract_metadata(video_file_path)
return (video_file_path, metadata)
def extract_metadata(self, file_path):
# Implement metadata extraction logic here
# This is an example, replace with actual logic
try:
if file_path.lower().endswith('.mp4'):
try:
tag = TinyTag.get(file_path)
metadata = tag.comment
return metadata
except Exception as e:
print("Error extracting metadata using TinyTag:", e)
return
else:
print("Unsupported file type.")
return
except Exception as e:
return f"Error extracting metadata: {str(e)}"
+2 -1
View File
@@ -1 +1,2 @@
Pillow
Pillow
tinytag