简介

本手册将以 libvips 的封装 govips 为主进行介绍,中间会穿插 pyvipssharp 的部分示例代码用于参考。

目录

使用方式

基本图像概念

基本概念

  • 像素和分辨率
  • 色彩模型:RGB、CMYK
  • Alpha 通道和透明度
  • 图像格式和特点(JPEG, PNG, TIFF 等)

使用方式

安装

  • 在不同操作系统上安装 libvips
  • 安装 Python、Go、Node.js 封装包

基本操作

  • 读取和导出图片
  • 基本的图片格式转换

基本图像操作

  • 缩放
  • 裁剪
  • 旋转
  • 翻转和镜像
  • 添加和删除 Alpha 通道

图像色彩调整

  • 色彩空间转换
  • 调整亮度、饱和度,色相

4. 基本图像操作

  • 添加和移除 Alpha 通道
  • 图像组合和图层操作
  • 图像滤波:平滑、锐化
  • 边缘检测和图像分割
  • 在 Python、Go、Node.js 中的实现对比

第二部分:进阶篇

1. 高级图像处理

  • 高级滤镜和效果
  • 颜色调整:色温、色调映射
  • 图像恢复和噪声消除
  • 动态图像处理(如 GIF)

2. 性能优化和内存管理

  • 使用延迟加载和管道处理
  • 内存使用策略和优化
  • 处理大型图像的策略

3. 错误处理和调试

  • libvips 错误处理机制
  • 性能分析和调优
  • 在 Python、Go、Node.js 中的错误处理和调试技巧

4. 特殊场景处理

  • 处理不常见的图像格式
  • 批量处理和自动化
  • 与其他图像处理库的结合使用(如 OpenCV)

第三部分:附加资源

1. 学习资源和社区

  • 官方文档链接
  • 相关论坛和社区

2. 常见问题解答

  • 安装和配置常见问题
  • 功能实现和性能优化常见问题

3. 更新记录

  • 教程版本更新
  • libvips 版本更新及影响

基本概念

Alpha 通道和透明度

Alpha 通道(Alpha Channel)是指图像中每个像素的额外通道,用于表示像素的透明度。它是一种图像通道,与红色(R)、绿色(G)和蓝色(B)通道一起构成了图像的颜色信息。Alpha 通道的取值范围通常是从 0 到 1,表示像素的透明度程度,其中 0 表示完全透明(像素不可见),1 表示完全不透明(像素完全可见)。

透明度(Opacity)是指物体或图像的可见程度或不透明程度。在图像处理中,透明度通常通过 Alpha 通道来表示。透明度的值可以是介于 0 和 1 之间的任意小数,其中 0 表示完全透明,物体或图像不可见,1 表示完全不透明,物体或图像完全可见。透明度的中间值表示部分透明,即物体或图像可见但部分透明。

通过使用 Alpha 通道和透明度,可以实现图像的透明效果。通过调整像素的透明度值,可以使图像中的某些区域变得透明,从而使底层的背景或其他图像可见。透明度的应用广泛,例如在图像编辑软件中,可以通过调整透明度来创建淡入淡出的效果、合成多个图像或将图像放置在不同的背景上。

安装

libvips 在不同的平台上有不同的安装方式,它可以在 Linux、macOS 和 Windows 上运行。

对于 Ubuntu 而言,安装 libvips 的方式是:

apt-get install libvips

macOS 是:

brew install vips

libvips 在不同的语言上有不同的实现(封装),在安装了 libvips 之后,我们可以根据自己需要的编程语言来安装对应的库:

Go

go get -u github.com/davidbyttow/govips/v2/vips

Python

pip3 install pyvips

Node

npm install sharp

基本操作

读取和导出图片

我们以 Go 为例,在安装了 govips 之后,可以使用以下代码进行基本图像操作:

package main

import (
	"github.com/davidbyttow/govips/v2/vips"
	"os"
)

func main() {
	vips.Startup(nil)
	defer vips.Shutdown()

	img, err := vips.NewImageFromFile("input.jpg")
	if err != nil {
		panic(err)
	}
	imageBytes, _, _ := img.ExportPng(vips.NewPngExportParams())
	_ = os.WriteFile("output.jpg", imageBytes, 0644)
}
Python 代码案例,点这里
import pyvips

image = pyvips.Image.new_from_file('some-image.jpg', access='sequential')
image *= [1, 2, 1]
mask = pyvips.Image.new_from_list([[-1, -1, -1],
                                   [-1, 16, -1],
                                   [-1, -1, -1]
                                  ], scale=8)
image = image.conv(mask, precision='integer')
image.write_to_file('x.jpg')
Javascript 代码案例,点这里
const sharp = require('sharp');
sharp('input.jpg')
  .rotate()
  .resize(200)
  .jpeg({ mozjpeg: true })
  .toBuffer()
  .then( data => { ... })
  .catch( err => { ... });

