# Dynamic Image Generation API ## Overview This system generates product images on-the-fly with configurable colors, hardware positions, and other options. It uses server-side image processing with PIL/Pillow to composite and recolor product images dynamically. ## Setup ### 1. Install Dependencies ```bash pip install -r requirements.txt ``` This will install: - Flask (web framework) - Pillow (image processing) - Other dependencies ### 2. Prepare Product Images For the COBRAI demo product: 1. Save the white storm door image as `app/images/cobrai.jpg` 2. The configuration is already in `app/data/image_configs.json` ### 3. Create Cache Directory The cache directory will be created automatically when the app runs: ``` app/cache/product_images/ ``` ## API Endpoints ### 1. Generate Product Image **Endpoint:** `GET /api/product-image/` **Parameters:** - `color` - Color name (default: 'white') - Options: white, black, bronze, sandstone - `hinge` - Hinge side (default: 'right') - Options: left, right - `material` - Material type (default: 'aluminum') - For future use - `format` - Return format (default: 'image') - Options: image, json - `cache` - Use caching (default: 'true') - Options: true, false **Examples:** Return image directly (for use in `` tags): ``` GET http://localhost:8080/api/product-image/cobrai?color=black&hinge=right GET http://localhost:8080/api/product-image/cobrai?color=bronze&hinge=left ``` Return JSON with base64 image: ``` GET http://localhost:8080/api/product-image/cobrai?color=white&hinge=right&format=json ``` **Response (format=image):** Raw PNG image file **Response (format=json):** ```json { "status": "success", "productCode": "cobrai", "color": "black", "hinge": "left", "material": "aluminum", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...", "format": "base64" } ``` ### 2. Get Product Configuration **Endpoint:** `GET /api/product-config/` Returns the complete configuration for a product including available colors, colorable regions, hardware positions, etc. **Example:** ``` GET http://localhost:8080/api/product-config/cobrai ``` **Response:** ```json { "status": "success", "productCode": "cobrai", "config": { "sourceImage": "images/cobrai.jpg", "productCode": "COBRAI", "availableColors": { "white": {"rgb": [255, 255, 255], "name": "White"}, "black": {"rgb": [30, 30, 30], "name": "Black"}, "bronze": {"rgb": [110, 80, 50], "name": "Bronze"} }, "colorableRegions": [...], "hardwarePositions": {...}, "metadata": {...} } } ``` ### 3. Clear Image Cache **Endpoint:** `POST /api/clear-image-cache` Clears cached images. Useful when updating source images or configurations. **Request Body (Optional):** ```json { "productCode": "cobrai" } ``` **Response:** ```json { "status": "success", "message": "Cache cleared for cobrai" } ``` ## Configuration File Format Location: `app/data/image_configs.json` ### Structure: ```json { "product_code": { "sourceImage": "path/to/source.jpg", "productCode": "PRODUCT_CODE", "description": "Product description", "availableColors": { "color_name": { "rgb": [R, G, B], "name": "Display Name" } }, "colorableRegions": [ { "name": "region_identifier", "topLeft": [x, y], "bottomRight": [x, y], "description": "What this region is" } ], "hardwarePositions": { "handle_right": { "x": 580, "y": 730, "image": "path/to/hardware.png", "flip": false } }, "glassRegions": [ { "name": "glass_area", "topLeft": [x, y], "bottomRight": [x, y], "description": "Glass/screen area" } ], "metadata": { "imageWidth": 720, "imageHeight": 1450, "defaultColor": "white", "defaultHinge": "right" }, "cache": { "enabled": true, "directory": "cache/product_images", "maxAge": 86400 } } } ``` ### Key Concepts: #### Colorable Regions Define rectangular areas to be recolored. The algorithm: 1. Preserves brightness/luminosity 2. Applies target color 3. Maintains shadows and highlights Coordinates are `[x, y]` where: - `topLeft`: Upper-left corner - `bottomRight`: Lower-right corner #### Hardware Positions Define where handles, locks, etc. should be placed: - `x, y`: Position to place hardware image - `image`: Path to hardware PNG (with transparency) - `flip`: Whether to flip horizontally (for left hinge) #### Glass Regions Define areas that should remain unchanged (glass, screens). Currently for documentation purposes; future feature. ## Frontend Integration ### Option 1: Direct Image URL ```html Storm Door ``` ### Option 2: JavaScript with Base64 ```javascript async function loadProductImage(productCode, color, hinge) { const response = await fetch( `/api/product-image/${productCode}?color=${color}&hinge=${hinge}&format=json` ); const data = await response.json(); if (data.status === 'success') { document.getElementById('product-img').src = data.image; } } // Usage loadProductImage('cobrai', 'black', 'left'); ``` ### Option 3: Dynamic URL Switching ```javascript function updateProductImage(color, hinge) { const img = document.getElementById('product-img'); img.src = `/api/product-image/cobrai?color=${color}&hinge=${hinge}`; } // On color change document.getElementById('color-select').addEventListener('change', (e) => { const color = e.target.value; const hinge = document.querySelector('[name="hinge"]:checked').value; updateProductImage(color, hinge); }); ``` ## How Image Processing Works ### Color Application 1. **Load base image** - White or neutral colored product photo 2. **Define regions** - Specify rectangles for frame, panels, etc. 3. **Calculate brightness** - For each pixel, determine relative brightness 4. **Apply target color** - Colorize while preserving brightness variations 5. **Result** - Natural-looking colored product with preserved shadows/highlights ### Brightness Preservation ```python # For each pixel in region: original_brightness = (r + g + b) / 3 brightness_factor = original_brightness / 255.0 new_r = target_color_r * brightness_factor new_g = target_color_g * brightness_factor new_b = target_color_b * brightness_factor ``` This maintains shadows (darker pixels stay darker) and highlights (lighter pixels stay lighter). ### Hardware Application 1. Load hardware PNG with transparency 2. Optionally flip horizontally for left hinge 3. Composite onto product image at specified position ### Caching - Generated images are cached using MD5 hash of parameters - Cache key: `{product_code}_{color}_{hinge}_{material}` - Stored as PNG in `cache/product_images/` - Subsequent requests return cached version instantly ## Testing the Endpoints ### Using cURL Test basic image generation: ```bash curl http://localhost:8080/api/product-image/cobrai?color=black > test_black.png ``` Test with different configurations: ```bash curl http://localhost:8080/api/product-image/cobrai?color=bronze&hinge=left > test_bronze_left.png ``` Get JSON response: ```bash curl http://localhost:8080/api/product-image/cobrai?color=white&format=json ``` Get configuration: ```bash curl http://localhost:8080/api/product-config/cobrai ``` Clear cache: ```bash curl -X POST http://localhost:8080/api/clear-image-cache -H "Content-Type: application/json" -d '{"productCode":"cobrai"}' ``` ### Using Browser Simply visit: ``` http://localhost:8080/api/product-image/cobrai?color=black&hinge=left ``` ### Using JavaScript Fetch ```javascript // Get image as blob fetch('/api/product-image/cobrai?color=bronze&hinge=left') .then(response => response.blob()) .then(blob => { const url = URL.createObjectURL(blob); document.getElementById('img').src = url; }); // Get as JSON fetch('/api/product-image/cobrai?color=black&format=json') .then(response => response.json()) .then(data => { document.getElementById('img').src = data.image; }); ``` ## Adding New Products ### Step 1: Prepare Source Image - Photograph product in white or neutral color - Clean background - Good lighting - High resolution (will be resized) - Save as JPG in `app/images/` ### Step 2: Determine Regions Open image in image editor and note pixel coordinates: - Frame edges (left, right, top, bottom) - Panels/kickplates - Rails/dividers Record as `topLeft [x, y]` and `bottomRight [x, y]` ### Step 3: Add to Configuration Add entry to `image_configs.json`: ```json { "your_product": { "sourceImage": "images/your_product.jpg", "productCode": "YOUR-PRODUCT", "availableColors": { "white": {"rgb": [255, 255, 255], "name": "White"}, "black": {"rgb": [30, 30, 30], "name": "Black"} }, "colorableRegions": [ { "name": "frame", "topLeft": [0, 0], "bottomRight": [100, 1450] } ], "metadata": { "imageWidth": 720, "imageHeight": 1450, "defaultColor": "white" } } } ``` ### Step 4: Test ```bash curl http://localhost:8080/api/product-image/your_product?color=black > test.png ``` ## Performance Notes ### First Request - ~100-500ms (depending on image size and region count) - Includes image loading, processing, and caching ### Cached Requests - ~10-50ms - Just file system read and serve ### Memory Usage - Base image kept in memory during processing - Minimal memory footprint when served from cache ### Optimization Tips 1. **Use caching** - Enabled by default 2. **Smaller source images** - 1000x1600px is usually sufficient 3. **Fewer regions** - Combine adjacent areas when possible 4. **CDN** - Serve cached images from CDN in production ## Production Deployment ### Recommended Setup 1. **Pre-generate** common combinations on deploy 2. **CDN** to serve cached images 3. **Redis cache** instead of filesystem (optional) 4. **Image optimization** - Use WebP format where supported 5. **Rate limiting** on generation endpoint ### Pre-generation Script ```python from image_generator import ProductImageGenerator generator = ProductImageGenerator() products = ['cobrai', 'product2'] colors = ['white', 'black', 'bronze'] hinges = ['left', 'right'] for product in products: for color in colors: for hinge in hinges: img = generator.generate_product_image(product, color, hinge) print(f'Generated: {product} {color} {hinge}') ``` ## Troubleshooting ### "PIL/Pillow not installed" ```bash pip install Pillow ``` ### "Product not found in configuration" Check that product code in URL matches key in `image_configs.json` ### "Could not load base image" Verify `sourceImage` path in config and that file exists ### Colors look wrong Adjust RGB values in `availableColors` section ### Regions not coloring 1. Verify coordinates are within image bounds 2. Check that region isn't transparent 3. Ensure topLeft is actually top-left of bottomRight ### Cache not working Check that `cache/product_images/` directory is writable ## Future Enhancements ### Planned Features - [ ] Material textures (wood grain, brushed metal) - [ ] Glass tinting/color - [ ] Shadow/lighting adjustments based on color - [ ] Multiple hardware styles - [ ] Size variations - [ ] Decorative glass patterns - [ ] WebP format support - [ ] Batch generation CLI tool - [ ] Admin UI for region configuration - [ ] Automatic region detection (AI/ML) ### Integration Ideas - Direct integration with product configurator - Real-time preview as user selects options - Download high-res configured images - Email configured image to customer - Social media sharing with custom image