From 7750375cef1de9a6ef591e7d4afde0a12f57894e Mon Sep 17 00:00:00 2001 From: Fillip Isgro Date: Sun, 9 Feb 2025 16:02:57 -0500 Subject: [PATCH] added clip scanner node --- __init__.py | 3 ++ nodes/FL_ClipScanner.py | 87 +++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + 3 files changed, 91 insertions(+) create mode 100644 nodes/FL_ClipScanner.py diff --git a/__init__.py b/__init__.py index 7b117ba..3242c0c 100644 --- a/__init__.py +++ b/__init__.py @@ -89,6 +89,7 @@ from .nodes.FL_API_ImageSaver import FL_API_ImageSaver from .nodes.FL_GoogleDriveImageDownloader import FL_GoogleDriveImageDownloader from .nodes.FL_AnimeLineExtractor import FL_AnimeLineExtractor from .nodes.FL_HunyuanDelight import FL_HunyuanDelight +from .nodes.FL_ClipScanner import FL_ClipScanner NODE_CLASS_MAPPINGS = { @@ -184,6 +185,7 @@ NODE_CLASS_MAPPINGS = { "FL_GoogleDriveImageDownloader": FL_GoogleDriveImageDownloader, "FL_AnimeLineExtractor": FL_AnimeLineExtractor, "FL_HunyuanDelight": FL_HunyuanDelight, + "FL_ClipScanner": FL_ClipScanner, } @@ -280,6 +282,7 @@ NODE_DISPLAY_NAME_MAPPINGS = { "FL_GoogleDriveImageDownloader": "FL Google Drive Image Downloader", "FL_AnimeLineExtractor": "FL Anime Line Extractor", "FL_HunyuanDelight": "FL Hunyuan Delight", + "FL_ClipScanner": "FL Clip Scanner (Kytra)", } diff --git a/nodes/FL_ClipScanner.py b/nodes/FL_ClipScanner.py new file mode 100644 index 0000000..046ac97 --- /dev/null +++ b/nodes/FL_ClipScanner.py @@ -0,0 +1,87 @@ +import open_clip + +class FL_ClipScanner: + def __init__(self): + self.current_model = None + self.current_pretrained = None + self.tokenizer = None + + # Model configurations + self.model_configs = { + "SDXL (ViT-G/14)": { + "model": "ViT-g-14", + "pretrained": "laion2b_s12b_b42k" + }, + "SD 1.5 (ViT-L/14)": { + "model": "ViT-L-14", + "pretrained": "openai" + }, + "FLUX (ViT-L/14)": { + "model": "ViT-L-14", + "pretrained": "openai" + } + } + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "model_type": (list(cls().model_configs.keys()),), + "text": ("STRING", {"default": "", "multiline": True}), + }, + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "analyze_tokens" + CATEGORY = "🏵️Fill Nodes/Analysis" + OUTPUT_NODE = True + + def initialize_model(self, model_type): + if (self.current_model != self.model_configs[model_type]["model"] or + self.current_pretrained != self.model_configs[model_type]["pretrained"]): + try: + config = self.model_configs[model_type] + self.tokenizer = open_clip.get_tokenizer(config["model"]) + self.current_model = config["model"] + self.current_pretrained = config["pretrained"] + return True + except Exception as e: + print(f"Error initializing CLIP tokenizer: {str(e)}") + self.tokenizer = None + return False + return True + + def analyze_tokens(self, model_type: str, text: str) -> tuple[str]: + if not self.initialize_model(model_type): + return ("Error: Failed to initialize CLIP tokenizer.",) + + try: + # Tokenize the input text + tokens = self.tokenizer(text) + + # Convert token IDs to words + decoded_tokens = [self.tokenizer.decoder.get(tok, f"[{tok}]") + for tok in tokens[0].tolist()] + + # Remove start, end, and padding tokens + filtered_tokens = [t for t in decoded_tokens + if not t.startswith("<") and t != "!"] + + # Create formatted output + output = [] + output.append("=" * 60) + output.append(f"Model: {model_type}") + output.append(f"CLIP: {self.current_model} ({self.current_pretrained})") + output.append(f"Prompt: \"{text}\"") + output.append(f"Tokenized Output: {filtered_tokens}") + output.append(f"Token Count: {len(filtered_tokens)}") + output.append("=" * 60) + + return ("\n".join(output),) + + except Exception as e: + return (f"Error analyzing tokens: {str(e)}",) + + @classmethod + def IS_CHANGED(cls, model_type, text): + return float("NaN") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 28acfca..869d4f5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,3 +19,4 @@ ollama kornia opencv-python gdown +open_clip_torch \ No newline at end of file