2013-02-01 11 views
5

Próbuję napisać krótki, który odczyta plik PNG i zamieni jeden kanał na inny (R, G, B) będący możliwe wybory.Golang - Zamień kanały PNG obrazu przy użyciu obrazu i obrazu/PNG

Nie mogę się jednak dowiedzieć, jak wyodrębnić liczbę całkowitą z obiektu color.Color zwróconego przez image.At (x, y). Zapisanie go z powrotem byłoby łatwiejsze dzięki image.Set (x, y, color), kiedy mogę skonstruować nowy kolor RGBA z zamienionymi kanałami.

Oto jestem teraz (można dość dużo przejść do ostatniej pętli):

package main 

import (
"flag" 
"fmt" 
//"image" 
"image/color" 
"image/png" 
"os" 
) 

type Choice struct { 
value string 
valid bool 
} 

func (c *Choice) validate() { 
goodchoices := []string{"R", "G", "B"} 
for _, v := range goodchoices { 
    if c.value == v { 
     c.valid = true 
    } 
} 
} 

func main() { 

var fname string 
var c1 Choice 
var c2 Choice 

flag.StringVar(&c1.value, "c1", "", "The color channel to swap - R or G or B ") 
flag.StringVar(&c2.value, "c2", "", "The color channel to swap with - R or G or B ") 
flag.StringVar(&fname, "f", "", "A .png image (normal map)") 
flag.Parse() 

c1.validate() 
c2.validate() 

if c1.valid == true && c2.valid == true { 
    fmt.Println("We could proceed..") 
    fmt.Println("Swapping channels:", c1.value, "<->", c2.value, "In", fname) //for testing 
} else { 
    fmt.Println("Invalid channel... Please use R, G or B.") 
    return 
} 

file, err := os.Open(fname) 
if err != nil { 
    fmt.Println(err) 
    return 
} 
defer file.Close() 

pic, err := png.Decode(file) 
if err != nil { 
    fmt.Fprintf(os.Stderr, "%s: %v\n", fname, err) 
    return 
} 

b := pic.Bounds() 

for y := b.Min.Y; y < b.Max.Y; y++ { 
    for x := b.Min.X; x < b.Max.X; x++ { 
     col := pic.At(x, y) 
     ???? How do I swap the channels in col ???? 
    } 
} 
} 

Jestem naprawdę nowego iść i programowania w ogóle, więc proszę traktować je w swojej odpowiedzi. Dziękuję Ci.

+1

Używam obrazów RGBA png (są to normalne mapy wyeksportowane z Blender3D). – Zoltan

Odpowiedz

7

Hmmm, to było trudniejsze, niż myślałem, że to będzie - zastanawiam się, czy ktoś może wymyślić lepszy pomysł!

Problem polega na tym, że nie znasz konkretnego typu, który zwraca png.Decode - może on zwracać dowolny typ obrazu. Masz tylko interfejs image.Image, który nie ma metody Set.

Aby obejść, że po pierwsze zdefiniować interfejs którym wszystkie typy obrazów, które można ustawić pikseli spełnia

type ImageSet interface { 
    Set(x, y int, c color.Color) 
} 

Następny zobaczyć czy pic implementuje ten interfejs (GO panikę, jeśli nie - użyj picSet , ok forma jeśli przeszkadza)

// Get an interface which can set pixels 
picSet := pic.(ImageSet) 

teraz pętla wygląda tak - ja tylko zamieniłem czerwony i zielony, dzięki czemu można zobaczyć ten pomysł.

for y := b.Min.Y; y < b.Max.Y; y++ { 
    for x := b.Min.X; x < b.Max.X; x++ { 
     col := pic.At(x, y) 
     r, g, b, a := col.RGBA() 
     // Swap green and red 
     newCol := color.RGBA{uint8(g>>8), uint8(r>>8), uint8(b>>8), uint8(a>>8)} 
     picSet.Set(x, y, newCol) 
    } 
} 

Podejrzewam, że wersja wysokie prowadzenie ten musiałby użyć przełącznika typu, aby określić, który obraz wpisać tak, to masz kod dostosowany do każdego z uint8s dla 24-bitowych obrazów i uint16s 48 bit obrazy itp.

Here is the complete working przykład, jeśli chcesz mieć szansę. Nie działa na placu zabaw - musisz go pobrać.


Aktualizacja: Właśnie zauważyłem komentarz. Jeśli wiesz, że masz obraz RGBA, możesz użyć asercji typu, aby uzyskać podstawowy obraz, co znacznie ułatwia sprawę.

// Get an image.RGBA if it is one 
rgba, ok := pic.(*image.RGBA) 
if !ok { 
    fmt.Println("That wasn't an RGBA!") 
    return 
} 

for y := b.Min.Y; y < b.Max.Y; y++ { 
    for x := b.Min.X; x < b.Max.X; x++ { 
     // Note type assertion to get a color.RGBA 
     col := rgba.At(x, y).(color.RGBA) 
     // Swap green and red 
     col.G, col.R = col.R, col.G 
     rgba.Set(x, y, col) 
    } 
} 
+0

Dokładnie to, czego szukałem .. Wielkie dzięki! Przepraszamy za dodatkową pracę z obrazami paletowanymi .. :) Powinienem był to uwzględnić w głównej treści postu. Niesamowite. Po prostu z ciekawości: Wygląda na to, że muszę napisać test przełącznika/obudowy wewnątrz wewnętrznej pętli, aby sprawdzić, który kanał wybieramy, aby go zastąpić. Czuje się (bardzo) nieefektywnie. Czy może być inaczej? – Zoltan

+0

Są tylko 3 możliwe przełączniki, które można zrobić, więc piszę 3 funkcje, aby je wykonać i użyć tabeli funkcji, aby ustalić, który z nich jest potrzebny. (Lub użyj przełącznika, aby wybrać wskaźnik funkcji). Następnie możesz po prostu wywołać wybraną funkcję w pętli za pomocą wskaźnika, który będzie miły i szybki. –

+0

Rzeczywiście ... Jeszcze raz dziękuję! – Zoltan

Powiązane problemy