Zoeken…


Invoering

Het afbeeldingspakket biedt basisfuncties voor het werken met 2D-afbeeldingen. Dit onderwerp beschrijft verschillende basisbewerkingen bij het werken met afbeeldingen, zoals het lezen en schrijven van een bepaalde afbeeldingsindeling, bijsnijden, toegang tot en wijzigen van pixels , kleurconversie, formaat wijzigen en basisafbeeldingsfiltering.

Basisconcepten

Een afbeelding vertegenwoordigt een rechthoekig raster met beeldelementen ( pixel ). In het afbeeldingspakket wordt de pixel weergegeven als een van de kleuren die zijn gedefinieerd in het afbeeldings- / kleurenpakket . De 2D-geometrie van de afbeelding wordt weergegeven als image.Rectangle . image.Rectangle , terwijl image.Point een positie op het raster aangeeft.

Afbeelding en 2D-geometrie

De bovenstaande afbeelding illustreert de basisconcepten van een afbeelding in het pakket. Een afbeelding met een afmeting van 15x14 pixels heeft een rechthoekige begrenzing die in de linkerbovenhoek is gestart (bijvoorbeeld coördinaat (-3, -4) in de bovenstaande afbeelding), en de assen ervan vergroten naar rechts en naar beneden naar de rechter benedenhoek (bijv. Coördinaat ( 12, 10) in de afbeelding). Merk op dat de grenzen niet noodzakelijk beginnen bij of punt (0,0) bevatten .

Afbeelding gerelateerd type

In Go implementeert een afbeelding altijd de volgende image.Image interface

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
}

waarin de interface color.Color is gedefinieerd als

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)
}

en color.Model is een interface die is gedeclareerd als

type Model interface {
    Convert(c Color) Color
}

Toegang tot afbeeldingsdimensie en pixel

Stel dat we een afbeelding hebben opgeslagen als variabele img , dan kunnen we de dimensie en afbeeldingspixel verkrijgen door:

// 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
    }
}

Merk op dat in het pakket de waarde van elke component R,G,B,A tussen 0-65535 ( 0x0000 - 0xffff ) ligt, niet 0-255 .

Afbeelding laden en opslaan

In het geheugen kan een afbeelding worden gezien als een matrix van pixels (kleur). Wanneer een afbeelding wordt opgeslagen in een permanente opslag, kan deze echter worden opgeslagen zoals deze is (RAW-indeling), Bitmap of andere afbeeldingsindelingen met een specifiek compressiealgoritme om opslagruimte te besparen, bijvoorbeeld PNG, JPEG, GIF, enz. Bij het laden van een afbeelding met een bepaald formaat moet de afbeelding worden gedecodeerd naar image.Image . image.Image met bijbehorend algoritme. Een functie image.Decode aangegeven als

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

is voorzien voor dit specifieke gebruik. Om verschillende afbeeldingsformaten te kunnen verwerken, moet de decoder worden geregistreerd via image, voordat de image.Decode wordt image.RegisterFormat

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

Momenteel ondersteunt het afbeeldingspakket drie bestandsindelingen: JPEG , GIF en PNG . Voeg het volgende toe om een decoder te registreren

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

om de toepassing van de main pakket. Ergens in uw code (niet noodzakelijk in main pakket), om een JPEG-afbeelding te laden, gebruikt u de volgende fragmenten:

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` ...

Opslaan in PNG

Om een afbeelding in een bepaald formaat op te slaan, moet de bijbehorende encoder expliciet worden geïmporteerd, dat wil zeggen

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

dan kan een afbeelding worden opgeslagen met de volgende fragmenten:

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
}

Als u een ander compressieniveau dan het DefaultCompression wilt opgeven, maakt u een encoder , bijv

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

Opslaan naar JPEG

Gebruik het volgende om op te slaan in jpeg indeling:

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
}

Opslaan in GIF

Gebruik de volgende fragmenten om de afbeelding in het GIF-bestand op te slaan.

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
}

Afbeelding bijsnijden

De meeste afbeeldingen in het afbeeldingspakket met de SubImage(r Rectangle) Image , behalve image.Uniform . Op basis van dit feit kunnen we als volgt een functie implementeren om een willekeurig beeld bij te snijden

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
}

Merk op dat de bijgesneden afbeelding de onderliggende pixels kan delen met de originele afbeelding. Als dit het geval is, heeft elke wijziging in de bijgesneden afbeelding invloed op de originele afbeelding.

Kleurenafbeelding omzetten in grijswaarden

Sommige digitale beeldverwerkingsalgoritmen zoals randdetectie, informatie gedragen door de beeldintensiteit (dwz grijswaardenwaarde) is voldoende. Het gebruik van kleurinformatie ( R, G, B kanaal) kan een iets beter resultaat opleveren, maar de complexiteit van het algoritme zal toenemen. In dit geval moeten we de kleurenafbeelding dus omzetten in grijswaardenafbeelding voordat we een dergelijk algoritme toepassen.

De volgende code is een voorbeeld van het converteren van willekeurige afbeelding naar 8-bits grijswaardenafbeelding. De afbeelding wordt opgehaald van een externe locatie met behulp van het net/http pakket, geconverteerd naar grijswaarden en uiteindelijk opgeslagen als PNG-afbeelding.

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)
    }
}

Kleurconversie vindt plaats bij het toewijzen van pixels via Set(x, y int, c color.Color) die is geïmplementeerd in image.go as

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
}

waarin color.GrayModel in color.go wordt gedefinieerd als

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)}
}

Op basis van de bovenstaande feiten wordt de intensiteit Y berekend met de volgende formule:

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

Als we verschillende formules / algoritmen willen toepassen om een kleur in een intentie om te zetten, bijvoorbeeld

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

vervolgens kunnen de volgende fragmenten worden gebruikt.

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

De bovenstaande berekening wordt gedaan door drijvende-komma-vermenigvuldiging en is zeker niet de meest efficiënte, maar het is voldoende om het idee te demonstreren. Het andere punt is dat bij het aanroepen van Set(x, y int, c color.Color) met color.Gray als derde argument, het kleurenmodel geen kleurconversie uitvoert, zoals te zien is in de vorige grayModel functie.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow