Last active
December 15, 2015 17:29
-
-
Save imjasonh/5297038 to your computer and use it in GitHub Desktop.
Go script to read a png image like those found at http://scrollboss.illmosis.net/sprites.php?g=xmen-konamiarc to figure out how to make one with Perler beads (WIP)
This file contains 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" | |
"fmt" | |
"image" | |
"image/color" | |
"image/png" | |
"log" | |
"math" | |
"os" | |
"sort" | |
"strconv" | |
) | |
const ( | |
pegboardH = 29 | |
pegboardW = 29 | |
perlerWidthInches = 0.181102362 // Inches width of a Perler bead | |
rgb8to16 = 0x101 // Multiply an 8-bit RGB value to 16-bit | |
newFilePrefix = "perlerized-" | |
) | |
var filename = flag.String("f", "", "Name of png file to perlerize") | |
func main() { | |
flag.Parse() | |
file, err := os.Open(*filename) | |
if err != nil { | |
log.Fatal(err) | |
} | |
defer file.Close() | |
img, err := png.Decode(file) | |
if err != nil { | |
log.Fatal(err) | |
} | |
bounds := img.Bounds() | |
size := bounds.Size() | |
w := size.X | |
h := size.Y | |
fmt.Printf("Image dimensions: %v x %v pixels\n", h, w) | |
str := func(x int) string { | |
return strconv.FormatFloat(float64(x)*perlerWidthInches, 'g', 3, 64) | |
} | |
realW := str(w) | |
realH := str(h) | |
fmt.Printf("Physical dimensions: %s\" x %s\" ", realH, realW) | |
boards := uint(math.Ceil(float64(w) / pegboardW) * math.Ceil(float64(h) / pegboardH)) | |
fmt.Printf("(%d pegboards)\n", boards) | |
// Make a palette out of all the possible bead colors | |
var palette color.Palette | |
for k, _ := range paletteMap { | |
palette = append(palette, k) | |
} | |
paletted := palettedImage{img, palette} | |
newFilename := newFilePrefix + *filename | |
newFile, err := os.Create(newFilename) | |
if err != nil { | |
log.Fatal(err) | |
} | |
defer newFile.Close() | |
png.Encode(newFile, paletted) | |
fmt.Println("Created:", newFilename) | |
var total uint32 | |
colors := make(map[string]uint32) | |
for i := bounds.Min.X; i < bounds.Max.X; i++ { | |
for j := bounds.Min.Y; j < bounds.Max.Y; j++ { | |
c := paletted.At(i, j) | |
_, _, _, a := c.RGBA() | |
if a == 0 { | |
continue | |
} | |
colors[paletteMap[c]]++ | |
total++ | |
} | |
} | |
fmt.Printf("%d total beads\n=======\n", total) | |
pairs := sortMap(colors) | |
for p := 0; p < len(pairs); p++ { | |
fmt.Println(pairs[p].key, pairs[p].val) | |
} | |
} | |
type palettedImage struct { | |
orig image.Image | |
palette color.Palette | |
} | |
func (p palettedImage) ColorIndexAt(x, y int) uint8 { | |
return uint8(p.palette.Index(p.orig.At(x, y))) | |
} | |
func (p palettedImage) At(x, y int) color.Color { | |
return convert(p.palette, p.orig.At(x, y)) | |
} | |
func (p palettedImage) Bounds() image.Rectangle { | |
return p.orig.Bounds() | |
} | |
func (p palettedImage) ColorModel() color.Model { | |
return color.ModelFunc(func(in color.Color) color.Color { | |
return convert(p.palette, in) | |
}) | |
} | |
func convert(p color.Palette, c color.Color) color.Color { | |
_, _, _, a := c.RGBA() | |
if a == 0 { | |
return color.Transparent | |
} | |
return p.Convert(c) | |
} | |
type myColor struct { | |
r, g, b uint32 | |
} | |
func (m myColor) RGBA() (r, g, b, a uint32) { | |
return m.r * rgb8to16, | |
m.g * rgb8to16, | |
m.b * rgb8to16, | |
255 * rgb8to16 | |
} | |
func col(r, g, b uint32) color.Color { | |
return myColor{r, g, b} | |
} | |
// Map of available Perler bead colors to their name | |
// From https://sites.google.com/site/degenatrons/other-stuff/bead-pattern-generator | |
var paletteMap = map[color.Color]string{ | |
col(0, 0, 0): "P18-BLACK", | |
col(95, 100, 100): "P92-DARK-GREY", | |
col(145, 150, 155): "H17-GREY", | |
col(150, 155, 160): "P17-GREY", | |
col(160, 160, 155): "N08-ASH-GREY", | |
col(255, 255, 255): "P01-WHITE", | |
col(255, 210, 75): "N14-YELLOW", | |
col(255, 215, 90): "H03-YELLOW", | |
col(255, 235, 55): "P03-YELLOW", | |
col(245, 240, 125): "H43-PASTEL-YELLOW", | |
col(245, 240, 155): "P56-PASTEL-YELLOW", | |
col(255, 245, 175): "N21-LIGHT-YELLOW", | |
col(250, 240, 195): "H02-CREAM", | |
col(240, 230, 195): "P02-CREAM", | |
col(245, 225, 215): "N12-IVORY", | |
col(230, 225, 225): "N10-LIGHT-GREY", | |
col(250, 200, 85): "P57-CHEDDAR", | |
col(240, 175, 95): "H60-TEDDY-BEAR", | |
col(240, 150, 110): "P90-BUTTERSCOTCH", | |
col(225, 185, 150): "H27-BEIGE", | |
col(240, 195, 150): "N07-SKIN", | |
col(185, 160, 145): "N06-BEIGE", | |
col(205, 165, 135): "P35-TAN", | |
col(210, 155, 125): "N20-LIGHT-BROWN", | |
col(190, 125, 85): "N05-BUTTERSCOTCH", | |
col(190, 130, 100): "H21-LIGHT-BROWN", | |
col(150, 85, 100): "N04-WINE-RED", | |
col(115, 75, 85): "H30-BURGUNDY", | |
col(90, 85, 80): "N02-DARK-BROWN", | |
col(105, 75, 80): "N03-BROWN-MEDIUM", | |
col(110, 90, 85): "P12-BROWN", | |
col(100, 75, 80): "H12-BROWN", | |
col(165, 90, 90): "P20-RUST", | |
col(170, 85, 80): "H20-BROWN", | |
col(145, 105, 100): "N27-BROWN", | |
col(160, 130, 95): "P21-LIGHT-BROWN", | |
col(195, 80, 115): "H29-RASPBERRY", | |
col(190, 70, 115): "P88-RASPBERRY", | |
col(175, 75, 85): "H22-DARK-RED", | |
col(215, 65, 85): "N19-LIGHT-RED", | |
col(205, 70, 90): "P05-RED", | |
col(240, 105, 95): "H04-ORANGE", | |
col(255, 115, 80): "P04-ORANGE", | |
col(225, 160, 85): "N29-PEARL-ORANGE", | |
col(255, 120, 165): "N25-OLD-ROSE", | |
col(255, 95, 200): "H32-FUCHSIA", | |
col(255, 60, 130): "P38-MAGENTA", | |
col(255, 90, 115): "P59-HOT-CORAL", | |
col(255, 120, 140): "H44-PASTEL-CORAL", | |
col(255, 150, 160): "P63-BLUSH", | |
col(245, 200, 190): "N18-LIGHT-PINK", | |
col(250, 205, 195): "P33-PEACH", | |
col(240, 170, 165): "H26-FLESH", | |
col(255, 185, 150): "N26-LIGHT-ORANGE", | |
col(240, 130, 175): "P06-BUBBLE-GUM", | |
col(240, 95, 165): "P83-PINK", | |
col(230, 135, 200): "H48-PASTEL-PINK", | |
col(215, 155, 200): "P55-PASTEL-PINK", | |
col(245, 155, 175): "H06-PINK", | |
col(245, 200, 230): "P79-LIGHT-PINK", | |
col(155, 135, 205): "P54-PASTEL-LAVENDER", | |
col(165, 140, 205): "H45-PASTEL-PURPLE", | |
col(200, 185, 240): "N24-LILAC", | |
col(115, 90, 155): "N11-PURPLE", | |
col(5, 150, 205): "P62-TURQUOISE", | |
col(85, 125, 185): "P70-PERIWINKLE", | |
col(45, 130, 200): "P09-LIGHT-BLUE", | |
col(0, 120, 210): "N17-BLUE", | |
col(25, 105, 180): "H09-LIGHT-BLUE", | |
col(35, 85, 160): "H08-BLUE", | |
col(35, 80, 145): "P08-DARK-BLUE", | |
col(175, 90, 160): "P60-PLUM", | |
col(120, 95, 155): "P07-PURPLE", | |
col(120, 90, 145): "H07-PURPLE", | |
col(105, 160, 175): "H31-TURQUOISE", | |
col(90, 160, 205): "P52-PASTEL-BLUE", | |
col(80, 170, 225): "H46-PASTEL-BLUE", | |
col(90, 170, 235): "N23-PASTEL-BLUE", | |
col(160, 205, 245): "N28-LIGHT-BLUE", | |
col(160, 215, 225): "P58-TOOTHPASTE", | |
col(200, 200, 120): "N30-OLIVE", | |
col(135, 210, 145): "P53-PASTEL-GREEN", | |
col(150, 230, 160): "H47-PASTEL-GREEN", | |
col(125, 210, 80): "P61-KIWI-LIME", | |
col(70, 85, 90): "H28-DARK-GREEN", | |
col(70, 100, 90): "N09-DARK-GREEN", | |
col(40, 140, 100): "P10-DARK-GREEN", | |
col(35, 125, 95): "H10-GREEN", | |
col(50, 145, 100): "N16-GREEN", | |
col(0, 150, 165): "P91-PARROT-GREEN", | |
col(70, 195, 165): "H11-LIGHT-GREEN", | |
col(75, 195, 180): "P11-LIGHT-GREEN", | |
col(115, 185, 115): "P80-BRIGHT-GREEN", | |
col(55, 170, 100): "N22-PEARL-GREEN", | |
} | |
type pair struct { | |
key string | |
val uint32 | |
} | |
type pairlist []pair | |
func (p pairlist) Swap(i, j int) { p[i], p[j] = p[j], p[i] } | |
func (p pairlist) Len() int { return len(p) } | |
func (p pairlist) Less(i, j int) bool { return p[i].val > p[j].val } | |
func sortMap(m map[string]uint32) pairlist { | |
p := make(pairlist, len(m)) | |
i := 0 | |
for k, v := range m { | |
p[i] = pair{k, v} | |
i++ | |
} | |
sort.Sort(p) | |
return p | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Where did you find the rgb values of the colors?