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