Sök…


Introduktion

Den bild Paketet innehåller grundläggande funktioner för att arbeta med 2-D bild. Detta ämne beskriver flera grundläggande operationer när du arbetar med bild, till exempel att läsa och skriva ett visst bildformat, beskära, komma åt och ändra pixel , färgkonvertering, ändra storlek och grundläggande bildfiltrering.

Grundläggande koncept

En bild representerar ett rektangulärt rutnät med bildelement ( pixel ). I bildpaketet är pixeln representeras som en av färgen definieras i bild / färgpaketet. Bildens 2-D geometri representeras som image.Rectangle , medan image.Point anger en position på rutnätet.

Bild och 2-D geometri

Figuren ovan illustrerar de grundläggande begreppen för en bild i paketet. En bild med storleken 15x14 pixlar har en rektangulär gräns som startas i det övre vänstra hörnet (t.ex. koordinat (-3, -4) i figuren ovan), och dess axlar ökar höger och ned till nedre högra hörnet (t.ex. koordinat ( 12, 10) i figuren). Observera att gränserna inte nödvändigtvis börjar från eller innehåller punkt (0,0) .

Bildrelaterad typ

I Go implementerar en bild alltid följande image.Image gränssnitt

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
}

där gränssnittet color.Color definieras som

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

och color.Model är ett gränssnitt som deklareras som

type Model interface {
    Convert(c Color) Color
}

Åtkomst till bilddimension och pixel

Anta att vi har en bild lagrad som variabel img , då kan vi få dimensionen och bildpixeln genom:

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

Observera att i paketet är värdet på varje R,G,B,A komponent mellan 0-65535 ( 0x0000 - 0xffff ), inte 0-255 .

Laddar och sparar bild

I minnet kan en bild ses som en matris med pixel (färg). Men när en bild lagras i en permanent lagring kan den lagras som den är (RAW-format), Bitmap eller andra bildformat med särskild komprimeringsalgoritm för att spara lagringsutrymme, t.ex. PNG, JPEG, GIF, etc. När du laddar en bild med särskilt format måste bilden avkodas till image.Image med motsvarande algoritm. En image.Decode funktion som deklareras som

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

tillhandahålls för denna speciella användning. För att kunna hantera olika bildformat, före anropa image.Decode funktionen måste avkodaren vara registrerad genom image.RegisterFormat funktion definieras som

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

För närvarande stöder bildpaketet tre filformat: JPEG , GIF och PNG . Lägg till följande för att registrera en avkodare

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

till programmets main Någonstans i din kod (inte nödvändigt i main paketet), för att ladda en JPEG-bild, använd följande utdrag:

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

Spara till PNG

För att spara en bild i ett visst format måste motsvarande kodare importeras uttryckligen, dvs.

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

då kan en bild sparas med följande utdrag:

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
}

Om du vill ange annan komprimeringsnivå än DefaultCompression , skapa en kodare , t.ex.

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

Spara i JPEG

För att spara i jpeg format använder du följande:

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
}

Spara till GIF

För att spara bilden i GIF-fil använder du följande utdrag.

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
}

Beskära bild

De flesta av bildtypen i bildpaketet som har SubImage(r Rectangle) Image förutom image.Uniform . Baserat på detta faktum kan vi implementera en funktion för att beskära en godtycklig bild på följande sätt

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
}

Observera att den beskurna bilden kan dela sina underliggande pixlar med originalbilden. Om detta är fallet kommer alla ändringar av den beskurna bilden att påverka originalbilden.

Konvertera färgbild till gråskala

Vissa digitala bildbehandlingsalgoritmer som kantdetektering, information som bärs av bildintensiteten (dvs. gråskalevärde) är tillräcklig. Att använda färginformation ( R, G, B kanal) kan ge något bättre resultat, men algoritmens komplexitet kommer att öka. I detta fall måste vi därför konvertera färgbilden till gråskalabild innan vi använder en sådan algoritm.

Följande kod är ett exempel på att konvertera godtycklig bild till 8-bitars gråskalabild. Bilden hämtas från fjärrplats med net/http paket, konverteras till gråskala och sparas slutligen som PNG-bild.

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

Färgkonvertering sker vid tilldelning av pixel genom Set(x, y int, c color.Color) som implementeras i image.go som

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
}

där color.GrayModel definieras i color.go som

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

Baserat på ovanstående fakta beräknas intensiteten Y med följande formel:

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

Om vi vill tillämpa olika formler / algoritmer för att konvertera en färg till en intesitet, t.ex.

Medel: 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

sedan kan följande utdrag användas.

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

Ovanstående beräkning görs genom multiplikation av flytande punkter och är verkligen inte den mest effektiva, men det räcker för att demonstrera idén. Den andra punkten är att när du ringer Set(x, y int, c color.Color) med color.Gray som tredje argument kommer färgmodellen inte att utföra färgkonvertering som kan ses i den tidigare grayModel funktionen.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow