Created
September 17, 2014 14:32
-
-
Save Lokno/dc8a1507f9daa5952bec to your computer and use it in GitHub Desktop.
Transforms an PNG image to simulate various color deficiencies.
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
// 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