Paquete image en Go: Guía Completa para Manipulación de Imágenes

El paquete image en Go proporciona herramientas fundamentales para crear, manipular y procesar imágenes rasterizadas. Esta guía ampliada incluye ejemplos prácticos avanzados, comparativas de formatos y técnicas de optimización.


1. Tipos de Imágenes y Casos de Uso

Tabla Comparativa de Formatos

Tipo de Imagen Profundidad Transparencia Uso Típico Almacenamiento por Pixel
image.RGBA 8 bits/canal Sí (alpha) Gráficos generales 4 bytes (R, G, B, A)
image.NRGBA 8 bits/canal Premultiplicado Web y formatos compatibles 4 bytes
image.RGBA64 16 bits/canal Imágenes HDR 8 bytes
image.Gray 8 bits No Escala de grises 1 byte
image.Paletted 8 bits índice Limitada GIFs y gráficos retro 1 byte + paleta

2. Creación de Imágenes Avanzadas

2.1 Generación de Gradiente Radial

package main

import (
    "image"
    "image/color"
    "image/png"
    "math"
    "os"
)

func createGradient(width, height int) *image.RGBA {
    img := image.NewRGBA(image.Rect(0, 0, width, height))
    centerX := float64(width)/2
    centerY := float64(height)/2
    maxDist := math.Sqrt(centerX*centerX + centerY*centerY)

    for y := 0; y < height; y++ {
        for x := 0; x < width; x++ {
            dx := float64(x) - centerX
            dy := float64(y) - centerY
            dist := math.Sqrt(dx*dx + dy*dy)/maxDist
            
            c := color.RGBA{
                R: uint8(255 * (1 - dist)),
                G: uint8(255 * dist),
                B: uint8(128),
                A: 255,
            }
            img.Set(x, y, c)
        }
    }
    return img
}

func main() {
    gradient := createGradient(400, 400)
    f, _ := os.Create("gradient.png")
    png.Encode(f, gradient)
    f.Close()
}

2.2 Conversión a Escala de Grises

func convertToGray(src image.Image) *image.Gray {
    bounds := src.Bounds()
    gray := image.NewGray(bounds)
    
    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            original := src.At(x, y)
            gray.Set(x, y, color.GrayModel.Convert(original))
        }
    }
    return gray
}

3. Manipulación de Imágenes

3.1 Aplicación de Filtro de Desenfoque

func applyBlur(src *image.RGBA, radius int) *image.RGBA {
    bounds := src.Bounds()
    dst := image.NewRGBA(bounds)
    kernel := createGaussianKernel(radius)

    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            var r, g, b, a float64
            weightSum := 0.0
            
            for ky := -radius; ky <= radius; ky++ {
                for kx := -radius; kx <= radius; kx++ {
                    xx := clamp(x+kx, bounds.Min.X, bounds.Max.X-1)
                    yy := clamp(y+ky, bounds.Min.Y, bounds.Max.Y-1)
                    weight := kernel[ky+radius][kx+radius]
                    
                    c := src.At(xx, yy).(color.RGBA)
                    r += float64(c.R) * weight
                    g += float64(c.G) * weight
                    b += float64(c.B) * weight
                    a += float64(c.A) * weight
                    weightSum += weight
                }
            }
            
            dst.Set(x, y, color.RGBA{
                R: uint8(r / weightSum),
                G: uint8(g / weightSum),
                B: uint8(b / weightSum),
                A: uint8(a / weightSum),
            })
        }
    }
    return dst
}

3.2 Recorte de Imágenes con Inset

func cropImage(src image.Image, margin int) image.Image {
    bounds := src.Bounds()
    cropped := bounds.Inset(margin)
    subImg := image.NewRGBA(cropped)
    
    for y := cropped.Min.Y; y < cropped.Max.Y; y++ {
        for x := cropped.Min.X; x < cropped.Max.X; x++ {
            subImg.Set(x - cropped.Min.X, y - cropped.Min.Y, src.At(x, y))
        }
    }
    return subImg
}

4. Animaciones y Formatos Especiales

4.1 Creación de GIF Animado

func createAnimatedGif() {
    var frames []*image.Paletted
    var delays []int
    palette := color.Palette{color.White, color.Black}

    // Generar 10 cuadros de animación
    for i := 0; i < 10; i++ {
        img := image.NewPaletted(image.Rect(0, 0, 100, 100), palette)
        img.SetColorIndex(50+i*2, 50, 1)
        frames = append(frames, img)
        delays = append(delays, 10)
    }

    f, _ := os.Create("animation.gif")
    gif.EncodeAll(f, &gif.GIF{
        Image: frames,
        Delay: delays,
    })
    f.Close()
}

