Suche…


Einführung

Das Image- Paket bietet grundlegende Funktionen für die Arbeit mit 2-D-Images. In diesem Thema werden einige grundlegende Vorgänge beim Arbeiten mit Bildern beschrieben, z. B. Lesen und Schreiben eines bestimmten Bildformats, Zuschneiden, Zugreifen auf und Ändern von Pixeln , Farbkonvertierung, Größenänderung und grundlegende Bildfilterung.

Grundlegendes Konzept

Ein Bild repräsentiert ein rechteckiges Gitter von Bildelementen ( Pixel ). In dem Bildpaket, wird das Pixel als eine der Farbe dargestellt in definierten Bild / Farb Paket. Die 2-D-Geometrie des Bildes wird als image.Rectangle , während image.Point eine Position im Raster image.Point .

Bild und 2-D-Geometrie

Die obige Abbildung veranschaulicht die grundlegenden Konzepte eines Bildes im Paket. Ein Bild der Größe 15x14 Pixel hat rechteckige Begrenzungen, die an der oberen linken Ecke beginnen (z. B. Koordinate (-3, -4) in der obigen Abbildung), und seine Achsen steigen nach rechts und nach unten in die untere rechte Ecke (z. B. Koordinate ( 12, 10) in der Figur). Beachten Sie, dass die Grenzen nicht unbedingt an Punkt (0,0) beginnen oder diesen enthalten .

Bildbezogener Typ

In Go implementiert ein Bild immer die folgende image.Image Schnittstelle

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 dem die color.Color Schnittstelle definiert ist 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)
}

und color.Model ist eine als deklarierte Schnittstelle

type Model interface {
    Convert(c Color) Color
}

Zugriff auf Bildgröße und Pixel

Angenommen, wir haben ein Bild als Variable img gespeichert, dann können wir die Dimension und das Bildpixel erhalten durch:

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

Beachten Sie, dass der Wert der einzelnen Komponenten R,G,B,A im Paket zwischen 0-65535 ( 0x0000 - 0xffff ) liegt und nicht zwischen 0-255 .

Bild laden und speichern

Im Speicher kann ein Bild als Matrix aus Pixeln (Farbe) betrachtet werden. Wenn ein Bild in einem permanenten Speicher gespeichert wird, kann es jedoch so gespeichert werden (RAW-Format), Bitmap oder andere Bildformate mit einem bestimmten Kompressionsalgorithmus zum Speichern von Speicherplatz, z Bei einem bestimmten Format muss das Bild in image.Image mit dem entsprechenden Algorithmus dekodiert werden. Eine image.Decode deklarierte image.Decode Funktion

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

wird für diese bestimmte Verwendung bereitgestellt. Um verschiedene Bildformate verarbeiten zu können, muss der Decoder vor dem Aufrufen der image.Decode Funktion über die als definierte image.RegisterFormat Funktion registriert werden

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

Derzeit unterstützt das Image-Paket drei Dateiformate: JPEG , GIF und PNG . Um einen Decoder zu registrieren, fügen Sie Folgendes hinzu

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

auf die Anwendung main Paket. Irgendwo in Ihrem Code (nicht notwendig in main Paket), ein JPEG - Bild zu laden, die folgenden Schnipsel verwenden:

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

Speichern Sie in PNG

Um ein Bild in einem bestimmten Format zu speichern, muss der entsprechende Encoder explizit importiert werden

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

Dann kann ein Bild mit den folgenden Ausschnitten gespeichert werden:

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
}

Wenn Sie eine andere Komprimierungsstufe als die DefaultCompression angeben DefaultCompression , erstellen Sie einen Encoder , z

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

Speichern Sie in JPEG

Verwenden Sie zum Speichern im jpeg Format Folgendes:

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
}

In GIF speichern

Verwenden Sie die folgenden Ausschnitte, um das Bild in einer GIF-Datei zu speichern.

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
}

Bild zuschneiden

Die meisten Bildtypen in Bildpaket mit SubImage(r Rectangle) Image Methode, außer image.Uniform . Basierend auf dieser Tatsache können Wir eine Funktion implementieren, um ein beliebiges Bild wie folgt zu beschneiden

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
}

Beachten Sie, dass das zugeschnittene Bild möglicherweise die darunter liegenden Pixel mit dem Originalbild teilt. Wenn dies der Fall ist, wirken sich Änderungen am zugeschnittenen Bild auf das Originalbild aus.

Konvertieren Sie ein Farbbild in Graustufen

Bei einigen digitalen Bildverarbeitungsalgorithmen wie der Kantenerkennung ist die durch die Bildintensität (dh den Graustufenwert) übertragene Information ausreichend. Die Verwendung von Farbinformationen ( R, G, B Kanal) kann ein etwas besseres Ergebnis liefern, die Algorithmuskomplexität wird jedoch erhöht. Daher müssen wir in diesem Fall das Farbbild vor der Anwendung eines solchen Algorithmus in ein Graustufenbild konvertieren.

Der folgende Code ist ein Beispiel für das Konvertieren eines beliebigen Bildes in ein 8-Bit-Graustufenbild. Das Abbild wird mit einem net/http Paket vom Remote-Standort abgerufen, in Graustufen konvertiert und schließlich als PNG-Abbild gespeichert.

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

Die Set(x, y int, c color.Color) erfolgt bei der Zuweisung von Pixeln über Set(x, y int, c color.Color) das in image.go as implementiert ist

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
}

wobei color.GrayModel in color.go als definiert ist

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

Basierend auf den obigen Tatsachen wird die Intensität Y mit der folgenden Formel berechnet:

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

Wenn wir verschiedene Formeln / Algorithmen anwenden möchten, um eine Farbe in eine Intensität zu konvertieren, z

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

Dann können die folgenden Ausschnitte verwendet werden.

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

Die obige Berechnung erfolgt durch Gleitkommamultiplikation und ist sicherlich nicht die effizienteste, reicht aber aus, um die Idee zu demonstrieren. Der andere Punkt ist, dass beim Aufruf von Set(x, y int, c color.Color) mit color.Gray als drittem Argument das Farbmodell keine color.Gray durchführt, wie in der vorherigen grayModel Funktion zu sehen ist.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow