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