Buscar..
Introducción
El paquete de imágenes proporciona funcionalidades básicas para trabajar con imágenes 2-D. Este tema describe varias operaciones básicas cuando se trabaja con imágenes, como leer y escribir un formato de imagen particular, recortar, acceder y modificar píxeles , conversión de color, cambio de tamaño y filtrado de imágenes básico.
Conceptos básicos
Una imagen representa una cuadrícula rectangular de elementos de imagen ( píxel ). En el paquete de imágenes , el píxel se representa como uno de los colores definidos en el paquete de imágenes / colores . La geometría 2-D de la imagen se representa como image.Rectangle
, mientras que image.Point
denota una posición en la cuadrícula.
La figura anterior ilustra los conceptos básicos de una imagen en el paquete. Una imagen de tamaño 15x14 píxeles tiene límites rectangulares iniciados en la esquina superior izquierda (por ejemplo, coordenada (-3, -4) en la figura anterior), y sus ejes aumentan a la derecha y hacia abajo a la esquina inferior derecha (por ejemplo, coordenadas ( 12, 10) en la figura). Tenga en cuenta que los límites no necesariamente comienzan o contienen el punto (0,0) .
Imagen relacionada con el tipo
En Go
, una imagen siempre implementa la siguiente image.Image
Interfaz de imagen
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
}
en el que la interfaz color.Color
se define como
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)
}
y color.Model
es una interfaz declarada como
type Model interface {
Convert(c Color) Color
}
Accediendo a la dimensión de la imagen y píxel.
Supongamos que tenemos una imagen almacenada como img
variable, luego podemos obtener la dimensión y el píxel de la imagen de la siguiente manera:
// 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
}
}
Tenga en cuenta que en el paquete, el valor de cada componente R,G,B,A
encuentra entre 0-65535
( 0x0000 - 0xffff
), no entre 0-255
.
Cargando y guardando imagen
En la memoria, una imagen puede verse como una matriz de píxeles (color). Sin embargo, cuando una imagen se almacena en un almacenamiento permanente, puede almacenarse tal como está (formato RAW), Bitmap u otros formatos de imagen con un algoritmo de compresión particular para ahorrar espacio de almacenamiento, por ejemplo, PNG, JPEG, GIF, etc. Al cargar una imagen con un formato particular, la imagen se debe decodificar a imagen. image.Image
con el algoritmo correspondiente. Una función image.Decode
declarada como
func Decode(r io.Reader) (Image, string, error)
Se proporciona para este uso particular. Para poder manejar varios formatos de imagen, antes de llamar a la función image.Decode
, el decodificador debe registrarse a través de image.RegisterFormat
función definida como
func RegisterFormat(name, magic string,
decode func(io.Reader) (Image, error), decodeConfig func(io.Reader) (Config, error))
Actualmente, el paquete de imágenes admite tres formatos de archivo: JPEG , GIF y PNG . Para registrar un decodificador, agregue lo siguiente
import _ "image/jpeg" //register JPEG decoder
al paquete main
la aplicación. En algún lugar de su código (no es necesario en el paquete main
), para cargar una imagen JPEG, use los siguientes fragmentos:
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` ...
Guardar en PNG
Para guardar una imagen en un formato particular, el codificador correspondiente debe importarse explícitamente, es decir,
import "image/png" //needed to use `png` encoder
luego se puede guardar una imagen con los siguientes fragmentos de código:
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
}
Si desea especificar un nivel de compresión distinto del nivel de compresión DefaultCompression
, cree un codificador , por ejemplo
enc := png.Encoder{
CompressionLevel: png.BestSpeed,
}
err := enc.Encode(f, img)
Guardar en JPEG
Para guardar en formato jpeg
, usa lo siguiente:
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
}
Guardar en GIF
Para guardar la imagen en un archivo GIF, use los siguientes fragmentos.
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
}
Recortar imagen
La mayor parte del tipo de imagen en el paquete de imagen tiene un SubImage(r Rectangle) Image
imagen de SubImage(r Rectangle) Image
, excepto image.Uniform
. En base a este hecho, podemos implementar una función para recortar una imagen arbitraria de la siguiente manera
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
}
Tenga en cuenta que la imagen recortada puede compartir sus píxeles subyacentes con la imagen original. Si este es el caso, cualquier modificación de la imagen recortada afectará a la imagen original.
Convertir imagen en color a escala de grises
Algún algoritmo de procesamiento de imágenes digitales, como la detección de bordes, la información transportada por la intensidad de la imagen (es decir, el valor en escala de grises) es suficiente. El uso de información de color ( R, G, B
canal R, G, B
) puede proporcionar un resultado ligeramente mejor, pero la complejidad del algoritmo aumentará. Por lo tanto, en este caso, debemos convertir la imagen en color a una imagen en escala de grises antes de aplicar dicho algoritmo.
El siguiente código es un ejemplo de conversión de una imagen arbitraria a una imagen en escala de grises de 8 bits. La imagen se recupera de una ubicación remota mediante net/http
paquete net/http
, se convierte a escala de grises y finalmente se guarda como imagen 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 conversión de color se produce cuando se asigna un píxel a través de Set(x, y int, c color.Color)
que se implementa en image.go
como
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
}
en el cual, color.GrayModel
se define en color.go
como
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)}
}
Sobre la base de los hechos anteriores, la intensidad Y
se calcula con la siguiente fórmula:
Luminancia: Y = 0.299 R + 0.587 G + 0.114 B
Si queremos aplicar diferentes fórmulas / algoritmos para convertir un color en una intensidad, por ejemplo
Media: Y = ( R + G + B ) / 3
Luma: Y = 0.2126 R + 0.7152 G + 0.0722 B
Brillo: Y = (min ( R , G , B ) + max ( R , G , B )) / 2
entonces, los siguientes fragmentos pueden ser utilizados.
// 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)
}
}
El cálculo anterior se realiza mediante la multiplicación de punto flotante, y ciertamente no es el más eficiente, pero es suficiente para demostrar la idea. El otro punto es, cuando se llama a Set(x, y int, c color.Color)
con color.Gray
como tercer argumento, el modelo de color no realizará la conversión de color como se puede ver en la función grayModel
anterior.