mojahed / image
PHP wrapper for the MdsImage binary — resize, crop, effects, watermark, batch processing, and image info. Requires no GD, Imagick, or any PHP image extension.
Requires
- php: >=7.4
This package is auto-updated.
Last update: 2026-06-02 10:52:14 UTC
README
PHP wrapper for the MdsImage.
Process images from PHP with zero extensions — no GD, no Imagick, no libvips.
All operations are handled by the MdsImage standalone binary. Drop the binary on any server and start processing.
Requirements
- PHP >= 7.4
- MdsImage binary
Install
composer require mojahed/image
Setup
Download the MdsImage binary for your platform and place it somewhere accessible:
# Linux chmod +x MdsImage-linux-amd64 sudo mv MdsImage-linux-amd64 /usr/local/bin/MdsImage # macOS chmod +x MdsImage-mac-arm64 sudo mv MdsImage-mac-arm64 /usr/local/bin/MdsImage
Usage
Basic pattern
use Mojahed\Image; // Set the binary path once, reuse $image everywhere $image = Image::setBin('/usr/local/bin/MdsImage'); // input() opens a file and returns a chainable pipeline // output() executes and saves the result $image->input('photo.jpg')->resize(800, 600)->output('out.jpg');
Resize
// Exact resize — ignores aspect ratio $image->input('photo.jpg')->resize(800, 600)->output('out.jpg'); // Fit within bounds — preserves aspect ratio, no crop $image->input('photo.jpg')->fit(1200, 800)->output('out.jpg'); // Fit but never enlarge a small image $image->input('photo.jpg')->noEnlarge()->fit(1200, 800)->output('out.jpg'); // Fill exact dimensions — crops from center $image->input('photo.jpg')->fill(400, 400)->output('out.jpg'); // Smart thumbnail (same as fill, clearer intent) $image->input('photo.jpg')->thumb(300, 300)->output('thumb.jpg');
Crop & Canvas
// Crop a region: 500×400 starting at pixel (50, 30) $image->input('photo.jpg')->crop(500, 400, 50, 30)->output('out.jpg'); // Pad canvas to exact size with a background colour // (fit first to preserve aspect ratio, then pad to square) $image->input('photo.jpg') ->fit(800, 800) ->pad(800, 800, '#F5F5F5') ->output('square.jpg'); // Add a border on all sides $image->input('photo.jpg')->border(20, '#000000')->output('out.jpg'); // Trim uniform background from edges (default threshold = 10) $image->input('photo.jpg')->trim()->output('trimmed.jpg'); $image->input('photo.jpg')->trim(25)->output('trimmed.jpg'); // more aggressive
Transforms
$image->input('photo.jpg')->flip('h')->output('out.jpg'); // horizontal mirror $image->input('photo.jpg')->flip('v')->output('out.jpg'); // upside-down $image->input('photo.jpg')->rotate(90)->output('out.jpg'); // 90 | 180 | 270 $image->input('photo.jpg')->autoOrient()->output('out.jpg'); // fix EXIF orientation
Color & Adjustments
$image->input('photo.jpg')->brightness(30)->output('out.jpg'); // -100 to 100 $image->input('photo.jpg')->contrast(20)->output('out.jpg'); // -100 to 100 $image->input('photo.jpg')->saturation(40)->output('out.jpg'); // -100 to 100 $image->input('photo.jpg')->gamma(1.5)->output('out.jpg'); // positive float $image->input('photo.jpg')->hue(120)->output('out.jpg'); // -360 to 360 $image->input('photo.jpg')->grayscale()->output('out.jpg'); $image->input('photo.jpg')->sepia()->output('out.jpg'); $image->input('photo.jpg')->invert()->output('out.jpg'); $image->input('photo.jpg')->tint('#FF6600')->output('out.jpg'); $image->input('photo.jpg')->stripExif()->output('out.jpg');
Filters
$image->input('photo.jpg')->blur(2.0)->output('out.jpg'); // Gaussian blur radius $image->input('photo.jpg')->sharpen(1.5)->output('out.jpg'); // unsharp mask amount
Watermark
// Positions: topleft | topright | bottomleft | bottomright | center $image->input('photo.jpg')->watermark('logo.png')->output('out.jpg'); $image->input('photo.jpg')->watermark('logo.png', 'topleft', 60)->output('out.jpg');
Output Options
// JPEG quality (default 85) $image->input('photo.jpg')->thumb(300, 300)->quality(95)->output('hq.jpg'); // Force output format $image->input('photo.jpg')->resize(400, 400)->format('png')->output('out.png'); // Suppress binary progress output $image->input('photo.jpg')->thumb(300, 300)->quiet()->output('out.jpg');
Output Modes
// Save to file $image->input('photo.jpg')->thumb(300, 300)->output('thumb.jpg'); // Write to stdout (for streaming HTTP responses) $image->input('photo.jpg')->thumb(300, 300)->output('-'); // Raw base64 string $b64 = $image->input('photo.jpg')->thumb(300, 300)->toBase64(); // data: URI — ready for <img src="">, CSS, or JSON API payloads $uri = $image->input('photo.jpg')->thumb(300, 300)->toDataUri(); echo '<img src="' . $uri . '">';
Image Info
// Get all metadata as an associative array $info = $image->input('photo.jpg')->info(); echo $info['width']; // 1024 echo $info['height']; // 768 echo $info['format']; // JPEG echo $info['mime_type']; // image/jpeg echo $info['file_size_human']; // 1.2 MB echo $info['megapixels']; // 0.79 echo $info['aspect_ratio']; // 4:3 echo $info['color_mode']; // RGB echo $info['has_alpha']; // false echo $info['average_color']['hex']; // C0BCAB foreach ($info['dominant_colors'] as $c) { echo "#{$c['hex']} rgb({$c['r']},{$c['g']},{$c['b']})\n"; } // JPEG-specific (present only for JPEG files) if (!empty($info['jpeg'])) { echo $info['jpeg']['exif_make']; // Apple echo $info['jpeg']['exif_model']; // iPhone 15 Pro echo $info['jpeg']['exif_datetime']; // 2024:03:15 14:22:00 echo $info['jpeg']['exif_iso']; // 100 echo $info['jpeg']['exif_fnumber']; // 1.8 echo $info['jpeg']['exif_exposure_time']; // 1/1000 s echo $info['jpeg']['exif_focal_length_mm']; // 24 if ($info['jpeg']['exif_gps_present']) { echo $info['jpeg']['exif_gps_lat'] . ', ' . $info['jpeg']['exif_gps_lon']; } } // PNG-specific (present only for PNG files) if (!empty($info['png'])) { echo $info['png']['color_type']; // RGBA echo $info['png']['interlaced']; // false // text_chunks: author, description, copyright, etc. } // Get info as a JSON string $json = $image->input('photo.jpg')->infoJson();
Chained Pipelines
Operations are applied in the exact order you call them:
// Full production pipeline $image->input('raw-upload.jpg') ->autoOrient() ->noEnlarge()->fit(1920, 1080) ->brightness(5) ->contrast(10) ->saturation(15) ->sharpen(0.8) ->watermark('brand.png', 'bottomright', 75) ->stripExif() ->quality(92) ->output('final.jpg'); // Thumbnail with sepia effect $image->input('photo.jpg') ->thumb(300, 300) ->sepia() ->output('thumb-sepia.jpg'); // E-commerce product image (exact square, no crop, no distortion) $image->input('product.jpg') ->trim() ->noEnlarge()->fit(800, 800) ->pad(800, 800, '#FFFFFF') ->sharpen(0.5) ->quality(90) ->output('product-800.jpg');
Batch Processing
Process multiple files concurrently with worker parallelism:
// All JPEGs in a folder → thumbnails $results = $image->batch() ->glob('photos/*.jpg') ->outDir('thumbs/') ->thumb(300, 300) ->quality(85) ->run(); // Process all images in a directory with a custom filename pattern $results = $image->batch() ->dir('uploads/') ->outDir('processed/') ->pattern('{name}-800w{ext}') // {name}, {ext}, {base} tokens ->noEnlarge()->fit(800, 600) ->sharpen(1.0) ->quality(88) ->workers(4) ->run(); // Convert folder to PNG $results = $image->batch() ->glob('assets/*.jpg') ->outDir('assets/png/') ->format('png') ->run(); // Check results foreach ($results as $r) { if ($r['error'] !== null) { echo "✗ {$r['input']}: {$r['error']}\n"; } else { echo "✓ {$r['input']} → {$r['output']}\n"; } }
Batch methods
| Method | Description |
|---|---|
->glob('photos/*.jpg') |
Input files matching a glob pattern |
->dir('uploads/') |
All images in a directory |
->outDir('thumbs/') |
Output directory (required) |
->pattern('{name}-sm{ext}') |
Output filename pattern |
->format('png') |
Force output format |
->quality(85) |
JPEG quality |
->workers(4) |
Parallel workers (default: CPU count, max 8) |
->quiet() |
Suppress progress output |
All image operations (resize, thumb, sepia, watermark, etc.) are available on BatchBuilder too.
Error Handling
try { $image->input('missing.jpg')->resize(800, 600)->output('out.jpg'); } catch (\RuntimeException $e) { echo $e->getMessage(); // "MdsImage failed (exit 1): input file not found: missing.jpg" }
Exit code 1 = processing or IO error.
Exit code 2 = usage error (won't occur from the PHP wrapper under normal use).
Supported Formats
| Direction | Formats |
|---|---|
| Input | JPEG, PNG, GIF, BMP, TIFF, WebP |
| Output | JPEG, PNG |
License
MIT © Md Mojahedul Islam