这里 img 会是一个 ImageRef 对象,后续的所有操作都是围绕这个对象来的,所以后文中所有的操作都假设你已经有了一个 ImageRef 对象,名字为 img

// ImageRef contains a libvips image and manages its lifecycle.
type ImageRef struct {
	// NOTE: We keep a reference to this so that the input buffer is
	// never garbage collected during processing. Some image loaders use random
	// access transcoding and therefore need the original buffer to be in memory.
	buf                 []byte
	image               *C.VipsImage
	format              ImageType
	originalFormat      ImageType
	lock                sync.Mutex
	preMultiplication   *PreMultiplicationState
	optimizedIccProfile string
}

转换图片格式

例如,如果我们希望将图片从 jpg 格式转换成 webp 格式,和我们的 WebP Server Go 项目一样,我们可以使用以下代码:

package main

import (
	"os"

	"github.com/davidbyttow/govips/v2/vips"
)

func main() {
	vips.Startup(nil)
	defer vips.Shutdown()

	img, err := vips.NewImageFromFile("input.jpg")
	if err != nil {
		panic(err)
	}
	imageBytes, _, _ := img.ExportWebp(vips.NewWebpExportParams())
	os.WriteFile("output.webp", imageBytes, 0644)
}

其中 vips.NewWebpExportParams() 会生成一个默认的 webp 导出参数,你可以根据需要进行调整,可调参数如下:

// WebpExportParams are options when exporting a WEBP to file or buffer
type WebpExportParams struct {
	StripMetadata   bool
	Quality         int
	Lossless        bool
	NearLossless    bool
	ReductionEffort int
	IccProfile      string
}
  • StripMetadata 是否去除元数据
  • Quality 图片质量,范围 0-100
  • Lossless 是否无损压缩,注意上面 Quality 100 并不代表无损压缩
  • NearLossless 是否近无损压缩
  • ReductionEffort 压缩力度,范围 0-6,用 NewWebpExportParams 的话这个值默认是 4,这里我们踩过一些坑,比如对于某些图片来说 Effort 设置为 0 会转换失败,相关的 Issue 可以见 https://github.com/libvips/libvips/issues/3568 。 (注意这里的压缩力度并不是一个线性的关系,不同的数值对应的 libvips 上是不同的压缩算法,但是数值越高,转换速度越慢)

Fun fact, 对于 AVIF 格式的导出可以使用 AvifExportParams ,内部也有 StripMetadata 选项来决定是否清除图片的源信息,但是这个功能直到 govips v2.14.0 版本发布之前都是假的,修复的 PR 是 Fix AvifExportParams StripMetadata #383

基本图像操作

本文假设你已经阅读了 基本操作,已经学会如何将一个图片读取为 ImageRef 对象和通过类似 img.ExportWebp + os.WriteFile 导出了。

这里包括了:

  • 缩放、裁剪、旋转
  • 翻转和镜像
  • 色彩调整:亮度、对比度、饱和度

缩放

func (r *ImageRef) Thumbnail(width, height int, crop Interesting) error

如果希望改变图片的尺寸,例如将一个 2000x1000px 的图片缩小成 200x100px,可以使用 Thumbnail 方法:

img.Thumbnail(200, 100, vips.InterestingAll)

注意这里如果设置的新的尺寸和原来的的长宽比例不一致,那么图片会被裁剪成新的尺寸,裁剪的方式可以通过 vips.Interesting 参数进行设置,可选项如下:

// Interesting constants represent areas of interest which smart cropping will crop based on.
const (
	InterestingNone      Interesting = C.VIPS_INTERESTING_NONE
	InterestingCentre    Interesting = C.VIPS_INTERESTING_CENTRE
	InterestingEntropy   Interesting = C.VIPS_INTERESTING_ENTROPY
	InterestingAttention Interesting = C.VIPS_INTERESTING_ATTENTION
	InterestingLow       Interesting = C.VIPS_INTERESTING_LOW
	InterestingHigh      Interesting = C.VIPS_INTERESTING_HIGH
	InterestingAll       Interesting = C.VIPS_INTERESTING_ALL
	InterestingLast      Interesting = C.VIPS_INTERESTING_LAST
)

我们针对不同的裁剪格式有一个完整的对比文章,敬请参考: libvips 中不同的 VipsInteresting 是怎么决定图片裁剪位置的 - WebP Cloud Services Blog

裁剪

func (r *ImageRef) Crop(left int, top int, width int, height int) error

例如,将图片从 200x100px 的位置裁剪出一个 500x500px 的小图出来:

img.Crop(200, 100, 500, 500)
原图裁剪后

旋转

func (r *ImageRef) Rotate(angle Angle) error

旋转分为多种不同的旋转,一种是 90 度的整数倍旋转,另一种是任意角度的旋转,前者非常简单,只需要使用:

img.Rotate(angle)

即可,其中 angle 可选项如下:

// Angle enum
const (
	Angle0   Angle = C.VIPS_ANGLE_D0
	Angle90  Angle = C.VIPS_ANGLE_D90
	Angle180 Angle = C.VIPS_ANGLE_D180
	Angle270 Angle = C.VIPS_ANGLE_D270
)

而对于任意角度的旋转,可以使用 Similarity 函数:

func (r *ImageRef) Similarity(scale float64, angle float64, backgroundColor *ColorRGBA,
	idx float64, idy float64, odx float64, ody float64) error

例子如下:

img.Similarity(1.0, float64(angleDegree), &vips.ColorRGBA{
				R: 255,
				G: 255,
				B: 255,
				A: 255,
			}, 0, 0, 0, 0)

其中 1.0 是缩放比例,angleDegree 是旋转角度,ColorRGBA 是背景颜色,上面的例子中我们用 255, 255, 255, 255 表示无透明度的白色,最后四个参数是填充的位置,这里我们用 0 表示填充到图片的四周,例如给下图旋转 30 度的效果:

原图旋转后

注意本文背景色是白色,和旋转后图片四周的填充色一致,所以看不出来填充的效果,实际上图片是被填充了的。

翻转和镜像

func (r *ImageRef) Flip(direction Direction) error

例如:

img.Flip(vips.DirectionHorizontal)

其中方向有两个可选:

const (
	DirectionHorizontal Direction = C.VIPS_DIRECTION_HORIZONTAL
	DirectionVertical   Direction = C.VIPS_DIRECTION_VERTICAL
)

如果需要上下和左右都反转,需要分别调用两次 Flip 方法并传入不同的 Direction。

原图DirectionHorizontal
DirectionVerticalDirectionVertical + DirectionHorizontal

图像色彩调整

色彩空间转换

色彩空间的介绍可以在 基本概念 中看到。

func (r *ImageRef) ToColorSpace(interpretation Interpretation) error

其中 Interpretation 的可选项有如下:

// Interpretation enum
const (
	InterpretationError     Interpretation = C.VIPS_INTERPRETATION_ERROR
	InterpretationMultiband Interpretation = C.VIPS_INTERPRETATION_MULTIBAND
	InterpretationBW        Interpretation = C.VIPS_INTERPRETATION_B_W
	InterpretationHistogram Interpretation = C.VIPS_INTERPRETATION_HISTOGRAM
	InterpretationXYZ       Interpretation = C.VIPS_INTERPRETATION_XYZ
	InterpretationLAB       Interpretation = C.VIPS_INTERPRETATION_LAB
	InterpretationCMYK      Interpretation = C.VIPS_INTERPRETATION_CMYK
	InterpretationLABQ      Interpretation = C.VIPS_INTERPRETATION_LABQ
	InterpretationRGB       Interpretation = C.VIPS_INTERPRETATION_RGB
	InterpretationRGB16     Interpretation = C.VIPS_INTERPRETATION_RGB16
	InterpretationCMC       Interpretation = C.VIPS_INTERPRETATION_CMC
	InterpretationLCH       Interpretation = C.VIPS_INTERPRETATION_LCH
	InterpretationLABS      Interpretation = C.VIPS_INTERPRETATION_LABS
	InterpretationSRGB      Interpretation = C.VIPS_INTERPRETATION_sRGB
	InterpretationYXY       Interpretation = C.VIPS_INTERPRETATION_YXY
	InterpretationFourier   Interpretation = C.VIPS_INTERPRETATION_FOURIER
	InterpretationGrey16    Interpretation = C.VIPS_INTERPRETATION_GREY16
	InterpretationMatrix    Interpretation = C.VIPS_INTERPRETATION_MATRIX
	InterpretationScRGB     Interpretation = C.VIPS_INTERPRETATION_scRGB
	InterpretationHSV       Interpretation = C.VIPS_INTERPRETATION_HSV
)

例子:

img.ToColorSpace(vips.InterpretationSRGB)

调整亮度、饱和度,色相

func (r *ImageRef) Modulate(brightness, saturation, hue float64) error

这个函数如传入参数,可以同时调整图片的亮度,饱和度和色相,brightnesssaturation 保持为 1 表示不变,hue 色相的调整稍微复杂一些,是一个角度,如果传入 0 表示不变,所以:

当我们仅调节图片亮度时,应该传入 brightness ,保持 saturation 是 1 ,hue 是 0,例如:

img.Modulate(1.5, 1, 0)
原图调整亮度后

当我们仅调节图片饱和度时,应该保持 brightness 是 1 ,仅传入 saturationhue 是 0,例如:

TODO

当我们仅调节图片色相时,应该保持 brightness 是 1,brightness 是 1,仅传入 hue ,例如:

TODO