Skip to content

Instantly share code, notes, and snippets.

@Lokno
Created September 17, 2014 14:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Lokno/dc8a1507f9daa5952bec to your computer and use it in GitHub Desktop.
Save Lokno/dc8a1507f9daa5952bec to your computer and use it in GitHub Desktop.
Transforms an PNG image to simulate various color deficiencies.
// Author : Jonathan Decker
// Description : Transforms an PNG image to simulate various color deficiencies.
// Valid Types : normal, protanopia, protanomaly, deuteranopia, deuteranomaly,
// tritanopia, tritanomaly, achromatopsia, achromatomaly
// Usage : color_blindness.go <input> <output> <type>
//
// RGB transform matrices generated by Michael of www.colorjack.com
// Which were created using code by Matthew Wickline and the
// Human-Computer Interaction Resource Network ( http://hcirn.com/ )
package main
import (
"image"
"image/png"
"os"
"fmt"
"image/color"
"time"
"runtime"
"strings"
)
func matrixMult( r, g, b float64, m [9]float64 ) (rr, gg, bb float64) {
rr = r * m[0] + g * m[1] + b * m[2];
gg = r * m[3] + g * m[4] + b * m[5];
bb = r * m[6] + g * m[7] + b * m[8];
return;
}
func transform(dst *image.RGBA, src image.Image, m [9]float64, x0, y0, w, h int, done chan bool) {
for x := x0; x < x0+w; x++ {
for y := y0; y < y0+h; y++ {
r,g,b,a := src.At(x,y).RGBA()
fr := float64(r)
fg := float64(g)
fb := float64(b)
fr,fg,fb = matrixMult(fr,fg,fb,m)
r = uint32(fr)
g = uint32(fg)
b = uint32(fb)
// RGBA colors between 0 and 0xffff, need to be between 0 and 0xff
dst.SetRGBA(x,y,color.RGBA{uint8(r >> 8),uint8(g >> 8),uint8(b >> 8),uint8(a >> 8)})
}
}
done <- true
}
// Seperates image into square blocks of BSIZE
const BSIZE int = 64
const interations float64 = 1
func main() {
if len(os.Args) < 4 {
fmt.Println(" usage: color_blindness.go <input> <output> <type>")
fmt.Println(" types:")
fmt.Println(" normal : normal vision (identity)")
fmt.Println(" protanopia : Red-Blind")
fmt.Println(" protanomaly : Red-Weak")
fmt.Println(" deuteranopia : Green-Blind")
fmt.Println(" deuteranomaly : Green-Weak")
fmt.Println(" tritanopia : Blue-Blind")
fmt.Println(" tritanomaly : Blue-Weak")
fmt.Println(" achromatopsia : Monochromacy")
fmt.Println(" achromatomaly : Blue Cone Monochromacy")
os.Exit(1)
}
// map of color transform matrices
colorMats := map[string][9]float64{
"normal": {1.0,0,0,0,1.0,0,0,0,1.0},
// Red-Blind
"protanopia": {0.567,0.433,0.000,
0.558,0.442,0.000,
0.000,0.242,0.758},
// Red-Weak
"protanomaly": {0.817,0.183,0.000,
0.333,0.667,0.000,
0.000,0.125,0.875},
// Green-Blind
"deuteranopia": {0.625,0.375,0.000,
0.700,0.300,0.000,
0.000,0.300,0.700},
// Green-Weak
"deuteranomaly": {0.800,0.200,0.000,
0.258,0.742,0.000,
0.000,0.142,0.858},
// Blue-Blind
"tritanopia": {0.950,0.050,0.000,
0.000,0.433,0.567,
0.000,0.475,0.525},
// Blue-Weak
"tritanomaly": {0.967,0.033,0.00,
0.00,0.733,0.267,
0.00,0.183,0.817},
// Monochromacy
"achromatopsia": {0.299,0.587,0.114,
0.299,0.587,0.114,
0.299,0.587,0.114},
// Blue Cone Monochromacy
"achromatomaly": {0.618,0.320,0.062,
0.163,0.775,0.062,
0.163,0.320,0.516},
}
deficiency := strings.ToLower(os.Args[3])
mat, matok := colorMats[deficiency]
if !matok {
fmt.Println("Error: Type not found")
os.Exit(2)
}
infile, inerr := os.Open(os.Args[1])
outfile, outerr := os.Create(os.Args[2])
// Set to run on 8 threads
runtime.GOMAXPROCS(8)
if inerr == nil && outerr == nil {
img, pngerr := png.Decode(infile)
infile.Close()
if inerr == nil && outerr == nil && pngerr == nil {
bnds := img.Bounds()
w := bnds.Max.X - bnds.Min.X
h := bnds.Max.Y - bnds.Min.Y
rgba := image.NewRGBA(image.Rect(0,0,w,h))
xBlocks := w / BSIZE
yBlocks := h / BSIZE
if w % BSIZE != 0 {
xBlocks++
}
if h % BSIZE != 0 {
yBlocks++
}
avgTime := 0.0
for i := 0; i < int(interations); i++ {
done := make(chan bool)
start := time.Now()
for x := 0; x < xBlocks; x++ {
for y := 0; y < yBlocks; y++ {
wBlockSize := BSIZE
hBlockSize := BSIZE
if x == xBlocks-1 {
wBlockSize = w - x*BSIZE
}
if y == yBlocks-1 {
hBlockSize = h - y*BSIZE
}
go transform(rgba,img,mat,x*BSIZE,y*BSIZE,wBlockSize,hBlockSize,done)
}
}
for x := 0; x < xBlocks; x++ {
for y := 0; y < yBlocks; y++ {
<-done
}
}
avgTime += time.Since(start).Seconds()
}
fmt.Printf("%fms\n", (avgTime / interations)*1000)
png.Encode(outfile, rgba)
} else {
fmt.Println("Error: Could not decode PNG")
}
outfile.Close()
} else {
fmt.Println("Error: Could not open files")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment