func ImageEnhanceGrey(img *image.Gray, enhanceDegress float32, enhanceOffset float32) *image.RGBA { rgbFunc := func(xpos, ypos int) (r0, g0, b0, a0 uint8) { gray := img.GrayAt(xpos, ypos) return gray.Y, gray.Y, gray.Y, gray.Y } imgEnhanced := imenhance(enhanceDegress, enhanceOffset, [3]bool{true, true, true}, false, img.Rect, rgbFunc) return imgEnhanced }
func emptyCol(m *image.Gray, r image.Rectangle, x int) bool { for y := r.Min.Y; y < r.Max.Y; y++ { if m.GrayAt(x, y).Y > 0 { return false } } return true }
func emptyRow(m *image.Gray, r image.Rectangle, y int) bool { for x := r.Min.X; x < r.Max.X; x++ { if m.GrayAt(x, y).Y > 0 { return false } } return true }
func meanHeight(g *image.Gray) uint8 { b := g.Bounds() var total uint64 for i := b.Min.X; i < b.Max.X; i++ { for j := b.Min.Y; j < b.Max.Y; j++ { total += uint64(g.GrayAt(i, j).Y) } } return uint8(total / uint64((b.Dx() * b.Dy()))) }
// Transforms gray input image color depth to the palette defined in p // The algorithm simply assigns the nearest palette color to each pixel, // without distributing error in any way, so expect color banding func ditheringAverage(img *image.Gray, p *color.Palette) { size := img.Bounds() for y := size.Min.Y; y < size.Max.Y; y++ { for x := size.Min.X; x < size.Max.X; x++ { c1 := img.GrayAt(x, y) c2 := p.Convert(c1).(color.Gray) img.SetGray(x, y, c2) } } }
//////////////////////////////////////////////////////////////////////////////// // Does a top to bottom, left to right scan of img starting at x,y. When a // // black pixel is encountered, the scan stops and the position is returned // //////////////////////////////////////////////////////////////////////////////// func nextPixel(x, y int, img *image.Gray, vis []bool) (int, int) { offset := img.Bounds().Max.X for cy := y; cy < img.Bounds().Max.Y; cy++ { start := 0 if cy == y { start = x } for cx := start; cx < img.Bounds().Max.X; cx++ { if !vis[cx+cy*offset] && img.GrayAt(cx, cy) == black { return cx, cy } } } return -1, -1 }
func ImageFilterGrey(img *image.Gray, filter [][]float32, lowQuality bool) *image.RGBA { if lowQuality { imgAva := ImageEnhanceGrey(img, filter[0][0], 0) return imfilter(filter, img.Rect, func(xpos, ypos int) (r0, g0, b0, a0 uint8) { gray := imgAva.RGBAAt(xpos, ypos) return gray.R, gray.R, gray.R, gray.A }, func(filterValue, colorValue float32) float32 { return colorValue }) } return imfilter(filter, img.Rect, func(xpos, ypos int) (r0, g0, b0, a0 uint8) { gray := img.GrayAt(xpos, ypos) return gray.Y, gray.Y, gray.Y, gray.Y }, func(filterValue, colorValue float32) float32 { return filterValue * colorValue }) }
// RecogniseDigit takes 28x28 gray image and tries to recognise a digit // panics if image has wrong size func RecogniseDigit(img image.Gray, threshold uint8) (int, float64) { if img.Bounds().Max.X != 28 || img.Bounds().Max.Y != 28 { panic("Image size is invalid, use 28x28.") } input := make([]float64, 28*28, 28*28) pos := 0 for x := 0; x < 28; x++ { for y := 0; y < 28; y++ { val := img.GrayAt(x, y).Y if val < threshold { val = 255 - val } else { val = 0 } img.SetGray(x, y, color.Gray{Y: val}) input[pos] = float64(val) / 255 pos++ } } digit, confidence := argmax(nn.Evaluate(input)) return digit, confidence }
func drawDroplet(img *image.Gray, x, y, r, h float64) { ri := int(r + 1) xi := int(x + 0.5) yi := int(y + 0.5) for i := yi - ri; i <= yi+ri; i++ { for j := xi - ri; j <= xi+ri; j++ { if i < 0 || i >= img.Rect.Dy() || j < 0 || j >= img.Rect.Dx() { continue } di := float64(i) - y dj := float64(j) - x if rr := di*di + dj*dj; rr <= r*r { vprev := img.GrayAt(j, i).Y v := 100*h/r*math.Pow(1-rr/r/r, 1.0)/math.Sqrt(1-di/r) + float64(vprev) if v > 255 { v = 255 } img.SetGray(j, i, color.Gray{uint8(v)}) } } } }
// Dithering algorithms // Dithering is the process of reducing the colorspace of an image to a more // restricted palette. Since the Game Boy Printer only has a 2bit colorspace // available, it's important that some dithering algorithms are implemented to // deal with the fact. Depending on the properties of the dithering algorithm // detail may or may not be preserved as well. Using simple techniques like // average dithering will yield results with lots of color banding, while employing // error diffusion techniques such as Floyd Steinberg will produce smoother // results (color banding is replaced by the introduction of noise) // The wikipedia article (http://en.wikipedia.org/wiki/Dither) is quite interesting // and talks about some dithering algorithms, some of which are implemented here. // Floyd Steinberg is an error diffusion dithering algorithm that adds the error // of each color conversion to the neighbours of each pixel. This way it's possible // to achieve a finer graded dithering func ditheringFloydSteinberg(img *image.Gray, p *color.Palette) { size := img.Bounds() for y := size.Min.Y; y < size.Max.Y; y++ { for x := size.Min.X; x < size.Max.X; x++ { c1 := img.GrayAt(x, y) c2 := p.Convert(c1).(color.Gray) delta := int(c1.Y) - int(c2.Y) switch { case x+1 < size.Max.X && y < size.Max.Y: img.SetGray(x+1, y, color.Gray{uint8(int(img.GrayAt(x+1, y).Y) + delta*7/16)}) fallthrough case x < size.Max.X && y+1 < size.Max.Y: img.SetGray(x, y+1, color.Gray{uint8(int(img.GrayAt(x, y+1).Y) + delta*5/16)}) fallthrough case x-1 >= size.Min.X && y+1 < size.Max.Y: img.SetGray(x-1, y+1, color.Gray{uint8(int(img.GrayAt(x-1, y+1).Y) + delta*3/16)}) fallthrough case x+1 < size.Max.X && y+1 < size.Max.Y: img.SetGray(x+1, y+1, color.Gray{uint8(int(img.GrayAt(x+1, y+1).Y) + delta*1/16)}) } img.Set(x, y, c2) } } }
func (g *generator) buildTerrain(mpath minecraft.Path, level *level, terrain, biomes, plants *image.Paletted, height, water *image.Gray, c chan paint) error { b := terrain.Bounds() proceed := make(chan uint8, 10) errChan := make(chan error, 1) go func() { defer close(proceed) cc := newCache(g.Terrain.Blocks) for j := 0; j < b.Max.Y; j += 16 { chunkZ := int32(j >> 4) for i := 0; i < b.Max.X; i += 16 { chunkX := int32(i >> 4) h := int32(meanHeight(height.SubImage(image.Rect(i, j, i+16, j+16)).(*image.Gray))) wh := int32(meanHeight(water.SubImage(image.Rect(i, j, i+16, j+16)).(*image.Gray))) var t uint8 if wh >= h<<1 { // more water than land... c <- paint{ color.RGBA{0, 0, 255, 255}, chunkX, chunkZ, } t = uint8(len(g.Terrain.Blocks) - 1) h = wh } else { t = modeTerrain(terrain.SubImage(image.Rect(i, j, i+16, j+16)).(*image.Paletted), len(g.Terrain.Palette)) c <- paint{ g.Terrain.Palette[t], chunkX, chunkZ, } } if err := mpath.SetChunk(cc.getFromCache(chunkX, chunkZ, t, h)); err != nil { errChan <- err return } proceed <- t } } }() ts := make([]uint8, 0, 1024) for i := 0; i < (b.Max.X>>4)+2; i++ { ts = append(ts, <-proceed) // get far enough ahead so all chunks are surrounded before shaping, to get correct lighting } select { case err := <-errChan: return err default: } for j := int32(0); j < int32(b.Max.Y); j += 16 { chunkZ := j >> 4 for i := int32(0); i < int32(b.Max.X); i += 16 { chunkX := i >> 4 var totalHeight int32 ot := ts[0] ts = ts[1:] oy, _ := level.GetHeight(i, j) for x := i; x < i+16; x++ { for z := j; z < j+16; z++ { if biomes != nil { level.SetBiome(x, z, g.Biomes.Values[biomes.ColorIndexAt(int(x), int(z))]) } h := int32(height.GrayAt(int(x), int(z)).Y) totalHeight += h wl := int32(water.GrayAt(int(x), int(z)).Y) y := oy if h > y { y = h } if wl > y { y = wl } for ; y > h && y > wl; y-- { level.SetBlock(x, y, z, minecraft.Block{}) } if plants != nil { p := g.Plants.Blocks[plants.ColorIndexAt(int(x), int(z))] py := int32(1) for ; py <= int32(p.Level); py++ { level.SetBlock(x, y+py, z, p.Base) } level.SetBlock(x, y+py, z, p.Top) } for ; y > h; y-- { level.SetBlock(x, y, z, minecraft.Block{ID: 9}) } t := terrain.ColorIndexAt(int(x), int(z)) tb := g.Terrain.Blocks[t] for ; y > h-int32(tb.Level); y-- { level.SetBlock(x, y, z, tb.Top) } if t != ot { h = 0 } else { h = oy } for ; y >= h; y-- { level.SetBlock(x, y, z, tb.Base) } } } c <- paint{ color.Alpha{uint8(totalHeight >> 8)}, chunkX, chunkZ, } select { case p, ok := <-proceed: if ok { ts = append(ts, p) } case err := <-errChan: return err } } } return nil }
func TestDecode(t *testing.T) { var j *image.Gray i, _, _ := Decode("../att_faces/s1/1.png") j = i.(*image.Gray) fmt.Println(j.GrayAt(0, 0).Y) }
func blobAt(x, y int, img *image.Gray, vis []bool) *Blob { if img.GrayAt(x, y) == white { return nil } width := img.Bounds().Max.X height := img.Bounds().Max.Y r := image.Rect(x, y, x, y) q := newQueue() q.q(point{x, y}) for q.length != 0 { p := q.dq() if vis[p.x+width*p.y] { continue } vis[p.x+width*p.y] = true if p.x > r.Max.X { r.Max.X = p.x } if p.x < r.Min.X { r.Min.X = p.x } if p.y > r.Max.Y { r.Max.Y = p.y } if p.y < r.Min.Y { r.Min.Y = p.y } // Up if p.y-1 >= 0 { if !vis[p.x+(p.y-1)*width] && img.GrayAt(p.x, p.y-1) == black { q.q(point{p.x, p.y - 1}) } } // Right if p.x+1 < width { if !vis[p.x+1+p.y*width] && img.GrayAt(p.x+1, p.y) == black { q.q(point{p.x + 1, p.y}) } } // Down if p.y+1 < height { if !vis[p.x+(p.y+1)*width] && img.GrayAt(p.x, p.y+1) == black { q.q(point{p.x, p.y + 1}) } } // Left if p.x-1 >= 0 { if !vis[p.x-1+p.y*width] && img.GrayAt(p.x-1, p.y) == black { q.q(point{p.x - 1, p.y}) } } } r.Max.Y += 1 r.Max.X += 1 return &Blob{Bounds: r, Img: img.SubImage(r)} }