Поиск…


Вступление

Пакет изображений предоставляет основные функции для работы с двумерным изображением. В этом разделе описываются несколько основных операций при работе с изображением, например, чтение и запись определенного формата изображения, обрезка, доступ и изменение пикселя , преобразование цвета, изменение размера и фильтрация основного изображения.

Основные понятия

Изображение представляет собой прямоугольную сетку элементов изображения ( пикселя ). В пакете изображений пиксель представлен как один из цветов, определенных в пакете изображений / цветов . image.Rectangle геометрия изображения представлена ​​как изображение. image.Rectangle , а image.Point обозначает положение на сетке.

Изображение и двумерная геометрия

На приведенном выше рисунке показаны основные понятия изображения в пакете. Изображение размером 15x14 пикселей имеет прямоугольные границы, начинающиеся в верхнем левом углу (например, координата (-3, -4) на приведенном выше рисунке), а его оси увеличиваются вправо и вправо до нижнего правого угла (например, координата ( 12, 10) на чертеже). Обратите внимание, что границы необязательно начинаются с точки (0,0) или содержат ее .

Тип изображения

В Go изображение всегда реализует следующий интерфейс image.Image

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
}

в котором интерфейс color.Color определяется как

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)
}

и color.Model - это интерфейс, объявленный как

type Model interface {
    Convert(c Color) Color
}

Доступ к размеру изображения и пикселю

Предположим, что у нас есть изображение, сохраненное как переменная img , тогда мы можем получить размерность и пиксель изображения:

// 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
    }
}

Обратите внимание, что в пакете значение каждого компонента R,G,B,A находится между 0-65535 ( 0x0000 - 0xffff ), а не 0-255 .

Загрузка и сохранение изображения

В памяти изображение можно рассматривать как матрицу пикселя (цвет). Однако, когда изображение хранится в постоянном хранилище, оно может быть сохранено как есть (формат RAW), растровое изображение или другие форматы изображений с использованием специального алгоритма сжатия для сохранения пространства для хранения, например PNG, JPEG, GIF и т. Д. При загрузке изображения с определенным форматом изображение должно быть декодировано в image.Image с соответствующим алгоритмом. Функция image.Decode объявленная как

func Decode(r io.Reader) (Image, string, error)

для этого конкретного использования. Чтобы иметь возможность обрабатывать различные форматы изображений, до вызова функции image.Decode , декодер должен быть зарегистрирован через image.RegisterFormat функция определена как

func RegisterFormat(name, magic string, 
    decode func(io.Reader) (Image, error), decodeConfig func(io.Reader) (Config, error))

В настоящее время пакет изображений поддерживает три формата файлов: JPEG , GIF и PNG . Чтобы зарегистрировать декодер, добавьте следующие

import _ "image/jpeg"    //register JPEG decoder

к main пакету приложения. Где-то в вашем коде (не обязательно в main пакете), чтобы загрузить изображение в формате JPEG, используйте следующие фрагменты:

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

Сохранить в PNG

Чтобы сохранить изображение в определенном формате, соответствующий кодировщик должен быть импортирован явно, т. Е.

import "image/png"    //needed to use `png` encoder

то изображение можно сохранить со следующими фрагментами:

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
}

Если вы хотите указать уровень сжатия, отличный от уровня DefaultCompression , создайте кодировщик , например

enc := png.Encoder{
    CompressionLevel: png.BestSpeed,
}
err := enc.Encode(f, img)

Сохранить в JPEG

Чтобы сохранить формат jpeg , используйте следующее:

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
}

Сохранить в GIF

Чтобы сохранить изображение в GIF-файле, используйте следующие фрагменты.

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
}

Обрезка изображения

Большая часть типа изображения в пакете изображений с SubImage(r Rectangle) Image Метод SubImage(r Rectangle) Image , за исключением image.Uniform . image.Uniform . Исходя из этого факта, мы можем реализовать функцию обрезания произвольного изображения следующим образом

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
}

Обратите внимание, что обрезанное изображение может делиться своими базовыми пикселями с исходным изображением. Если это так, любая модификация обрезанного изображения повлияет на исходное изображение.

Преобразование цветного изображения в оттенки серого

Некоторый алгоритм обработки цифрового изображения, такой как обнаружение края, информация, переносимая интенсивностью изображения (например, оттенки серого), достаточна. Использование информации о цвете (канал R, G, B ) может обеспечить немного лучший результат, но сложность алгоритма будет увеличена. Таким образом, в этом случае нам нужно преобразовать цветное изображение в полутоновое изображение до применения такого алгоритма.

Следующий код является примером преобразования произвольного изображения в 8-битное изображение в оттенках серого. Изображение извлекается из удаленного места с помощью net/http пакета, преобразуется в оттенки серого и, наконец, сохраняется как изображение 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)
    }
}

Преобразование цвета происходит при назначении пикселя через Set(x, y int, c color.Color) который реализован в image.go как

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
}

в котором, color.GrayModel определяется в color.go as

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)}
}

Исходя из приведенных выше фактов, интенсивность Y рассчитывается по следующей формуле:

Яркость: Y = 0,299 R + 0,587 G + 0,114 B

Если мы хотим применить различные формулы / алгоритмы для преобразования цвета в интуицию, например

Среднее значение: Y = ( R + G + B ) / 3
Luma: Y = 0,2126 R + 0,7152 G + 0,0722 B
Luster: Y = (min ( R , G , B ) + max ( R , G , B )) / 2

то можно использовать следующие фрагменты.

// 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)
    }
}

Вышеуказанный расчет выполняется с помощью умножения с плавающей запятой и, конечно, не самый эффективный, но этого достаточно для демонстрации идеи. Другой момент заключается в том, что при вызове Set(x, y int, c color.Color) с color.Gray качестве третьего аргумента цветовая модель не будет выполнять преобразование цветов, как это видно в предыдущей функции grayModel .



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow