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