Created
September 27, 2024 21:38
-
-
Save Lokno/4f8e8b589daf3bff6b23d90f0f1682c6 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 : Lokno Ketchup | |
// 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