Ricerca…
introduzione
Il pacchetto di immagini offre funzionalità di base per lavorare con immagini 2D. Questo argomento descrive diverse operazioni di base quando si lavora con immagini come la lettura e la scrittura di un particolare formato di immagine, il ritaglio, l'accesso e la modifica di pixel , la conversione del colore, il ridimensionamento e il filtro di base delle immagini.
Concetti basilari
Un'immagine rappresenta una griglia rettangolare di elementi dell'immagine ( pixel ). Nel pacchetto immagine , il pixel è rappresentato come uno dei colori definiti nel pacchetto immagine / colore . La geometria 2D dell'immagine è rappresentata come immagine. image.Rectangle
, mentre image.Point
indica una posizione sulla griglia.
La figura sopra illustra i concetti di base di un'immagine nel pacchetto. Un'immagine di dimensioni 15x14 pixel ha un limite rettangolare iniziato nell'angolo in alto a sinistra (es. Coordinata (-3, -4) nella figura sopra), e i suoi assi aumentano verso destra e verso il basso nell'angolo in basso a destra (es. Coordinata ( 12, 10) nella figura). Si noti che i limiti non iniziano necessariamente da o contengono il punto (0,0) .
Immagine relativa al tipo
In Go
, un'immagine implementa sempre la seguente image.Image
Interfaccia immagine
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 cui l'interfaccia color.Color
è definita come
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)
}
e color.Model
è un'interfaccia dichiarata come
type Model interface {
Convert(c Color) Color
}
Accesso alla dimensione e al pixel dell'immagine
Supponiamo di avere un'immagine memorizzata come img
variabile, quindi possiamo ottenere la dimensione e il pixel dell'immagine per:
// 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
}
}
Si noti che nel pacchetto, il valore di ciascun componente R,G,B,A
è compreso tra 0-65535
( 0x0000 - 0xffff
), non 0-255
.
Caricamento e salvataggio dell'immagine
In memoria, un'immagine può essere vista come una matrice di pixel (colore). Tuttavia, quando un'immagine viene archiviata in una memoria permanente, può essere memorizzata come è (formato RAW), bitmap o altri formati di immagine con un particolare algoritmo di compressione per risparmiare spazio di archiviazione, ad esempio PNG, JPEG, GIF, ecc. Quando si carica un'immagine con un particolare formato, l'immagine deve essere decodificata in immagine. image.Image
con algoritmo corrispondente. Una funzione image.Decode
dichiarata come
func Decode(r io.Reader) (Image, string, error)
è fornito per questo particolare utilizzo. Per poter gestire vari formati di immagine, prima di chiamare la funzione image.Decode
, il decoder deve essere registrato tramite l' image.RegisterFormat
Funzione image.RegisterFormat
definita come
func RegisterFormat(name, magic string,
decode func(io.Reader) (Image, error), decodeConfig func(io.Reader) (Config, error))
Attualmente, il pacchetto di immagini supporta tre formati di file: JPEG , GIF e PNG . Per registrare un decodificatore, aggiungere quanto segue
import _ "image/jpeg" //register JPEG decoder
al pacchetto main
dell'applicazione. Da qualche parte nel tuo codice (non necessario nel pacchetto main
), per caricare un'immagine JPEG, usa i seguenti frammenti:
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` ...
Salva in PNG
Per salvare un'immagine in un formato particolare, il codificatore corrispondente deve essere importato esplicitamente, ad es
import "image/png" //needed to use `png` encoder
quindi un'immagine può essere salvata con i seguenti frammenti:
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
}
Se si desidera specificare un livello di compressione diverso dal livello DefaultCompression
, creare un encoder , ad es
enc := png.Encoder{
CompressionLevel: png.BestSpeed,
}
err := enc.Encode(f, img)
Salva in JPEG
Per salvare in formato jpeg
, utilizzare quanto segue:
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
}
Salva in GIF
Per salvare l'immagine nel file GIF, utilizzare i seguenti frammenti.
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
}
Ritagliare l'immagine
La maggior parte del tipo di immagine nel pacchetto di immagini con SubImage(r Rectangle) Image
Metodo SubImage(r Rectangle) Image
, ad eccezione di image.Uniform
. Sulla base di questo fatto, possiamo implementare una funzione per ritagliare un'immagine arbitraria come segue
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
}
Si noti che l'immagine ritagliata potrebbe condividere i suoi pixel sottostanti con l'immagine originale. Se questo è il caso, qualsiasi modifica all'immagine ritagliata influenzerà l'immagine originale.
Converti l'immagine a colori in scala di grigi
È sufficiente un algoritmo di elaborazione delle immagini digitali come il rilevamento dei bordi, le informazioni trasportate dall'intensità dell'immagine (ad es. Il valore della scala dei grigi). L'uso delle informazioni sul colore (canale R, G, B
) può fornire risultati leggermente migliori, ma la complessità dell'algoritmo sarà aumentata. Pertanto, in questo caso, è necessario convertire l'immagine a colori in un'immagine in scala di grigi prima di applicare tale algoritmo.
Il seguente codice è un esempio di conversione di un'immagine arbitraria in un'immagine in scala di grigi a 8 bit. L'immagine viene recuperata dalla posizione remota utilizzando il pacchetto net/http
, convertita in scala di grigi e infine salvata come immagine PNG.
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)
}
}
La conversione del colore si verifica quando si assegna pixel attraverso Set(x, y int, c color.Color)
che è implementato in image.go
come
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
}
in cui, color.GrayModel
è definito in color.go
come
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)}
}
In base ai fatti sopra riportati, l'intensità Y
viene calcolata con la seguente formula:
Luminanza: Y = 0,299 R + 0,587 G + 0,114 B
Se vogliamo applicare diverse formule / algoritmi per convertire un colore in un'intesità, ad es
Media: Y = ( R + G + B ) / 3
Luma: Y = 0,2126 R + 0,7152 G + 0,0722 B
Lustro: Y = (min ( R , G , B ) + max ( R , G , B )) / 2
quindi, è possibile utilizzare i seguenti frammenti.
// 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)
}
}
Il calcolo sopra è fatto per moltiplicazione in virgola mobile, e certamente non è il più efficiente, ma è sufficiente per dimostrare l'idea. L'altro punto è, quando si chiama Set(x, y int, c color.Color)
con color.Gray
come terzo argomento, il modello di colore non eseguirà la conversione del colore come si può vedere nella precedente funzione grayModel
.