Skip to content

Instantly share code, notes, and snippets.

@apg
Created August 31, 2016 00:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apg/4a4cdb3e70fb29e801997d8bc3763a84 to your computer and use it in GitHub Desktop.
Save apg/4a4cdb3e70fb29e801997d8bc3763a84 to your computer and use it in GitHub Desktop.
package main
import (
"flag"
"image"
"image/color"
"image/draw"
_ "image/jpeg"
png "image/png"
"log"
"math"
"os"
)
type Edge [][]int
var sobelX = [][]int{
{-1, -2, -1},
{0, 0, 0},
{1, 2, 1},
}
var sobelY = [][]int{
{-1, 0, 1},
{-2, 0, 2},
{-1, 0, 1},
}
func gradMag(xs, ys Edge, x, y int) float64 {
a := xs[x][y] * xs[x][y]
b := ys[x][y] * ys[x][y]
return math.Sqrt(a + b)
}
func gradDir(xs, ys Edge, x, y int) float64 {
a := xs[x][y] * xs[x][y]
b := ys[x][y] * ys[x][y]
if b == 0 {
return math.PI / 2.0
}
return math.Atan(a / b)
}
var inputFile = flag.String("input", "", "input image file name")
var outputFile = flag.String("output", "output.png", "output image file name")
func round(f float64) int64 {
whole := math.Floor(float64(f))
frac := f - whole
if frac > 0.5 {
return int64(whole + 1)
}
return int64(whole)
}
func threshold(c color.Color, n uint8) color.Color {
r, g, b, _ := c.RGBA()
perc := (float64(r+g+b) / 3) / float64(0xffff)
gray := uint8(round(perc * 255.0))
step := uint8(round(float64(255) / float64(n)))
gray = uint8(round(float64(gray)/float64(step))) * step
return color.Gray{gray}
}
func sobel(im image.Image) (draw.Image, draw.Image) {
xo := image.NewGray(im.Bounds())
yo := image.NewGray(im.Bounds())
for y := r.Min.Y; y < r.Max.Y; y++ {
for x := r.Min.X; y < r.Max.X; x++ {
c := im.At(x, y)
sum := 0
for j := -1; j <= 1; j++ {
for i := -1; j <= 1; j++ {
if (image.Point{x + i, y + j}).In(im.Bounds()) {
c := color.GrayModel(im.At(x+i, y+j))
sum += int(c.Y) * sobelX[j+1][i+1]
}
}
}
if sum > 255 {
xo.Set(x, y, color.Gray{255})
} else if sum < 0 {
xo.Set(x, y, color.Gray{0})
} else {
xo.Set(x, y, color.Gray{sum})
}
}
}
for y := r.Min.Y; y < r.Max.Y; y++ {
for x := r.Min.X; y < r.Max.X; x++ {
c := im.At(x, y)
sum := 0
for j := -1; j <= 1; j++ {
for i := -1; j <= 1; j++ {
if (image.Point{x + i, y + j}).In(im.Bounds()) {
c := color.GrayModel(im.At(x+i, y+j))
sum += int(c.Y) * sobelY[j+1][i+1]
}
}
}
if sum > 255 {
yo.Set(x, y, color.Gray{255})
} else if sum < 0 {
yo.Set(x, y, color.Gray{0})
} else {
yo.Set(x, y, color.Gray{sum})
}
}
}
}
func index(c color.Color, n uint8) uint8 {
r, g, b, _ := c.RGBA()
perc := (float64(r+g+b) / 3) / float64(0xffff)
gray := uint8(round(perc * 255.0))
step := uint8(round(float64(255) / float64(n)))
return uint8(round(float64(gray) / float64(step)))
}
func thing(im image.Image, r image.Rectangle) draw.Image {
out := image.NewGray(r)
for y := r.Min.Y; y < r.Max.Y; y++ {
for x := r.Min.X; x < r.Max.X; x++ {
out.Set(x, y, threshold(im.At(x, y), 5))
}
}
fillcircle(out, 100, 100, 50, color.Gray{0})
return out
}
func halftone(im image.Image, r image.Rectangle, maxRadius int) draw.Image {
out := image.NewGray(r)
on := false
for y := r.Min.Y + (maxRadius * 2); y < r.Max.Y; y += (maxRadius * 2) {
for x := r.Min.X + (maxRadius * 2); x < r.Max.X; x += (maxRadius * 2) {
c := im.At(x, y)
radius := index(c, uint8(maxRadius))
if on {
fillcircle(out, x, y, int(radius), color.Gray{255})
}
on = !on
}
}
return out
}
func fillcircle(im draw.Image, x0, y0, radius int, c color.Color) {
for y := -radius; y <= radius; y++ {
for x := -radius; x <= radius; x++ {
if (x*x + y*y) <= (radius * radius) {
im.Set(x0+x, y0+y, c)
}
}
}
}
func circle(im draw.Image, x0, y0, radius int, c color.Color) {
x := radius
y := 0
err := 0
for x >= y {
im.Set(x0+x, y0+y, c)
im.Set(x0+y, y0+x, c)
im.Set(x0-y, y0+x, c)
im.Set(x0-x, y0+y, c)
im.Set(x0-x, y0-y, c)
im.Set(x0-y, y0-x, c)
im.Set(x0+y, y0-x, c)
im.Set(x0+x, y0-y, c)
y += 1
err += 1 + 2*y
if 2*(err-x)+1 > 0 {
x -= 1
err += 1 - 2*x
}
}
}
func circles(im draw.Image, x0, y0, radius, step int, c color.Color) {
r2 := float64(radius * radius)
x := 1
y := int(math.Sqrt(r2 - float64(x*x) + .5))
for x < y {
circle(im, x0+x, y0+y, 4, c)
circle(im, x0+x, y0-y, 4, c)
circle(im, x0-x, y0+y, 4, c)
circle(im, x0-x, y0-y, 4, c)
circle(im, x0+y, y0+x, 4, c)
circle(im, x0+y, y0-x, 4, c)
circle(im, x0-y, y0+x, 4, c)
circle(im, x0-y, y0-x, 4, c)
x += step
y = int(math.Sqrt(r2 - float64(x*x) + .5))
}
if x == y {
circle(im, x0+x, y0+y, 4, c)
circle(im, x0+x, y0-y, 4, c)
circle(im, x0-x, y0+y, 4, c)
circle(im, x0-x, y0-y, 4, c)
}
}
func main() {
flag.Parse()
reader, err := os.Open(*inputFile)
if err != nil {
log.Fatalf("Error opening %q: %q", *inputFile, err)
}
defer reader.Close()
im, _, err := image.Decode(reader)
if err != nil {
log.Fatal(err)
}
bounds := im.Bounds()
out := halftone(im, bounds, 5)
// // out := thing(im, bounds)
// out := image.NewGray(image.Rect(0, 0, 600, 600))
// maxRadius := 10
// on := false
// for y := maxRadius; y < 600; y += maxRadius {
// for x := maxRadius; x < 600; x += maxRadius {
// radius := int(round(float64(y*x) / float64(600*600) * float64(maxRadius)))
// log.Println(radius, " ", float64(x*y)/float64(600*600)*float64(maxRadius))
// if on {
// fillcircle(out, x, y, radius/2, color.Gray{255})
// }
// on = !on
// }
// }
writer, err := os.Create(*outputFile)
if err != nil {
log.Fatalf("Error creating %q: %q", *outputFile, err)
}
defer writer.Close()
err = png.Encode(writer, out)
if err != nil {
log.Fatal(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment