all repos — captcha @ 69b50fb6730cbea5fb4d367f1817415a215e7a9a

Go package captcha implements generation and verification of image and audio CAPTCHAs.

Initial version (numbers).
Dmitry Chestnykh dmitry@codingrobots.com
Tue, 19 Apr 2011 20:32:40 +0200
commit

69b50fb6730cbea5fb4d367f1817415a215e7a9a

1 files changed, 268 insertions(+), 0 deletions(-)

jump to
A captcha.go

@@ -0,0 +1,268 @@

+package main + +import ( + "image" + "image/png" + "os" + "rand" + "time" + crand "crypto/rand" + "io" +) + +var numbers = [][]byte{ + { + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 0, 1, 1, 1, 0, + }, + { + 0, 0, 1, 0, 0, + 0, 1, 1, 0, 0, + 1, 0, 1, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, + 1, 1, 1, 1, 1, + }, + { + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 0, 0, 0, 0, 1, + 0, 0, 0, 1, 1, + 0, 1, 1, 0, 0, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + }, + { + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, + 0, 0, 0, 1, 1, + 0, 1, 1, 0, 0, + 0, 0, 0, 1, 0, + 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, + 1, 1, 1, 1, 0, + }, + { + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 0, 0, 1, 0, + 1, 1, 1, 1, 1, + 0, 0, 0, 1, 0, + 0, 0, 0, 1, 0, + 0, 0, 0, 1, 0, + }, + { + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 1, 1, 1, 0, + 0, 0, 0, 1, 1, + 0, 0, 0, 0, 1, + 0, 0, 0, 1, 1, + 1, 1, 1, 1, 0, + }, + { + 0, 0, 1, 1, 1, + 0, 1, 0, 0, 0, + 1, 0, 0, 0, 0, + 1, 1, 1, 1, 0, + 1, 1, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 0, 0, 1, + 0, 1, 1, 1, 0, + }, + { + 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, + 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, + 0, 1, 0, 0, 0, + 0, 1, 0, 0, 0, + 0, 1, 0, 0, 0, + }, + { + 0, 1, 1, 1, 0, + 1, 0, 0, 0, 1, + 1, 1, 0, 1, 1, + 0, 1, 1, 1, 0, + 1, 1, 0, 1, 1, + 1, 0, 0, 0, 1, + 1, 1, 0, 1, 1, + 0, 1, 1, 1, 0, + }, + { + 0, 1, 1, 1, 0, + 1, 0, 0, 1, 1, + 1, 0, 0, 0, 1, + 1, 1, 0, 0, 1, + 0, 1, 1, 1, 1, + 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, + 1, 1, 1, 1, 0, + }, +} + +const ( + NumberWidth = 5 + NumberHeight = 8 + DotSize = 6 + SkewFactor = 3 +) + +func drawHorizLine(img *image.NRGBA, color image.Color, fromX, toX, y int) { + for x := fromX; x <= toX; x++ { + img.Set(x, y, color) + } +} + +func drawCircle(img *image.NRGBA, color image.Color, x0, y0, radius int) { + f := 1 - radius + ddF_x := 1 + ddF_y := -2 * radius + x := 0 + y := radius + + img.Set(x0, y0+radius, color) + img.Set(x0, y0-radius, color) + //img.Set(x0+radius, y0, color) + //img.Set(x0-radius, y0, color) + drawHorizLine(img, color, x0-radius, x0+radius, y0) + + for x < y { + // ddF_x == 2 * x + 1; + // ddF_y == -2 * y; + // f == x*x + y*y - radius*radius + 2*x - y + 1; + if f >= 0 { + y-- + ddF_y += 2 + f += ddF_y + } + x++ + ddF_x += 2 + f += ddF_x + //img.Set(x0+x, y0+y, color) + //img.Set(x0-x, y0+y, color) + drawHorizLine(img, color, x0-x, x0+x, y0+y) + //img.Set(x0+x, y0-y, color) + //img.Set(x0-x, y0-y, color) + drawHorizLine(img, color, x0-x, x0+x, y0-y) + //img.Set(x0+y, y0+x, color) + //img.Set(x0-y, y0+x, color) + drawHorizLine(img, color, x0-y, x0+y, y0+x) + //img.Set(x0+y, y0-x, color) + //img.Set(x0-y, y0-x, color) + drawHorizLine(img, color, x0-y, x0+y, y0-x) + } +} + +func min3(x, y, z uint8) (o uint8) { + o = x + if y < o { + o = y + } + if z < o { + o = z + } + return +} + +func max3(x, y, z uint8) (o uint8) { + o = x + if y > o { + o = y + } + if z > o { + o = z + } + return +} + +func setRandomBrightness(c *image.NRGBAColor, max uint8) { + minc := min3(c.R, c.G, c.B) + maxc := max3(c.R, c.G, c.B) + if maxc > max { + return + } + n := rand.Intn(int(max-maxc)) - int(minc) + c.R = uint8(int(c.R) + n) + c.G = uint8(int(c.G) + n) + c.B = uint8(int(c.B) + n) +} + +func fillWithCircles(img *image.NRGBA, n, maxradius int) { + maxx := img.Bounds().Max.X + maxy := img.Bounds().Max.Y + color := image.NRGBAColor{0, 0, 0x80, 0xFF} + for i := 0; i < n; i++ { + setRandomBrightness(&color, 255) + r := rand.Intn(maxradius-1)+1 + drawCircle(img, color, rand.Intn(maxx-r*2)+r, rand.Intn(maxy-r*2)+r, r) + } +} + +func drawNumber(img *image.NRGBA, number []byte, x, y int, color image.NRGBAColor) { + skf := rand.Intn(SkewFactor)-SkewFactor/2 + if skf < 0 { + x -= skf * NumberHeight + } + for y0 := 0; y0 < NumberHeight; y0++ { + for x0 := 0; x0 < NumberWidth; x0++ { + radius := rand.Intn(DotSize/2)+DotSize/2 + addx := rand.Intn(radius/2) + addy := rand.Intn(radius/2) + if number[y0*NumberWidth+x0] == 1 { + drawCircle(img, color, x+x0*DotSize+DotSize+addx, y+y0*DotSize+DotSize+addy, radius) + } + } + x += skf + } +} + +func drawNumbersToImage(ns []byte) { + img := image.NewNRGBA(NumberWidth*(DotSize+2)*len(ns)+DotSize, NumberHeight*DotSize+(DotSize*6)) + for y := 0; y < img.Bounds().Max.Y; y++ { + for x := 0; x < img.Bounds().Max.X; x++ { + img.Set(x, y, image.NRGBAColor{0xFF, 0xFF, 0xFF, 0xFF}) + } + } + fillWithCircles(img, 60, 3) + x := rand.Intn(DotSize) + y := 0 + color := image.NRGBAColor{0, 0, 0x80, 0xFF} + setRandomBrightness(&color, 180) + for _, n := range ns { + y = rand.Intn(DotSize*4) + drawNumber(img, numbers[n], x, y, color) + x += DotSize * NumberWidth + rand.Intn(SkewFactor)+3 + } + f, err := os.Create("captcha.png") + if err != nil { + panic(err) + } + defer f.Close() + png.Encode(f, img) +} + +func main() { + rand.Seed(time.Seconds()) + n := make([]byte, 6) + if _, err := io.ReadFull(crand.Reader, n); err != nil { + panic(err) + } + for i := range n { + n[i] %= 10 + } + drawNumbersToImage(n) +} +