Published in

--

# Part 1 — Utility Functions

I have been interested in image manipulation for some time. Last year I worked on a small package that does simple image manipulation using Go’s standard image package: github.com/KorayGocmen/image

Let’s look at how to read an image from a provided file path and creating an image object. This is what the structs will look like.

`// GrayscaleAverage, GrayscaleLuma, GrayscaleDesaturation// are used by grayscale to choose between algorithmsconst (  GrayscaleAverage      = 0  GrayscaleLuma         = 1  GrayscaleDesaturation = 2)// Pixel is a single pixel in 2d arraytype Pixel struct {  R int  G int  B int  A int}// Image is the main object that holds information about the// image file. Also is a wrapper around the decoded image// from the standard image library.type Image struct {  Pixels [][]Pixel  Width  int  Height int  _Rect  image.Rectangle  _Image image.Image}`

An image is basically a matrix of pixels. There are different image encoding techniques. I am using RGBA colour space, it stands for Red, Green, Blue and Alpha. Red, Green, Blue is pretty self-explanatory and Alpha is basically opacity of the pixel.

I am going to need 3 helper functions to quickly get/set a certain pixel and to transform the decoded pixel to my pixel format. This is how they are going to look:

`// Get pixel value with key namefunc (pix *Pixel) Get(keyName string) int {  switch keyName {  case "R":    return pix.R  case "G":    return pix.G  case "B":    return pix.B  case "A":    return pix.A  default:    return -1  }}// Set pixel value with key name and new valuefunc (pix *Pixel) Set(keyName string, val int) Pixel {  switch keyName {  case "R":    pix.R = val  case "G":    pix.G = val  case "B":    pix.B = val  case "A":    pix.A = val  }  return *pix}// rgbaToPixel alpha-premultiplied red, green, blue and alpha values// to 8 bit red, green, blue and alpha values.func rgbaToPixel(r uint32, g uint32, b uint32, a uint32) Pixel {  return Pixel{    R: int(r / 257),    G: int(g / 257),    B: int(b / 257),    A: int(a / 257),  }}`

`rgbaToPixel` function converts the pixels image package returns into pixels I defined. The RGBA values in standard package are uint32 type, I preferred int type. Therefore I need to divide the values by 257 and cast them as int.

Now, I am ready to read the image from a provided `filepath` and create my image object. This code can decode “jpeg”/”jpg” and “png” formats. I am going to keep the native image packages “img” object for future reference under my own image object via the key “_Image”. This function will return the image object or maybe an error.

`// New reads an image from the given file path and return a// new `Image` struct.func New(filePath string) (*Image, error) {  s := strings.Split(filePath, ".")  imgType := s[len(s)-1]  switch imgType {  case "jpeg", "jpg":    image.RegisterFormat("jpeg", "jpeg", jpeg.Decode, jpeg.DecodeConfig)  case "png":    image.RegisterFormat("png", "png", png.Decode, png.DecodeConfig)  default:    return nil, errors.New("unknown image type")  }  imgReader, err := os.Open(filePath)  if err != nil {    return nil, err  }  img, _, err := image.Decode(imgReader)  if err != nil {    return nil, err  }  bounds := img.Bounds()  width, height := bounds.Max.X, bounds.Max.Y  var pixels [][]Pixel  for y := 0; y < height; y++ {    var row []Pixel    for x := 0; x < width; x++ {      pixel := rgbaToPixel(img.At(x, y).RGBA())      row = append(row, pixel)    }    pixels = append(pixels, row)  }  return &Image{    Pixels: pixels,    Width:  width,    Height: height,    _Rect:  img.Bounds(),    _Image: img,  }, nil}`

Now I can read an image from a file but we also need to rewrite our manipulated image back to a file. This is pretty much doing everything I did in the `New` function in reverse as one might have guessed.

`// WriteToFile writes iamges to the given filepath.// Returns an error if it occurs.func (img *Image) WriteToFile(outputPath string) error {  cimg := image.NewRGBA(img._Rect)  draw.Draw(cimg, img._Rect, img._Image, image.Point{}, draw.Over)  for y := 0; y < img.Height; y++ {    for x := 0; x < img.Width; x++ {      rowIndex, colIndex := y, x      pixel := img.Pixels[rowIndex][colIndex]      cimg.Set(x, y, color.RGBA{        uint8(pixel.R),        uint8(pixel.G),        uint8(pixel.B),        uint8(pixel.A),      })    }  }  s := strings.Split(outputPath, ".")  imgType := s[len(s)-1]  switch imgType {  case "jpeg", "jpg", "png":    fd, err := os.Create(outputPath)    if err != nil {      return err    }    switch imgType {    case "jpeg", "jpg":      jpeg.Encode(fd, cimg, nil)    case "png":      png.Encode(fd, cimg)    }  default:    return errors.New("unknown image type")  }  return nil}`

Alright, I have the utility functions ready, now it is time for the fun part.