如何使用Golang将多个图片合并为一个平铺图

前言

Golang 是一种流行的编程语言,在图像处理方面具有优异的性能和出色的并发处理能力,本文将介绍如何使用 Golang 将多个图片合并为一个平铺图。

快速预览

这里是将三个不同颜色的图片合并为一个 3x1 的平铺图的示例:

环境搭建

安装Golang

首先,需要在本地安装 Golang,Golang 官网提供了[下载链接](https://golang.org/doc/install),根据操作系统选择对应的安装包进行下载和安装,安装完成后可通过命令行中的以下命令进行版本验证:

go version

安装第三方库

接下来,需要安装第三方图像处理库来帮助我们处理图像,Golang 提供了一些图像处理库,例如 image 和 image/draw,但是这些库并不适合本文的需求。这里我们选择使用第三方图像处理库 go-cairo。

使用以下命令安装 go-cairo:

go get github.com/ungerik/go-cairo

如果以上命令执行失败,您可以到[这个链接](https://github.com/ungerik/go-cairo)下载 go-cairo,然后按照 README 文件中的说明进行安装。

图像处理流程

在开始编码之前,需要了解一下本文要实现的图像处理流程。在本文中,我们要将多个单独的图像合并为一个平铺图,并且每个小图像之间会有一定的间隔。

图像处理流程如下:

1. 创建一张大小合适的画布(即平铺图)。

2. 从磁盘读取需要合并的多个图片。

3. 依次将每个图片绘制到画布上。

4. 将画布保存到磁盘。

有了以上流程,我们就可以开始编写 Golang 代码了。

编写Golang代码

创建画布

首先,我们需要创建一个大小合适的画布,可以根据所有需要合并的图片的大小和数量计算出画布的大小,这里的大小不仅包括宽度和高度,还包括间隔(即每个小图片之间的距离)。

下面是创建画布的函数:

package main

import (

"os"

"github.com/ungerik/go-cairo"

)

func createSurface(width, height, spacing int) *cairo.Surface {

surface := cairo.NewSurface(cairo.FORMAT_ARGB32, width, height)

ctx := cairo.Create(surface)

// 绘制背景色

ctx.Rectangle(0, 0, float64(width), float64(height))

ctx.SetSourceRGB(1, 1, 1)

ctx.Fill()

// 设置小图片之间的间隔

ctx.MoveTo(0, 0)

ctx.LineTo(float64(width), 0)

ctx.LineTo(float64(width), float64(height))

ctx.LineTo(0, float64(height))

ctx.LineTo(0, 0)

ctx.SetSourceRGB(0, 0, 0)

ctx.LineWidth = float64(spacing)

ctx.Stroke()

ctx.Destroy()

return surface

}

该函数接收三个整数参数:width、height 和 spacing,分别表示画布的宽度、高度和小图片之间的间隔,函数返回一个 cairo.Surface 对象。

在函数内部,使用 cairo 包创建一个新的画布 surface,然后调用 cairo.Context 对象的方法依次完成以下操作:

- 绘制背景色:使用白色填充整个画布。

- 绘制间隔:在画布上绘制一个黑色的矩形,该矩形大小为画布大小减去一个间隔末端的大小。设置线的宽度,然后绘制该矩形。

最后,销毁 Context 对象并返回 Surface 对象。

绘制小图片

接下来,我们需要完成将多个小图片绘制到画布上的过程。在绘制之前,需要确定每个小图片的位置。

下面是一个简化的函数,用于确定每个小图片的位置:

func initPositions(w, h, sw, sh, spacing int) [][]float64 {

var positions [][]float64

for y := 0; y < h; y++ {

row := []float64{}

for x := 0; x < w; x++ {

offsetX := x*sw + x*spacing

offsetY := y*sh + y*spacing

row = append(row, float64(offsetX), float64(offsetY))

}

positions = append(positions, row)

}

return positions

}

该函数接收五个整数参数:w、h、sw、sh 和 spacing,分别表示所需合并图像的宽度、高度、单个小图片的宽度、高度以及小图片之间的间隔。函数返回一个 size 为 (h, w) 的二维数组,其中包含每个小图片的位置信息。

实现思路是,首先循环遍历高度和宽度,然后计算出每个小图片的位置信息,并将其存储在一个二维数组中。

有了位置信息后,可以开始将每个小图片绘制到画布上了。

下面是一个用于将一个小图片绘制到画布上的函数:

func mergeImage(surface *cairo.Surface, image *cairo.Surface, x, y float64) {

ctx := cairo.Create(surface)

ctx.Translate(x, y)

ctx.SetSourceSurface(image, 0, 0)

ctx.Paint()

ctx.Destroy()

}

该函数接收三个参数:surface、image 和 x、y 这两个 float 类型的。其中,surface 表示画布,image 表示要绘制到画布上的小图片,x 和 y 分别表示小图片的 x 和 y 坐标。

在此函数中,使用 cairo 创建一个 Context 对象 ctx,并使用 Context 对象的 Translate 方法将坐标系移动 x 像素和 y 像素,然后使用 SetSourceSurface 方法将需要绘制的小图片设置为源。最后,使用 Paint 方法将图像绘制到画布上并销毁 Context 对象。

合并图像

最后,我们需要对以上内容进行组合,并编写一个函数将多个图片合并到一张画布上并保存到磁盘上。

下面是一个将图片合并到画布上的函数:

func mergeImages(images []string, out string, sw, sh, spacing int) error {

surfaceWidth := len(images)*(sw+spacing) - spacing

surfaceHeight := sh

surface := createSurface(surfaceWidth, surfaceHeight, spacing)

positions := initPositions(len(images), 1, sw, sh, spacing)

for idx, imageFile := range images {

reader, err := os.Open(imageFile)

if err != nil {

return err

}

defer reader.Close()

image, err := cairo.NewSurfaceFromPNG(reader)

if err != nil {

return err

}

defer image.Destroy()

x, y := positions[0][idx], positions[1][idx]

mergeImage(surface, image, x, y)

}

err := surface.WriteToPNG(out)

if err != nil {

return err

}

return nil

}

该函数接收四个参数:images、out、sw 和 sh。其中,images 是一个字符串切片,包含需要合并的所有图像的文件路径;out 表示输出文件的路径;sw 和 sh 分别表示图像宽度和高度。

在函数内部,首先根据图片数量计算出所需的画布大小,然后创建画布。接着,调用 initPositions 函数初始化每个小图片的位置。然后,循环遍历 images 对应的文件路径,读取图像并将其绘制到画布上。最后,将画布保存到磁盘。

到这里,您已经成功地将多个单独的图像合并为一个平铺图了!

运行程序

要启动程序,只需创建一个 main.go 文件,并在其中调用 mergeImages 函数即可。

例如,在 main.go 文件中添加以下内容:

package main

func main() {

images := []string{

"path/to/first/image.png",

"path/to/second/image.png",

"path/to/third/image.png",

}

out := "path/to/output/merged-image.png"

err := mergeImages(images, out, 300, 300, 10)

if err != nil {

panic(err)

}

}

请注意,在上面的 main 函数中,我们传递了三个参数 image、out 和 sw、sh 和 spacing。其中,前三个参数应分别表示您要合并的图像文件的路径、输出文件的路径和每个小图像的大小(以及小图像之间的间隔),请根据您的需求进行修改。

执行该程序并查看输出路径,您应该会看到一个包含所有输入图像的平铺图像。

总结

在本文中,我们介绍了如何使用 Golang 将多个单独的图像合并为一个平铺图。我们使用 go-cairo 包进行图像操作,并编写了代表创建画布、初始化位置、绘制单个图像和合并所有图像的函数。如果您需要使用 Golang 进行图像处理,本文提供的示例代码将是一个不错的起点。

后端开发标签