Skip to content

davepeloso/flambient

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

30 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐ŸŽจ Terminal Flazsh - Flambient Photography Processor

A production-ready Laravel 11 CLI application for automated flambient photography processing with ImageMagick local blending and Imagen AI cloud enhancement.

Flambient Photography: A real estate photography technique combining ambient (natural light) and flash (artificial light) exposures using advanced blending to create perfectly balanced, professional interior photos.

PHP Laravel Tests License


๐Ÿ“– Table of Contents


โœจ Features

Complete End-to-End Workflow

Local Processing: - โœ… Flexible EXIF Classification - Choose from 6+ fields (Flash, Exposure Mode, White Balance, ISO, etc.) - โœ… Dual EXIF Extraction - Numeric values for logic + human-readable labels for display - โœ… Smart Image Grouping - Automatic pairing of ambient/flash sequences by timestamp - โœ… ImageMagick Blending - 7-step flambient algorithm with customizable parameters - โœ… Batch Processing - Handle hundreds of images efficiently

Cloud Enhancement (Optional): - โœ… Imagen AI Integration - Native PHP client for automated cloud enhancement - โœ… Progress Tracking - Real-time upload/processing/download progress with emojis - โœ… Automatic Polling - Monitor AI processing until completion (up to 2 hours) - โœ… JPEG Export - Automatic conversion and download of enhanced images - โœ… Resume Capability - Save project UUID to database for later access

Developer Experience

  • โœ… Laravel Prompts - Beautiful interactive CLI with validation
  • โœ… Type Safety - PHP 8.2+ read only classes, backed enums
  • โœ… Database State - SQLite tracks all workflow execution
  • โœ… Comprehensive Testing - 46 PHPUnit tests with 100% Http::fake() coverage
  • โœ… Error Handling - Graceful failures with actionable messages
  • โœ… Configurable - Environment-based settings with sane defaults

Production Ready

  • โœ… State Machine - 8-step workflow with explicit transitions
  • โœ… Audit Trail - Database logging of all operations
  • โœ… Progress Indicators - Spinners, tables, emoji status updates
  • โœ… Resumable - Pause/resume long-running operations
  • โœ… Testable - Mock all external dependencies (exiftool, ImageMagick, Imagen API)

๐Ÿ—๏ธ Architecture

This application represents a complete architectural redesign from fragile shell scripts to a robust, Laravel-native system with a plugin-based architecture for image processing techniques.

Key Design Principles

<<<<<<< HEAD

  1. Plugin Architecture - Modular script generators for different processing techniques
  2. Explicit State Management - All workflow state persisted to SQLite
  3. Type Safety - Enums, DTOs, readonly classes throughout
  4. Separation of Concerns - Services for EXIF, ImageMagick, Imagen AI
  5. Laravel-Native - HTTP client, Prompts, Collections, Eloquent
  6. Testability - All external dependencies mockable

Plugin-Based Processing

The application uses a ScriptGeneratorInterface pattern allowing multiple image processing techniques:

FlambientProcessCommand
    โ†“
ScriptGeneratorRegistry โ†’ [FlambientGenerator, HDRGenerator, DMECGenerator, ...]
    โ†“
ImageMagickService (generic executor)

Benefits:

  • โœ… Add new processing techniques without modifying core code
  • โœ… Each technique is self-contained with its own configuration
  • โœ… Easy to test individual generators independently
  • โœ… Future-proof for new ImageMagick workflows =======
  1. Explicit State Management - All workflow state persisted to SQLite
  2. Type Safety - Enums, DTOs, readonly classes throughout
  3. Separation of Concerns - Services for EXIF, ImageMagick, Imagen AI
  4. Laravel-Native - HTTP client, Prompts, Collections, Eloquent
  5. Testability - All external dependencies mockable

origin/main

Workflow Steps

Step Name Purpose Skippable
1 Prepare Validate inputs, create workspace โŒ
2 Analyze Extract EXIF, classify & group images โŒ
3 Process Generate & execute ImageMagick scripts โŒ
4 Upload Upload blended images to Imagen AI โœ… --local
5 Edit Submit for AI enhancement โœ… --local
6 Monitor Poll processing status โœ… --local
7 Export Convert to JPEG format โœ… --local
8 Download Retrieve enhanced images โœ… --local

