强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

GraphicsMagick 图像处理完整教程 / 第10章 编程接口 (API)

第10章 编程接口 (API)

10.1 API 概览

GraphicsMagick 提供多语言绑定,核心库为 C 实现:

┌─────────────────────────────────────────┐
│          编程语言绑定层                    │
├─────┬──────┬──────┬──────┬──────┬───────┤
│Python│Node.js│ Go  │ Ruby │ Perl │ C++   │
│(gm) │(gm)  │(gm) │(rm) │(GM) │(Magick++)│
├─────┴──────┴──────┴──────┴──────┴───────┤
│         libGraphicsMagickWand            │
│              (C API)                     │
├─────────────────────────────────────────┤
│          libGraphicsMagick               │
│            (Core C lib)                  │
└─────────────────────────────────────────┘
语言库名安装方式成熟度
ClibGraphicsMagick系统包⭐⭐⭐⭐⭐
C++Magick++系统包⭐⭐⭐⭐⭐
Pythonpgmagick / Wandpip⭐⭐⭐⭐
Node.jsgmnpm⭐⭐⭐⭐
Gogographics/imagickgo get⭐⭐⭐⭐
Rubyrmagickgem⭐⭐⭐
PerlGraphicsMagickCPAN⭐⭐⭐⭐

10.2 Python 接口

10.2.1 Wand(推荐)

Wand 是 Python 对 ImageMagick/GraphicsMagick 的绑定,使用 ctypes 调用 C API。

# 安装
pip install Wand
# 基本使用
from wand.image import Image
from wand.display import display

# 读取并缩放
with Image(filename='input.jpg') as img:
    img.resize(800, 600)
    img.save(filename='output.jpg')

# 查看图像信息
with Image(filename='input.jpg') as img:
    print(f"尺寸: {img.width}x{img.height}")
    print(f"格式: {img.format}")
    print(f"色彩空间: {img.colorspace}")
    print(f"色深: {img.depth}")

10.2.2 Wand 常用操作

from wand.image import Image
from wand.color import Color
from wand.drawing import Drawing

# 缩放
with Image(filename='input.jpg') as img:
    img.resize(800, 600)          # 指定尺寸
    img.sample(400, 300)          # 快速缩放(最近邻)
    img.transform(resize='800x600')  # 限制最大尺寸
    img.transform(resize='800x600>') # 仅缩小

# 裁剪
with Image(filename='input.jpg') as img:
    img.crop(width=400, height=300, gravity='center')

# 旋转
with Image(filename='input.jpg') as img:
    img.rotate(90)
    img.rotate(45, background=Color('#FF0000'))

# 翻转
with Image(filename='input.jpg') as img:
    img.flip()     # 垂直翻转
    img.flop()     # 水平翻转

# 颜色调整
with Image(filename='input.jpg') as img:
    img.modulate(brightness=110, saturation=130, hue=100)
    img.level(black=0.1, white=0.9, gamma=0.8)
    img.negate()
    img.normalize()
    img.equalize()

# 模糊和锐化
with Image(filename='input.jpg') as img:
    img.gaussian_blur(radius=5, sigma=3)
    img.sharpen(radius=0, sigma=1)
    img.unsharp_mask(radius=0, sigma=1, amount=0.5, threshold=0)

# 特效
with Image(filename='input.jpg') as img:
    img.emboss(radius=2, sigma=1)
    img.charcoal(radius=2, sigma=1)
    img.sepia_tone(threshold=0.8)
    img.swirl(degrees=45)
    img.vignette()

# 格式转换
with Image(filename='input.png') as img:
    img.format = 'jpeg'
    img.compression_quality = 85
    img.save(filename='output.jpg')

# 添加文字
with Image(width=400, height=100, background=Color('#333')) as img:
    with Drawing() as draw:
        draw.font = 'Helvetica'
        draw.font_size = 36
        draw.fill_color = Color('white')
        draw.text(20, 50, 'Hello World')
        draw(img)
    img.save(filename='text.png')

# 水印
with Image(filename='photo.jpg') as img:
    with Image(filename='watermark.png') as watermark:
        img.watermark(watermark, transparency=0.5, left=20, top=20)
    img.save(filename='watermarked.jpg')

# 多帧/动图处理
with Image(filename='animation.gif') as img:
    print(f"帧数: {len(img.sequence)}")
    for i, frame in enumerate(img.sequence):
        with Image(frame) as frame_img:
            frame_img.save(filename=f'frame_{i}.png')

10.2.3 pgmagick

pip install pgmagick
from pgmagick import Image, Geometry, Color

# 基本操作
img = Image('input.jpg')
img.resize(Geometry(800, 600))
img.quality(85)
img.write('output.jpg')

# 模糊
img.gaussianBlur(0, 3)
img.write('blurred.jpg')

# 旋转
img.rotate(45)
img.write('rotated.jpg')

10.2.4 Python API 对比

特性Wandpgmagick
绑定方式ctypes (cffi)SWIG
安装难度简单需要编译
API 风格Pythonic类 C++
文档丰富较少
维护活跃较少
推荐度⭐⭐⭐⭐⭐⭐⭐⭐

10.3 Node.js 接口

10.3.1 gm 模块

npm install gm
const gm = require('gm').subClass({ imageMagick: false }); // 使用 GraphicsMagick

// 基本操作
gm('input.jpg')
  .resize(800, 600)
  .quality(85)
  .write('output.jpg', (err) => {
    if (err) console.error(err);
    else console.log('处理完成');
  });

// 链式调用
gm('input.jpg')
  .autoOrient()
  .resize('800x600>')
  .quality(85)
  .strip()
  .sharpen(0, 1)
  .write('optimized.jpg', (err) => {
    if (err) console.error(err);
  });

// 获取图像信息
gm('input.jpg').identify((err, info) => {
  if (err) console.error(err);
  console.log('尺寸:', info.size.width, 'x', info.size.height);
  console.log('格式:', info.format);
  console.log('文件大小:', info.Filesize);
  console.log('EXIF:', info['Profile-EXIF']);
});

// Buffer 操作
const fs = require('fs');
const inputBuffer = fs.readFileSync('input.jpg');

gm(inputBuffer, 'input.jpg')
  .resize(200, 200)
  .toBuffer('PNG', (err, buffer) => {
    if (err) console.error(err);
    fs.writeFileSync('output.png', buffer);
  });

// Stream 操作
const http = require('http');
const fs = require('fs');

http.createServer((req, res) => {
  gm('photo.jpg')
    .resize(800, 600)
    .quality(85)
    .stream('jpeg')
    .pipe(res);
}).listen(3000);

10.3.2 Node.js 完整操作示例

const gm = require('gm').subClass({ imageMagick: false });
const path = require('path');

// 综合处理函数
function processImage(inputPath, outputPath, options = {}) {
  return new Promise((resolve, reject) => {
    let cmd = gm(inputPath);

    // 自动旋转
    if (options.autoOrient !== false) {
      cmd = cmd.autoOrient();
    }

    // 缩放
    if (options.width || options.height) {
      const w = options.width || '';
      const h = options.height || '';
      const mod = options.exact ? '!' : (options.onlyShrink ? '>' : '');
      cmd = cmd.resize(`${w}x${h}${mod}`);
    }

    // 裁剪
    if (options.crop) {
      cmd = cmd.crop(options.crop.width, options.crop.height,
                     options.crop.x, options.crop.y);
    }

    // 质量
    if (options.quality) {
      cmd = cmd.quality(options.quality);
    }

    // 锐化
    if (options.sharpen) {
      cmd = cmd.sharpen(0, options.sharpen);
    }

    // 去除元数据
    if (options.strip) {
      cmd = cmd.strip();
    }

    // 输出格式
    const ext = path.extname(outputPath).toLowerCase();
    if (ext === '.webp') cmd = cmd.setFormat('webp');
    if (ext === '.png') cmd = cmd.setFormat('png');

    cmd.write(outputPath, (err) => {
      if (err) reject(err);
      else resolve(outputPath);
    });
  });
}

// 使用示例
(async () => {
  try {
    await processImage('input.jpg', 'thumb.jpg', {
      width: 200,
      height: 200,
      quality: 80,
      strip: true,
      onlyShrink: true
    });
    console.log('处理完成');
  } catch (err) {
    console.error('处理失败:', err);
  }
})();

10.4 Go 接口

10.4.1 gographics/imagick

go get github.com/gographics/imagick/imagick
package main

import (
    "fmt"
    "github.com/gographics/imagick/imagick"
)

func main() {
    imagick.Initialize()
    defer imagick.Terminate()

    mw := imagick.NewMagickWand()
    defer mw.Destroy()

    // 读取图像
    err := mw.ReadImage("input.jpg")
    if err != nil {
        panic(err)
    }

    // 获取信息
    fmt.Printf("尺寸: %dx%d\n", mw.GetImageWidth(), mw.GetImageHeight())
    fmt.Printf("格式: %s\n", mw.GetImageFormat())

    // 缩放
    err = mw.ResizeImage(800, 600, imagick.FILTER_LANCZOS, 1)
    if err != nil {
        panic(err)
    }

    // 设置质量
    mw.SetImageCompressionQuality(85)

    // 去除元数据
    mw.StripImage()

    // 写入
    err = mw.WriteImage("output.jpg")
    if err != nil {
        panic(err)
    }

    fmt.Println("处理完成")
}

10.4.2 Go 综合处理

package main

import (
    "github.com/gographics/imagick/imagick"
)

// ProcessImage 处理图像
func ProcessImage(input, output string, width, height uint, quality uint) error {
    imagick.Initialize()
    defer imagick.Terminate()

    mw := imagick.NewMagickWand()
    defer mw.Destroy()

    // 读取
    if err := mw.ReadImage(input); err != nil {
        return err
    }

    // 自动旋转(EXIF)
    mw.AutoOrientImage()

    // 缩放
    if err := mw.ResizeImage(width, height, imagick.FILTER_LANCZOS, 1); err != nil {
        return err
    }

    // 质量
    mw.SetImageCompressionQuality(quality)

    // 锐化
    if err := mw.SharpenImage(0, 0.5); err != nil {
        return err
    }

    // 去除元数据
    mw.StripImage()

    // 写入
    return mw.WriteImage(output)
}

// Thumbnail 生成缩略图
func Thumbnail(input, output string, size uint) error {
    imagick.Initialize()
    defer imagick.Terminate()

    mw := imagick.NewMagickWand()
    defer mw.Destroy()

    if err := mw.ReadImage(input); err != nil {
        return err
    }

    // 按比例缩放
    w := mw.GetImageWidth()
    h := mw.GetImageHeight()
    if w > h {
        mw.ResizeImage(size, 0, imagick.FILTER_LANCZOS, 1)
    } else {
        mw.ResizeImage(0, size, imagick.FILTER_LANCZOS, 1)
    }

    // 居中裁剪
    w = mw.GetImageWidth()
    h = mw.GetImageHeight()
    x := int(w-size) / 2
    y := int(h-size) / 2
    mw.CropImage(size, size, x, y)

    mw.SetImageCompressionQuality(85)
    mw.StripImage()

    return mw.WriteImage(output)
}

10.5 Ruby 接口

10.5.1 RMagick

gem install rmagick
require 'RMagick'

# 读取图像
img = Magick::Image.read('input.jpg').first

# 获取信息
puts "尺寸: #{img.columns}x#{img.rows}"
puts "格式: #{img.format}"

# 缩放
resized = img.resize_to_fit(800, 600)
resized.write('resized.jpg') { self.quality = 85 }

# 裁剪
cropped = img.crop_resized(400, 400, Magick::CenterGravity)
cropped.write('cropped.jpg')

# 旋转
rotated = img.rotate(90)
rotated.write('rotated.jpg')

# 模糊
blurred = img.gaussian_blur(0, 3)
blurred.write('blurred.jpg')

# 锐化
sharpened = img.unsharp_mask(0, 1, 0.5, 0)
sharpened.write('sharpened.jpg')

# 水印
watermark = Magick::Image.read('watermark.png').first
result = img.composite(watermark, Magick::SouthEastGravity, 20, 20, Magick::DissolveCompositeOp)
result.write('watermarked.jpg')

# 批量处理
Dir.glob('*.jpg').each do |file|
  img = Magick::Image.read(file).first
  img.resize_to_fit!(800, 600)
  img.write("output/#{file}") { self.quality = 85 }
  puts "处理: #{file}"
end

10.6 C 语言接口

10.6.1 Wand API (推荐)

#include <stdio.h>
#include <wand/MagickWand.h>

int main(int argc, char **argv) {
    MagickWand *wand;

    // 初始化
    MagickWandGenesis();

    // 创建 Wand
    wand = NewMagickWand();

    // 读取图像
    if (MagickReadImage(wand, "input.jpg") == MagickFalse) {
        fprintf(stderr, "读取失败\n");
        return 1;
    }

    // 获取尺寸
    size_t width = MagickGetImageWidth(wand);
    size_t height = MagickGetImageHeight(wand);
    printf("原始尺寸: %zux%zu\n", width, height);

    // 缩放
    MagickResizeImage(wand, 800, 600, LanczosFilter, 1.0);

    // 设置质量
    MagickSetImageCompressionQuality(wand, 85);

    // 去除元数据
    MagickStripImage(wand);

    // 写入
    MagickWriteImage(wand, "output.jpg");

    // 清理
    wand = DestroyMagickWand(wand);
    MagickWandTerminus();

    printf("处理完成\n");
    return 0;
}

编译:

gm-config --cflags --libs
gcc -o process process.c $(gm-config --cflags --libs)

10.6.2 C 语言批量处理

#include <stdio.h>
#include <string.h>
#include <wand/MagickWand.h>

int process_thumbnail(const char *input, const char *output, size_t size) {
    MagickWand *wand = NewMagickWand();

    if (MagickReadImage(wand, input) == MagickFalse) {
        DestroyMagickWand(wand);
        return -1;
    }

    // 自动旋转
    MagickAutoOrientImage(wand);

    // 获取尺寸
    size_t w = MagickGetImageWidth(wand);
    size_t h = MagickGetImageHeight(wand);

    // 按比例缩放
    if (w > h) {
        MagickResizeImage(wand, size, h * size / w, LanczosFilter, 1.0);
    } else {
        MagickResizeImage(wand, w * size / h, size, LanczosFilter, 1.0);
    }

    // 居中裁剪
    w = MagickGetImageWidth(wand);
    h = MagickGetImageHeight(wand);
    size_t x = (w - size) / 2;
    size_t y = (h - size) / 2;
    MagickCropImage(wand, size, size, x, y);

    // 设置属性
    MagickSetImageCompressionQuality(wand, 85);
    MagickStripImage(wand);

    // 写入
    int result = MagickWriteImage(wand, output) == MagickTrue ? 0 : -1;

    DestroyMagickWand(wand);
    return result;
}

int main() {
    MagickWandGenesis();

    const char *files[] = {"photo1.jpg", "photo2.jpg", "photo3.jpg"};
    int n = sizeof(files) / sizeof(files[0]);

    for (int i = 0; i < n; i++) {
        char output[256];
        snprintf(output, sizeof(output), "thumb_%s", files[i]);
        if (process_thumbnail(files[i], output, 200) == 0) {
            printf("✅ %s\n", files[i]);
        } else {
            printf("❌ %s\n", files[i]);
        }
    }

    MagickWandTerminus();
    return 0;
}

10.7 命令行调用 API(通用方案)

任何语言都可以通过命令行调用 GraphicsMagick:

10.7.1 Python subprocess

import subprocess
import json

def gm_identify(image_path):
    """获取图像信息"""
    result = subprocess.run(
        ['gm', 'identify', '-verbose', image_path],
        capture_output=True, text=True
    )
    return result.stdout

def gm_convert(input_path, output_path, options=None):
    """图像转换"""
    cmd = ['gm', 'convert']
    if options:
        cmd.extend(options)
    cmd.extend([input_path, output_path])
    result = subprocess.run(cmd, capture_output=True, text=True)
    return result.returncode == 0

# 使用
info = gm_identify('photo.jpg')
gm_convert('photo.jpg', 'output.jpg', ['-resize', '800x600', '-quality', '85'])

10.7.2 Node.js child_process

const { execFile } = require('child_process');
const util = require('util');
const exec = util.promisify(execFile);

async function gmIdentify(imagePath) {
    const { stdout } = await exec('gm', ['identify', '-verbose', imagePath]);
    return stdout;
}

async function gmConvert(input, output, options = []) {
    await exec('gm', ['convert', ...options, input, output]);
}

// 使用
(async () => {
    await gmConvert('input.jpg', 'output.jpg', ['-resize', '800x600', '-quality', '85']);
    console.log('处理完成');
})();

10.8 API 选择建议

场景推荐方案理由
Python Web 服务Wand功能全面,Pythonic
Python 快速脚本subprocess无需额外依赖
Node.js 服务gm 模块流式支持好
Go 高性能服务gographics/imagick原生性能
Ruby 项目RMagick成熟稳定
C/C++ 嵌入MagickWand底层控制
通用subprocess跨语言兼容

10.9 本章小结

要点说明
Wand 是 Python 最佳选择ctypes 绑定,安装简单
gm 模块是 Node.js 标准方案支持流式处理
gographics/imagick 是 Go 方案原生 CGO 绑定
subprocess 是万能方案任何语言都能用
图形初始化必须配对Initialize/Terminate, Genesis/Terminus
注意资源释放DestroyWand 避免内存泄漏

扩展阅读

  1. Wand Python 文档
  2. gm Node.js 文档
  3. gographics/imagick Go 文档
  4. MagickWand C API
  5. GraphicsMagick API 参考

上一章第09章 图像格式详解 下一章第11章 Docker 与服务化