Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

limit options for palette and brush #97

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log"
"math/rand"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
Expand All @@ -18,6 +19,7 @@ import (

var (
Input string
Colors string
Outputs flagArray
Background string
Configs shapeConfigArray
Expand Down Expand Up @@ -62,7 +64,8 @@ func (i *shapeConfigArray) Set(value string) error {
}

func init() {
flag.StringVar(&Input, "i", "", "input image path")
flag.StringVar(&Input, "i", "", "Input image path")
flag.StringVar(&Colors, "c", "", "input image with example colors")
flag.Var(&Outputs, "o", "output image path")
flag.Var(&Configs, "n", "number of primitives")
flag.StringVar(&Background, "bg", "", "background color (hex)")
Expand Down Expand Up @@ -132,6 +135,19 @@ func main() {
if Workers < 1 {
Workers = runtime.NumCPU()
}
if Colors != "" {
fmt.Println("here")
args := []string{
Input,
"-dither",
"Riemersma",
"-remap",
Colors,
"dithered.png"}
cmd := exec.Command("convert", args...)
cmd.Run()
Input = "dithered.png"
}

// read input image
primitive.Log(1, "reading %s\n", Input)
Expand Down Expand Up @@ -166,7 +182,7 @@ func main() {

// find optimal shape and add it to the model
t := time.Now()
n := model.Step(primitive.ShapeType(config.Mode), config.Alpha, config.Repeat)
n := model.Step(primitive.ShapeType(config.Mode), config.Alpha, config.Repeat, frame, config.Count)
nps := primitive.NumberString(float64(n) / time.Since(t).Seconds())
elapsed := time.Since(start).Seconds()
primitive.Log(1, "%d: t=%.3f, score=%.6f, n=%d, n/s=%s\n", frame, elapsed, model.Score, n, nps)
Expand Down
29 changes: 28 additions & 1 deletion primitive/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,33 @@ import (
"math"
)

func closestColor(r1 int, g1 int, b1 int) Color {
palette := [8][3]int{
{255, 255, 255},
{255, 0, 25},
{247, 236, 70},
{0, 225, 132},
{21, 54, 177},
{102, 74, 41},
{208, 0, 250},
{0, 0, 0}}

var closestDis = float64(10000)
var cc = Color{0, 0, 0, 0}
for _, rgb2 := range palette {
r2 := rgb2[0]
g2 := rgb2[1]
b2 := rgb2[2]
dis := float64(math.Pow(math.Pow(float64(r1-r2), 2)+math.Pow(float64(g1-g2), 2)+math.Pow(float64(b1-b2), 2), 0.5))
if dis < closestDis {
closestDis = dis
cc = Color{r2, g2, b2, 255}
}
}
return cc

}

func computeColor(target, current *image.RGBA, lines []Scanline, alpha int) Color {
var rsum, gsum, bsum, count int64
a := 0x101 * 255 / alpha
Expand All @@ -30,7 +57,7 @@ func computeColor(target, current *image.RGBA, lines []Scanline, alpha int) Colo
r := clampInt(int(rsum/count)>>8, 0, 255)
g := clampInt(int(gsum/count)>>8, 0, 255)
b := clampInt(int(bsum/count)>>8, 0, 255)
return Color{r, g, b, alpha}
return closestColor(r, g, b)
}

func copyLines(dst, src *image.RGBA, lines []Scanline) {
Expand Down
33 changes: 17 additions & 16 deletions primitive/ellipse.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,25 @@ func NewRandomEllipse(worker *Worker) *Ellipse {
return &Ellipse{worker, x, y, rx, ry, false}
}

func Max(x, y int) int {
if x < y {
return y
}
return x
}

func NewRandomCircle(worker *Worker) *Ellipse {
rnd := worker.Rnd
x := rnd.Intn(worker.W)
y := rnd.Intn(worker.H)
r := rnd.Intn(32) + 1
var r = int(0)
if worker.Frame < (worker.N / 3) {
r = Max(worker.W, worker.H) * 3 / 100
} else if worker.Frame < (worker.N * 2 / 3) {
r = Max(worker.W, worker.H) * 18 / 1000
} else {
r = Max(worker.W, worker.H) * 74 / 10000
}
return &Ellipse{worker, x, y, r, r, true}
}

Expand All @@ -52,21 +66,8 @@ func (c *Ellipse) Mutate() {
w := c.Worker.W
h := c.Worker.H
rnd := c.Worker.Rnd
switch rnd.Intn(3) {
case 0:
c.X = clampInt(c.X+int(rnd.NormFloat64()*16), 0, w-1)
c.Y = clampInt(c.Y+int(rnd.NormFloat64()*16), 0, h-1)
case 1:
c.Rx = clampInt(c.Rx+int(rnd.NormFloat64()*16), 1, w-1)
if c.Circle {
c.Ry = c.Rx
}
case 2:
c.Ry = clampInt(c.Ry+int(rnd.NormFloat64()*16), 1, h-1)
if c.Circle {
c.Rx = c.Ry
}
}
c.X = clampInt(c.X+int(rnd.NormFloat64()*16), 0, w-1)
c.Y = clampInt(c.Y+int(rnd.NormFloat64()*16), 0, h-1)
}

func (c *Ellipse) Rasterize() []Scanline {
Expand Down
11 changes: 6 additions & 5 deletions primitive/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Model struct {
Sw, Sh int
Scale float64
Background Color
Frame int
Target *image.RGBA
Current *image.RGBA
Context *gg.Context
Expand Down Expand Up @@ -116,13 +117,13 @@ func (model *Model) Add(shape Shape, alpha int) {
shape.Draw(model.Context, model.Scale)
}

func (model *Model) Step(shapeType ShapeType, alpha, repeat int) int {
state := model.runWorkers(shapeType, alpha, 1000, 100, 16)
func (model *Model) Step(shapeType ShapeType, alpha, repeat int, frame int, n int) int {
state := model.runWorkers(shapeType, alpha, 1000, 100, 16, frame, n)
// state = HillClimb(state, 1000).(*State)
model.Add(state.Shape, state.Alpha)

for i := 0; i < repeat; i++ {
state.Worker.Init(model.Current, model.Score)
state.Worker.Init(model.Current, model.Score, frame, n)
a := state.Energy()
state = HillClimb(state, 100).(*State)
b := state.Energy()
Expand All @@ -144,7 +145,7 @@ func (model *Model) Step(shapeType ShapeType, alpha, repeat int) int {
return counter
}

func (model *Model) runWorkers(t ShapeType, a, n, age, m int) *State {
func (model *Model) runWorkers(t ShapeType, a, n, age, m int, frame int, totN int) *State {
wn := len(model.Workers)
ch := make(chan *State, wn)
wm := m / wn
Expand All @@ -153,7 +154,7 @@ func (model *Model) runWorkers(t ShapeType, a, n, age, m int) *State {
}
for i := 0; i < wn; i++ {
worker := model.Workers[i]
worker.Init(model.Current, model.Score)
worker.Init(model.Current, model.Score, frame, totN)
go model.runWorker(worker, t, a, n, age, wm, ch)
}
var bestEnergy float64
Expand Down
30 changes: 29 additions & 1 deletion primitive/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,30 @@ func uniformRGBA(r image.Rectangle, c color.Color) *image.RGBA {
return im
}

func closestBGColor(r1 int, g1 int, b1 int) (int, int, int) {
palette := [5][3]int{{39, 79, 156},
{95, 98, 103},
{8, 8, 8},
{117, 14, 30},
{98, 92, 74}}

var closestDis = float64(10000)
var cc = [3]int{0, 0, 0}

for _, rgb2 := range palette {
r2 := rgb2[0]
g2 := rgb2[1]
b2 := rgb2[2]
dis := float64(math.Pow(math.Pow(float64(r1-r2), 2)+math.Pow(float64(g1-g2), 2)+math.Pow(float64(b1-b2), 2), 0.5))
if dis < closestDis {
closestDis = dis
cc = [3]int{r2, g2, b2}
}
}
return cc[0], cc[1], cc[2]

}

func AverageImageColor(im image.Image) color.NRGBA {
rgba := imageToRGBA(im)
size := rgba.Bounds().Size()
Expand All @@ -198,8 +222,12 @@ func AverageImageColor(im image.Image) color.NRGBA {
b += int(c.B)
}
}

r /= w * h
g /= w * h
b /= w * h
return color.NRGBA{uint8(r), uint8(g), uint8(b), 255}

r1, g1, b1 := closestBGColor(r, g, b)

return color.NRGBA{uint8(r1), uint8(g1), uint8(b1), 255}
}
6 changes: 5 additions & 1 deletion primitive/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (

type Worker struct {
W, H int
Frame int
N int
Target *image.RGBA
Current *image.RGBA
Buffer *image.RGBA
Expand All @@ -36,10 +38,12 @@ func NewWorker(target *image.RGBA) *Worker {
return &worker
}

func (worker *Worker) Init(current *image.RGBA, score float64) {
func (worker *Worker) Init(current *image.RGBA, score float64, frame int, n int) {
worker.Current = current
worker.Score = score
worker.Counter = 0
worker.N = n
worker.Frame = frame
worker.Heatmap.Clear()
}

Expand Down