Database Schema

workflow_runs
โ”œโ”€โ”€ id (UUID primary key)
โ”œโ”€โ”€ project_name (string, unique)
โ”œโ”€โ”€ config (JSON snapshot)
โ”œโ”€โ”€ status (pending|running|completed|failed|paused)
โ”œโ”€โ”€ imagen_project_uuid (nullable)
โ””โ”€โ”€ total_images_processed, total_groups_created

workflow_steps
โ”œโ”€โ”€ workflow_run_id (foreign key)
โ”œโ”€โ”€ step_name (prepare|analyze|process|upload|monitor|export|download|finalize)
โ”œโ”€โ”€ status, input_data, output_data (JSON)
โ””โ”€โ”€ duration_seconds, retry_count

workflow_files
โ”œโ”€โ”€ workflow_run_id (foreign key)
โ”œโ”€โ”€ original_path, processed_path
โ”œโ”€โ”€ file_type (ambient|flash|blended)
โ””โ”€โ”€ exif_data (JSON)

๐Ÿ“ฆ Installation

Prerequisites

  • PHP 8.2+ with SQLite extension
  • Composer for dependency management
  • ImageMagick (magick command available)
  • exiftool for EXIF metadata extraction
  • Imagen AI API key (optional, for cloud enhancement)

Setup

# 1. Clone repository
git clone https://github.com/davepeloso/flambient.git
cd terminal-flazsh

# 2. Install dependencies
composer install

# 3. Configure environment
cp .env.example .env
php artisan key:generate

# 4. Set up database
touch database/database.sqlite
php artisan migrate

# 5. (Optional) Configure Imagen AI
# Edit .env and add:
# IMAGEN_AI_API_KEY=your_key_here
# IMAGEN_PROFILE_KEY=309406

Verify Installation

# Check PHP version
php -v  # Should be 8.2+

# Check ImageMagick
magick -version

# Check exiftool
exiftool -ver

# Run tests
php artisan test

๐Ÿš€ Quick Start

Local-Only Processing (No Cloud)

php artisan flambient:process \
  --project="(date '+%m-%d-%y')_ID$(gshuf -i 10000-99999 -n 1)" \
  --dir="public/123-main-street" \
  --local

  echo "$(date '+%m-%d-%y')_ID$(gshuf -i 10000-99999 -n 1)"
  ID="$(date '+%m-%d-%y')_ID$(gshuf -i 10000-99999 -n 1)"
echo "$ID"

This will: 1. Extract EXIF metadata from your images 2. Classify images as Ambient or Flash based on EXIF fields 3. Group images by timestamp 4. Generate ImageMagick blend scripts 5. Create blended flambient images in storage/flambient/my-first-shoot/flambient/

Full Workflow (With Imagen AI)

php artisan flambient:process \
  --project="real-estate-shoot" \
  --dir="/path/to/images"

This will: 1. Perform local ImageMagick blending (steps 1-3) 2. Upload blended images to Imagen AI (step 4) 3. Monitor AI processing with real-time progress (step 5-6) 4. Export and download enhanced images (steps 7-8) 5. Save both blended and enhanced versions locally

Interactive Mode

Simply run without arguments for a guided experience:

php artisan flambient:process

The CLI will interactively prompt for: - Project name - Image directory - EXIF classification strategy - Processing mode (local vs. cloud)


๐Ÿ“– Usage

Command Options

php artisan flambient:process [options]

Options:
  --project=NAME      Project name (unique identifier)
  --dir=PATH          Directory containing JPG images
  --output=PATH       Output directory (default: storage/flambient/{project})
  --local             Skip cloud processing (ImageMagick only)

EXIF Classification

During analysis, you'll choose which EXIF field to use for classifying Ambient vs. Flash:

โ”Œ Which EXIF field should identify Ambient images? โ”€โ”€โ”€โ”€โ”
โ”‚ โ— Flash (Flash = 16 for Ambient)                      โ”‚
โ”‚ โ—‹ Exposure Program                                    โ”‚
โ”‚ โ—‹ Exposure Mode                                       โ”‚
โ”‚ โ—‹ White Balance                                       โ”‚
โ”‚ โ—‹ ISO Value                                           โ”‚
โ”‚ โ—‹ Shutter Speed                                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  Choose the field that differs between your ambient and flash shots

You can optionally view sample EXIF data first:

โ”Œ How many sample images to display? โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ โ— 3 samples                                            โ”‚
โ”‚ โ—‹ 5 samples                                            โ”‚
โ”‚ โ—‹ 10 samples                                           โ”‚
โ”‚ โ—‹ 25 samples                                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Filename     โ”‚ Flash            โ”‚ ExposureProgram  โ”‚ ExposureMode โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ IMG_001.jpg  โ”‚ 16 (No Flash)    โ”‚ 1 (Manual)       โ”‚ 0 (Auto)     โ”‚
โ”‚ IMG_002.jpg  โ”‚ 0 (Flash Fired)  โ”‚ 2 (Program AE)   โ”‚ 1 (Manual)   โ”‚
โ”‚ IMG_003.jpg  โ”‚ 16 (No Flash)    โ”‚ 1 (Manual)       โ”‚ 0 (Auto)     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Example Output

Step 1/3: Preparing workspace
โ ง Validating inputs and creating directories...
โœ“ Workspace created: /storage/flambient/my-shoot

Step 2/3: Analyzing images
โ ง Extracting EXIF metadata and grouping images...
โœ“ Classified 54 images into 27 groups (9 Ambient, 18 Flash)

Step 3/3: Processing with ImageMagick
โ ง Generating and executing ImageMagick scripts...
โœ“ Created 27 blended images in 8.5s

Steps 4-8: Skipped (local-only mode - Imagen AI processing disabled)

โœ“ Processing complete!
  Output: /storage/flambient/my-shoot/flambient/
  Images: 27

With Imagen AI

Step 4/8: Upload to Imagen AI
โš  This will upload 27 blended images to Imagen AI for enhancement.

โ”Œ Proceed with Imagen AI upload and processing? โ”€โ”€โ”€โ”€โ”
โ”‚ โ— Yes / โ—‹ No                                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ ง Creating Imagen AI project...
โœ“ Project created: abc-123-def-456
  View at: https://app.imagen-ai.com/projects/abc-123-def-456

Uploading 27 images to Imagen AI...
  Uploaded 5/27: flambient_01.jpg
  Uploaded 10/27: flambient_05.jpg
โœ“ Upload complete: 27/27 files (100% success)

Step 6/8: Monitoring AI processing
This typically takes 10-30 minutes depending on image count.

โณ Processing: 0% - queued
๐Ÿ”„ Processing: 25% - editing
โšก Processing: 50% - editing
๐Ÿš€ Processing: 90% - editing
๐ŸŽ‰ Processing: 100% - completed
โœ“ AI editing complete!

Step 8/8: Downloading enhanced images
  Downloaded 5/27: edited_01.jpg
  Downloaded 10/27: edited_05.jpg
โœ“ Download complete: 27/27 files (100% success)
  Output: /storage/flambient/my-shoot/edited/

๐ŸŽ‰ Workflow Complete                           SUCCESS

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Metric          โ”‚ Value                         โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Project Name    โ”‚ my-shoot                      โ”‚
โ”‚ Total Duration  โ”‚ 25 minutes                    โ”‚
โ”‚ Images Processedโ”‚ 54                            โ”‚
โ”‚ Groups Created  โ”‚ 27                            โ”‚
โ”‚ Blended Output  โ”‚ /storage/.../flambient/       โ”‚
โ”‚ Enhanced Output โ”‚ /storage/.../edited/          โ”‚
โ”‚ Imagen Project  โ”‚ abc-123-def-456               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐ŸŽจ Blended images: /storage/flambient/my-shoot/flambient/
โœจ Enhanced images: /storage/flambient/my-shoot/edited/
๐ŸŒ Imagen project: https://app.imagen-ai.com/projects/abc-123-def-456

โš™๏ธ Configuration

Environment Variables

Edit .env to configure:

Imagen AI Settings

IMAGEN_AI_API_KEY=your_api_key_here
IMAGEN_API_BASE_URL=https://api-beta.imagen-ai.com/v1
IMAGEN_PROFILE_KEY=309406
IMAGEN_TIMEOUT=30
IMAGEN_POLL_INTERVAL=30              # Seconds between status checks
IMAGEN_POLL_MAX_ATTEMPTS=240         # Max attempts (240 ร— 30s = 2 hours)

ImageMagick Settings

IMAGEMAGICK_BINARY=magick
IMAGEMAGICK_LEVEL_LOW=40%            # Ambient mask threshold
IMAGEMAGICK_LEVEL_HIGH=140%          # Ambient mask upper bound
IMAGEMAGICK_GAMMA=1.0                # Gamma correction
IMAGEMAGICK_OUTPUT_PREFIX=flambient
IMAGEMAGICK_DARKEN_EXPORT=false      # Export darkened flash composite
IMAGEMAGICK_DARKEN_SUFFIX=_tmp

Workflow Settings

FLAMBIENT_STORAGE_PATH=              # Default: storage/flambient
FLAMBIENT_KEEP_TEMP=false            # Keep temporary files
FLAMBIENT_PARALLEL_UPLOADS=5         # Concurrent uploads
FLAMBIENT_PARALLEL_DOWNLOADS=5       # Concurrent downloads

Configuration File

Advanced settings in config/flambient.php:

return [
    'imagen' => [
        'api_key' => env('IMAGEN_AI_API_KEY'),
        'base_url' => env('IMAGEN_API_BASE_URL', 'https://api-beta.imagen-ai.com/v1'),
        'profile_key' => env('IMAGEN_PROFILE_KEY', 309406),
        'timeout' => env('IMAGEN_TIMEOUT', 30),
        'poll_interval' => env('IMAGEN_POLL_INTERVAL', 30),
        'poll_max_attempts' => env('IMAGEN_POLL_MAX_ATTEMPTS', 240),
    ],

    'imagemagick' => [
        'level_low' => env('IMAGEMAGICK_LEVEL_LOW', '40%'),
        'level_high' => env('IMAGEMAGICK_LEVEL_HIGH', '140%'),
        'gamma' => env('IMAGEMAGICK_GAMMA', '1.0'),
    ],
];

๐Ÿงช Testing

Run All Tests

php artisan test

Expected output:

PASS  Tests\Unit\ExifServiceTest
โœ“ it extracts exif metadata from images
โœ“ it classifies images using flash strategy
โœ“ it classifies images using exposure program strategy
... (11 tests)

PASS  Tests\Unit\ImageMagickServiceTest
โœ“ it generates imagemagick scripts for groups
โœ“ it generates correct mgk script content
... (9 tests)

PASS  Tests\Unit\ImagenClientTest
โœ“ it creates a project
โœ“ it handles create project failure
... (18 tests)

PASS  Tests\Feature\FlambientProcessCommandTest
โœ“ it processes flambient workflow end to end
โœ“ it allows selecting different classification strategies
... (8 tests)

Tests:    46 passed (117 assertions)
Duration: 2.34s

Run Specific Test Suites

# EXIF extraction and classification
php artisan test --filter=ExifServiceTest

# ImageMagick script generation
php artisan test --filter=ImageMagickServiceTest

# Imagen AI client
php artisan test --filter=ImagenClientTest

# End-to-end workflow
php artisan test --filter=FlambientProcessCommandTest

Test Coverage

The test suite covers:

  • โœ… EXIF extraction (numeric + pretty)
  • โœ… Image classification (6 strategies)
  • โœ… Image grouping by timestamp
  • โœ… ImageMagick script generation
  • โœ… Imagen AI client (all endpoints)
  • โœ… Upload/download with progress
  • โœ… Polling with status tracking
  • โœ… End-to-end workflow command
  • โœ… Error handling and retries
  • โœ… Success rate calculations

All external dependencies (exiftool, ImageMagick, Imagen API) are mocked using Process::fake() and Http::fake().


๐Ÿ“ Project Structure

terminal-flazsh/
โ”œโ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ Console/Commands/
โ”‚   โ”‚   โ””โ”€โ”€ FlambientProcessCommand.php       # Main interactive CLI
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ DataObjects/
โ”‚   โ”‚   โ”œโ”€โ”€ WorkflowConfig.php                # Immutable configuration DTO
โ”‚   โ”‚   โ””โ”€โ”€ ProcessingResult.php              # Step result DTO
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ Enums/
โ”‚   โ”‚   โ”œโ”€โ”€ WorkflowStatus.php                # Workflow states
โ”‚   โ”‚   โ”œโ”€โ”€ StepName.php                      # Workflow steps
โ”‚   โ”‚   โ”œโ”€โ”€ ImageType.php                     # ambient|flash|blended
โ”‚   โ”‚   โ””โ”€โ”€ ImageClassificationStrategy.php   # EXIF field selection
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ Models/
โ”‚   โ”‚   โ”œโ”€โ”€ WorkflowRun.php                   # Main workflow record
โ”‚   โ”‚   โ”œโ”€โ”€ WorkflowStep.php                  # Individual step tracking
โ”‚   โ”‚   โ””โ”€โ”€ WorkflowFile.php                  # Processed file tracking
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ Providers/
โ”‚   โ”‚   โ””โ”€โ”€ ImageProcessorServiceProvider.php # Register script generators
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ Services/
โ”‚   โ”‚   โ”œโ”€โ”€ ImageProcessor/                   # NEW: Plugin-based architecture
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Contracts/
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ ScriptGeneratorInterface.php  # Generator contract
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ Generators/
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ FlambientGenerator.php    # Flambient technique
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ HDRMergeGenerator.php     # HDR (future)
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ FocusStackGenerator.php   # Focus stacking (future)
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ DMECGenerator.php         # D-MEC technique (future)
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ ExifService.php               # EXIF extraction & classification
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ ImageMagickService.php        # Generic script executor
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ ScriptGeneratorRegistry.php   # Generator management
โ”‚   โ”‚   โ”‚
โ”‚   โ”‚   โ””โ”€โ”€ ImagenAI/
โ”‚   โ”‚       โ”œโ”€โ”€ ImagenClient.php              # Complete API client
โ”‚   โ”‚       โ”œโ”€โ”€ ImagenException.php           # Custom exception
โ”‚   โ”‚       โ””โ”€โ”€ DTOs.php                      # All Imagen DTOs & enums
โ”‚   โ”‚
โ”‚   โ””โ”€โ”€ ...
โ”‚
โ”œโ”€โ”€ config/
โ”‚   โ””โ”€โ”€ flambient.php                         # Centralized configuration
โ”‚
โ”œโ”€โ”€ database/
โ”‚   โ”œโ”€โ”€ database.sqlite                       # SQLite database
โ”‚   โ””โ”€โ”€ migrations/
โ”‚       โ”œโ”€โ”€ *_create_workflow_runs_table.php
โ”‚       โ”œโ”€โ”€ *_create_workflow_steps_table.php
โ”‚       โ””โ”€โ”€ *_create_workflow_files_table.php
โ”‚
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ Unit/
โ”‚   โ”‚   โ”œโ”€โ”€ ExifServiceTest.php              # 11 tests
โ”‚   โ”‚   โ”œโ”€โ”€ ImageMagickServiceTest.php       # 9 tests
โ”‚   โ”‚   โ””โ”€โ”€ ImagenClientTest.php             # 18 tests
โ”‚   โ”‚
โ”‚   โ””โ”€โ”€ Feature/
โ”‚       โ””โ”€โ”€ FlambientProcessCommandTest.php  # 8 tests
โ”‚
โ”œโ”€โ”€ ARCHITECTURE_REDESIGN.md                 # Original architectural analysis
โ”œโ”€โ”€ IMAGEN_INTEGRATION.md                    # Imagen AI integration guide
โ””โ”€โ”€ README.md                                # This file

๐Ÿ“š API Documentation

Plugin Architecture

Creating a New Generator

To add a new image processing technique, implement ScriptGeneratorInterface:

use App\Services\ImageProcessor\Contracts\ScriptGeneratorInterface;

class MyCustomGenerator implements ScriptGeneratorInterface
{
    public function getKey(): string
    {
        return 'my-custom'; // Unique identifier
    }

    public function getName(): string
    {
        return 'My Custom Technique';
    }

    public function getDescription(): string
    {
        return 'Description of what this technique does';
    }

    public function getClassificationStrategy(): ?string
    {
        return 'flash'; // or 'exposure_program', 'custom', null
    }

    public function getConfigurationSchema(): array
    {
        return [
            'parameter_one' => [
                'type' => 'text',
                'label' => 'Parameter One',
                'default' => '100%',
                'description' => 'What this parameter controls',
            ],
        ];
    }

    public function generateScript(
        array $images,
        array $config,
        string $outputPath
    ): string {
        // Build your ImageMagick script here
        $script = [];
        $script[] = "# My Custom Processing";
        $script[] = "( " . implode(" ", $images['ambient']) . " )";
        $script[] = "-write \"{$outputPath}\"";

        return implode("\n", $script);
    }
}

Then register it in ImageProcessorServiceProvider:

public function register(): void
{
    $this->app->singleton(ScriptGeneratorRegistry::class, function () {
        $registry = new ScriptGeneratorRegistry();

        $registry->register(new FlambientGenerator());
        $registry->register(new MyCustomGenerator()); // Add your generator

        return $registry;
    });
}

ExifService

<<<<<<< HEAD

use App\Services\ImageProcessor\ExifService;
=======
``` php
use App\Services\Flambient\ExifService;
>>>>>>> origin/main
use App\Enums\ImageClassificationStrategy;

$service = new ExifService(
    strategy: ImageClassificationStrategy::ExposureMode,
    ambientValue: 0 // Auto = Ambient
);

// Extract EXIF with both numeric and human-readable values
$metadata = $service->extractMetadata('/path/to/images');

// Get sample values for user preview
$samples = $service->getSampleExifValues('/path/to/images', count: 10);

// Group images by ambient/flash sequences
$groups = $service->groupImages($metadata);

ImageMagickService

<<<<<<< HEAD

use App\Services\ImageProcessor\ImageMagickService;
use App\Services\ImageProcessor\ScriptGeneratorRegistry;
=======
``` php
use App\Services\Flambient\ImageMagickService;
>>>>>>> origin/main

$service = new ImageMagickService(binary: 'magick');
$registry = app(ScriptGeneratorRegistry::class);

// Get a generator
$generator = $registry->get('flambient');

// Generate .mgk scripts for all groups
$scripts = $service->generateScripts(
    generator: $generator,
    groups: $groups,
    config: ['level_low' => '40%', 'level_high' => '140%'],
    imageDirectory: '/path/to/images',
    scriptsDirectory: '/path/to/scripts',
    outputDirectory: '/path/to/output'
);

// Execute all scripts
$result = $service->executeAllScripts('/path/to/scripts');
// Returns: ['success' => true, 'output' => '...', 'error' => '...']

ImagenClient

use App\Services\ImagenAI\ImagenClient;
use App\Services\ImagenAI\ImagenEditOptions;
use App\Services\ImagenAI\ImagenPhotographyType;

$client = new ImagenClient();

// Quick workflow (all-in-one)
$result = $client->quickEdit(
    filePaths: ['/path/to/flambient_01.jpg', ...],
    profileKey: '309406',
    outputDirectory: '/path/to/output',
    editOptions: new ImagenEditOptions(
        crop: true,
        windowPull: true,
        photographyType: ImagenPhotographyType::REAL_ESTATE
    )
);

// Or step-by-step:
$project = $client->createProject('My Shoot');
$uploadResult = $client->uploadImages($project->uuid, $filePaths);
$client->startEditing($project->uuid, '309406');
$editStatus = $client->pollEditStatus($project->uuid);
$client->exportProject($project->uuid);
$exportLinks = $client->getExportLinks($project->uuid);
$downloadResult = $client->downloadFiles($exportLinks, '/output');

For complete API reference, see: - IMAGEN_INTEGRATION.md - Imagen AI client guide - ARCHITECTURE_REDESIGN.md - Architectural overview


๐Ÿ”ฎ Future Improvements

1. Additional Image Processing Generators โœ… Architecture Complete

<<<<<<< HEAD Current: Plugin architecture implemented with Flambient generator Status: โœ… Ready to add new generators - infrastructure complete!

The plugin architecture is now live! Adding new processing techniques is as simple as:

  1. Create a new class implementing ScriptGeneratorInterface
  2. Register it in ImageProcessorServiceProvider
  3. No core code changes needed!

Potential Generators to Add:

  • HDRMergeGenerator - HDR tone mapping with multiple exposures
  • DMECGenerator - Dissimilar Multiple Exposure Composition (cherry-pick attributes)
  • WindowPullGenerator - Specialized window light enhancement
  • FocusStackGenerator - Focus stacking for deep depth of field
  • PerspectiveFixGenerator - Vertical/horizontal correction
  • FlashBalanceGenerator - Auto-balance flash intensity
  • CustomGenerator - User-defined .mgk script templates

Example Implementation:

See the "Creating a New Generator" section in API Documentation for a complete example.

Migration to Generic Command (Optional Phase 4):

# Future: Generic command with technique selection
php artisan process:images --technique=flambient
php artisan process:images --technique=hdr-merge
php artisan process:images --technique=dmec

# Current: Flambient-specific command (works with plugin architecture)
php artisan flambient:process --local
=======
**Current:** Single flambient blend algorithm **Future:** Multiple processing profiles to choose from

