Ricerca…


introduzione

Il pacchetto di immagini offre funzionalità di base per lavorare con immagini 2D. Questo argomento descrive diverse operazioni di base quando si lavora con immagini come la lettura e la scrittura di un particolare formato di immagine, il ritaglio, l'accesso e la modifica di pixel , la conversione del colore, il ridimensionamento e il filtro di base delle immagini.

Concetti basilari

Un'immagine rappresenta una griglia rettangolare di elementi dell'immagine ( pixel ). Nel pacchetto immagine , il pixel è rappresentato come uno dei colori definiti nel pacchetto immagine / colore . La geometria 2D dell'immagine è rappresentata come immagine. image.Rectangle , mentre image.Point indica una posizione sulla griglia.

Immagine e geometria 2D

La figura sopra illustra i concetti di base di un'immagine nel pacchetto. Un'immagine di dimensioni 15x14 pixel ha un limite rettangolare iniziato nell'angolo in alto a sinistra (es. Coordinata (-3, -4) nella figura sopra), e i suoi assi aumentano verso destra e verso il basso nell'angolo in basso a destra (es. Coordinata ( 12, 10) nella figura). Si noti che i limiti non iniziano necessariamente da o contengono il punto (0,0) .

Immagine relativa al tipo

In Go , un'immagine implementa sempre la seguente image.Image Interfaccia immagine

type Image interface {
    // ColorModel returns the Image's color model.
    ColorModel() color.Model
    // Bounds returns the domain for which At can return non-zero color.
    // The bounds do not necessarily contain the point (0, 0).
    Bounds() Rectangle
    // At returns the color of the pixel at (x, y).
    // At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid.
    // At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one.
    At(x, y int) color.Color
}

in cui l'interfaccia color.Color è definita come

type Color interface {
    // RGBA returns the alpha-premultiplied red, green, blue and alpha values
    // for the color. Each value ranges within [0, 0xffff], but is represented
    // by a uint32 so that multiplying by a blend factor up to 0xffff will not
    // overflow.
    //
    // An alpha-premultiplied color component c has been scaled by alpha (a),
    // so has valid values 0 <= c <= a.
    RGBA() (r, g, b, a uint32)
}

e color.Model è un'interfaccia dichiarata come

type Model interface {
    Convert(c Color) Color
}

Accesso alla dimensione e al pixel dell'immagine

Supponiamo di avere un'immagine memorizzata come img variabile, quindi possiamo ottenere la dimensione e il pixel dell'immagine per:

// Image bounds and dimension
b := img.Bounds()
width, height := b.Dx(), b.Dy()
// do something with dimension ...

// Corner co-ordinates
top := b.Min.Y
left := b.Min.X
bottom := b.Max.Y
right := b.Max.X
    
// Accessing pixel. The (x,y) position must be
// started from (left, top) position not (0,0)
for y := top; y < bottom; y++ {
    for x := left; x < right; x++ {
        cl := img.At(x, y)
        r, g, b, a := cl.RGBA()
        // do something with r,g,b,a color component
    }
}

Si noti che nel pacchetto, il valore di ciascun componente R,G,B,A è compreso tra 0-65535 ( 0x0000 - 0xffff ), non 0-255 .

Caricamento e salvataggio dell'immagine

In memoria, un'immagine può essere vista come una matrice di pixel (colore). Tuttavia, quando un'immagine viene archiviata in una memoria permanente, può essere memorizzata come è (formato RAW), bitmap o altri formati di immagine con un particolare algoritmo di compressione per risparmiare spazio di archiviazione, ad esempio PNG, JPEG, GIF, ecc. Quando si carica un'immagine con un particolare formato, l'immagine deve essere decodificata in immagine. image.Image con algoritmo corrispondente. Una funzione image.Decode dichiarata come

func Decode(r io.Reader) (Image, string, error)

è fornito per questo particolare utilizzo. Per poter gestire vari formati di immagine, prima di chiamare la funzione image.Decode , il decoder deve essere registrato tramite l' image.RegisterFormat Funzione image.RegisterFormat definita come

func RegisterFormat(name, magic string, 
    decode func(io.Reader) (Image, error), decodeConfig func(io.Reader) (Config, error))

Attualmente, il pacchetto di immagini supporta tre formati di file: JPEG , GIF e PNG . Per registrare un decodificatore, aggiungere quanto segue

import _ "image/jpeg"    //register JPEG decoder

al pacchetto main dell'applicazione. Da qualche parte nel tuo codice (non necessario nel pacchetto main ), per caricare un'immagine JPEG, usa i seguenti frammenti:

f, err := os.Open("inputimage.jpg")
if err != nil {
    // Handle error
}
defer f.Close()

img, fmtName, err := image.Decode(f)
if err != nil {
    // Handle error
}

// `fmtName` contains the name used during format registration
// Work with `img` ...

Salva in PNG

Per salvare un'immagine in un formato particolare, il codificatore corrispondente deve essere importato esplicitamente, ad es

import "image/png"    //needed to use `png` encoder

quindi un'immagine può essere salvata con i seguenti frammenti:

f, err := os.Create("outimage.png")
if err != nil {
    // Handle error
}
defer f.Close()

// Encode to `PNG` with `DefaultCompression` level
// then save to file
err = png.Encode(f, img)
if err != nil {
    // Handle error
}

Se si desidera specificare un livello di compressione diverso dal livello DefaultCompression , creare un encoder , ad es

enc := png.Encoder{
    CompressionLevel: png.BestSpeed,
}
err := enc.Encode(f, img)

Salva in JPEG

Per salvare in formato jpeg , utilizzare quanto segue:

import "image/jpeg"

// Somewhere in the same package
f, err := os.Create("outimage.jpg")
if err != nil {
    // Handle error
}
defer f.Close()

// Specify the quality, between 0-100
// Higher is better
opt := jpeg.Options{
    Quality: 90,
}
err = jpeg.Encode(f, img, &opt)
if err != nil {
    // Handle error
}

Salva in GIF

Per salvare l'immagine nel file GIF, utilizzare i seguenti frammenti.

import "image/gif"

// Samewhere in the same package
f, err := os.Create("outimage.gif")
if err != nil {
    // Handle error
}
defer f.Close()

opt := gif.Options {
    NumColors: 256, 
    // Add more parameters as needed
}

err = gif.Encode(f, img, &opt)
if err != nil {
    // Handle error
}

Ritagliare l'immagine

La maggior parte del tipo di immagine nel pacchetto di immagini con SubImage(r Rectangle) Image Metodo SubImage(r Rectangle) Image , ad eccezione di image.Uniform . Sulla base di questo fatto, possiamo implementare una funzione per ritagliare un'immagine arbitraria come segue

func CropImage(img image.Image, cropRect image.Rectangle) (cropImg image.Image, newImg bool) {
    //Interface for asserting whether `img`
    //implements SubImage or not.
    //This can be defined globally.
    type CropableImage interface {
        image.Image
        SubImage(r image.Rectangle) image.Image
    }

    if p, ok := img.(CropableImage); ok {
        // Call SubImage. This should be fast,
        // since SubImage (usually) shares underlying pixel.
        cropImg = p.SubImage(cropRect)
    } else if cropRect = cropRect.Intersect(img.Bounds()); !cropRect.Empty() {
        // If `img` does not implement `SubImage`,
        // copy (and silently convert) the image portion to RGBA image.
        rgbaImg := image.NewRGBA(cropRect)
        for y := cropRect.Min.Y; y < cropRect.Max.Y; y++ {
            for x := cropRect.Min.X; x < cropRect.Max.X; x++ {
                rgbaImg.Set(x, y, img.At(x, y))
            }
        }
        cropImg = rgbaImg
        newImg = true
    } else {
        // Return an empty RGBA image
        cropImg = &image.RGBA{}
        newImg = true
    }

    return cropImg, newImg
}

Si noti che l'immagine ritagliata potrebbe condividere i suoi pixel sottostanti con l'immagine originale. Se questo è il caso, qualsiasi modifica all'immagine ritagliata influenzerà l'immagine originale.

Converti l'immagine a colori in scala di grigi

È sufficiente un algoritmo di elaborazione delle immagini digitali come il rilevamento dei bordi, le informazioni trasportate dall'intensità dell'immagine (ad es. Il valore della scala dei grigi). L'uso delle informazioni sul colore (canale R, G, B ) può fornire risultati leggermente migliori, ma la complessità dell'algoritmo sarà aumentata. Pertanto, in questo caso, è necessario convertire l'immagine a colori in un'immagine in scala di grigi prima di applicare tale algoritmo.

Il seguente codice è un esempio di conversione di un'immagine arbitraria in un'immagine in scala di grigi a 8 bit. L'immagine viene recuperata dalla posizione remota utilizzando il pacchetto net/http , convertita in scala di grigi e infine salvata come immagine PNG.

package main

import (
    "image"
    "log"
    "net/http"
    "os"

    _ "image/jpeg"
    "image/png"
)

func main() {
    // Load image from remote through http
    // The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/)
    // Images are available under the Creative Commons 3.0 Attributions license.
    resp, err := http.Get("http://golang.org/doc/gopher/fiveyears.jpg")
    if err != nil {
        // handle error
        log.Fatal(err)
    }
    defer resp.Body.Close()

    // Decode image to JPEG
    img, _, err := image.Decode(resp.Body)
    if err != nil {
        // handle error
        log.Fatal(err)
    }
    log.Printf("Image type: %T", img)

    // Converting image to grayscale
    grayImg := image.NewGray(img.Bounds())
    for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
        for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
            grayImg.Set(x, y, img.At(x, y))
        }
    }

    // Working with grayscale image, e.g. convert to png
    f, err := os.Create("fiveyears_gray.png")
    if err != nil {
        // handle error
        log.Fatal(err)
    }
    defer f.Close()

    if err := png.Encode(f, grayImg); err != nil {
        log.Fatal(err)
    }
}

La conversione del colore si verifica quando si assegna pixel attraverso Set(x, y int, c color.Color) che è implementato in image.go come

func (p *Gray) Set(x, y int, c color.Color) {
    if !(Point{x, y}.In(p.Rect)) {
        return
    }

    i := p.PixOffset(x, y)
    p.Pix[i] = color.GrayModel.Convert(c).(color.Gray).Y
}

in cui, color.GrayModel è definito in color.go come

func grayModel(c Color) Color {
    if _, ok := c.(Gray); ok {
        return c
    }
    r, g, b, _ := c.RGBA()

    // These coefficients (the fractions 0.299, 0.587 and 0.114) are the same
    // as those given by the JFIF specification and used by func RGBToYCbCr in
    // ycbcr.go.
    //
    // Note that 19595 + 38470 + 7471 equals 65536.
    //
    // The 24 is 16 + 8. The 16 is the same as used in RGBToYCbCr. The 8 is
    // because the return value is 8 bit color, not 16 bit color.
    y := (19595*r + 38470*g + 7471*b + 1<<15) >> 24

    return Gray{uint8(y)}
}

In base ai fatti sopra riportati, l'intensità Y viene calcolata con la seguente formula:

Luminanza: Y = 0,299 R + 0,587 G + 0,114 B

Se vogliamo applicare diverse formule / algoritmi per convertire un colore in un'intesità, ad es

Media: Y = ( R + G + B ) / 3
Luma: Y = 0,2126 R + 0,7152 G + 0,0722 B
Lustro: Y = (min ( R , G , B ) + max ( R , G , B )) / 2

quindi, è possibile utilizzare i seguenti frammenti.

// Converting image to grayscale
grayImg := image.NewGray(img.Bounds())
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
    for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
        R, G, B, _ := img.At(x, y).RGBA()
        //Luma: Y = 0.2126*R + 0.7152*G + 0.0722*B
        Y := (0.2126*float64(R) + 0.7152*float64(G) + 0.0722*float64(B)) * (255.0 / 65535)
        grayPix := color.Gray{uint8(Y)}
        grayImg.Set(x, y, grayPix)
    }
}

Il calcolo sopra è fatto per moltiplicazione in virgola mobile, e certamente non è il più efficiente, ma è sufficiente per dimostrare l'idea. L'altro punto è, quando si chiama Set(x, y int, c color.Color) con color.Gray come terzo argomento, il modello di colore non eseguirà la conversione del colore come si può vedere nella precedente funzione grayModel .



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow