Golang how to concatenate/append images to one another
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)
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)
}
}