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:
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
Tutorials
+15.2k Golang : Intercept Ctrl-C interrupt or kill signal and determine the signal type
+6.9k Nginx : How to block user agent ?
+5k Golang : Print instead of building pyramids
+10.4k Golang : Simple File Server
+13.2k Facebook PHP getUser() returns 0
+20.4k PHP : Convert(cast) int to double/float
+7k Golang : How to iterate a slice without using for loop?
+16.1k Golang : Find out mime type from bytes in buffer
+14.2k Golang : Reset buffer example
+13.8k Javascript : Prompt confirmation before exit
+5.2k Golang : Intercept, inject and replay HTTP traffics from web server
+18.2k Golang : Write file with io.WriteString