Golang如何将图像连接/追加到一起

huangapple go评论85阅读模式
英文:

Golang how to concatenate/append images to one another

问题

Go语言拥有出色的图像处理和数据库,但我在创建一个由多个小图像组成的大图像时遇到了问题。有人知道如何在Go语言中将两个或多个png或jpeg文件连接起来,形成一个包含这些文件的大图像吗?

我目前是这样读取png文件的:

imgFile, err := os.Open(path)
if err != nil {
    return Image{}, err
}
img, _, err := image.Decode(imgFile)
if err != nil {
    return Image{}, err
}

rgba := image.NewRGBA(img.Bounds())
if rgba.Stride != rgba.Rect.Size().X*4 {
    return Image{}, fmt.Errorf("unsupported stride")
}
draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src)

我对如何将这个png的RGBA数据与其他RGBA数据连接起来或将其合并到一个“空”图像中感到困惑。

英文:

Go has great image manipulation and data libraries however I'm having trouble creating one big image from smaller ones. Does anyone know how to take two png or jpeg files in Golang and concatenate them to form one big image that encompasses the two (or more) files?

i'm currently reading png files like so:

imgFile, err := os.Open(path)
if err != nil {
	return Image{}, err
}
img, _, err := image.Decode(imgFile)
if err != nil {
	return Image{}, err
}

rgba := image.NewRGBA(img.Bounds())
if rgba.Stride != rgba.Rect.Size().X*4 {
	return Image{}, fmt.Errorf("unsupported stride")
}
draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src)

I'm confused on how to take this png RGBA data and concatenate with other RGBA data and/or combine that into an "empty" image.

答案1

得分: 40

创建一个新的空图像(NewRGBA),其边界足够大以容纳两个图像。然后使用Draw方法将每个图像绘制在这个新大图像的适当部分。

以下是具体步骤和代码。

加载两个图像。

imgFile1, err := os.Open("test1.jpg")
if err != nil {
    fmt.Println(err)
}
imgFile2, err := os.Open("test2.jpg")
if err != nil {
    fmt.Println(err)
}
img1, _, err := image.Decode(imgFile1)
if err != nil {
    fmt.Println(err)
}
img2, _, err := image.Decode(imgFile2)
if err != nil {
    fmt.Println(err)
}

让我们将第二个图像绘制在第一个图像的右侧。因此,它的起始点应该在(w, 0),其中w是第一个图像的宽度。第一个图像的右下角点将是第二个图像的左下角点。

// 第二个图像的起始位置(左下角)
sp2 := image.Point{img1.Bounds().Dx(), 0}

它应该在足够大的矩形内。

// 第二个图像的新矩形
r2 := image.Rectangle{sp2, sp2.Add(img2.Bounds().Size())}

现在创建一个足够宽以容纳两个图像的大矩形。

// 大图像的矩形
r := image.Rectangle{image.Point{0, 0}, r2.Max}

注意:这个大图像的高度将与第二个图像的高度相同。如果第一个图像更高,它将被裁剪。

创建一个新图像。

rgba := image.NewRGBA(r)

现在,你可以将两个图像绘制到这个新图像中。

draw.Draw(rgba, img1.Bounds(), img1, image.Point{0, 0}, draw.Src)
draw.Draw(rgba, r2, img2, image.Point{0, 0}, draw.Src)

由于我们创建了r2,所以它在第一个图像的右侧,第二个图像将被绘制在右侧。

最后,你可以导出它。

out, err := os.Create("./output.jpg")
if err != nil {
    fmt.Println(err)
}

var opt jpeg.Options
opt.Quality = 80

jpeg.Encode(out, rgba, &opt)
英文:

Create a new empty image (NewRGBA) that has bounds large enough to hold both images. Then use the Draw method to draw each image on appropriate parts of this new large image.

Here are steps with code.

Load two images.

imgFile1, err := os.Open("test1.jpg")
if err != nil {
	fmt.Println(err)
}
imgFile2, err := os.Open("test2.jpg")
if err != nil {
	fmt.Println(err)
}
img1, _, err := image.Decode(imgFile1)
if err != nil {
	fmt.Println(err)
}
img2, _, err := image.Decode(imgFile2)
if err != nil {
	fmt.Println(err)
}

Let's draw the second image to the right of the first image. So the starting point of it should be at (w, 0) where w is the width of the first image. The bottom right point of the first image will be the bottom left point of the second.

//starting position of the second image (bottom left)
sp2 := image.Point{img1.Bounds().Dx(), 0}

It should be in a rectangle large enough to hold it.

//new rectangle for the second image
r2 := image.Rectangle{sp2, sp2.Add(img2.Bounds().Size())}

Now create a large rectangle that will be wide enough to hold both images.

//rectangle for the big image
r := image.Rectangle{image.Point{0, 0}, r2.Max}

Note This large image will have the height of the second image. If the first image is higher it will be cropped.

Create a new image.

rgba := image.NewRGBA(r)

Now you can draw the two images into this new image

draw.Draw(rgba, img1.Bounds(), img1, image.Point{0, 0}, draw.Src)
draw.Draw(rgba, r2, img2, image.Point{0, 0}, draw.Src)

Since we created r2 so its to the right of the first image, second image will be drawn to the right.

Finally you can export it.

out, err := os.Create("./output.jpg")
if err != nil {
	fmt.Println(err)
}

var opt jpeg.Options
opt.Quality = 80

jpeg.Encode(out, rgba, &opt)

答案2

得分: 9

你的生活会变得更轻松,如果你将一些东西封装成函数,并创建一个结构体来处理每个像素。

// 创建一个用于处理像素的结构体
type Pixel struct {
Point image.Point
Color color.Color
}

// 保持DRY原则,不需要重复打开文件和解码
func OpenAndDecode(filepath string) (image.Image, string, error) {
imgFile, err := os.Open(filepath)
if err != nil {
panic(err)
}
defer imgFile.Close()
img, format, err := image.Decode(imgFile)
if err != nil {
panic(err)
}
return img, format, nil
}

// 将image.Image的像素数据解码为[]*Pixel
func DecodePixelsFromImage(img image.Image, offsetX, offsetY int) []*Pixel {
pixels := []*Pixel{}
for y := 0; y <= img.Bounds().Max.Y; y++ {
for x := 0; x <= img.Bounds().Max.X; x++ {
p := &Pixel{
Point: image.Point{x + offsetX, y + offsetY},
Color: img.At(x, y),
}
pixels = append(pixels, p)
}
}
return pixels
}

func main() {
img1, _, err := OpenAndDecode("makey.png")
if err != nil {
panic(err)
}
img2, _, err := OpenAndDecode("sample.jpg")
if err != nil {
panic(err)
}
// 从每个图像收集像素数据
pixels1 := DecodePixelsFromImage(img1, 0, 0)
// 第二个图像的Y偏移量为img1的最大Y值(附加在底部)
pixels2 := DecodePixelsFromImage(img2, 0, img1.Bounds().Max.Y)
pixelSum := append(pixels1, pixels2...)

// 为新图像设置新的大小,宽度等于较大图像的最大宽度,高度等于两个图像的高度之和
newRect := image.Rectangle{
    Min: img1.Bounds().Min,
    Max: image.Point{
        X: img2.Bounds().Max.X,
        Y: img2.Bounds().Max.Y + img1.Bounds().Max.Y,
    },
}
finImage := image.NewRGBA(newRect)
// 这是很酷的部分,你只需要循环遍历每个像素,并即时设置图像的颜色
for _, px := range pixelSum {
    finImage.Set(
        px.Point.X,
        px.Point.Y,
        px.Color,
    )
}
draw.Draw(finImage, finImage.Bounds(), finImage, image.Point{0, 0}, draw.Src)

// 创建一个新文件并将图像写入其中
out, err := os.Create("./output.png")
if err != nil {
    panic(err)
    os.Exit(1)
}
err = png.Encode(out, finImage)
if err != nil {
    panic(err)
    os.Exit(1)
}

}

英文:

Your life would be much easier if you make a few things into functions and create a struct to make sense of each pixel.

// Create a struct to deal with pixel
type Pixel struct {
	Point image.Point
	Color color.Color
}

// Keep it DRY so don&#39;t have to repeat opening file and decode
func OpenAndDecode(filepath string) (image.Image, string, error) {
	imgFile, err := os.Open(filepath)
	if err != nil {
	    panic(err)
	}
	defer imgFile.Close()
	img, format, err := image.Decode(imgFile)
	if err != nil {
	    panic(err)
	}
	return img, format, nil
}

// Decode image.Image&#39;s pixel data into []*Pixel
func DecodePixelsFromImage(img image.Image, offsetX, offsetY int) []*Pixel {
	pixels := []*Pixel{}
	for y := 0; y &lt;= img.Bounds().Max.Y; y++ {
		for x := 0; x &lt;= img.Bounds().Max.X; x++ {
			p := &amp;Pixel{
				Point: image.Point{x + offsetX, y + offsetY},
				Color: img.At(x, y),
			}
			pixels = append(pixels, p)
		}
	}
	return pixels
}