4.2 Codificación en JPEG con Calidad Ajustable

import "image/jpeg"

func saveJpeg(img image.Image, path string, quality int) error {
    f, err := os.Create(path)
    if err != nil {
        return err
    }
    defer f.Close()

    return jpeg.Encode(f, img, &jpeg.Options{Quality: quality})
}

5. Mejores Prácticas y Optimización

5.1 Acceso Eficiente a Píxeles

// Método óptimo para procesamiento por bloques
func processImageFast(img *image.RGBA) {
    bounds := img.Bounds()
    pix := img.Pix  // Acceso directo al slice de píxeles
    width := bounds.Dx() * 4

    for y := 0; y < bounds.Dy(); y++ {
        row := pix[y*img.Stride : y*img.Stride + width]
        for x := 0; x < len(row); x += 4 {
            // Modificar RGBA directamente
            row[x] = 255 - row[x]     // R
            row[x+1] = 255 - row[x+1] // G
            row[x+2] = 255 - row[x+2] // B
            // Alpha (x+3) permanece igual
        }
    }
}

5.2 Manejo de Errores Robusto

func loadImage(path string) (image.Image, error) {
    f, err := os.Open(path)
    if err != nil {
        return nil, fmt.Errorf("error abriendo archivo: %w", err)
    }
    defer f.Close()

    img, _, err := image.Decode(f)
    if err != nil {
        return nil, fmt.Errorf("error decodificando imagen: %w", err)
    }
    return img, nil
}

6. Integración con Paquetes Externos

6.1 Uso de WebP con github.com/chai2010/webp

import "github.com/chai2010/webp"

func saveWebP(img image.Image, path string) error {
    f, err := os.Create(path)
    if err != nil {
        return err
    }
    defer f.Close()
    
    return webp.Encode(f, img, &webp.Options{Lossless: true})
}

6.2 Procesamiento con Paralelismo

func parallelProcess(img *image.RGBA) {
    bounds := img.Bounds()
    numWorkers := runtime.NumCPU()
    var wg sync.WaitGroup
    wg.Add(numWorkers)

    sectionHeight := bounds.Dy() / numWorkers
    for i := 0; i < numWorkers; i++ {
        go func(yStart, yEnd int) {
            defer wg.Done()
            for y := yStart; y < yEnd; y++ {
                for x := bounds.Min.X; x < bounds.Max.X; x++ {
                    // Procesamiento paralelo
                }
            }
        }(i*sectionHeight, (i+1)*sectionHeight)
    }
    wg.Wait()
}

7. Ejemplo Completo: Editor de Imágenes Básico

package main

import (
    "image"
    "image/color"
    "image/jpeg"
    "math"
    "os"
)

type Filter func(*image.RGBA)

func main() {
    // Cargar imagen
    img, _ := loadImage("input.jpg")
    rgba := image.NewRGBA(img.Bounds())
    
    // Aplicar filtros
    filters := []Filter{
        applyContrast(1.5),
        applyVignette(0.8),
        applySepia(),
    }
    
    for _, filter := range filters {
        filter(rgba)
    }
    
    // Guardar resultado
    saveJpeg(rgba, "output.jpg", 90)
}

func applySepia() Filter {
    return func(img *image.RGBA) {
        bounds := img.Bounds()
        for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
            for x := bounds.Min.X; x < bounds.Max.X; x++ {
                c := img.RGBAAt(x, y)
                r := float64(c.R)*0.393 + float64(c.G)*0.769 + float64(c.B)*0.189
                g := float64(c.R)*0.349 + float64(c.G)*0.686 + float64(c.B)*0.168
                b := float64(c.R)*0.272 + float64(c.G)*0.534 + float64(c.B)*0.131
                img.SetRGBA(x, y, color.RGBA{
                    R: uint8(math.Min(r, 255)),
                    G: uint8(math.Min(g, 255)),
                    B: uint8(math.Min(b, 255)),
                    A: c.A,
                })
            }
        }
    }
}

Conclusión

Esta guía proporciona técnicas avanzadas para el manejo de imágenes en Go, incluyendo procesamiento de píxeles, optimización de rendimiento y formatos especiales. Los ejemplos muestran aplicaciones prácticas desde filtros básicos hasta animaciones, siguiendo mejores prácticas de programación y eficiencia.