Created
September 27, 2024 21:39
-
-
Save Lokno/386c9cc6701db6a0b10e9600feb07e3b to your computer and use it in GitHub Desktop.
Takes a stereo image (side-by-side) or two images for each eye and composes them into an anaglyph image
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 : Takes a stereo image (side-by-side) or | |
// two images for each eye and composes them into an anaglyph image | |
// usage: anaglyph.go [-s,--single <side-by-side>|<left> <right>] <anaglyph.png> [RGB] | |
// | |
// parameters: | |
// -s,--single <side-by-side> - One image file with side-by-side images one for each eye | |
// <left> <right> - Two image files, one for each eye. | |
// <anaglyph.png> - path to write the composite PNG image. | |
// [RGB] - (Optional) How images are distributed between the eyes. | |
// write a three character sequence of 'L' (left eye) and 'R' (right eye) | |
// (default RLL - red comes from right image, blue and green come from left) | |
package main | |
import ( | |
"image" | |
"image/jpeg" | |
"image/png" | |
"image/gif" | |
"os" | |
"fmt" | |
"image/color" | |
"strings" | |
"time" | |
"runtime" | |
) | |
func stereo(dst *image.RGBA, lft, rgt image.Image, x0, y0, w, h, offset int, rLft, gLft, bLft bool, done chan bool) { | |
for x := x0; x < x0+w; x++ { | |
for y := y0; y < y0+h; y++ { | |
lr,lg,lb,_ := lft.At(x,y).RGBA() | |
rr,rg,rb,_ := rgt.At(x+offset,y).RGBA() | |
cr := rr | |
cg := rg | |
cb := rb | |
if rLft { | |
cr = lr | |
} | |
if gLft { | |
cg = lg | |
} | |
if bLft { | |
cb = lb | |
} | |
dst.SetRGBA(x,y,color.RGBA{uint8(cr >> 8),uint8(cg >> 8),uint8(cb >> 8),uint8(255)}) | |
} | |
} | |
done <- true | |
} | |
func readImage( filename string ) (image.Image) { | |
infile, inerr := os.Open(filename) | |
var img image.Image | |
if inerr == nil { | |
var imgerr error | |
fileNameLen := len(filename) | |
fileExtension := filename[fileNameLen-4 : fileNameLen] | |
fileExtension = strings.ToLower(fileExtension) | |
if fileExtension == ".png" { | |
img, imgerr = png.Decode(infile) | |
} else if fileExtension == ".jpg" || filename[fileNameLen-5:fileNameLen] == ".jpeg" { | |
img, imgerr = jpeg.Decode(infile) | |
} else if fileExtension == ".gif" { | |
img, imgerr = gif.Decode(infile) | |
} else { | |
fmt.Printf("Error: Image Format Not Supported\n") | |
infile.Close() | |
os.Exit(-1) | |
} | |
if imgerr != nil { | |
fmt.Printf("Error: Could not decode image\n") | |
} | |
infile.Close() | |
} else { | |
fmt.Printf("Error: Could not open file\n") | |
os.Exit(-1) | |
} | |
return img | |
} | |
const BSIZE int = 64 | |
const interations float64 = 1 | |
func main() { | |
argc := len(os.Args) | |
if argc < 4 { | |
fmt.Println(" usage anaglyph.go [-s,--single <side-by-side>|<left> <right>] <anaglyph.png> [RGB]") | |
os.Exit(1) | |
} | |
var outfile *os.File | |
var outerr error | |
isSideBySide := false | |
if os.Args[1] == "-s" || os.Args[1] == "--single" { | |
isSideBySide = true | |
} | |
outfile, outerr = os.Create(os.Args[3]) | |
channels := "RLL" | |
rLft := false | |
gLft := true | |
bLft := true | |
if argc == 5 { | |
channels = os.Args[4] | |
} | |
if len(channels) == 3 { | |
rLft = false | |
gLft = false | |
bLft = false | |
if channels[0] == 'L' { | |
rLft = true | |
} | |
if channels[1] == 'L' { | |
gLft = true | |
} | |
if channels[2] == 'L' { | |
bLft = true | |
} | |
} else { | |
fmt.Println("Unexpected channel parameter, defaulting to RLL...") | |
} | |
runtime.GOMAXPROCS(8) | |
if outerr == nil { | |
var lftImage image.Image | |
var rgtImage image.Image | |
offset := 0 | |
if isSideBySide { | |
lftImage = readImage(os.Args[2]) | |
} else { | |
lftImage = readImage(os.Args[1]) | |
rgtImage = readImage(os.Args[2]) | |
} | |
bnds := lftImage.Bounds() | |
w := bnds.Max.X - bnds.Min.X | |
h := bnds.Max.Y - bnds.Min.Y | |
if w < 1 || w % 2 != 0 { | |
fmt.Println("Error: Invalid Width") | |
os.Exit(2) | |
} | |
if isSideBySide { | |
w /= 2 | |
offset = w | |
rgtImage = lftImage | |
} | |
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 stereo(rgba,lftImage,rgtImage,x*BSIZE,y*BSIZE,wBlockSize,hBlockSize,offset,rLft,gLft,bLft,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) | |
imgerr2 := png.Encode(outfile, rgba) | |
if imgerr2 != nil { | |
fmt.Printf("Error: could not encode PNG") | |
} | |
} else { | |
fmt.Println("Error: Could not open file for writing") | |
} | |
outfile.Close() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment