Golang 如何将 concatenate/append 图像相互发送
Golang how to concatenate/append images to one another
Go 有很好的图像处理和数据库,但是我在从小图像创建大图像时遇到了问题。有谁知道如何在 Golang 中获取两个 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 数据连接起来感到困惑 and/or 将其组合成 "empty" 图像。
创建一个新的空白图像 (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
是第一张图片的宽度。第一张图片的右下角将成为第二张图片的左下角。
//starting position of the second image (bottom left)
sp2 := image.Point{img1.Bounds().Dx(), 0}
它应该在一个足够大的矩形中。
//new rectangle for the second image
r2 := image.Rectangle{sp2, sp2.Add(img2.Bounds().Size())}
现在创建一个足以容纳两张图片的大矩形。
//rectangle for the big image
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 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)
}
}
我专门为此目的构建了一个库。
您可以按如下方式使用;
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})
调整图像大小并拼接它们。
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
}
Go 有很好的图像处理和数据库,但是我在从小图像创建大图像时遇到了问题。有谁知道如何在 Golang 中获取两个 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 数据连接起来感到困惑 and/or 将其组合成 "empty" 图像。
创建一个新的空白图像 (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
是第一张图片的宽度。第一张图片的右下角将成为第二张图片的左下角。
//starting position of the second image (bottom left)
sp2 := image.Point{img1.Bounds().Dx(), 0}
它应该在一个足够大的矩形中。
//new rectangle for the second image
r2 := image.Rectangle{sp2, sp2.Add(img2.Bounds().Size())}
现在创建一个足以容纳两张图片的大矩形。
//rectangle for the big image
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 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)
}
}
我专门为此目的构建了一个库。
您可以按如下方式使用;
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})
调整图像大小并拼接它们。
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
}