phpmlkit / opal
High-performance image processing for PHP, backed by libvips via FFI.
1.0.0
2026-05-23 11:43 UTC
Requires
- php: ^8.2
- ext-ffi: *
- codewithkyrian/platform-package-installer: ^2.0
- jcupitt/vips: ^2.0
- phpmlkit/ndarray: ^2.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^10.0
- symfony/var-dumper: ^6.4.11|^7.1.5|^8.0
README
Opal
High-performance image processing for PHP, backed by libvips
Features
- Lazy Evaluation: All image operations are lazy and only executed when needed (saving, encoding, exporting)
- Immutable Operations: Every transformation returns a new image instance
- NDArray Integration: Seamless interoperability with
phpmlkit/ndarrayfor ML workflows - Comprehensive API: Support for all common image operations
- Type-Safe: Full PHP 8.2+ type safety with enums and value objects
- Memory Efficient: Zero-copy operations where possible
Performance
Load → resize → rotate 45° → sharpen (full pipeline):
| Image | GD | Imagick | Opal |
|---|---|---|---|
| 640×480 | 8.48 ms | 128.55 ms | 1.56 ms |
| 1920×1080 | 49.67 ms | 721.97 ms | 5.36 ms |
| 4000×2670 | 152.74 ms | 2,039.15 ms | 15.47 ms |
| 6000×4000 | 299.43 ms | 3,538.47 ms | 33.17 ms |
Apple M1, macOS. Run: php benchmarks/opal-vs-gd-vs-imagick.php
Installation
composer require phpmlkit/opal
Quick Start
use PhpMlKit\Opal\Image; use PhpMlKit\Opal\Color; use PhpMlKit\Opal\ColorSpace; use PhpMlKit\Opal\Kernel; // Load an image $image = Image::fromFile('path/to/image.jpg'); // Resize with high-quality kernel $resized = $image->resize(800, 600, Kernel::Lanczos3); // Convert to grayscale $grayscale = $resized->toGrayscale(); // Save the result $grayscale->toFile('output.jpg');
Core Concepts
Lazy Evaluation
All image operations are lazy - they build a pipeline that's only executed when you call a terminal operation:
// This doesn't process anything yet $pipeline = $image ->resize(800, 600) ->toGrayscale() ->brightness(1.2); // This triggers the actual processing $pipeline->toFile('output.jpg');
Immutable Operations
Every transformation returns a new image instance:
$original = Image::fromFile('input.jpg'); $modified = $original->resize(800, 600); // $original is unchanged $original->toFile('original.jpg'); $modified->toFile('resized.jpg');
Usage Examples
Basic Image Operations
use PhpMlKit\Opal\Image; use PhpMlKit\Opal\Color; use PhpMlKit\Opal\ColorSpace; use PhpMlKit\Opal\Kernel; use PhpMlKit\Opal\FlipDirection; // Load image $image = Image::fromFile('photo.jpg'); // Resize $resized = $image->resize(1920, 1080); $scaled = $image->scale(0.5); // Thumbnail via shrink-on-load — faster than load+resize $thumbnail = Image::thumbnail('photo.jpg', 300); // Crop $cropped = $image->crop(100, 100, 800, 600); $centerCropped = $image->centerCrop(800, 600); // Flip and rotate $flipped = $image->flip(FlipDirection::Horizontal); $rotated = $image->rotate(45); $rotated90 = $image->rot90(); // Save $resized->toFile('resized.jpg');
Color Space Conversion
use PhpMlKit\Opal\Image; use PhpMlKit\Opal\ColorSpace; $image = Image::fromFile('photo.jpg'); // Convert to grayscale $grayscale = $image->toGrayscale(); // Convert to RGB $rgb = $grayscale->toRGB(); // Add alpha channel $rgba = $image->toRGBA(); // Remove alpha $rgb = $rgba->removeAlpha();
Color Adjustments
use PhpMlKit\Opal\Image; $image = Image::fromFile('photo.jpg'); // Brightness (1.0 = unchanged, >1 = brighter, <1 = darker) $brighter = $image->brightness(1.2); // Contrast (1.0 = unchanged, >1 = more contrast, <1 = less contrast) $highContrast = $image->contrast(1.5); // Saturation (0 = grayscale, 1.0 = unchanged, >1 = more saturated) $vivid = $image->saturation(1.3); // Hue rotation $hueShifted = $image->hue(90); // Gamma correction $gammaCorrected = $image->gamma(2.2);
Filters and Effects
use PhpMlKit\Opal\Image; $image = Image::fromFile('photo.jpg'); // Sharpen $sharpened = $image->sharpen(sigma: 2.0); // Blur $blurred = $image->blur(sigma: 3.0); // Median blur (good for noise reduction) $denoised = $image->medianBlur(5); // Invert colors $inverted = $image->invert();
Drawing and Compositing
use PhpMlKit\Opal\Image; use PhpMlKit\Opal\Color; use PhpMlKit\Opal\BoundingBox; $image = Image::fromFile('photo.jpg'); // Draw rectangle $withRect = $image->drawRect( 100, 100, 200, 150, Color::red() ); // Draw circle $withCircle = $image->drawCircle( 400, 300, 50, Color::blue(), fill: true ); // Draw text $withText = $image->drawText( 'Hello World', 100, 100, color: Color::white() ); // Composite images $overlay = Image::fromFile('overlay.png'); $composited = $image->composite($overlay, 50, 50);
NDArray Interoperability
use PhpMlKit\Opal\Image; use PhpMlKit\NDArray\NDArray; use PhpMlKit\Opal\ColorSpace; use PhpMlKit\Opal\ChannelFormat; // Convert image to NDArray $image = Image::fromFile('photo.jpg'); $array = $image->toArray(ChannelFormat::HWC); // [H, W, C] // Convert NDArray to image $array = NDArray::zeros([224, 224, 3]); $image = Image::fromArray($array, ColorSpace::RGB, ChannelFormat::HWC);
Advanced Options
use PhpMlKit\Opal\Image; use PhpMlKit\Opal\LoadOptions; use PhpMlKit\Opal\SaveOptions; // Load with options $image = Image::fromFile('photo.jpg', LoadOptions::default() ->withAutoRotate(true) ->withShrink(2) ); // Save with options $image->toFile('output.jpg', SaveOptions::jpeg( quality: 90, strip: true, progressive: true )); // PNG with compression $image->toFile('output.png', SaveOptions::png( compression: 9, interlace: true )); // WebP with lossless $image->toFile('output.webp', SaveOptions::webp( quality: 80, lossless: false ));
API Reference
Value Objects
Color
Immutable RGBA color representation.
$red = Color::rgb(255, 0, 0); $blue = Color::fromHex('#0000ff'); $white = Color::white(); $transparent = Color::transparent();
ImageSize
Immutable 2D size descriptor.
$size = new ImageSize(1920, 1080); $aspectRatio = $size->aspectRatio(); $pixels = $size->pixels();
BoundingBox
Immutable axis-aligned bounding box.
$box = new BoundingBox(10, 10, 100, 50); $iou = $box->iou($otherBox); $clamped = $box->clamp($imageWidth, $imageHeight);
Enums
ColorSpace: RGB, RGBA, BGR, BGRA, Grayscale, Lab, HSV, CMYKImageFormat: JPEG, PNG, WebP, TIFF, GIF, BMP, AVIF, HEIFKernel: Nearest, Linear, Cubic, Mitchell, Lanczos2, Lanczos3, Mks211, Mks213FlipDirection: Horizontal, Vertical, BothBandFormat: UChar, UInt16, Float, DoubleChannelFormat: HWC, CHW
Options Classes
LoadOptions
Control image loading behavior.
$options = LoadOptions::default() ->withPage(0) ->withAutoRotate(true) ->withShrink(2);
SaveOptions
Control image saving behavior.
$options = SaveOptions::jpeg(quality: 90, strip: true); $options = SaveOptions::png(compression: 9); $options = SaveOptions::webp(quality: 80, lossless: false);
Performance Tips
- Use lazy evaluation: Chain operations together before saving
- Use shrink-on-load: Load with
Image::thumbnail()orLoadOptions::withShrink()for large images - Choose appropriate interpolation: Use
Lanczosfor quality,Bilinearfor speed - Dispose explicitly: Call
dispose()when done with large images
Documentation
Development
# Install dependencies composer install # Run tests composer test # Run static analysis composer lint # Format code composer cs:fix
Requirements
- PHP 8.2 or higher
- ext-ffi extension
License
MIT
Credits
- libvips — image processing engine
- jcupitt/vips — PHP FFI bindings for libvips
- phpmlkit/ndarray — numerical computing for ML workflows
