Initial implementation of Random Image Picker node
- Single image and folder random selection modes - Subfolder scanning support - Resolution output (width/height) - Seed control for reproducible random selection - Support for PNG, JPEG, WebP, BMP, GIF formats
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/cache
|
||||
@@ -0,0 +1,35 @@
|
||||
# Code Style and Conventions
|
||||
|
||||
## Naming Conventions
|
||||
- **Classes**: PascalCase (例: `RandomImagePicker`)
|
||||
- **Functions/Methods**: snake_case (例: `load_image`, `get_image_files`)
|
||||
- **Constants**: UPPER_SNAKE_CASE (例: `NODE_CLASS_MAPPINGS`)
|
||||
- **Private methods**: アンダースコアなし(staticmethodを活用)
|
||||
|
||||
## Type Hints
|
||||
- **使用**: 全ての関数/メソッドパラメータと戻り値に型ヒントを使用
|
||||
- **例**: `def load_image_file(image_path: str) -> Tuple[torch.Tensor, int, int]:`
|
||||
|
||||
## Docstrings
|
||||
- **スタイル**: Google style docstrings
|
||||
- **必須箇所**:
|
||||
- すべてのクラス
|
||||
- すべてのpublicメソッド/関数
|
||||
- staticmethod
|
||||
- **内容**:
|
||||
- 簡潔な説明
|
||||
- Args: 引数の説明
|
||||
- Returns: 戻り値の説明
|
||||
|
||||
## Import Order
|
||||
1. 標準ライブラリ
|
||||
2. サードパーティライブラリ
|
||||
3. ComfyUI関連
|
||||
4. ローカルモジュール
|
||||
|
||||
## ComfyUI Specific
|
||||
- **INPUT_TYPES**: classmethodで定義、辞書形式で入力パラメータを定義
|
||||
- **RETURN_TYPES**: タプル形式で出力型を定義
|
||||
- **RETURN_NAMES**: タプル形式で出力名を定義
|
||||
- **FUNCTION**: 実行される関数名を文字列で指定
|
||||
- **CATEGORY**: ノードのカテゴリを文字列で指定
|
||||
@@ -0,0 +1,32 @@
|
||||
# Codebase Structure
|
||||
|
||||
## Directory Layout
|
||||
```
|
||||
Comfyui-Random_Image_Picker/
|
||||
├── .github/
|
||||
│ └── copilot-instructions.md # AI assistant instructions
|
||||
├── .serena/ # Serena project config
|
||||
├── __init__.py # Package entry point
|
||||
└── random_image_picker.py # Main node implementation
|
||||
```
|
||||
|
||||
## File Descriptions
|
||||
|
||||
### `__init__.py`
|
||||
- パッケージのエントリーポイント
|
||||
- `NODE_CLASS_MAPPINGS`と`NODE_DISPLAY_NAME_MAPPINGS`をインポートしてエクスポート
|
||||
- ComfyUIがカスタムノードを認識するために必要
|
||||
|
||||
### `random_image_picker.py`
|
||||
- メインの実装ファイル
|
||||
- `RandomImagePicker`クラス: ノードの本体
|
||||
- `INPUT_TYPES()`: 入力パラメータの定義
|
||||
- `get_image_files()`: 画像ファイルのリスト取得
|
||||
- `load_image_file()`: 画像ファイルの読み込みとテンソル変換
|
||||
- `load_image()`: メイン処理(モードに応じた画像読み込み)
|
||||
- ノード登録用の定数
|
||||
|
||||
## Future Extensions
|
||||
- 追加の画像処理機能
|
||||
- フィルタリングオプション
|
||||
- プレビュー機能
|
||||
@@ -0,0 +1,22 @@
|
||||
# Project Purpose
|
||||
|
||||
**Project Name**: Comfyui-Random_Image_Picker
|
||||
|
||||
## Overview
|
||||
ComfyUIカスタムノード - ランダム画像選択機能
|
||||
|
||||
## Core Features
|
||||
1. **単一画像ロード**: 指定された画像ファイルを読み込み
|
||||
2. **フォルダランダムロード**: フォルダ内からランダムに画像を選択
|
||||
3. **モード切り替え**: ボタンで単一/フォルダモードを切り替え
|
||||
4. **サブフォルダスキャン**: オプションでサブフォルダも検索対象に
|
||||
5. **解像度情報出力**: 画像と共にwidth, heightを出力
|
||||
|
||||
## Output
|
||||
- IMAGE: ComfyUI形式の画像テンソル
|
||||
- width: 画像の幅(整数)
|
||||
- height: 画像の高さ(整数)
|
||||
|
||||
## Target System
|
||||
- ComfyUI custom node
|
||||
- Python-based implementation
|
||||
@@ -0,0 +1,28 @@
|
||||
# Suggested Commands
|
||||
|
||||
## Windows System Commands
|
||||
- `ls` または `dir`: ディレクトリ内容の表示
|
||||
- `cd <path>`: ディレクトリ移動
|
||||
- `Get-Content <file>`: ファイル内容の表示
|
||||
- `Select-String -Path <file> -Pattern <pattern>`: ファイル内検索
|
||||
|
||||
## Git Commands
|
||||
- `git status`: 変更状況の確認
|
||||
- `git add .`: 変更をステージング
|
||||
- `git commit -m "message"`: コミット
|
||||
- `git push`: リモートへプッシュ
|
||||
|
||||
## Python Development
|
||||
- `python -m py_compile <file>`: Pythonファイルの構文チェック
|
||||
- `python <file>`: Pythonスクリプトの実行
|
||||
|
||||
## ComfyUI Testing
|
||||
- ComfyUIを起動してカスタムノードをテスト
|
||||
- カスタムノードは `custom_nodes/Comfyui-Random_Image_Picker/` に配置
|
||||
|
||||
## Code Quality (推奨)
|
||||
- **Black**: `python -m black <file>` - コードフォーマット
|
||||
- **Flake8**: `python -m flake8 <file>` - リンティング
|
||||
- **MyPy**: `python -m mypy <file>` - 型チェック
|
||||
|
||||
Note: これらのツールは必要に応じてインストール
|
||||
@@ -0,0 +1,34 @@
|
||||
# Task Completion Checklist
|
||||
|
||||
## タスク完了時の必須作業
|
||||
|
||||
### 1. コード品質チェック
|
||||
- [ ] 構文エラーがないことを確認
|
||||
- [ ] 型ヒントが正しく付与されているか
|
||||
- [ ] Docstringsが適切に書かれているか
|
||||
|
||||
### 2. ComfyUI互換性
|
||||
- [ ] `INPUT_TYPES`が正しく定義されているか
|
||||
- [ ] `RETURN_TYPES`と`RETURN_NAMES`が一致しているか
|
||||
- [ ] ノードの登録(`NODE_CLASS_MAPPINGS`)が正しいか
|
||||
|
||||
### 3. ドキュメント
|
||||
- [ ] README.mdが最新の情報か
|
||||
- [ ] 使用方法が明確に記載されているか
|
||||
- [ ] 公開情報に個人情報が含まれていないか
|
||||
|
||||
### 4. Git管理
|
||||
- [ ] 変更をコミット: `git add . && git commit -m "descriptive message"`
|
||||
- [ ] 適切なタイミングでプッシュ: `git push`
|
||||
|
||||
### 5. テスト
|
||||
- [ ] ComfyUIでノードが正しく読み込まれるか
|
||||
- [ ] 単一画像モードが動作するか
|
||||
- [ ] フォルダモードが動作するか
|
||||
- [ ] サブフォルダスキャンが動作するか
|
||||
- [ ] 出力(IMAGE, width, height)が正しいか
|
||||
|
||||
## 区切りのいいタイミング
|
||||
- 機能追加完了時
|
||||
- バグ修正完了時
|
||||
- リファクタリング完了時
|
||||
@@ -0,0 +1,23 @@
|
||||
# Tech Stack
|
||||
|
||||
## Programming Language
|
||||
- **Python 3.x**
|
||||
|
||||
## Dependencies
|
||||
- **ComfyUI**: カスタムノードのベースシステム
|
||||
- **PyTorch**: 画像テンソル処理
|
||||
- **PIL (Pillow)**: 画像ファイル読み込み
|
||||
- **NumPy**: 配列処理
|
||||
- **folder_paths**: ComfyUI標準パス管理
|
||||
|
||||
## Supported Image Formats
|
||||
- PNG (.png)
|
||||
- JPEG (.jpg, .jpeg)
|
||||
- WebP (.webp)
|
||||
- BMP (.bmp)
|
||||
- GIF (.gif)
|
||||
|
||||
## ComfyUI Node Structure
|
||||
- `NODE_CLASS_MAPPINGS`: ノードクラスの登録辞書
|
||||
- `NODE_DISPLAY_NAME_MAPPINGS`: 表示名の登録辞書
|
||||
- `__init__.py`: エントリーポイント
|
||||
@@ -0,0 +1,84 @@
|
||||
# list of languages for which language servers are started; choose from:
|
||||
# al bash clojure cpp csharp csharp_omnisharp
|
||||
# dart elixir elm erlang fortran go
|
||||
# haskell java julia kotlin lua markdown
|
||||
# nix perl php python python_jedi r
|
||||
# rego ruby ruby_solargraph rust scala swift
|
||||
# terraform typescript typescript_vts yaml zig
|
||||
# Note:
|
||||
# - For C, use cpp
|
||||
# - For JavaScript, use typescript
|
||||
# Special requirements:
|
||||
# - csharp: Requires the presence of a .sln file in the project folder.
|
||||
# When using multiple languages, the first language server that supports a given file will be used for that file.
|
||||
# The first language is the default language and the respective language server will be used as a fallback.
|
||||
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
|
||||
languages:
|
||||
- python
|
||||
|
||||
# the encoding used by text files in the project
|
||||
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
|
||||
encoding: "utf-8"
|
||||
|
||||
# whether to use the project's gitignore file to ignore files
|
||||
# Added on 2025-04-07
|
||||
ignore_all_files_in_gitignore: true
|
||||
|
||||
# list of additional paths to ignore
|
||||
# same syntax as gitignore, so you can use * and **
|
||||
# Was previously called `ignored_dirs`, please update your config if you are using that.
|
||||
# Added (renamed) on 2025-04-07
|
||||
ignored_paths: []
|
||||
|
||||
# whether the project is in read-only mode
|
||||
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
|
||||
# Added on 2025-04-18
|
||||
read_only: false
|
||||
|
||||
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
|
||||
# Below is the complete list of tools for convenience.
|
||||
# To make sure you have the latest list of tools, and to view their descriptions,
|
||||
# execute `uv run scripts/print_tool_overview.py`.
|
||||
#
|
||||
# * `activate_project`: Activates a project by name.
|
||||
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
|
||||
# * `create_text_file`: Creates/overwrites a file in the project directory.
|
||||
# * `delete_lines`: Deletes a range of lines within a file.
|
||||
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
|
||||
# * `execute_shell_command`: Executes a shell command.
|
||||
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
|
||||
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
|
||||
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
|
||||
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
|
||||
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
|
||||
# * `initial_instructions`: Gets the initial instructions for the current project.
|
||||
# Should only be used in settings where the system prompt cannot be set,
|
||||
# e.g. in clients you have no control over, like Claude Desktop.
|
||||
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
|
||||
# * `insert_at_line`: Inserts content at a given line in a file.
|
||||
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
|
||||
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
|
||||
# * `list_memories`: Lists memories in Serena's project-specific memory store.
|
||||
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
|
||||
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
|
||||
# * `read_file`: Reads a file within the project directory.
|
||||
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
|
||||
# * `remove_project`: Removes a project from the Serena configuration.
|
||||
# * `replace_lines`: Replaces a range of lines within a file with new content.
|
||||
# * `replace_symbol_body`: Replaces the full definition of a symbol.
|
||||
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
|
||||
# * `search_for_pattern`: Performs a search for a pattern in the project.
|
||||
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
|
||||
# * `switch_modes`: Activates modes by providing a list of their names
|
||||
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
|
||||
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
|
||||
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
|
||||
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
||||
excluded_tools: []
|
||||
|
||||
# initial prompt for the project. It will always be given to the LLM upon activating the project
|
||||
# (contrary to the memories, which are loaded on demand).
|
||||
initial_prompt: ""
|
||||
|
||||
project_name: "Comfyui-Random_Image_Picker"
|
||||
included_optional_tools: []
|
||||
@@ -0,0 +1,99 @@
|
||||
# Random Image Picker - ComfyUI Custom Node
|
||||
|
||||
A ComfyUI custom node that provides flexible image loading with support for both single file and random folder selection.
|
||||
|
||||
## Features
|
||||
|
||||
- **Single Image Mode**: Load a specific image file
|
||||
- **Folder Mode**: Randomly select an image from a folder
|
||||
- **Subfolder Support**: Option to include subfolders in the search
|
||||
- **Resolution Output**: Automatically outputs image width and height
|
||||
- **Reproducible Random**: Seed control for consistent random selection
|
||||
|
||||
## Installation
|
||||
|
||||
1. Clone or download this repository to your ComfyUI custom nodes directory:
|
||||
```bash
|
||||
cd ComfyUI/custom_nodes
|
||||
git clone <repository-url> Comfyui-Random_Image_Picker
|
||||
```
|
||||
|
||||
2. Restart ComfyUI
|
||||
|
||||
## Usage
|
||||
|
||||
### Node Inputs
|
||||
|
||||
- **path** (STRING):
|
||||
- In Single mode: Full path to an image file
|
||||
- In Folder mode: Full path to a folder containing images
|
||||
|
||||
- **folder_mode** (BOOLEAN):
|
||||
- `OFF` (Single): Load the specified image file
|
||||
- `ON` (Folder): Randomly select an image from the folder
|
||||
|
||||
- **include_subfolders** (BOOLEAN):
|
||||
- `OFF`: Only scan the specified folder
|
||||
- `ON`: Include all subfolders in the search
|
||||
|
||||
- **seed** (INT):
|
||||
- Random seed for reproducible image selection in Folder mode
|
||||
- Range: 0 to 18446744073709551615
|
||||
|
||||
### Node Outputs
|
||||
|
||||
- **IMAGE**: The loaded image in ComfyUI tensor format
|
||||
- **width** (INT): Image width in pixels
|
||||
- **height** (INT): Image height in pixels
|
||||
|
||||
### Supported Image Formats
|
||||
|
||||
- PNG (.png)
|
||||
- JPEG (.jpg, .jpeg)
|
||||
- WebP (.webp)
|
||||
- BMP (.bmp)
|
||||
- GIF (.gif)
|
||||
|
||||
## Examples
|
||||
|
||||
### Single Image Mode
|
||||
```
|
||||
path: D:/images/photo.png
|
||||
folder_mode: OFF
|
||||
include_subfolders: OFF
|
||||
seed: 0
|
||||
```
|
||||
Loads the specific image at `D:/images/photo.png`
|
||||
|
||||
### Random Folder Mode
|
||||
```
|
||||
path: D:/images/
|
||||
folder_mode: ON
|
||||
include_subfolders: OFF
|
||||
seed: 42
|
||||
```
|
||||
Randomly selects one image from `D:/images/` (excluding subfolders)
|
||||
|
||||
### Random with Subfolders
|
||||
```
|
||||
path: D:/images/
|
||||
folder_mode: ON
|
||||
include_subfolders: ON
|
||||
seed: 42
|
||||
```
|
||||
Randomly selects one image from `D:/images/` and all its subfolders
|
||||
|
||||
## Technical Details
|
||||
|
||||
- Images are converted to RGB format
|
||||
- Output tensor format: `[batch, height, width, channels]`
|
||||
- Pixel values normalized to 0.0-1.0 range
|
||||
- Uses PyTorch tensors for ComfyUI compatibility
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||
|
||||
## Author
|
||||
|
||||
Created for ComfyUI
|
||||
@@ -0,0 +1,8 @@
|
||||
"""
|
||||
Random Image Picker Node for ComfyUI
|
||||
Loads single images or random images from folders
|
||||
"""
|
||||
|
||||
from .random_image_picker import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
@@ -0,0 +1,141 @@
|
||||
"""
|
||||
Random Image Picker Node Implementation
|
||||
"""
|
||||
|
||||
import os
|
||||
import random
|
||||
from pathlib import Path
|
||||
from typing import Tuple, List
|
||||
import folder_paths
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
|
||||
class RandomImagePicker:
|
||||
"""
|
||||
ComfyUI node that loads either a single image or a random image from a folder.
|
||||
Supports subfolder scanning and outputs image with resolution information.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"path": ("STRING", {"default": "", "multiline": False}),
|
||||
"folder_mode": ("BOOLEAN", {"default": False, "label_on": "Folder", "label_off": "Single"}),
|
||||
"include_subfolders": ("BOOLEAN", {"default": False, "label_on": "ON", "label_off": "OFF"}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "INT", "INT")
|
||||
RETURN_NAMES = ("IMAGE", "width", "height")
|
||||
FUNCTION = "load_image"
|
||||
CATEGORY = "image"
|
||||
|
||||
@staticmethod
|
||||
def get_image_files(directory: str, include_subfolders: bool = False) -> List[str]:
|
||||
"""
|
||||
Get all image files from a directory.
|
||||
|
||||
Args:
|
||||
directory: Directory path to scan
|
||||
include_subfolders: Whether to include subfolders in the scan
|
||||
|
||||
Returns:
|
||||
List of image file paths
|
||||
"""
|
||||
supported_formats = {'.png', '.jpg', '.jpeg', '.webp', '.bmp', '.gif'}
|
||||
image_files = []
|
||||
|
||||
if include_subfolders:
|
||||
for root, _, files in os.walk(directory):
|
||||
for file in files:
|
||||
if Path(file).suffix.lower() in supported_formats:
|
||||
image_files.append(os.path.join(root, file))
|
||||
else:
|
||||
for file in os.listdir(directory):
|
||||
file_path = os.path.join(directory, file)
|
||||
if os.path.isfile(file_path) and Path(file).suffix.lower() in supported_formats:
|
||||
image_files.append(file_path)
|
||||
|
||||
return image_files
|
||||
|
||||
@staticmethod
|
||||
def load_image_file(image_path: str) -> Tuple[torch.Tensor, int, int]:
|
||||
"""
|
||||
Load an image file and convert to ComfyUI format.
|
||||
|
||||
Args:
|
||||
image_path: Path to the image file
|
||||
|
||||
Returns:
|
||||
Tuple of (image_tensor, width, height)
|
||||
"""
|
||||
img = Image.open(image_path)
|
||||
|
||||
# Convert to RGB if needed
|
||||
if img.mode != 'RGB':
|
||||
img = img.convert('RGB')
|
||||
|
||||
# Get dimensions
|
||||
width, height = img.size
|
||||
|
||||
# Convert to numpy array
|
||||
img_array = np.array(img).astype(np.float32) / 255.0
|
||||
|
||||
# Convert to torch tensor and add batch dimension
|
||||
# ComfyUI expects [batch, height, width, channels]
|
||||
img_tensor = torch.from_numpy(img_array)[None,]
|
||||
|
||||
return img_tensor, width, height
|
||||
|
||||
def load_image(self, path: str, folder_mode: bool, include_subfolders: bool, seed: int):
|
||||
"""
|
||||
Load image based on mode settings.
|
||||
|
||||
Args:
|
||||
path: File or folder path
|
||||
folder_mode: True for folder mode, False for single file mode
|
||||
include_subfolders: Whether to scan subfolders
|
||||
seed: Random seed for folder mode
|
||||
|
||||
Returns:
|
||||
Tuple of (image_tensor, width, height)
|
||||
"""
|
||||
if not path or not os.path.exists(path):
|
||||
raise ValueError(f"Path does not exist: {path}")
|
||||
|
||||
if folder_mode:
|
||||
# Folder mode: pick random image
|
||||
if not os.path.isdir(path):
|
||||
raise ValueError(f"Folder mode enabled but path is not a directory: {path}")
|
||||
|
||||
image_files = self.get_image_files(path, include_subfolders)
|
||||
|
||||
if not image_files:
|
||||
raise ValueError(f"No image files found in directory: {path}")
|
||||
|
||||
# Use seed for reproducible random selection
|
||||
random.seed(seed)
|
||||
selected_image = random.choice(image_files)
|
||||
|
||||
print(f"[Random Image Picker] Selected: {selected_image}")
|
||||
return self.load_image_file(selected_image)
|
||||
else:
|
||||
# Single file mode
|
||||
if not os.path.isfile(path):
|
||||
raise ValueError(f"Single file mode enabled but path is not a file: {path}")
|
||||
|
||||
return self.load_image_file(path)
|
||||
|
||||
|
||||
# Node registration
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"RandomImagePicker": RandomImagePicker
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"RandomImagePicker": "Random Image Picker"
|
||||
}
|
||||
Reference in New Issue
Block a user