eslave
19th February 2009, 11:05
AnnaFan777's question (http://forum.doom9.org/showthread.php?p=1243776#post1243776) from the fauxD thread (http://forum.doom9.org/showthread.php?t=143855) has led me down this path.
As previously mentioned (http://forum.doom9.org/showthread.php?p=1244315#post1244315) recovering left/right stereo-pairs from an anaglyph image isn't entirely trivial. Most of the links in that post (http://forum.doom9.org/showthread.php?p=1244315#post1244315) referred to the recovery of a 'new' right frame, given the original left frame and its stereo anaglyph. Only one software package claimed to recover stereo-pairs from only the anaglyph image, but its not free and the demo expired before I ever cared to test it.
I tinkered with edge detectors as I thought about what would be required to synthesize new color channels (views) for each stereo anaglyph image. (Uncomment the return line of your choice.)
# Sobel.AVS : Sobel edge/feature detector (straight from the AVISynth docs)
# Others detectors can be found at: homepages.inf.ed.ac.uk/rbf/HIPR2/featops.htm
# ----------------------------------------------------------------------------------------------------------------
function SobelH(clip s) { # Horizontal (Sobel) edge detection:
return s.GeneralConvolution(128, "
1 2 1
0 0 0
-1 -2 -1 ", 8) }
function SobelV(clip s) { # Vertical (Sobel) Edge Detection:
return s.GeneralConvolution(128, "
1 0 -1
2 0 -2
1 0 -1 ", 8) }
function Sobel(clip s) { return Overlay(s.SobelH, s.SobelV, mode="Blend") }
# ----------------------------------------------------------------------------------------------------------------
f = "D:\somepath\stereodepth_A_S.mpg" #~6.5MB, found at www.archive.org/details/stereodepth
c = DirectshowSource(f).ConvertToRgb32(matrix="PC.601")
#return Sobel(c)
red = MergeRGB(c.ShowRed, c.blankclip, c.blankclip ).ConvertToYUY2(matrix="PC.601").Tweak(bright=25, cont=2.0, coring=false).ConvertToRgb32(matrix="PC.601").subtitle("Red(rightview)",300) #increase brightness/contrast to better match the other eye's greyscale
cyan = MergeRGB(c.blankclip, c.ShowGreen, c.ShowBlue).subtitle("Cyan(leftview)")
#return StackVertical( red.Sobel.GreyScale , cyan.Sobel.GreyScale )
#return Interleave(red.greyscale, cyan.greyscale).AssumeFrameBased
return Interleave(red.Sobel.greyscale, cyan.Sobel.greyscale).AssumeFrameBased
Eventually I realized it would take a lot of block-matching and interpolating to transform the color channels correctly, and I seemed to remember there was already plugin that could do all those things. Slowly it dawned on me that MVTools has the required facilities built-in (just like AnnaFan777's original suggestion).
So here's my first attempt at such a script:# DeAnaglyph.AVS: De-Anaglyph proof-of-concept test script (for RED-CYAN videos)
# eslave(2009) - based on an idea from AnnaFan777
SetMemoryMax(128)
LoadPlugin("D:\somepath\MVTools.dll") #MVTools v2.31
f = "D:\somepath\stereodepth_A_S.mpg" #~6.5MB, found at www.archive.org/details/stereodepth
#f = "D:\somepath\samplehoshinobg4.jpg" #uncomment the appropriate red/cyan 'fix' lines for this image ( forum.doom9.org/showthread.php?p=1243776#post1243776 )
# ----------------------------------------------------------------------------------------------------------------
global mat = "PC.601"
#global mat = "Rec601" #causes MAnalyse to fail on some test clips
function myRGB(clip c) { return c.ConvertToRgb32(matrix=mat) }
function myYUV(clip c) { return c.ConvertToYUY2(matrix=mat) }
function DeAnaglyph(clip c)
{
c = c.IsRGB32 ? c : c.myRGB
c = (c.height%2==0) ? c : c.CropBottom(1) #make height even for YUV operations
c = (c.width%2==0) ? c : c.Crop(0,0,-1,-0) #make width even for YUV operations
#Separate views & create manual or auto-leveled clips (hopefully with similar relative intensity for MAnalyse)
red = MergeRGB(c.ShowRed, c.blankclip, c.blankclip ) #seen by RIGHT eye through CYAN filter (single channel)
red2 = MergeRGB(c.ShowRed, c.ShowRed, c.blankclip ) #try increasing reds 'energy' by duplicating the channel (and make it similar to cyan's intensity)
redfix = red2.myYUV.ColorYUV(autogain=true, autowhite=true).myRGB #.Subtitle("RED(rightview)", x=300) #most videos
#redfix = red2.Sharpen(0.5).myYUV.Tweak(bright=-80, cont=1.0, coring=false).myRGB #.Subtitle("RED(rightview)", x=300) #(JPG sample)
cyan = MergeRGB(c.blankclip, c.ShowGreen, c.ShowBlue) #seen by LEFT eye through RED filter
cyanfix = cyan.myYUV.ColorYUV(autogain=true, autowhite=true).myRGB #.Subtitle("CYAN(leftview)") #most videos
#cyanfix = cyan.Sharpen(0.5) #.Subtitle("CYAN(leftview)") #(JPG sample)
#return StackVertical(redfix.greyscale, cyanfix.greyscale) #uncomment to see stacked views (adjust red/cyan FIX parameters until they appear very similar)
#Make double frame-length clips of alternating L/R views for MAnalyse
mixRC = Interleave(redfix.greyscale, cyanfix.greyscale).AssumeFrameBased
mixCR = Interleave(cyanfix.greyscale, redfix.greyscale).AssumeFrameBased #reversed frame-order for vector testing
#return mixRC #uncomment to see alternating views (keep adjusting red/cyan FIX parameters until they appear very similar)
#Get per-block vectors for compensation
vecR = MAnalyse(mixRC.myYUV.MSuper, isb = false, blksize = 4, overlap=2).SelectOdd #vectors for matching RED to CYAN
vecC = MAnalyse(mixCR.myYUV.MSuper, isb = false, blksize = 4, overlap=2).SelectOdd #vectors for matching CYAN to RED
#vecC = MAnalyse(mixRC.myYUV.MSuper, isb = true, blksize = 4, overlap=2).SelectOdd #for MFlowInter
#return MShow(mixRC.myYUV.MSuper, vecR).SelectOdd #uncomment to see motion field (test red/cyan FIX parameters)
#Synthesize new channels (views)
newred = MFlow(red.myYUV, red.myYUV.MSuper, vecR) #looks almost OK
newcyan = MFlow(cyan.myYUV, cyan.myYUV.MSuper, vecC) #looks less than OK
#newcyan = MFlowInter(cyan.myYUV, cyan.myYUV.MSuper, vecb, vecf, 100.0) #looks even stranger
#Fuse synthesized and original channels into 'full-color' left/right views
mat = "Rec601" #seems to reduce some color shimmering
left = MergeRGB(newred.myRGB.ShowRed, cyan.ShowGreen, cyan.ShowBlue) #new LEFT frames
right = MergeRGB(red.ShowRed, newcyan.myRGB.ShowGreen, newcyan.myRGB.ShowBlue) #new RIGHT frames
#uncomment your preferred output method (or Import fauxD.avsi for other stereo frame types. i.e.: AnaglyphRedCyan, AnaglyphYellowBlue)
return StackVertical( left , right ) #over-under
#return StackHorizontal( left , right ) #side-by-side
#return Interleave(left, right).AssumeFieldBased.Weave.BilinearResize(left.width*2, left.height*2) #for some shutterglasses (do not vertically resize output)
}
# ----------------------------------------------------------------------------------------------------------------
src = (Findstr(UCase(f), ".JPG") > 0) ? ImageSource(f, end=24*30, fps=24) : DirectshowSource(f) #.Trim(Int(5*30000/1001), 0) #cut straight to the stereo part
DeAnaglyph(src)
Sample results: (the URLs for these sources are in the script)
http://www.freeimagehosting.net/uploads/th.19bb8f1bf9.jpg (http://www.freeimagehosting.net/uploads/19bb8f1bf9.jpg) http://www.freeimagehosting.net/uploads/th.093bc3bb6c.jpg (http://www.freeimagehosting.net/uploads/093bc3bb6c.jpg) http://www.freeimagehosting.net/uploads/th.18a04b7ff6.jpg (http://www.freeimagehosting.net/uploads/18a04b7ff6.jpg) http://www.freeimagehosting.net/uploads/th.3a530f9e0e.jpg (http://www.freeimagehosting.net/uploads/3a530f9e0e.jpg)
Although the motion vectors failed to appear in image #2 you can still see the final result is correct, which would not have occurred without motion vectors.
I certainly know it could use improvement, I'm just uncertain exactly how. It works well enough with my shutter-glasses, although visible errors such as bad vectors, occlusions, and post-anaglyph ghosts still remain. Also, some images have little or no information in large areas of one color channel (source view). The most troublesome part is fixing the RED and CYAN images to look similar enough that MAnalyse can do its thing, and such fixes would probably require adjustment every time the scene or lighting changes.
Any ideas?
-eslave
As previously mentioned (http://forum.doom9.org/showthread.php?p=1244315#post1244315) recovering left/right stereo-pairs from an anaglyph image isn't entirely trivial. Most of the links in that post (http://forum.doom9.org/showthread.php?p=1244315#post1244315) referred to the recovery of a 'new' right frame, given the original left frame and its stereo anaglyph. Only one software package claimed to recover stereo-pairs from only the anaglyph image, but its not free and the demo expired before I ever cared to test it.
I tinkered with edge detectors as I thought about what would be required to synthesize new color channels (views) for each stereo anaglyph image. (Uncomment the return line of your choice.)
# Sobel.AVS : Sobel edge/feature detector (straight from the AVISynth docs)
# Others detectors can be found at: homepages.inf.ed.ac.uk/rbf/HIPR2/featops.htm
# ----------------------------------------------------------------------------------------------------------------
function SobelH(clip s) { # Horizontal (Sobel) edge detection:
return s.GeneralConvolution(128, "
1 2 1
0 0 0
-1 -2 -1 ", 8) }
function SobelV(clip s) { # Vertical (Sobel) Edge Detection:
return s.GeneralConvolution(128, "
1 0 -1
2 0 -2
1 0 -1 ", 8) }
function Sobel(clip s) { return Overlay(s.SobelH, s.SobelV, mode="Blend") }
# ----------------------------------------------------------------------------------------------------------------
f = "D:\somepath\stereodepth_A_S.mpg" #~6.5MB, found at www.archive.org/details/stereodepth
c = DirectshowSource(f).ConvertToRgb32(matrix="PC.601")
#return Sobel(c)
red = MergeRGB(c.ShowRed, c.blankclip, c.blankclip ).ConvertToYUY2(matrix="PC.601").Tweak(bright=25, cont=2.0, coring=false).ConvertToRgb32(matrix="PC.601").subtitle("Red(rightview)",300) #increase brightness/contrast to better match the other eye's greyscale
cyan = MergeRGB(c.blankclip, c.ShowGreen, c.ShowBlue).subtitle("Cyan(leftview)")
#return StackVertical( red.Sobel.GreyScale , cyan.Sobel.GreyScale )
#return Interleave(red.greyscale, cyan.greyscale).AssumeFrameBased
return Interleave(red.Sobel.greyscale, cyan.Sobel.greyscale).AssumeFrameBased
Eventually I realized it would take a lot of block-matching and interpolating to transform the color channels correctly, and I seemed to remember there was already plugin that could do all those things. Slowly it dawned on me that MVTools has the required facilities built-in (just like AnnaFan777's original suggestion).
So here's my first attempt at such a script:# DeAnaglyph.AVS: De-Anaglyph proof-of-concept test script (for RED-CYAN videos)
# eslave(2009) - based on an idea from AnnaFan777
SetMemoryMax(128)
LoadPlugin("D:\somepath\MVTools.dll") #MVTools v2.31
f = "D:\somepath\stereodepth_A_S.mpg" #~6.5MB, found at www.archive.org/details/stereodepth
#f = "D:\somepath\samplehoshinobg4.jpg" #uncomment the appropriate red/cyan 'fix' lines for this image ( forum.doom9.org/showthread.php?p=1243776#post1243776 )
# ----------------------------------------------------------------------------------------------------------------
global mat = "PC.601"
#global mat = "Rec601" #causes MAnalyse to fail on some test clips
function myRGB(clip c) { return c.ConvertToRgb32(matrix=mat) }
function myYUV(clip c) { return c.ConvertToYUY2(matrix=mat) }
function DeAnaglyph(clip c)
{
c = c.IsRGB32 ? c : c.myRGB
c = (c.height%2==0) ? c : c.CropBottom(1) #make height even for YUV operations
c = (c.width%2==0) ? c : c.Crop(0,0,-1,-0) #make width even for YUV operations
#Separate views & create manual or auto-leveled clips (hopefully with similar relative intensity for MAnalyse)
red = MergeRGB(c.ShowRed, c.blankclip, c.blankclip ) #seen by RIGHT eye through CYAN filter (single channel)
red2 = MergeRGB(c.ShowRed, c.ShowRed, c.blankclip ) #try increasing reds 'energy' by duplicating the channel (and make it similar to cyan's intensity)
redfix = red2.myYUV.ColorYUV(autogain=true, autowhite=true).myRGB #.Subtitle("RED(rightview)", x=300) #most videos
#redfix = red2.Sharpen(0.5).myYUV.Tweak(bright=-80, cont=1.0, coring=false).myRGB #.Subtitle("RED(rightview)", x=300) #(JPG sample)
cyan = MergeRGB(c.blankclip, c.ShowGreen, c.ShowBlue) #seen by LEFT eye through RED filter
cyanfix = cyan.myYUV.ColorYUV(autogain=true, autowhite=true).myRGB #.Subtitle("CYAN(leftview)") #most videos
#cyanfix = cyan.Sharpen(0.5) #.Subtitle("CYAN(leftview)") #(JPG sample)
#return StackVertical(redfix.greyscale, cyanfix.greyscale) #uncomment to see stacked views (adjust red/cyan FIX parameters until they appear very similar)
#Make double frame-length clips of alternating L/R views for MAnalyse
mixRC = Interleave(redfix.greyscale, cyanfix.greyscale).AssumeFrameBased
mixCR = Interleave(cyanfix.greyscale, redfix.greyscale).AssumeFrameBased #reversed frame-order for vector testing
#return mixRC #uncomment to see alternating views (keep adjusting red/cyan FIX parameters until they appear very similar)
#Get per-block vectors for compensation
vecR = MAnalyse(mixRC.myYUV.MSuper, isb = false, blksize = 4, overlap=2).SelectOdd #vectors for matching RED to CYAN
vecC = MAnalyse(mixCR.myYUV.MSuper, isb = false, blksize = 4, overlap=2).SelectOdd #vectors for matching CYAN to RED
#vecC = MAnalyse(mixRC.myYUV.MSuper, isb = true, blksize = 4, overlap=2).SelectOdd #for MFlowInter
#return MShow(mixRC.myYUV.MSuper, vecR).SelectOdd #uncomment to see motion field (test red/cyan FIX parameters)
#Synthesize new channels (views)
newred = MFlow(red.myYUV, red.myYUV.MSuper, vecR) #looks almost OK
newcyan = MFlow(cyan.myYUV, cyan.myYUV.MSuper, vecC) #looks less than OK
#newcyan = MFlowInter(cyan.myYUV, cyan.myYUV.MSuper, vecb, vecf, 100.0) #looks even stranger
#Fuse synthesized and original channels into 'full-color' left/right views
mat = "Rec601" #seems to reduce some color shimmering
left = MergeRGB(newred.myRGB.ShowRed, cyan.ShowGreen, cyan.ShowBlue) #new LEFT frames
right = MergeRGB(red.ShowRed, newcyan.myRGB.ShowGreen, newcyan.myRGB.ShowBlue) #new RIGHT frames
#uncomment your preferred output method (or Import fauxD.avsi for other stereo frame types. i.e.: AnaglyphRedCyan, AnaglyphYellowBlue)
return StackVertical( left , right ) #over-under
#return StackHorizontal( left , right ) #side-by-side
#return Interleave(left, right).AssumeFieldBased.Weave.BilinearResize(left.width*2, left.height*2) #for some shutterglasses (do not vertically resize output)
}
# ----------------------------------------------------------------------------------------------------------------
src = (Findstr(UCase(f), ".JPG") > 0) ? ImageSource(f, end=24*30, fps=24) : DirectshowSource(f) #.Trim(Int(5*30000/1001), 0) #cut straight to the stereo part
DeAnaglyph(src)
Sample results: (the URLs for these sources are in the script)
http://www.freeimagehosting.net/uploads/th.19bb8f1bf9.jpg (http://www.freeimagehosting.net/uploads/19bb8f1bf9.jpg) http://www.freeimagehosting.net/uploads/th.093bc3bb6c.jpg (http://www.freeimagehosting.net/uploads/093bc3bb6c.jpg) http://www.freeimagehosting.net/uploads/th.18a04b7ff6.jpg (http://www.freeimagehosting.net/uploads/18a04b7ff6.jpg) http://www.freeimagehosting.net/uploads/th.3a530f9e0e.jpg (http://www.freeimagehosting.net/uploads/3a530f9e0e.jpg)
Although the motion vectors failed to appear in image #2 you can still see the final result is correct, which would not have occurred without motion vectors.
I certainly know it could use improvement, I'm just uncertain exactly how. It works well enough with my shutter-glasses, although visible errors such as bad vectors, occlusions, and post-anaglyph ghosts still remain. Also, some images have little or no information in large areas of one color channel (source view). The most troublesome part is fixing the RED and CYAN images to look similar enough that MAnalyse can do its thing, and such fixes would probably require adjustment every time the scene or lighting changes.
Any ideas?
-eslave