'Golang how to concatenate/append images to one another
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.
Solution 1:[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'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's pixel data into []*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)
}
// collect pixel data from each image
pixels1 := DecodePixelsFromImage(img1, 0, 0)
// the second image has a Y-offset of img1'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'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("./output.png")
if err != nil {
panic(err)
os.Exit(1)
}
err = png.Encode(out, finImage)
if err != nil {
panic(err)
os.Exit(1)
}
}
Solution 2:[2]
I built a library exactly for this purpose.
You can use it as follows;
import gim "github.com/ozankasikci/go-image-merge"
grids := []*gim.Grid{
{ImageFilePath: "test1.jpg"},
{ImageFilePath: "test2.png"},
}
// 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("file/path.jpg|png")
err = jpeg.Encode(file, rgba, &jpeg.Options{Quality: 80})
Solution 3:[3]
Resize images and concat them.
import (
"fmt"
"github.com/nfnt/resize"
"image"
"image/draw"
)
/**
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
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | Sailist |
