Created
August 31, 2016 00:04
-
-
Save apg/4a4cdb3e70fb29e801997d8bc3763a84 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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