519 lines
14 KiB
Markdown
519 lines
14 KiB
Markdown
# Metadata System Usage Guide
|
|
|
|
This guide provides detailed instructions and examples for using the Metadata System for ComfyUI. It covers basic and advanced usage, as well as integration with ComfyUI nodes.
|
|
|
|
## Table of Contents
|
|
|
|
- [Basic Usage](#basic-usage)
|
|
- [Initialization](#initialization)
|
|
- [Writing Metadata](#writing-metadata)
|
|
- [Reading Metadata](#reading-metadata)
|
|
- [Merging Metadata](#merging-metadata)
|
|
- [Metadata Structure](#metadata-structure)
|
|
- [Basic Section](#basic-section)
|
|
- [Analysis Section](#analysis-section)
|
|
- [AI Info Section](#ai-info-section)
|
|
- [Regions Section](#regions-section)
|
|
- [ComfyUI Node Usage](#comfyui-node-usage)
|
|
- [Metadata Save Image Node](#metadata-save-image-node)
|
|
- [Metadata Entry Node](#metadata-entry-node)
|
|
- [Metadata Query Node](#metadata-query-node)
|
|
- [Workflow Extractor Node](#workflow-extractor-node)
|
|
- [Advanced Features](#advanced-features)
|
|
- [Human-readable Text Format](#human-readable-text-format)
|
|
- [Database Queries](#database-queries)
|
|
- [Resource Identifiers](#resource-identifiers)
|
|
- [Batch Operations](#batch-operations)
|
|
- [Troubleshooting](#troubleshooting)
|
|
- [Common Issues](#common-issues)
|
|
- [Debug Mode](#debug-mode)
|
|
|
|
## Basic Usage
|
|
|
|
### Initialization
|
|
|
|
Initialize the MetadataService to start working with the system:
|
|
|
|
```python
|
|
from Metadata_system import MetadataService
|
|
|
|
# Basic initialization
|
|
service = MetadataService()
|
|
|
|
# With debug mode enabled for troubleshooting
|
|
service = MetadataService(debug=True)
|
|
|
|
# With human-readable text format
|
|
service = MetadataService(human_readable_text=True)
|
|
|
|
# Using as a context manager (ensures proper cleanup)
|
|
with MetadataService() as service:
|
|
# Your code here
|
|
pass
|
|
```
|
|
|
|
### Writing Metadata
|
|
|
|
Write metadata to one or more storage formats:
|
|
|
|
```python
|
|
# Create metadata dictionary
|
|
metadata = {
|
|
'basic': {
|
|
'title': 'Mountain Landscape',
|
|
'description': 'AI generated mountain landscape',
|
|
'keywords': ['mountains', 'landscape', 'nature', 'AI'],
|
|
'rating': 4
|
|
},
|
|
'ai_info': {
|
|
'generation': {
|
|
'model': 'stable-diffusion-v1-5',
|
|
'prompt': 'majestic mountains with snow caps',
|
|
'negative_prompt': 'ugly, blurry',
|
|
'sampler': 'euler_a',
|
|
'steps': 30,
|
|
'cfg_scale': 7.5,
|
|
'seed': 1234567890
|
|
}
|
|
}
|
|
}
|
|
|
|
# Write to all supported formats
|
|
result = service.write_metadata('path/to/image.png', metadata)
|
|
|
|
# Write to specific formats
|
|
result = service.write_metadata('path/to/image.png', metadata,
|
|
targets=['embedded', 'xmp'])
|
|
|
|
# Check results
|
|
for target, success in result.items():
|
|
print(f"{target}: {'Succeeded' if success else 'Failed'}")
|
|
```
|
|
|
|
### Reading Metadata
|
|
|
|
Read metadata from storage formats:
|
|
|
|
```python
|
|
# Read from embedded metadata with fallback
|
|
metadata = service.read_metadata('path/to/image.png', source='embedded', fallback=True)
|
|
|
|
# Read from specific source without fallback
|
|
xmp_metadata = service.read_metadata('path/to/image.png', source='xmp', fallback=False)
|
|
|
|
# Access specific sections
|
|
if 'basic' in metadata:
|
|
print(f"Title: {metadata['basic'].get('title', 'No title')}")
|
|
|
|
if 'ai_info' in metadata and 'generation' in metadata['ai_info']:
|
|
gen = metadata['ai_info']['generation']
|
|
print(f"Model: {gen.get('model')}")
|
|
print(f"Prompt: {gen.get('prompt')}")
|
|
```
|
|
|
|
### Merging Metadata
|
|
|
|
Merge new metadata with existing metadata:
|
|
|
|
```python
|
|
# New metadata to merge
|
|
new_metadata = {
|
|
'basic': {
|
|
'keywords': ['new', 'tags', 'to', 'add']
|
|
},
|
|
'analysis': {
|
|
'technical': {
|
|
'blur': {
|
|
'score': 0.92,
|
|
'higher_better': True
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Merge with existing metadata
|
|
result = service.merge_metadata('path/to/image.png', new_metadata)
|
|
|
|
# Merge and write to specific targets
|
|
result = service.merge_metadata('path/to/image.png', new_metadata,
|
|
targets=['embedded', 'txt'])
|
|
```
|
|
|
|
## Metadata Structure
|
|
|
|
The metadata is organized into sections for different types of information:
|
|
|
|
### Basic Section
|
|
|
|
Contains essential image information:
|
|
|
|
```python
|
|
basic = {
|
|
'title': 'Image Title', # Image title
|
|
'description': 'Detailed description', # Description/caption
|
|
'keywords': ['keyword1', 'keyword2'], # Tags/keywords
|
|
'rating': 4, # Rating (1-5)
|
|
'creator': 'Author Name', # Creator/author
|
|
'rights': 'Copyright information', # Copyright/license
|
|
'create_date': '2025-03-15T12:34:56' # Creation timestamp
|
|
}
|
|
```
|
|
|
|
### Analysis Section
|
|
|
|
Contains analysis results from various tools:
|
|
|
|
```python
|
|
analysis = {
|
|
'technical': {
|
|
'blur': {
|
|
'score': 0.92, # Blur detection score
|
|
'higher_better': True # Whether higher is better
|
|
},
|
|
'noise': {
|
|
'score': 0.08, # Noise detection score
|
|
'higher_better': False # Whether higher is better
|
|
}
|
|
},
|
|
'aesthetic': {
|
|
'composition': 7.8, # Composition score
|
|
'color_harmony': 8.2, # Color harmony score
|
|
'overall': 7.9 # Overall aesthetic score
|
|
},
|
|
'pyiqa': {
|
|
'niqe': { # No-reference IQA model
|
|
'score': 3.45,
|
|
'higher_better': False,
|
|
'range': [0, 25]
|
|
},
|
|
'musiq': { # Multi-scale IQA model
|
|
'score': 78.2,
|
|
'higher_better': True,
|
|
'range': [0, 100]
|
|
}
|
|
},
|
|
'classification': {
|
|
'style': 'photorealistic', # Detected style
|
|
'content_type': 'landscape', # Content type
|
|
'has_text': False # Text detection result
|
|
}
|
|
}
|
|
```
|
|
|
|
### AI Info Section
|
|
|
|
Contains AI generation information:
|
|
|
|
```python
|
|
ai_info = {
|
|
'generation': {
|
|
'model': 'stable-diffusion-v1-5', # Base model
|
|
'prompt': 'mountain landscape', # Positive prompt
|
|
'negative_prompt': 'ugly, blurry', # Negative prompt
|
|
'sampler': 'euler_a', # Sampler name
|
|
'steps': 30, # Sampling steps
|
|
'cfg_scale': 7.5, # CFG scale
|
|
'seed': 1234567890, # Generation seed
|
|
'width': 512, # Image width
|
|
'height': 512, # Image height
|
|
'loras': [ # LoRAs used
|
|
{
|
|
'name': 'example_lora',
|
|
'strength': 0.8
|
|
}
|
|
]
|
|
},
|
|
'workflow': { # Full workflow data
|
|
# ComfyUI workflow data (simplified for brevity)
|
|
'nodes': { ... }
|
|
}
|
|
}
|
|
```
|
|
|
|
### Regions Section
|
|
|
|
Contains information about specific regions in the image:
|
|
|
|
```python
|
|
regions = {
|
|
'faces': [ # Detected faces
|
|
{
|
|
'type': 'Face',
|
|
'name': 'Person 1',
|
|
'area': { # Face coordinates
|
|
'x': 0.2, # Normalized (0-1)
|
|
'y': 0.3,
|
|
'w': 0.1,
|
|
'h': 0.15
|
|
},
|
|
'extensions': { # Extended face data
|
|
'eiqa': {
|
|
'face_analysis': {
|
|
'gender': 'female',
|
|
'age': 28,
|
|
'emotion': 'happy'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
],
|
|
'areas': [ # Other detected areas
|
|
{
|
|
'type': 'Object',
|
|
'name': 'Mountain',
|
|
'area': {
|
|
'x': 0.1,
|
|
'y': 0.2,
|
|
'w': 0.8,
|
|
'h': 0.5
|
|
}
|
|
}
|
|
],
|
|
'summary': { # Summary statistics
|
|
'face_count': 1,
|
|
'detector_type': 'deepface'
|
|
}
|
|
}
|
|
```
|
|
|
|
## ComfyUI Node Usage
|
|
|
|
### Metadata Save Image Node
|
|
|
|
The `eric_metadata_save_image` node saves images with metadata:
|
|
|
|
1. Connect your image output to the node's `images` input
|
|
2. Connect workflow data (optional) to the `workflow_data` input
|
|
3. Set basic parameters:
|
|
- `save_path`: Directory to save images
|
|
- `filename_prefix`: Prefix for generated filenames
|
|
- `filename_number_padding`: Number of digits for sequential numbering
|
|
- `filename_number_start`: Starting number for sequential filenames
|
|
4. Metadata options:
|
|
- `title`: Image title
|
|
- `description`: Image description
|
|
- `keywords`: Comma-separated keywords
|
|
- `save_metadata_to`: Formats to save metadata (embedded,xmp,txt,db)
|
|
- `human_readable_text`: Use human-readable text format
|
|
|
|
This node automatically captures workflow data, generation parameters, and technical image information.
|
|
|
|
### Metadata Entry Node
|
|
|
|
The `eric_metadata_entry` node allows adding custom metadata:
|
|
|
|
1. Connect to any workflow position
|
|
2. Fill in metadata fields:
|
|
- Basic information (title, description, keywords)
|
|
- Analysis scores
|
|
- Custom fields
|
|
3. Connect to other nodes that process metadata
|
|
|
|
### Metadata Query Node
|
|
|
|
The `eric_metadata_query_node` filters and retrieves metadata:
|
|
|
|
1. Connect to image input
|
|
2. Set query parameters:
|
|
- `query_type`: Type of query to perform
|
|
- `field_path`: Path to the metadata field to query
|
|
- `comparison`: Comparison operator (equals, greater_than, etc.)
|
|
- `value`: Value to compare against
|
|
3. Use outputs:
|
|
- `result`: Query result
|
|
- `metadata`: Full metadata dictionary
|
|
- `image`: Passed-through image
|
|
|
|
### Workflow Extractor Node
|
|
|
|
The `eric_workflow_extractor` node extracts workflow data from images:
|
|
|
|
1. Connect to image input
|
|
2. Set extraction parameters:
|
|
- `extraction_mode`: How to extract workflow data
|
|
- `include_basic_metadata`: Whether to include basic metadata
|
|
3. Use outputs:
|
|
- `workflow_data`: Extracted workflow JSON
|
|
- `metadata`: Full metadata dictionary
|
|
- `image`: Passed-through image
|
|
|
|
## Advanced Features
|
|
|
|
### Human-readable Text Format
|
|
|
|
The system can create human-readable text files for easy viewing:
|
|
|
|
```python
|
|
# Enable human-readable text format
|
|
service = MetadataService(human_readable_text=True)
|
|
|
|
# Or change format at runtime
|
|
service.set_text_format(human_readable=True)
|
|
|
|
# Write metadata (will use human-readable format for text files)
|
|
service.write_metadata('path/to/image.png', metadata)
|
|
```
|
|
|
|
Example output:
|
|
```
|
|
Last update: 2025-03-15T10:23:45.123456
|
|
Metadata for image.png
|
|
|
|
Basic Information:
|
|
Title: Mountain Landscape
|
|
Description: AI generated mountain landscape
|
|
Keywords: mountains, landscape, nature, AI
|
|
|
|
AI Generation Information:
|
|
Prompt: majestic mountains with snow caps
|
|
Negative prompt: ugly, blurry
|
|
Model: stable-diffusion-v1-5
|
|
Sampler: euler_a
|
|
Steps: 30
|
|
Cfg_scale: 7.5
|
|
Seed: 1234567890
|
|
```
|
|
|
|
### Database Queries
|
|
|
|
Perform advanced queries on the database:
|
|
|
|
```python
|
|
from Metadata_system.handlers.db import DatabaseHandler
|
|
|
|
db = DatabaseHandler()
|
|
|
|
# Search for high-quality mountain images
|
|
results = db.search_images({
|
|
'scores': [
|
|
('aesthetic', 'overall', '>', 7.0),
|
|
('technical', 'blur.score', '>', 0.8)
|
|
],
|
|
'keywords': ['mountains'],
|
|
'classifications': [('style', 'photorealistic')],
|
|
'orientation': 'landscape',
|
|
'order_by': 'images.created_date DESC',
|
|
'limit': 10
|
|
})
|
|
|
|
# Process results
|
|
for image in results:
|
|
print(f"Found image: {image['filepath']}")
|
|
print(f"Rating: {image.get('rating', 'N/A')}")
|
|
```
|
|
|
|
### Resource Identifiers
|
|
|
|
Set custom resource identifiers for XMP metadata:
|
|
|
|
```python
|
|
# Set resource identifier for all handlers
|
|
service.set_resource_identifier("file:///custom/path/to/resource.jpg")
|
|
|
|
# Write metadata with custom resource identifier
|
|
service.write_metadata('path/to/image.jpg', metadata)
|
|
```
|
|
|
|
### Batch Operations
|
|
|
|
Perform operations on multiple files:
|
|
|
|
```python
|
|
# Using DatabaseHandler for batch operations
|
|
from Metadata_system.handlers.db import DatabaseHandler
|
|
|
|
db = DatabaseHandler()
|
|
|
|
# Define file paths
|
|
file_paths = [
|
|
'path/to/image1.png',
|
|
'path/to/image2.jpg',
|
|
'path/to/image3.webp'
|
|
]
|
|
|
|
# Read metadata from multiple files
|
|
results = db.batch_operation('read', file_paths)
|
|
|
|
# Update metadata for multiple files
|
|
new_metadata = {
|
|
'basic': {
|
|
'keywords': ['batch', 'processed']
|
|
}
|
|
}
|
|
results = db.batch_operation('write', file_paths, new_metadata)
|
|
|
|
# Check results
|
|
for filepath, success in results.items():
|
|
print(f"{filepath}: {'Succeeded' if success else 'Failed'}")
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
**Issue**: Cannot write embedded metadata to PNG file
|
|
**Solution**: Ensure PyExiv2 is installed or use ExifTool as a fallback
|
|
|
|
```python
|
|
# Check if PyExiv2 is available
|
|
try:
|
|
import pyexiv2
|
|
print("PyExiv2 is available")
|
|
except ImportError:
|
|
print("PyExiv2 is not available, using ExifTool fallback")
|
|
```
|
|
|
|
**Issue**: Files not showing metadata in other applications
|
|
**Solution**: Verify format compatibility and try XMP sidecars
|
|
|
|
```python
|
|
# Force write to XMP sidecar
|
|
service.write_metadata('path/to/image.png', metadata, targets=['xmp'])
|
|
```
|
|
|
|
**Issue**: Database errors
|
|
**Solution**: Check SQLite installation and file permissions
|
|
|
|
```python
|
|
# Use try-except to handle database errors
|
|
try:
|
|
from Metadata_system.handlers.db import DatabaseHandler
|
|
db = DatabaseHandler()
|
|
# Operations with db
|
|
except Exception as e:
|
|
print(f"Database error: {str(e)}")
|
|
# Fall back to file-based storage
|
|
service.write_metadata('path/to/image.png', metadata, targets=['embedded', 'xmp', 'txt'])
|
|
```
|
|
|
|
### Debug Mode
|
|
|
|
Enable debug mode for detailed logging:
|
|
|
|
```python
|
|
# Initialize with debug mode
|
|
service = MetadataService(debug=True)
|
|
|
|
# Operations will now produce detailed logs
|
|
service.write_metadata('path/to/image.png', metadata)
|
|
```
|
|
|
|
Debug logs include:
|
|
- Format detection results
|
|
- Handler selection details
|
|
- Operation traces
|
|
- Error messages with stack traces
|
|
|
|
Access error history from handlers:
|
|
|
|
```python
|
|
# Get embedded handler
|
|
embedded_handler = service._get_embedded_handler()
|
|
|
|
# Print error history
|
|
for error in embedded_handler.error_history:
|
|
print(f"[{error['timestamp']}] {error['level']}: {error['message']}")
|
|
if error['error']:
|
|
print(f"Error: {error['error']}")
|
|
print("---")
|
|
``` |