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.

Imagen y geometría 2-D.

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.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow