首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP Imagick 擴展完整指南:安裝、核心功能與實戰類庫封裝

     編輯:PHP 綜合知識

什麼是 Imagick?

Imagick 是 PHP 呼叫 ImageMagick 函式庫的擴展介面。ImageMagick 是功能強大的圖片處理套件,支援 185+ 種格式(JPEG、PNG、GIF、TIFF、PDF、WebP、HEIC 等),提供縮放、裁切、旋轉、銳化、濾鏡、水印、動畫處理等專業級功能。

環境安裝

Windows (XAMPP/WAMP)

  1. 下載 ImageMagick 安裝程式:
    https://imagemagick.org/archive/binaries/ImageMagick-7.x.x-Q16-HDRI-x64-dll.exe
  2. 安裝時勾選「Add to PATH」與「Install legacy utilities」
  3. 下載對應 PHP 版本的 php_imagick.dll(區分 TS/NTS、VC15/VS16)
    https://windows.php.net/downloads/pecl/releases/imagick/
  4. 放入 php/ext/,php.ini 加入 extension=imagick
  5. 重啟 Apache,phpinfo() 確認 imagick 區塊出現

Linux (Ubuntu/Debian/CentOS)

# Ubuntu/Debian
sudo apt update && sudo apt install imagemagick libmagickwand-dev
sudo pecl install imagick
echo "extension=imagick.so" | sudo tee /etc/php/8.x/mods-available/imagick.ini
sudo phpenmod imagick
sudo systemctl restart apache2

# CentOS/RHEL
sudo yum install ImageMagick ImageMagick-devel
sudo pecl install imagick
# 同樣啟用擴展並重啟服務

核心 API 快速上手

基本操作:建立縮圖並輸出

<?php
header('Content-Type: image/jpeg');
$im = new Imagick('image.jpg');
$im->thumbnailImage(100, 0); // 寬 100px,高度自動等比
echo $im;
?>

批次處理目錄下所有圖片

<?php
$images = new Imagick(glob('images/*.JPG'));
foreach ($images as $image) {
    $image->thumbnailImage(1024, 0); // 長邊 1024px
}
$images->writeImages(); // 覆寫原檔
?>

GIF 動畫縮放(保持幀延遲)

<?php
$im = new Imagick("animation.gif");
foreach ($im as $frame) {
    $frame->thumbnailImage(50, 50);
    $frame->setImagePage(50, 50, 0, 0); // 修正虛擬畫布
}
$im->writeImages("animation_small.gif", true); // true = 多幀寫入
?>

進階功能:邊框、模糊、色彩調整、特效

<?php
$im = new Imagick('photo.jpg');

// 邊框
$color = new ImagickPixel('rgb(220,220,220)');
$im->borderImage($color, 10, 10);

// 模糊(半徑 5, sigma 3)
$im->blurImage(5, 3);

// 高斯模糊
$im->gaussianBlurImage(3, 2);

// 色階調整
$im->levelImage(0.2, 1.0, 0.8); // 黑點、伽瑪、白點

// 亮度/飽和度/色調
$im->modulateImage(120, 110, 100); // 百分比

// 特效:素描
$im->charcoalImage(3, 1);

// 特效:油畫
$im->oilPaintImage(4);

// 翻轉
$im->flipImage();   // 垂直
$im->flopImage();   // 水平

// 輸出
header('Content-Type: image/jpeg');
echo $im;
?>

實戰封裝:支援 GIF 動畫的通用圖片處理類

以下類庫封裝了裁切、縮放、水印、文字、格式轉換等常用操作,並自動處理 GIF 多幀問題:

<?php
/**
 * 通用圖片處理類 - 基於 Imagick
 * 支援:裁切、縮放(多種模式)、水印圖/文字、GIF 動畫保持
 */
class ImageProcessor {
    private $imagick = null;
    private $format = null;
    private $isAnimated = false;
    private $frameCount = 0;

    public function __construct() {}

    public function __destruct() {
        if ($this->imagick !== null) $this->imagick->destroy();
    }

    // 載入圖片
    public function load(string $path): bool {
        try {
            $this->imagick = new Imagick($path);
            $this->format = strtolower($this->imagick->getImageFormat());
            $this->isAnimated = ($this->format === 'gif' && $this->imagick->getNumberImages() > 1);
            $this->frameCount = $this->imagick->getNumberImages();
            return true;
        } catch (ImagickException $e) {
            error_log("Image load failed: " . $e->getMessage());
            return false;
        }
    }

    // 裁切
    public function crop(int $x, int $y, int $width, int $height): self {
        if ($this->isAnimated) {
            $this->applyToAllFrames(function($frame) use ($x, $y, $width, $height) {
                $frame->cropImage($width, $height, $x, $y);
                $frame->setImagePage($width, $height, 0, 0);
            });
        } else {
            $this->imagick->cropImage($width, $height, $x, $y);
            $this->imagick->setImagePage($width, $height, 0, 0);
        }
        return $this;
    }

    // 縮放 - 多種模式
    public function resize(int $width, int $height, string $mode = 'fit', array $bgColor = [255,255,255,0]): self {
        $modes = [
            'fit'      => fn($w, $h) => $this->imagick->thumbnailImage($w, $h, true),   // 等比縮放至框內
            'fill'     => fn($w, $h) => $this->imagick->thumbnailImage($w, $h, false),  // 強制填滿(可能變形)
            'crop'     => fn($w, $h) => $this->smartCrop($w, $h),                       // 智能裁切填滿
            'stretch'  => fn($w, $h) => $this->imagick->resizeImage($w, $h, Imagick::FILTER_LANCZOS, 1), // 非等比拉伸
        ];
        
        if ($this->isAnimated) {
            $canvas = new Imagick();
            foreach ($this->imagick as $frame) {
                $newFrame = clone $frame;
                $modes[$mode]($width, $height);
                if ($mode === 'fit' && $bgColor) {
                    // 填充背景邏輯...
                }
                $canvas->addImage($newFrame);
                $canvas->setImageDelay($newFrame->getImageDelay());
                $canvas->setImagePage($width, $height, 0, 0);
            }
            $this->imagick->destroy();
            $this->imagick = $canvas;
        } else {
            $modes[$mode]($width, $height);
        }
        return $this;
    }

    private function smartCrop(int $targetW, int $targetH): void {
        $srcW = $this->imagick->getImageWidth();
        $srcH = $this->imagick->getImageHeight();
        $ratio = max($targetW/$srcW, $targetH/$srcH);
        $newW = (int)($srcW * $ratio);
        $newH = (int)($srcH * $ratio);
        $this->imagick->thumbnailImage($newW, $newH, true);
        $x = (int)(($newW - $targetW) / 2);
        $y = (int)(($newH - $targetH) / 2);
        $this->imagick->cropImage($targetW, $targetH, $x, $y);
        $this->imagick->setImagePage($targetW, $targetH, 0, 0);
    }

    private function applyToAllFrames(callable $fn): void {
        $frames = $this->imagick->coalesceImages();
        $canvas = new Imagick();
        foreach ($frames as $frame) {
            $fn($frame);
            $canvas->addImage($frame);
            $canvas->setImageDelay($frame->getImageDelay());
        }
        $this->imagick->destroy();
        $this->imagick = $canvas;
    }

    // 加浮水印圖片
    public function watermark(string $watermarkPath, int $x, int $y, float $opacity = 1.0): self {
        $wm = new Imagick($watermarkPath);
        $wm->evaluateImage(Imagick::EVALUATE_MULTIPLY, $opacity, Imagick::CHANNEL_ALPHA);
        
        $draw = new ImagickDraw();
        $draw->composite($wm->getImageCompose(), $x, $y, $wm->getImageWidth(), $wm->getImageHeight(), $wm);
        
        if ($this->isAnimated) {
            $this->applyToAllFrames(fn($f) => $f->drawImage($draw));
        } else {
            $this->imagick->drawImage($draw);
        }
        return $this;
    }

    // 加文字水印
    public function text(string $text, int $x, int $y, array $style = []): self {
        $draw = new ImagickDraw();
        $draw->setFont($style['font'] ?? 'DejaVu-Sans');
        $draw->setFontSize($style['size'] ?? 20);
        $draw->setFillColor($style['color'] ?? 'white');
        if (isset($style['stroke'])) {
            $draw->setStrokeColor($style['stroke']);
            $draw->setStrokeWidth($style['stroke_width'] ?? 1);
        }
        
        if ($this->isAnimated) {
            $this->applyToAllFrames(fn($f) => $f->annotateImage($draw, $x, $y, $style['angle'] ?? 0, $text));
        } else {
            $this->imagick->annotateImage($draw, $x, $y, $style['angle'] ?? 0, $text);
        }
        return $this;
    }

    // 設定輸出格式
    public function setFormat(string $format): self {
        $this->format = $format;
        $this->imagick->setImageFormat($format);
        return $this;
    }

    // 儲存至檔案
    public function save(string $path): bool {
        try {
            if ($this->isAnimated || $this->frameCount > 1) {
                $this->imagick->writeImages($path, true);
            } else {
                $this->imagick->writeImage($path);
            }
            return true;
        } catch (ImagickException $e) {
            error_log("Save failed: " . $e->getMessage());
            return false;
        }
    }

    // 直接輸出到瀏覽器
    public function output(): void {
        header('Content-Type: ' . $this->getMimeType());
        echo $this->imagick->getImagesBlob();
    }

    private function getMimeType(): string {
        return match($this->format) {
            'jpg', 'jpeg' => 'image/jpeg',
            'png' => 'image/png',
            'gif' => 'image/gif',
            'webp' => 'image/webp',
            default => 'application/octet-stream'
        };
    }
}

// 使用範例
$processor = new ImageProcessor();
$processor->load('input.gif')
    ->resize(300, 300, 'crop')
    ->watermark('logo.png', 10, 10, 0.7)
    ->text('© MySite', 15, 30, ['size' => 14, 'color' => 'white', 'stroke' => 'black'])
    ->save('output.gif');
?>

常見坑與避雷指南

問題原因解決
GIF 縮放後變靜態未使用 coalesceImages() 處理幀遍歷每幀處理,保持 delay 與 page 設定
透明背景變黑新建畫布預設黑色使用 newImage() 指定背景色或 setImageBackgroundColor()
記憶體耗盡大圖/多幀載入記憶體設定 imagick::setResourceLimit()、分批處理、銷毀不用物件
字體找不到ImageMagick 字體路徑未設定使用絕對路徑 setFont('/path/to/font.ttf') 或設定 MAGICK_FONT_PATH
PDF 處理失敗安全策略禁止 PDF 委派修改 /etc/ImageMagick-6/policy.xml 允許 PDF 讀寫

效能優化建議

  • 使用 thumbnailImage() 而非 resizeImage() 產生縮圖(更快、記憶體較低)
  • 處理大量圖片時,及時 destroy() 釋放記憶體
  • 啟用 OpenMP 多執行緒(ImageMagick 編譯支援時)
  • 考慮使用 Imagick::pingImage() 僅讀取元資料不載入像素

Imagick 將專業級圖片處理能力帶入 PHP,配合合理的封裝與錯誤處理,能滿足從簡單縮圖到複雜動畫處理的各種業務需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved