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) │
└─────────────────────────────────────────┘
| 语言 | 库名 | 安装方式 | 成熟度 |
|---|---|---|---|
| C | libGraphicsMagick | 系统包 | ⭐⭐⭐⭐⭐ |
| C++ | Magick++ | 系统包 | ⭐⭐⭐⭐⭐ |
| Python | pgmagick / Wand | pip | ⭐⭐⭐⭐ |
| Node.js | gm | npm | ⭐⭐⭐⭐ |
| Go | gographics/imagick | go get | ⭐⭐⭐⭐ |
| Ruby | rmagick | gem | ⭐⭐⭐ |
| Perl | GraphicsMagick | CPAN | ⭐⭐⭐⭐ |
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 对比
| 特性 | Wand | pgmagick |
|---|---|---|
| 绑定方式 | 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 避免内存泄漏 |
扩展阅读
上一章:第09章 图像格式详解 下一章:第11章 Docker 与服务化