当我们需要在端上展示用户上传的图片时,由于用户上传的图片尺寸各异,为了良好的视觉效果,我们必须对尺寸各异的图片做处理。在这篇文章里我们介绍一种常用的处理图片方法即图片按比例缩小。这种方法需要实现两个目的一是压缩图片,二是统一图片比例。下面是在 Golang 中的实现方式。
基础操作
图片缩放
在 Golang 里进行图片缩放我们可以直接使用这个库 https://github.com/nfnt/resize
这个库提供了多种图片缩放的算法,包括以下
- NearestNeighbor: Nearest-neighbor interpolation
- Bilinear: Bilinear interpolation
- Bicubic: Bicubic interpolation
- MitchellNetravali: Mitchell-Netravali interpolation
- Lanczos2: Lanczos resampling with a=2
- Lanczos3: Lanczos resampling with a=3
关于几种图像缩放中的插值算法可以看这篇文档。
基本使用方法
1
2
3
4
5
|
// 第一个参数为缩放后的宽度
// 第二个参数为缩放后的高度
// 第三个参数为待缩放图片
//第四个参数为使用哪种插值算法
m := resize.Resize(1000, 0, img, resize.Lanczos3)
|
图片绘制
在 Golang 里我们经常使用官方库 image/draw
来绘制图片。
两个重要方法
1
2
3
4
5
6
7
8
|
func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op)
func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op)
// dst 绘图的背景图
// r 背景图的绘图区域;
// src 要绘制的图
// sp src绘制的开始点,一般是 src 图的左上角
// op 包括 draw.Over 和 draw.Src
// op 对于没有 mask 的情况,draw.Src 和 draw.Over 一样, 意思就是 src 图片直接盖在 dst 图片上,dst 被盖住的部分是不可见的
|
关于 draw 的参数理解可以参考这篇文章。
图片按比例缩小
有了上述的背景知识后再来实现图片按特定比例缩放就容易多了。下面的代码实现是把任意一张图片按照 16:9 的比例缩小,并且对比例失调的部分用白边补齐。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
// ResizeImage 图片按 16:9 缩小,白边补齐
func ResizeImage(file io.Reader) (out io.Reader, err error) {
// file 是原图
buffer, err := ioutil.ReadAll(file)
if err != nil {
return
}
// 检查图片类型
fileType := http.DetectContentType(buffer)
var img image.Image
if fileType == "image/png" {
// decode jpeg into image.Image
img, err = png.Decode(bytes.NewReader(buffer))
} else if fileType == "image/jpeg" {
img, err = jpeg.Decode(bytes.NewReader(buffer))
} else {
err = errors.New("invalid file type")
return
}
if err != nil {
return
}
// 原图的尺寸
width := img.Bounds().Dx()
height := img.Bounds().Dy()
var (
required = 16 / 9
ration = width / height
newW, newH, backW, backH int
)
// 如果原图尺寸超过 1280 * 720 则缩小;不超过则保持原尺寸只进行白边补齐
if ration > required {
newH = 0
if width > 1280 {
newW = 1280
} else {
newW = width
}
backW = newW
backH = backW * 9 / 16
} else {
newW = 0
if newH > 720 {
newH = 720
} else {
newH = height
}
backH = newH
backW = backH * 16 / 9
}
// 图片缩小
m := resize.Resize(uint(newW), uint(newH), img, resize.NearestNeighbor)
// 生成背景图
newImg := image.NewRGBA(image.Rect(0, 0, backW, backH))
// 背景图变成白色
c := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
for x := 0; x < backW; x++ {
for y := 0; y < backH; y++ {
newImg.Set(x, y, c)
}
}
// 在背景图上绘制缩小后的图片,实现补齐白边
if ration > required {
draw.Draw(newImg, image.Rectangle{Min: image.Point{Y: (backH - m.Bounds().Dy()) / 2}, Max: image.Point{X: backW, Y: (backH + m.Bounds().Dy()) / 2}}, m, m.Bounds().Min, draw.Src)
} else {
draw.Draw(newImg, image.Rectangle{Min: image.Point{X: (backW - m.Bounds().Dx()) / 2}, Max: image.Point{X: (backW + m.Bounds().Dx()) / 2, Y: backH}}, m, m.Bounds().Min, draw.Src)
}
// 编码生成图片
outBuffer := new(bytes.Buffer)
if err = jpeg.Encode(outBuffer, newImg, nil); err != nil {
return
}
out = outBuffer
return
}
|
效果一, 3788 * 832 图片缩小成 1280 * 720 (深色模式会对比的很清楚)
效果二, 512 * 406 图片按照 16:9 比例补齐
希望这篇文章中在 Golang 里对图片的操作方法能给大家带来参考和提升。