func main() {
	img1, _, err := OpenAndDecode(&quot;makey.png&quot;)
	if err != nil {
		panic(err)
	}
	img2, _, err := OpenAndDecode(&quot;sample.jpg&quot;)
	if err != nil {
		panic(err)
	}
    // collect pixel data from each image
	pixels1 := DecodePixelsFromImage(img1, 0, 0)
    // the second image has a Y-offset of img1&#39;s max Y (appended at bottom)
	pixels2 := DecodePixelsFromImage(img2, 0, img1.Bounds().Max.Y)
	pixelSum := append(pixels1, pixels2...)

    // Set a new size for the new image equal to the max width
    // of bigger image and max height of two images combined
	newRect := image.Rectangle{
		Min: img1.Bounds().Min,
		Max: image.Point{
			X: img2.Bounds().Max.X,
			Y: img2.Bounds().Max.Y + img1.Bounds().Max.Y,
		},
	}
	finImage := image.NewRGBA(newRect)
    // This is the cool part, all you have to do is loop through
    // each Pixel and set the image&#39;s color on the go
	for _, px := range pixelSum {
			finImage.Set(
				px.Point.X,
				px.Point.Y,
				px.Color,
			)
	}
	draw.Draw(finImage, finImage.Bounds(), finImage, image.Point{0, 0}, draw.Src)

    // Create a new file and write to it
	out, err := os.Create(&quot;./output.png&quot;)
	if err != nil {
		panic(err)
		os.Exit(1)
	}
	err = png.Encode(out, finImage)
	if err != nil {
		panic(err)
		os.Exit(1)
	}
}

答案3

得分: 8

我为这个目的专门构建了一个库。

您可以按照以下方式使用它:

import gim "github.com/ozankasikci/go-image-merge"

grids := []*gim.Grid{
    {ImageFilePath: "test1.jpg"},
    {ImageFilePath: "test2.png"},
}

// 将图像合并为2x1的网格
rgba, err := gim.New(grids, 2, 1).Merge()

// 将输出保存为jpg或png
file, err := os.Create("file/path.jpg|png")
err = jpeg.Encode(file, rgba, &jpeg.Options{Quality: 80})

https://github.com/ozankasikci/go-image-merge

英文:

I built a library exactly for this purpose.

You can use it as follows;

import gim &quot;github.com/ozankasikci/go-image-merge&quot;

grids := []*gim.Grid{
    {ImageFilePath: &quot;test1.jpg&quot;},
    {ImageFilePath: &quot;test2.png&quot;},
}

// merge the images into a 2x1 grid
rgba, err := gim.New(grids, 2, 1).Merge()

// save the output to jpg or png
file, err := os.Create(&quot;file/path.jpg|png&quot;)
err = jpeg.Encode(file, rgba, &amp;jpeg.Options{Quality: 80})

https://github.com/ozankasikci/go-image-merge

答案4

得分: 0

调整图像大小并拼接它们。

import (
	"fmt"
	"github.com/nfnt/resize"
	"image"
	"image/draw"
)

/**
w: 调整后的宽度
h: 调整后的高度
v: 是否垂直拼接
images: 图像列表
*/
func Concat(w uint, h uint, v bool, images ...image.Image) *image.RGBA {

	var acc uint = 0
	if w == 0 {
		v = false
	} else if h == 0 {
		v = true
	}

	for i, img := range images {
		rimg := resize.Resize(w, h, img, resize.Bilinear)
		if v { // 垂直拼接,累加高度
			acc += uint(rimg.Bounds().Dy())
		} else {
			acc += uint(rimg.Bounds().Dx())
		}
		images[i] = rimg
	}

	if v {
		h = acc
	} else {
		w = acc
	}

	r := image.Rectangle{image.Point{0, 0}, image.Point{int(w), int(h)}}
	rgba := image.NewRGBA(r)

	dx := 0
	dy := 0

	for _, img := range images {

		rec := img.Bounds()
		draw.Draw(rgba, image.Rect(dx, dy, dx+rec.Dx(), dy+rec.Dy()), img, image.Point{0, 0}, draw.Src)
		fmt.Println(image.Point{dx, dy})
		if v {
			dy += img.Bounds().Dy()
		} else {
			dx += img.Bounds().Dx()
		}
	}

	return rgba
}
英文:

Resize images and concat them.

import (
&quot;fmt&quot;
&quot;github.com/nfnt/resize&quot;
&quot;image&quot;
&quot;image/draw&quot;
)
/**
w: resize weight
h: resize height
v: concat dim
images: list
*/
func Concat(w uint, h uint, v bool, images ...image.Image) *image.RGBA {
var acc uint = 0
if w == 0 {
v = false
} else if h == 0 {
v = true
}
for i, img := range images {
rimg := resize.Resize(w, h, img, resize.Bilinear)
if v { // vertical concat, accumulate height
acc += uint(rimg.Bounds().Dy())
} else {
acc += uint(rimg.Bounds().Dx())
}
images[i] = rimg
}
if v {
h = acc
} else {
w = acc
}
r := image.Rectangle{image.Point{0, 0}, image.Point{int(w), int(h)}}
rgba := image.NewRGBA(r)
dx := 0
dy := 0
for _, img := range images {
rec := img.Bounds()
draw.Draw(rgba, image.Rect(dx, dy, dx+rec.Dx(), dy+rec.Dy()), img, image.Point{0, 0}, draw.Src)
fmt.Println(image.Point{dx, dy})
if v {
dy += img.Bounds().Dy()
} else {
dx += img.Bounds().Dx()
}
}
return rgba
}

huangapple
  • 本文由 发表于 2016年3月13日 07:36:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/35964656.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定