2016-03-12 13 views
6

Go ma świetną manipulację grafiką i bibliotekami danych, jednak mam problem z utworzeniem jednego dużego obrazu z mniejszych. Czy ktoś wie, jak wziąć dwa pliki png lub jpeg w Golang i połączyć je, tworząc jeden duży obraz, który obejmuje dwa (lub więcej) plików?Golang jak łączyć/dołączać obrazy do siebie

Jestem obecnie czytania plików PNG tak:

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) 

Jestem zdezorientowany, jak wziąć to png dane RGBA i łączyć z innymi danymi RGBA i/lub połączone w „pustym” obraz .

+3

Jakie masz problemy? Czego spróbowałeś do tej pory? W każdym razie, ogólną zasadą jest utworzenie nowego, dużego obrazu docelowego, a następnie skopiowanie pikseli ze zdjęć źródłowych do obrazu docelowego. – Aedolon

+0

Cóż, do tej pory mogę pobierać dane pikseli z png i tworzyć nowe RGBA, a następnie dekodować je wewnętrznie jako png, które przechodzę do OpenGL. Jednak nie jestem całkiem pewien, jak wziąć to "zdekodowane" dane png, które odczytałem z pliku .png i skopiować/utworzyć większy obraz z tego – efel

Odpowiedz

10

Utwórz nowy pusty obraz (NewRGBA), który ma wystarczająco duże krawędzie, aby pomieścić oba obrazy. Następnie użyj metody Draw, aby narysować każdy obraz na odpowiednich częściach tego nowego dużego obrazu.

Oto kroki z kodem.

Załaduj dwa obrazy.

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

Narysujmy drugi obraz po prawej stronie pierwszego obrazu. Punktem wyjścia powinno być więc (w, 0), gdzie w jest szerokością pierwszego obrazu. Dolny prawy punkt pierwszego obrazu będzie dolnym lewym punktem drugiego.

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

Powinien znajdować się w prostokątu wystarczająco dużym, aby go pomieścić.

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

Teraz utwórz duży prostokąt, który będzie wystarczająco szeroki, aby pomieścić oba obrazy.

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

Uwaga Ten duży obraz będzie miał wysokość drugiego obrazu. Jeśli pierwszy obraz jest wyższy, zostanie przycięty.

Utwórz nowy obraz.

rgba := image.NewRGBA(r) 

Teraz można wyciągnąć dwa obrazy w tym nowym obrazie

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

Ponieważ stworzyliśmy r2 więc jej na prawo od pierwszego obrazu, drugi obraz zostanie sporządzony w prawo.

Wreszcie można go wyeksportować.

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

var opt jpeg.Options 
opt.Quality = 80 

jpeg.Encode(out, rgba, &opt) 
+1

Bardzo podoba mi się ta odpowiedź, ponieważ jest bardzo dobrze sformatowana, objaśnia każdą sekcję indywidualnie, zwięźle, wykorzystał wiele istniejących funkcji go. – efel

+0

efel, dzięki. wygląda na to, że pomyliłem się między prawym a lewym ealiarem. edytowane do naprawienia :) –

6

Twoje życie będzie o wiele łatwiejsze, jeśli wprowadzisz kilka funkcji do funkcji i stworzysz strukturę, która będzie zrozumiała dla każdego piksela.

// 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) 
    } 
} 
+2

Dużo pracy poszło do tej odpowiedzi, też ją lubię. Bardzo modułowy. – efel