``` bash
php artisan flambient:process --profile=hdr-merge
php artisan flambient:process --profile=window-pull
php artisan flambient:process --profile=flash-balance
php artisan flambient:process --profile=custom

Potential Profiles: - flambient (default) - Standard ambient/flash blend - hdr-merge - HDR tone mapping with multiple exposures - window-pull - Specialized window light enhancement - flash-balance - Auto-balance flash intensity - perspective-fix - Vertical/horizontal correction - custom - User-defined .mgk script templates

Implementation Plan:

// app/Enums/ProcessingProfile.php
enum ProcessingProfile: string {
    case Flambient = 'flambient';
    case HdrMerge = 'hdr-merge';
    case WindowPull = 'window-pull';
    case FlashBalance = 'flash-balance';

    public function getScriptTemplate(): string;
    public function getDefaultParams(): array;
}

// app/Services/Flambient/ScriptGenerator.php
class ScriptGenerator {
    public function generate(ProcessingProfile $profile, array $params): string;
}
>>>>>>> origin/main

2. Capture One Integration

Current: Post-processing of existing JPGs Future: Live ingestion during wireless camera tethering

# Watch mode - monitor Capture One output folder
php artisan flambient:watch \
  --capture-one-session=/path/to/session \
  --auto-process \
  --interval=30s

# Output:
โ ง Watching for new images...
โœ“ Detected IMG_001.jpg (Ambient)
โœ“ Detected IMG_002.jpg (Flash)
โšก Auto-processing group 1 (2 images)
โœ“ Blended: flambient_01.jpg

Features: - File watcher - Monitor Capture One Capture folder - Real-time classification - Instant EXIF analysis on import - Auto-grouping - Smart pairing based on shoot sequence - Progressive processing - Process groups as they complete - Live preview - Optional web server for client viewing - Pause/resume - Control processing during shoot - Backup strategy - Copy originals before processing

Implementation Plan:

// app/Console/Commands/FlambientWatchCommand.php
class FlambientWatchCommand extends Command {
    protected $signature = 'flambient:watch
        {--capture-one-session= : Capture One session folder}
        {--auto-process : Auto-process complete groups}
        {--interval=30s : Check interval}';

    public function handle() {
        $watcher = new CaptureOneWatcher(
            sessionPath: $this->option('capture-one-session'),
            autoProcess: $this->option('auto-process')
        );

        $watcher->watch();
    }
}

// app/Services/CaptureOne/CaptureOneWatcher.php
class CaptureOneWatcher {
    public function watch(): void {
        while (true) {
            $newFiles = $this->detectNewFiles();
            $completeGroups = $this->findCompleteGroups($newFiles);

            if ($this->autoProcess && !empty($completeGroups)) {
                $this->processGroups($completeGroups);
            }

            sleep($this->interval);
        }
    }

    private function detectNewFiles(): array;
    private function findCompleteGroups(array $files): array;
    private function processGroups(array $groups): void;
}

// app/Services/CaptureOne/SessionParser.php
class SessionParser {
    public function parseSession(string $path): CaptureOneSession;
    public function getCaptureFolder(): string;
    public function getOutputSettings(): array;
}

Capture One Session Structure:

CaptureOneSession/
โ”œโ”€โ”€ Capture/               # New images appear here
โ”‚   โ”œโ”€โ”€ IMG_001.jpg
โ”‚   โ”œโ”€โ”€ IMG_002.jpg
โ”‚   โ””โ”€โ”€ ...
โ”œโ”€โ”€ Output/                # Processed images (optional)
โ”œโ”€โ”€ Cache/                 # Capture One metadata
โ””โ”€โ”€ Settings/              # Session settings

Benefits: - โœ… Faster turnaround - Process during shoot, not after - โœ… Immediate feedback - See blended results in real-time - โœ… Client preview - Show progress to clients on-site - โœ… Reduce post-processing - Images ready when shoot ends - โœ… Better organization - Auto-naming and grouping


๐Ÿค Contributing

Development Workflow

# 1. Create feature branch
git checkout -b feature/my-feature

# 2. Make changes and add tests
# Edit app/Services/...
# Edit tests/Unit/...

# 3. Run tests
php artisan test

# 4. Format code
./vendor/bin/pint

# 5. Commit and push
git add .
git commit -m "feat: add my feature"
git push origin feature/my-feature

# 6. Create pull request

Code Standards

  • PSR-12 coding style (enforced by Laravel Pint)
  • Type safety - Use type hints, return types, readonly classes
  • Test coverage - Add tests for all new features
  • Documentation - Update README and inline docs
  • Laravel conventions - Follow Laravel best practices

Testing Requirements

All new features must include: - โœ… Unit tests for services/classes - โœ… Feature tests for commands - โœ… Mock external dependencies (Http::fake(), Process::fake()) - โœ… Assertions for success and failure cases


๐Ÿ“ License

MIT License - see LICENSE for details


๐Ÿ™ Acknowledgments


๐Ÿ“ž Support

For issues, questions, or feature requests:


๐Ÿ”„ Recent Updates

v2.0 - Plugin Architecture (December 2024)

Major Refactoring: Transformed from tightly-coupled Flambient-only processing to a modular, plugin-based architecture.

What Changed:

โœ… New Plugin System

  • ScriptGeneratorInterface - Contract for all processing techniques
  • ScriptGeneratorRegistry - Central registry for generators
  • FlambientGenerator - Extracted flambient logic into a plugin
  • ImageProcessorServiceProvider - Service provider for generator registration

โœ… Refactored Services

  • Moved from App\Services\Flambient\* to App\Services\ImageProcessor\*
  • ImageMagickService now generic (accepts any generator)
  • ExifService moved to ImageProcessor namespace
  • FlambientProcessCommand updated to use registry pattern

โœ… Backwards Compatible

  • Existing php artisan flambient:process command still works
  • Same workflow and CLI experience
  • No breaking changes for users

Benefits:

  • ๐ŸŽฏ Add new techniques without modifying core code
  • ๐Ÿงฉ Each technique is self-contained and testable
  • ๐Ÿš€ Easy to implement D-MEC, HDR, Focus Stacking, etc.
  • ๐Ÿ“ฆ Future-proof architecture for growth

See: docs/refactoring-plan.md for complete implementation details.


=======


origin/main

Built with โค๏ธ using Laravel 11, PHP 8.2, and modern development practices

Production-ready flambient photography automation for professional photographers

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •