跳转到内容

EXIF 自动方向修正、正交旋转与任意角度旋转

复制本页

在 libvips 中,“旋转图片”根据角度与元数据的不同,对应三套完全不同的底层算子。混用它们不仅会浪费 CPU,还可能导致方向被重复旋转两次,或者出现意料之外的黑角背景。

用于旋转变换的 1938×1292 夜景原图
输入原图:JPEG1938 × 1292
顺时针旋转 30 度并以 #1d2d35 填充扩展四角后的夜景图像
任意角度旋转(30°):WebP2324 × 2088,背景色 #1d2d35(govips 参考输出)
旋转场景 底层算子 是否插值 输出画布尺寸 典型用途
按 EXIF Orientation 自动扶正 vips_autorot 否(精确像素重排) 90°/270° 时宽高互换 手机拍摄照片上传后的方向归一化
90° / 180° / 270° 正交旋转 vips_rot 否(精确像素重排) 90°/270° 时宽高互换 用户手动点击“向左/向右旋转 90°”
任意角度旋转與缩放 vips_similarity / vips_affine 是(双线性/双三次插值) 外接矩形扩大,四角填充背景色 倾斜校正、创意排版、透视/仿射矩阵变换

1. 按 EXIF Orientation 自动扶正(AutoRotate / autoOrient)

Section titled “1. 按 EXIF Orientation 自动扶正(AutoRotate / autoOrient)”

智能手机相机通常固定以传感器原生方向写入像素,再把拍摄时的握持角度记录在 EXIF Orientation 标签(取值 1..8)中。如果在裁剪或加水印前没有先扶正方向,水印就会被印在错误的角落。

调用 AutoRotate(sharp 中为 .autoOrient() 或无参 .rotate())会根据 EXIF 标签完成翻转/90°旋转,并自动移除或重置 EXIF Orientation 标签为 1,防止浏览器在展示时再次旋转。

package main
import (
"fmt"
"os"
"github.com/davidbyttow/govips/v2/vips"
)
func main() {
if err := vips.Startup(nil); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer vips.Shutdown()
image, err := vips.NewImageFromFile("input.jpg")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer image.Close()
// 根据 EXIF Orientation 自动扶正像素并重置 Orientation 标签
if err := image.AutoRotate(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
data, _, err := image.ExportWebp(vips.NewWebpExportParams())
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
if err := os.WriteFile("autorotated.webp", data, 0o644); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

2. 正交 90° / 180° / 270° 旋转(Rotate / rot)

Section titled “2. 正交 90° / 180° / 270° 旋转(Rotate / rot)”

当旋转角度严格为 90° 的整数倍时,应使用正交旋转算子。它直接重排像素坐标,不经过插值重采样,因此不会引入任何模糊,也不会产生四角背景区域:

// 支持 vips.Angle0、vips.Angle90、vips.Angle180、vips.Angle270(顺时针)
if err := image.Rotate(vips.Angle90); err != nil {
return err
}

3. 任意角度旋转与 2×2 仿射变换(Similarity / Affine)

Section titled “3. 任意角度旋转与 2×2 仿射变换(Similarity / Affine)”

对于非 90° 倍数的任意角度(如本页顶部展示的顺时针 30°),libvips 使用相似变换(vips_similarity)计算外接画布并对像素插值。原图 1938 × 1292 旋转 30° 后,外接矩形扩展为 2324 × 2088,新露出的四个三角形角落由 background 参数填充(若未指定,默认填充黑色 [0, 0, 0])。

如果需要同时执行剪切(Shear)或非等比仿射矩阵变换(2×2 矩阵 [a, b, c, d]),sharp、pyvips 与 vips CLI 暴露了 affine 接口,而 govips v2.19.0 公开层仅暴露了等比缩放加旋转的 Similarity(与二维坐标查找表重映射 Mapim):

// Similarity(scale, angle, backgroundColor, idx, idy, odx, ody)
bg := &vips.ColorRGBA{R: 29, G: 45, B: 53, A: 255}
if err := image.Similarity(1.0, 30.0, bg, 0, 0, 0, 0); err != nil {
return err
}