def testBlurRange( self ): constant = GafferImage.Constant() constant["format"].setValue( GafferImage.Format( 5, 5, 1.000 ) ) constant["color"].setValue( IECore.Color4f( 1, 1, 1, 1 ) ) cropDot = GafferImage.Crop() cropDot["area"].setValue( IECore.Box2i( IECore.V2i( 2, 2 ), IECore.V2i( 3, 3 ) ) ) cropDot["affectDisplayWindow"].setValue( False ) cropDot["in"].setInput( constant["out"] ) blur = GafferImage.Blur() blur["expandDataWindow"].setValue( True ) blur["in"].setInput( cropDot["out"] ) blur["radius"]["y"].setInput( blur["radius"]["x"] ) expression = Gaffer.Expression() blur.addChild( expression ) expression.setExpression( 'parent["radius"]["x"] = context[ "loop:index" ] * 0.2', "python" ) loopInit = GafferImage.Constant() loopInit["format"].setValue( GafferImage.Format( 5, 5, 1.000 ) ) imageLoop = GafferImage.ImageLoop() imageLoop["in"].setInput( loopInit["out"] ) merge = GafferImage.Merge() merge["in"].addChild( GafferImage.ImagePlug( "in2", flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) ) merge["in"]["in0"].setInput( blur["out"] ) merge["in"]["in1"].setInput( imageLoop["previous"] ) offset = GafferImage.Offset() offset["offset"].setValue( IECore.V2i( -5, 0 ) ) offset["in"].setInput( merge["out"] ) imageLoop["next"].setInput( offset["out"] ) deleteChannels = GafferImage.DeleteChannels() deleteChannels["mode"].setValue( GafferImage.DeleteChannels.Mode.Keep ) deleteChannels["channels"].setValue( IECore.StringVectorData( [ 'R' ] ) ) deleteChannels["in"].setInput( imageLoop["out"] ) finalCrop = GafferImage.Crop() finalCrop["areaSource"].setValue( 1 ) finalCrop["in"].setInput( deleteChannels["out"] ) # Enable to write out images for visual comparison if False: testWriter = GafferImage.ImageWriter() testWriter["in"].setInput( finalCrop["out"] ) testWriter["fileName"].setValue( "/tmp/blurRange.exr" ) testWriter["openexr"]["dataType"].setValue( 'float' ) testWriter["task"].execute() expectedReader = GafferImage.ImageReader() expectedReader["fileName"].setValue( os.path.dirname( __file__ ) + "/images/blurRange.exr" ) self.assertImagesEqual( finalCrop["out"], expectedReader["out"], maxDifference = 0.00001, ignoreMetadata = True )
def testIterationsContext(self): script = Gaffer.ScriptNode() script["c"] = GafferImage.Constant() script["loop"] = GafferImage.ImageLoop() script["loop"]["in"].setInput(script["c"]["out"]) script["grade"] = GafferImage.Grade() script["grade"]["offset"].setValue(imath.Color3f(.1)) script["grade"]["in"].setInput(script["loop"]["previous"]) script["loop"]["next"].setInput(script["grade"]["out"]) script["sampler"] = GafferImage.ImageSampler() script["sampler"]["pixel"].setValue(imath.V2f(10)) script["sampler"]["image"].setInput(script["loop"]["out"]) script["expression"] = Gaffer.Expression() script["expression"].setExpression( inspect.cleandoc(""" assert( context.get( "image:channelName", None ) is None ) assert( context.get( "image:tileOrigin", None ) is None ) parent["loop"]["iterations"] = 4 """)) with script.context(): self.assertAlmostEqual(script["sampler"]["color"]["r"].getValue(), .4)
def testLoop(self): script = Gaffer.ScriptNode() script["c"] = GafferImage.Constant() script["loop"] = GafferImage.ImageLoop() script["loop"]["in"].setInput(script["c"]["out"]) script["grade"] = GafferImage.Grade() script["grade"]["offset"].setValue(IECore.Color3f(.1)) script["grade"]["in"].setInput(script["loop"]["previous"]) script["loop"]["next"].setInput(script["grade"]["out"]) script["sampler"] = GafferImage.ImageSampler() script["sampler"]["pixel"].setValue(IECore.V2f(10)) script["sampler"]["image"].setInput(script["loop"]["out"]) with script.context(): script["loop"]["iterations"].setValue(2) self.assertAlmostEqual(script["sampler"]["color"]["r"].getValue(), .2) script["loop"]["iterations"].setValue(4) self.assertAlmostEqual(script["sampler"]["color"]["r"].getValue(), .4) script2 = Gaffer.ScriptNode() script2.execute(script.serialise()) with script2.context(): script2["loop"]["iterations"].setValue(3) self.assertAlmostEqual(script2["sampler"]["color"]["r"].getValue(), .3) script2["loop"]["iterations"].setValue(5) self.assertAlmostEqual(script2["sampler"]["color"]["r"].getValue(), .5)
def __init__(self, name='BleedFill'): GafferImage.ImageProcessor.__init__(self, name) self.addChild(Gaffer.BoolPlug("expandDataWindow")) self.addChild(Gaffer.IntPlug("__blurIterations")) self['__blurIterations'].setFlags(Gaffer.Plug.Flags.Serialisable, False) self["__blurIterationsExpression"] = Gaffer.Expression() self["__blurIterationsExpression"].setExpression( inspect.cleandoc(""" import math f = parent["in"]["format"] parent["__blurIterations"] = int( math.log( min( f.width(), f.height() ), 2 ) ) """), "python") self["__displayWindowConstant"] = GafferImage.Constant() self["__displayWindowConstant"]["color"].setValue( imath.Color4f(0, 0, 0, 0)) self["__displayWindowExpression"] = Gaffer.Expression() self["__displayWindowExpression"].setExpression( 'parent["__displayWindowConstant"]["format"] = parent["in"]["format"]', "python") self["__expandMerge"] = GafferImage.Merge() self["__expandMerge"]["in"][0].setInput(self["in"]) self["__expandMerge"]["in"][1].setInput( self["__displayWindowConstant"]["out"]) self["__expandMerge"]["operation"].setValue( GafferImage.Merge.Operation.Over) self["__expandSwitch"] = Gaffer.Switch() self["__expandSwitch"].setup(self["in"]) self["__expandSwitch"]["in"][0].setInput(self["in"]) self["__expandSwitch"]["in"][1].setInput(self["__expandMerge"]["out"]) self["__expandSwitch"]["index"].setInput(self["expandDataWindow"]) # First blur via repeated downsampling self["__blurLoop"] = GafferImage.ImageLoop() self["__blurLoop"]["iterations"].setInput(self["__blurIterations"]) self["__blurLoop"]["in"].setInput(self["__expandSwitch"]["out"]) self["__downsample"] = GafferImage.Resize() self["__downsample"]["in"].setInput(self["__blurLoop"]["previous"]) self["__downsample"]["filter"].setValue("sharp-gaussian") self["__downsampleExpression"] = Gaffer.Expression() self["__downsampleExpression"].setExpression( inspect.cleandoc(""" import GafferImage import IECore f = parent["in"]["format"] divisor = 2 << context.get("loop:index", 0) parent["__downsample"]["format"] = GafferImage.Format( imath.Box2i( f.getDisplayWindow().min() / divisor, f.getDisplayWindow().max() / divisor ), 1.0 ) """), "python") # Multiply each successive octave by a falloff factor so that we prioritize higher frequencies when they exist self["__grade"] = GafferImage.Grade("Grade") self["__grade"]['channels'].setValue("*") self["__grade"]['multiply'].setValue(imath.Color4f(0.1)) self["__grade"]["in"].setInput(self["__downsample"]["out"]) self["__blurLoop"]["next"].setInput(self["__grade"]["out"]) self["__reverseLoopContext"] = GafferImage.ImageContextVariables() self["__reverseLoopContext"]["in"].setInput( self["__blurLoop"]["previous"]) self["__reverseLoopContext"]["variables"].addMember( "loop:index", IECore.IntData(0), "loopIndex") self["__reverseLoopExpression"] = Gaffer.Expression() self["__reverseLoopExpression"].setExpression( inspect.cleandoc(""" parent["__reverseLoopContext"]["variables"]["loopIndex"]["value"] = parent["__blurIterations"] - context.get( "loop:index", 0 ) """), "python") # Loop through image resolution levels combining the most downsampled image with less downsampled versions, # one level at a time self["__combineLoop"] = GafferImage.ImageLoop() self["__combineLoopExpression"] = Gaffer.Expression() self["__combineLoopExpression"].setExpression( 'parent["__combineLoop"]["iterations"] = parent["__blurIterations"] + 1', "python") self["__upsample"] = GafferImage.Resize() self["__upsample"]["in"].setInput(self["__combineLoop"]["previous"]) self["__upsample"]["filter"].setValue("smoothGaussian") self["__upsampleExpression"] = Gaffer.Expression() self["__upsampleExpression"].setExpression( inspect.cleandoc(""" import GafferImage import IECore f = parent["in"]["format"] divisor = 1 << ( parent["__blurIterations"] - context.get("loop:index", 0) ) parent["__upsample"]["format"] = GafferImage.Format( imath.Box2i( f.getDisplayWindow().min() / divisor, f.getDisplayWindow().max() / divisor ), 1.0 ) """), "python") self["__merge"] = GafferImage.Merge() self["__merge"]["operation"].setValue(GafferImage.Merge.Operation.Over) self["__merge"]["in"][0].setInput(self["__upsample"]["out"]) self["__merge"]["in"][1].setInput(self["__reverseLoopContext"]["out"]) self["__combineLoop"]["next"].setInput(self["__merge"]["out"]) # When downsampling to target display window sizes with a non-square image, # the data window size gets rounded up to the nearest integer, potentially introducing # a small error in data window size that gets amplified during repeated upsampling. # To fix this, crop to the data window after scaling. self["__restoreDataSize"] = GafferImage.Crop() self["__restoreDataSize"]["in"].setInput(self["__combineLoop"]["out"]) self["__restoreDataSize"]["affectDisplayWindow"].setValue(False) self["__restoreDataExpression"] = Gaffer.Expression() self["__restoreDataExpression"].setExpression( 'parent["__restoreDataSize"]["area"] = parent["__expandSwitch"]["out"]["dataWindow"]', "python") self["__unpremult"] = GafferImage.Unpremultiply() self["__unpremult"]['channels'].setValue("*") self["__unpremult"]["in"].setInput(self["__restoreDataSize"]["out"]) self["__resetAlpha"] = GafferImage.Shuffle() self["__resetAlpha"]["channels"].addChild( GafferImage.Shuffle.ChannelPlug("A", "__white")) self["__resetAlpha"]["in"].setInput(self["__unpremult"]["out"]) self["__disableSwitch"] = Gaffer.Switch() self["__disableSwitch"].setup(self["in"]) self["__disableSwitch"]["in"][0].setInput(self["in"]) self["__disableSwitch"]["in"][1].setInput(self["__resetAlpha"]["out"]) self["__disableSwitch"]["index"].setInput(self["enabled"]) self['out'].setFlags(Gaffer.Plug.Flags.Serialisable, False) self["out"].setInput(self["__disableSwitch"]["out"])
def testDefaultName(self): l = GafferImage.ImageLoop() self.assertEqual(l.getName(), "ImageLoop")