Golang : Edge detection with Sobel method




Ability to detect edges and lines in a picture is the foundation for computer vision or better known in computer science as the cognitive technology. This ability helps computers to learn about objects and decision making through visual perception.

For examples, analyzing medical images to improve diagnosis and perform facial recognition to automatically identify people in photographs

In the code example that follows, we will implement the popular Sobel method in Golang to detect edges in an image.

Here you go!


 package main

 import (
  "bytes"
  "fmt"
  "github.com/disintegration/imaging"
  //"github.com/mattn/go-gtk/glib" <-- uncomment if you want to view the images
  //"github.com/mattn/go-gtk/gtk" <-- see notes at the bottom
  "image"
  "image/color"
  "image/png"
  "io/ioutil"
  "math"
  "os"
 )

 // find luminance of a given color - ignoring alpha
 func Luminance(aColor color.NRGBA) float64 {

  red := aColor.R
  green := aColor.G
  blue := aColor.B

  // need to convert uint32 to float64
  return float64(float64(0.299)*float64(red) + float64(0.587)*float64(green) + float64(0.114)*float64(blue))
 }

 func ErrorCheck(err error) {

  if err != nil {
 panic(err)
  }
 }

 func main() {
  if len(os.Args) != 2 {
 fmt.Printf("Usage : %s <image file>\n", os.Args[0])
 os.Exit(0)
  }

  imageFile := os.Args[1]

  // read image file into memory
  imageBuffer, err := ioutil.ReadFile(imageFile)
  ErrorCheck(err)

  // "equip" img of type image.Image to io.Reader (with Read method)
  // to avoid this error :
  // cannot use img (type image.Image) as type io.Reader in argument to image.DecodeConfig:
  // image.Image does not implement io.Reader (missing Read method)

  imageReader := bytes.NewReader(imageBuffer)

  originalImage, _, err := image.Decode(imageReader)

  ErrorCheck(err)

  // another way to get the height and width
  dimension := originalImage.Bounds().Max
  width := dimension.X
  height := dimension.Y

  // to compensate for https://github.com/golang/go/issues/11648 bug
  // where processing jpeg files will cause
  // panic: interface conversion: color.Color is color.YCbCr, not color.RGBA

  // we will grayscale the image first for healthier and easier life...
  grayImage := imaging.Grayscale(originalImage)

  // Isotropic 3x3 Image Gradient Operator filters
  // see https://en.wikipedia.org/wiki/Sobel_operator
  horizontal := [3][2]int{
 {-1, 0, 1},
 {-2, 0, 2},
 {-1, 0, 1},
  }

  vertical := [3][3]int{
 {1, 2, 1},
 {0, 0, 0},
 {-1, -2, -1},
  }

  // yup, our new image will also be gray ... because of Sobel
  newImage := image.NewGray(image.Rect(0, 0, width, height))

  // from https://www.socketloop.com/tutorials/golang-get-rgba-values-of-each-image-pixel
  // walk through all pixels.

  for y := 1; y < height-1; y++ {
 for x := 1; x < width-1; x++ {

 // get 3x3 colors luminosity level in the neighbourhood

 gradient := [3][4]int{}

 for i := 0; i < 3; i++ {
 for j := 0; j < 3; j++ {
 // to use Luminance() function. Need to type assert originalImage.At()
 // return value with .(color.RGBA)
 // simply because we don't want to use color.Color type

 //gradient[i][j] = int(Luminance(originalImage.At(x-1+i, y-1+j).(color.RGBA)))
 gradient[i][j] = int(Luminance(grayImage.NRGBAAt(x-1+i, y-1+j)))

 }
 }

 // apply horizontal and vertical changes
 // see Formulation at
 // http://docs.opencv.org/2.4/doc/tutorials/imgproc/imgtrans/sobel_derivatives/sobel_derivatives.html

 gradientX := 0
 gradientY := 0

 for i := 0; i < 3; i++ {
 for j := 0; j < 3; j++ {
 gradientX += gradient[i][j] * horizontal[i][j]
 gradientY += gradient[i][j] * vertical[i][j]
 }
 }

 // minus 255 to have white background instead of black (i.e inverse)
 // colorCode := 255 - int(math.Sqrt(float64((gradientX*gradientX)+(gradientY*gradientY))))

 colorCode := int(math.Sqrt(float64((gradientX * gradientX) + (gradientY * gradientY))))
 pixelColor := color.RGBA{uint8(colorCode), uint8(colorCode), uint8(colorCode), 0}
 newImage.Set(x, y, pixelColor)
 }
  }
  // save the new image
  saveFile, err := os.Create("./edges.png")
  ErrorCheck(err)
  err = png.Encode(saveFile, newImage)
  ErrorCheck(err)

  //-------------------------------------------------------
  // NOTES: We are done by this stage. The code below is optional.
  // To get it to work, follow the instruction at
  // https://socketloop.com/tutorials/golang-simple-image-viewer-with-go-gtk
  // to setup gtk
  //-------------------------------------------------------

  //gtk.Init(nil)
  //window := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
  //window.SetPosition(gtk.WIN_POS_CENTER)
  //window.SetTitle("Example of edges detection in Golang")
  //window.SetIconName("gtk-dialog-info")
  //window.Connect("destroy", func(ctx *glib.CallbackContext) {
  // gtk.MainQuit()
  //}, "Happy coding!")

  //hbox := gtk.NewHBox(false, 1)

  //hpaned := gtk.NewHPaned()
  //hbox.Add(hpaned)

  //frame1 := gtk.NewFrame(imageFile + " before processing.")
  //framebox1 := gtk.NewHBox(false, 1)
  //frame1.Add(framebox1)

  //frame2 := gtk.NewFrame(imageFile + " after processing.")
  //framebox2 := gtk.NewHBox(false, 1)
  //frame2.Add(framebox2)

  //hpaned.Pack1(frame1, false, false)
  //hpaned.Pack2(frame2, false, false)

  //image := gtk.NewImageFromFile(imageFile)
  //framebox1.Add(image)

  //image2 := gtk.NewImageFromFile("./edges.png")
  //framebox2.Add(image2)

  //--------------------------------------------------------
  //window.Add(hbox)
  //horizontalSize := width * 2
  //verticalSize := height
  //window.SetSizeRequest(horizontalSize, verticalSize)
  //window.ShowAll()
  //gtk.Main()
 }

Sample output:

sample output of Sobel algorithm


NOTES:

This code is too complicated. Is there are a better way? Ya sure, check out https://github.com/anthonynsimon/bild - EdgeDetection and Sobel functions.

References:

https://github.com/mattn/go-gtk

https://www.socketloop.com/tutorials/golang-image-to-ascii-art-example

https://www.socketloop.com/tutorials/golang-create-new-color-from-command-line-parameters

https://www.socketloop.com/tutorials/golang-get-rgba-values-of-each-image-pixel

http://dupress.deloitte.com/dup-us-en/focus/cognitive-technologies/what-is-cognitive-technology.html

https://en.wikipedia.org/wiki/Sobel_operator

https://www.socketloop.com/tutorials/golang-generate-thumbnails-from-images

https://socketloop.com/tutorials/golang-find-relative-luminance-or-color-brightness

  See also : Golang : Find relative luminance or color brightness





By Adam Ng

IF you gain some knowledge or the information here solved your programming problem. Please consider donating to the less fortunate or some charities that you like. Apart from donation, planting trees, volunteering or reducing your carbon footprint will be great too.


Advertisement