summaryrefslogtreecommitdiff
path: root/autoart/autoimages.go
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2019-06-16 12:46:14 -0400
committerLeo Tenenbaum <pommicket@gmail.com>2019-06-16 12:46:14 -0400
commit1b7d30292c0d6233f38121366adc400cb4be029a (patch)
treee466f5b28d34a3a8a7bb593cde548a0cf5f0d9a0 /autoart/autoimages.go
Initial commit0.0
Diffstat (limited to 'autoart/autoimages.go')
-rw-r--r--autoart/autoimages.go257
1 files changed, 257 insertions, 0 deletions
diff --git a/autoart/autoimages.go b/autoart/autoimages.go
new file mode 100644
index 0000000..53eea18
--- /dev/null
+++ b/autoart/autoimages.go
@@ -0,0 +1,257 @@
+/*
+Copyright (C) 2019 Leo Tenenbaum
+
+This file is part of AutoArt.
+
+AutoArt is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+AutoArt is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with AutoArt. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+package autoart
+
+import (
+ "image"
+ "image/color"
+ "math"
+ "math/rand"
+ "fmt"
+ "github.com/pommicket/autoart/autoutils"
+)
+
+const (
+ XY = iota
+ RTHETA
+)
+
+const (
+ RGB = iota
+ GRAYSCALE
+ CMYK
+ HSV
+ YCbCr
+)
+
+const (
+ MOD = iota
+ CLAMP
+ SIGMOID
+)
+
+type Config struct {
+ FunctionLength int
+ ColorSpace int
+ CoordinateSys int
+ Alpha bool
+ Rectifier int // What to do with out-of-bounds values
+}
+
+func sigmoid(x float64) float64 {
+ return 1 / (1 + math.Exp(-x))
+}
+
+func rectify(x float64, rectifier int) float64 {
+ switch rectifier {
+ case MOD:
+ return math.Mod(x, 1)
+ case CLAMP:
+ if x > 1 {
+ return 1
+ } else if x < 0 {
+ return 0
+ }
+ case SIGMOID:
+ return sigmoid(x)
+ }
+ return 0
+}
+
+func (conf *Config) nFunctions() int {
+ a := 0
+ if conf.Alpha { a = 1 }
+ switch conf.ColorSpace {
+ case GRAYSCALE:
+ return a + 1
+ case RGB, HSV, YCbCr:
+ return a + 3
+ case CMYK:
+ return a + 4
+ }
+ panic("Invalid color space!")
+ return a
+}
+
+const defaultFunctionLength = 40
+
+func GenerateImageFromFunctions(width int, height int, config Config,
+ functions []autoutils.Function,
+ vars []float64) image.Image {
+ var rect = image.Rectangle{image.Point{0, 0}, image.Point{width, height}}
+ img := image.NewRGBA(rect)
+ colorSpace := config.ColorSpace
+ alpha := config.Alpha
+ rectifier := config.Rectifier
+ nfunctions := len(functions)
+ rets := make([]uint8, nfunctions)
+ fwidth, fheight := float64(width), float64(height)
+ for y := 0; y < height; y++ {
+ for x := 0; x < width; x++ {
+ switch config.CoordinateSys {
+ case XY:
+ vars[0], vars[1] = float64(x)/fwidth, float64(y)/fheight
+ case RTHETA:
+ dx, dy := float64(x - width/2), float64(y - height/2)
+ vars[0] = math.Sqrt(dx * dx + dy * dy) / ((fwidth+fheight)/2) // r
+ vars[1] = math.Atan2(dy, dx) // theta
+ }
+ for i := range rets {
+ ret := rectify(functions[i].Evaluate(vars), rectifier)
+ rets[i] = uint8(255 * ret)
+ }
+ var r, g, b, a uint8
+ a = 255
+ switch (colorSpace) {
+ case RGB:
+ r, g, b = rets[0], rets[1], rets[2]
+ case GRAYSCALE:
+ r, g, b = rets[0], rets[0], rets[0]
+ case CMYK:
+ r, g, b = color.CMYKToRGB(rets[0], rets[1], rets[2], rets[3])
+ case HSV:
+ r, g, b = autoutils.HSVToRGB(rets[0], rets[1], rets[2])
+ case YCbCr:
+ r, g, b = color.YCbCrToRGB(rets[0], rets[1], rets[2])
+ }
+ if (alpha) {
+ a = rets[nfunctions-1]
+ }
+ img.Set(x, y, color.RGBA{r, g, b, a})
+ }
+ }
+ return img
+}
+
+func GenerateImage(width int, height int, config Config) image.Image {
+ if config.FunctionLength == 0 {
+ // 0 value of config shouldn't have empty functions
+ config.FunctionLength = defaultFunctionLength
+ }
+
+ functionLength := config.FunctionLength
+
+ nfunctions := config.nFunctions()
+ functions := make([]autoutils.Function, nfunctions)
+ for i := range functions {
+ functions[i].Generate(2, functionLength)
+ }
+ vars := []float64{0, 0}
+ return GenerateImageFromFunctions(width, height, config, functions, vars)
+}
+
+
+func GenerateImages(width int, height int, config Config, number int, verbose bool) []image.Image {
+ c := make(chan image.Image)
+ for i := 0; i < number; i++ {
+ go func () {
+ c <- GenerateImage(width, height, config)
+ }()
+ }
+ imgs := make([]image.Image, number)
+ for i := range imgs {
+ imgs[i] = <-c
+ if verbose {
+ fmt.Println("Generating images...", i+1, "/", number)
+ }
+ }
+ return imgs
+}
+
+type PaletteConfig struct {
+ NColors int
+ Alpha bool
+ FunctionLength int
+ CoordinateSys int
+}
+
+func GenerateImagePaletteFrom(width int, height int, conf PaletteConfig,
+ funcs []autoutils.Function, vars []float64,
+ palette []color.RGBA) image.Image {
+ img := image.NewRGBA(image.Rectangle{image.Point{0,0}, image.Point{width, height}})
+ fwidth, fheight := float64(width), float64(height)
+ for y := 0; y < height; y++ {
+ for x := 0; x < width; x++ {
+ switch conf.CoordinateSys {
+ case XY:
+ vars[0], vars[1] = float64(x)/fwidth, float64(y)/fheight
+ case RTHETA:
+ dx, dy := float64(x - width/2), float64(y - height/2)
+ vars[0] = math.Sqrt(dx * dx + dy * dy) / ((fwidth+fheight)/2) // r
+ vars[1] = math.Atan2(dy, dx) // theta
+ }
+ for i := range palette {
+ if i == conf.NColors - 1 {
+ // Background color
+ img.Set(x, y, palette[i])
+ } else if funcs[i].Evaluate(vars) < 0 {
+ img.Set(x, y, palette[i])
+ break
+ }
+ }
+ }
+ }
+ return img
+}
+
+func GenerateImagePalette(width int, height int, conf PaletteConfig) image.Image {
+ nColors := conf.NColors
+ alpha := conf.Alpha
+ functionLength := conf.FunctionLength
+
+ funcs := make([]autoutils.Function, nColors - 1)
+ palette := make([]color.RGBA, nColors)
+
+ // Choose palette
+ for i := range palette {
+ r, g, b := rand.Intn(256), rand.Intn(256), rand.Intn(256)
+ var a int
+ if alpha {
+ a = rand.Intn(256)
+ } else {
+ a = 255
+ }
+ palette[i] = color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
+ }
+ // Choose functions
+ for i := range funcs {
+ funcs[i].Generate(2, functionLength)
+ }
+
+ vars := make([]float64, 2)
+ return GenerateImagePaletteFrom(width, height, conf, funcs, vars, palette)
+}
+
+func GenerateImagesPalette(width int, height int, conf PaletteConfig, number int, verbose bool) []image.Image {
+ c := make(chan image.Image)
+ for i := 0; i < number; i++ {
+ go func() {
+ c <- GenerateImagePalette(width, height, conf)
+ }()
+ }
+ images := make([]image.Image, number)
+ for i := 0; i < number; i++ {
+ images[i] = <-c
+ if verbose {
+ fmt.Println("Generating images...", i+1, "/", number)
+ }
+ }
+ return images
+}