def testPassThrough(self): c = GafferImage.Constant() f = GafferImage.Reformat() f["in"].setInput(c["out"]) f["format"].setValue( GafferImage.Format(IECore.Box2i(IECore.V2i(0), IECore.V2i(10)), 1)) d = GafferImage.ImageMetadata() d["metadata"].addMember( "comment", IECore.StringData("reformated and metadata updated")) d["in"].setInput(f["out"]) m = GafferImage.Merge() m["in"][0].setInput(c["out"]) m["in"][1].setInput(d["out"]) self.assertEqual(m["out"]["format"].hash(), c["out"]["format"].hash()) self.assertEqual(m["out"]["metadata"].hash(), c["out"]["metadata"].hash()) self.assertEqual(m["out"]["format"].getValue(), c["out"]["format"].getValue()) self.assertEqual(m["out"]["metadata"].getValue(), c["out"]["metadata"].getValue()) m["in"][0].setInput(d["out"]) m["in"][1].setInput(c["out"]) self.assertEqual(m["out"]["format"].hash(), d["out"]["format"].hash()) self.assertEqual(m["out"]["metadata"].hash(), d["out"]["metadata"].hash()) self.assertEqual(m["out"]["format"].getValue(), d["out"]["format"].getValue()) self.assertEqual(m["out"]["metadata"].getValue(), d["out"]["metadata"].getValue())
def testHash( self ) : c = Gaffer.Context() c.setFrame( 1 ) c2 = Gaffer.Context() c2.setFrame( 2 ) writer = GafferImage.ImageWriter() # empty file produces no effect self.assertEqual( writer["fileName"].getValue(), "" ) self.assertEqual( writer.hash( c ), IECore.MurmurHash() ) # no input image produces no effect writer["fileName"].setValue( self.temporaryDirectory() + "/test.exr" ) self.assertEqual( writer.hash( c ), IECore.MurmurHash() ) # now theres a file and an image, we get some output constant = GafferImage.Constant() writer["in"].setInput( constant["out"] ) self.assertNotEqual( writer.hash( c ), IECore.MurmurHash() ) # output doesn't vary by time yet self.assertEqual( writer.hash( c ), writer.hash( c2 ) ) # now it does vary writer["fileName"].setValue( self.temporaryDirectory() + "/test.#.exr" ) self.assertNotEqual( writer.hash( c ), writer.hash( c2 ) ) # other plugs matter too current = writer.hash( c ) writer["openexr"]["mode"].setValue( GafferImage.ImageWriter.Mode.Tile ) self.assertNotEqual( writer.hash( c ), current ) current = writer.hash( c ) writer["channels"].setValue( IECore.StringVectorData( [ "R" ] ) ) self.assertNotEqual( writer.hash( c ), current )
def testChannelDataPassThrough(self): # Resize to the same size as the input image. c = GafferImage.Constant() c["format"].setValue( GafferImage.Format(imath.Box2i(imath.V2i(0), imath.V2i(512)), 1)) c["color"].setValue(imath.Color4f(0.25, 0.5, 0.75, 1)) r = GafferImage.Resize() r["in"].setInput(c["out"]) r["format"].setValue( GafferImage.Format(imath.Box2i(imath.V2i(0), imath.V2i(512)), 1)) # Assert that the pixel data is passed clean through. for channel in ["R", "G", "B", "A"]: self.assertEqual( c["out"].channelDataHash(channel, imath.V2i(0)), r["out"].channelDataHash(channel, imath.V2i(0)), ) self.assertTrue(c["out"].channelData( channel, imath.V2i(0), _copy=False).isSame(r["out"].channelData(channel, imath.V2i(0), _copy=False)))
def testDisabling( self ) : script = Gaffer.ScriptNode() script["constant"] = GafferImage.Constant() script["expression"] = Gaffer.Expression() script["expression"].setExpression( 'parent["constant"]["color"]["r"] = context["frame"]' ) script["timeWarp"] = Gaffer.TimeWarp() script["timeWarp"].setup( GafferImage.ImagePlug() ) script["timeWarp"]["offset"].setValue( 1 ) script["timeWarp"]["in"].setInput( script["constant"]["out"] ) with script.context() : c = GafferImage.ImageAlgo.image( script["constant"]["out"] ) cHash = GafferImage.ImageAlgo.imageHash( script["constant"]["out"] ) t = GafferImage.ImageAlgo.image( script["timeWarp"]["out"] ) tHash = GafferImage.ImageAlgo.imageHash( script["timeWarp"]["out"] ) self.assertNotEqual( c, t ) self.assertNotEqual( cHash, tHash ) script["timeWarp"]["enabled"].setValue( False ) with script.context() : c = GafferImage.ImageAlgo.image( script["constant"]["out"] ) cHash = GafferImage.ImageAlgo.imageHash( script["constant"]["out"] ) t = GafferImage.ImageAlgo.image( script["timeWarp"]["out"] ) tHash = GafferImage.ImageAlgo.imageHash( script["timeWarp"]["out"] ) self.assertEqual( c, t ) self.assertEqual( cHash, tHash )
def testWriteIntermediateFile( self ) : # This tests a fairly common usage pattern whereby # an ImageReader loads an image generated higher # up the task tree, some processing is done, and # then an ImageWriter is used to write out the modified # image. A good example of this is a render node being # used to generate an image which is then pushed through # a slapcomp process. Because the rendered image to be # read/written doesn't exist at the point of dispatch # we have to make sure this doesn't cause problems. s = Gaffer.ScriptNode() s["c"] = GafferImage.Constant() s["w1"] = GafferImage.ImageWriter() s["w1"]["in"].setInput( s["c"]["out"] ) s["w1"]["fileName"].setValue( self.temporaryDirectory() + "/test1.exr" ) s["r"] = GafferImage.ImageReader() s["r"]["fileName"].setValue( self.temporaryDirectory() + "/test1.exr" ) s["w2"] = GafferImage.ImageWriter() s["w2"]["in"].setInput( s["r"]["out"] ) s["w2"]["fileName"].setValue( self.temporaryDirectory() + "/test2.exr" ) s["w2"]["preTasks"][0].setInput( s["w1"]["task"] ) d = GafferDispatch.LocalDispatcher() d["jobsDirectory"].setValue( self.temporaryDirectory() + "/jobs" ) with s.context() : d.dispatch( [ s["w2"] ] ) self.assertTrue( os.path.isfile( s["w1"]["fileName"].getValue() ) ) self.assertTrue( os.path.isfile( s["w2"]["fileName"].getValue() ) )
def testDisabling(self): c1 = GafferImage.Catalogue() c1["images"].addChild( GafferImage.Catalogue.Image.load( "${GAFFER_ROOT}/python/GafferImageTest/images/checker.exr")) c2 = GafferImage.Catalogue() c2["images"].addChild( GafferImage.Catalogue.Image.load( "${GAFFER_ROOT}/python/GafferImageTest/images/checker.exr")) self.assertImagesEqual(c1["out"], c2["out"]) c2["enabled"].setValue(False) self.assertNotEqual(c2["out"]["format"].getValue(), c1["out"]["format"].getValue()) self.assertNotEqual(c2["out"]["dataWindow"].getValue(), c1["out"]["dataWindow"].getValue()) self.assertEqual(c2["out"]["dataWindow"].getValue(), IECore.Box2i()) disabledConstant = GafferImage.Constant() disabledConstant["enabled"].setValue(False) self.assertImagesEqual(c2["out"], disabledConstant["out"])
def testOffsetDisplayWindowWrite( self ) : c = GafferImage.Constant() format = GafferImage.Format( IECore.Box2i( IECore.V2i( -20, -15 ), IECore.V2i( 29, 14 ) ), 1. ) c["format"].setValue( format ) testFile = self.__testFile( "offsetDisplayWindow", "RGBA", "exr" ) w = GafferImage.ImageWriter() w["in"].setInput( c["out"] ) w["fileName"].setValue( testFile ) w["task"].execute() self.failUnless( os.path.exists( testFile ) ) i = IECore.Reader.create( testFile ).read() # Cortex uses the EXR convention, which differs # from Gaffer's, so we use the conversion methods to # check that the image windows are as expected. self.assertEqual( format.toEXRSpace( format.getDisplayWindow() ), i.displayWindow )
def testDefaultFormatFromScript(self): s = Gaffer.ScriptNode() self.assertFalse("defaultFormat" in s) s["c"] = GafferImage.Constant() self.assertFalse("defaultFormat" in s) defaultFormatPlug = GafferImage.FormatPlug.acquireDefaultFormatPlug(s) self.assertTrue(defaultFormatPlug.isSame(s["defaultFormat"])) with s.context(): self.assertFalse( GafferImage.BufferAlgo.empty( s["c"]["out"]["format"].getValue().getDisplayWindow())) f = GafferImage.Format(100, 200, 2) defaultFormatPlug.setValue(f) self.assertEqual(s["c"]["out"]["format"].getValue(), f) f = GafferImage.Format(200, 400, 1) defaultFormatPlug.setValue(f) self.assertEqual(s["c"]["out"]["format"].getValue(), f)
def testDefaultFormatFromContext(self): constant = GafferImage.Constant() with Gaffer.Context() as context: # Even if we haven't specified a default context, we should still # be given something. self.assertFalse( GafferImage.BufferAlgo.empty( constant["out"]["format"].getValue().getDisplayWindow())) # And if we specify something specific, we should get it. f = GafferImage.Format(100, 200, 2) GafferImage.FormatPlug.setDefaultFormat(context, f) self.assertEqual(GafferImage.FormatPlug.getDefaultFormat(context), f) self.assertEqual(constant["out"]["format"].getValue(), f) f = GafferImage.Format(200, 400, 1) GafferImage.FormatPlug.setDefaultFormat(context, f) self.assertEqual(GafferImage.FormatPlug.getDefaultFormat(context), f) self.assertEqual(constant["out"]["format"].getValue(), f)
def testHashIncludesBlackPixels(self): constant = GafferImage.Constant() constant["format"].setValue(GafferImage.Format(1000, 1000)) constant["color"].setValue(imath.Color4f(1)) crop = GafferImage.Crop() crop["in"].setInput(constant["out"]) crop["areaSource"].setValue(crop.AreaSource.Area) crop["area"].setValue(imath.Box2i(imath.V2i(0), imath.V2i(200))) crop["affectDisplayWindow"].setValue(False) crop["affectDataWindow"].setValue(False) # Samples the whole data window sampler1 = GafferImage.Sampler( crop["out"], "R", imath.Box2i(imath.V2i(0), imath.V2i(200)), boundingMode=GafferImage.Sampler.BoundingMode.Black) # Samples the whole data window and then some. sampler2 = GafferImage.Sampler( crop["out"], "R", imath.Box2i(imath.V2i(0), imath.V2i(210)), boundingMode=GafferImage.Sampler.BoundingMode.Black) # Samples the whole data window and then some and then some more. sampler3 = GafferImage.Sampler( crop["out"], "R", imath.Box2i(imath.V2i(0), imath.V2i(220)), boundingMode=GafferImage.Sampler.BoundingMode.Black) # The hashes must take account of the additional pixels being sampled. self.assertNotEqual(sampler1.hash(), sampler2.hash()) self.assertNotEqual(sampler2.hash(), sampler3.hash()) self.assertNotEqual(sampler3.hash(), sampler1.hash())
def testChannelPassThrough(self): # we should get a perfect pass-through without cache duplication when # all the colour plugs are at their defaults and clamping is disabled s = Gaffer.ScriptNode() s["c"] = GafferImage.Constant() s["g"] = GafferImage.Grade() s["g"]["blackClamp"].setValue(False) s["g"]["in"].setInput(s["c"]["out"]) for channelName in ("R", "G", "B", "A"): self.assertEqual( s["g"]["out"].channelDataHash(channelName, imath.V2i(0)), s["c"]["out"].channelDataHash(channelName, imath.V2i(0)), ) c = Gaffer.Context(s.context()) c["image:channelName"] = channelName c["image:tileOrigin"] = imath.V2i(0) with c: self.assertTrue( s["g"]["out"]["channelData"].getValue(_copy=False).isSame( s["c"]["out"]["channelData"].getValue(_copy=False)))
def testEnergyPreservation(self): constant = GafferImage.Constant() constant["color"].setValue(IECore.Color4f(1)) crop = GafferImage.Crop() crop["in"].setInput(constant["out"]) crop["area"].setValue(IECore.Box2i(IECore.V2i(10), IECore.V2i(11))) crop["affectDisplayWindow"].setValue(False) blur = GafferImage.Blur() blur["in"].setInput(crop["out"]) blur["expandDataWindow"].setValue(True) stats = GafferImage.ImageStats() stats["in"].setInput(blur["out"]) stats["area"].setValue(IECore.Box2i(IECore.V2i(5), IECore.V2i(15))) for i in range(0, 10): blur["radius"].setValue(IECore.V2f(i * 0.5)) self.assertAlmostEqual(stats["average"]["r"].getValue(), 1 / 100., delta=0.0001)
def testSourceScene(self): b = Gaffer.Box() b2 = Gaffer.Box() p = GafferScene.Plane() b["Box2"] = b2 b2["Plane"] = p expectedPath = "Box.Box2.Plane.out" self.assertEqual(p["out"].fullName(), expectedPath) # Make a test image, we don't have a renderer to use to invoke the real output # mechanism so we'll mock the result here. c = GafferImage.Constant() m = GafferImage.ImageMetadata() o = GafferImage.ImageWriter() m["in"].setInput(c["out"]) o["in"].setInput(m["out"]) pathWithoutMeta = os.path.join(self.temporaryDirectory(), "sceneAlgoSourceSceneWithoutMeta.exr") o["fileName"].setValue(pathWithoutMeta) o.execute() pathWithMeta = os.path.join(self.temporaryDirectory(), "sceneAlgoSourceSceneWithMeta.exr") m["metadata"].addChild( Gaffer.NameValuePlug("gaffer:sourceScene", IECore.StringData(expectedPath), True, "sourceScene")) o["fileName"].setValue(pathWithMeta) o.execute() inm = GafferImage.ImageReader() im = GafferImage.ImageReader() inm["fileName"].setValue(pathWithoutMeta) im["fileName"].setValue(pathWithMeta) self.assertTrue( "gaffer:sourceScene" not in inm["out"].metadata().keys()) self.assertTrue("gaffer:sourceScene" in im["out"].metadata().keys()) # Test path retrieval self.assertEqual(GafferScene.SceneAlgo.sourceSceneName(inm["out"]), "") self.assertEqual(GafferScene.SceneAlgo.sourceSceneName(im["out"]), expectedPath) # Check plug retrieval without a script node self.assertIsNone(GafferScene.SceneAlgo.sourceScene(inm["out"])) self.assertIsNone(GafferScene.SceneAlgo.sourceScene(im["out"])) # Add to a script s = Gaffer.ScriptNode() s["Box"] = b s["ImageNoMeta"] = inm s["ImageMeta"] = im self.assertIsNone(GafferScene.SceneAlgo.sourceScene(inm["out"])) self.assertTrue(p["out"].isSame( GafferScene.SceneAlgo.sourceScene(im["out"]))) # Remove target plug del s["Box"]["Box2"] self.assertIsNone(GafferScene.SceneAlgo.sourceScene(inm["out"])) self.assertIsNone(GafferScene.SceneAlgo.sourceScene(im["out"]))
def test( self ) : getRed = GafferOSL.OSLShader() getRed.loadShader( "ImageProcessing/InChannel" ) getRed["parameters"]["channelName"].setValue( "R" ) getGreen = GafferOSL.OSLShader() getGreen.loadShader( "ImageProcessing/InChannel" ) getGreen["parameters"]["channelName"].setValue( "G" ) getBlue = GafferOSL.OSLShader() getBlue.loadShader( "ImageProcessing/InChannel" ) getBlue["parameters"]["channelName"].setValue( "B" ) floatToColor = GafferOSL.OSLShader() floatToColor.loadShader( "Conversion/FloatToColor" ) floatToColor["parameters"]["r"].setInput( getBlue["out"]["channelValue"] ) floatToColor["parameters"]["g"].setInput( getGreen["out"]["channelValue"] ) floatToColor["parameters"]["b"].setInput( getRed["out"]["channelValue"] ) outRGB = GafferOSL.OSLShader() outRGB.loadShader( "ImageProcessing/OutLayer" ) outRGB["parameters"]["layerColor"].setInput( floatToColor["out"]["c"] ) imageShader = GafferOSL.OSLShader() imageShader.loadShader( "ImageProcessing/OutImage" ) imageShader["parameters"]["in0"].setInput( outRGB["out"]["layer"] ) reader = GafferImage.ImageReader() reader["fileName"].setValue( os.path.expandvars( "$GAFFER_ROOT/python/GafferImageTest/images/rgb.100x100.exr" ) ) image = GafferOSL.OSLImage() image["in"].setInput( reader["out"] ) # we haven't connected the shader yet, so the node should act as a pass through self.assertEqual( image["out"].image(), reader["out"].image() ) # that should all change when we hook up a shader image["channels"].addChild( Gaffer.NameValuePlug( "", GafferOSL.ClosurePlug(), "testClosure" ) ) cs = GafferTest.CapturingSlot( image.plugDirtiedSignal() ) def checkDirtiness( expected): self.assertEqual( [ i[0].fullName() for i in cs ], [ "OSLImage." + i for i in expected ] ) del cs[:] image["channels"]["testClosure"]["value"].setInput( imageShader["out"]["out"] ) checkDirtiness( [ "channels.testClosure.value", "channels.testClosure", "channels", "__shader", "__shading", "out.channelNames", "out.channelData", "out" ] ) inputImage = reader["out"].image() outputImage = image["out"].image() self.assertNotEqual( inputImage, outputImage ) self.assertEqual( outputImage["R"], inputImage["B"] ) self.assertEqual( outputImage["G"], inputImage["G"] ) self.assertEqual( outputImage["B"], inputImage["R"] ) # changes in the shader network should signal more dirtiness getGreen["parameters"]["channelName"].setValue( "R" ) checkDirtiness( [ "channels.testClosure.value", "channels.testClosure", "channels", "__shader", "__shading", "out.channelNames", "out.channelData", "out" ] ) floatToColor["parameters"]["r"].setInput( getRed["out"]["channelValue"] ) checkDirtiness( [ "channels.testClosure.value", "channels.testClosure", "channels", "__shader", "__shading", "out.channelNames", "out.channelData", "out" ] ) inputImage = reader["out"].image() outputImage = image["out"].image() self.assertEqual( outputImage["R"], inputImage["R"] ) self.assertEqual( outputImage["G"], inputImage["R"] ) self.assertEqual( outputImage["B"], inputImage["R"] ) image["in"].setInput( None ) checkDirtiness( [ 'in.format', 'in.dataWindow', 'in.metadata', 'in.channelNames', 'in.channelData', 'in', '__shading', 'out.channelNames', 'out.channelData', 'out.format', 'out.dataWindow', 'out.metadata', 'out' ] ) image["defaultFormat"]["displayWindow"]["max"]["x"].setValue( 200 ) checkDirtiness( [ 'defaultFormat.displayWindow.max.x', 'defaultFormat.displayWindow.max', 'defaultFormat.displayWindow', 'defaultFormat', '__defaultIn.format', '__defaultIn.dataWindow', '__defaultIn', '__shading', 'out.channelNames', 'out.channelData', 'out.format', 'out.dataWindow', 'out' ] ) constant = GafferImage.Constant() image["in"].setInput( constant["out"] ) checkDirtiness( [ 'in.format', 'in.dataWindow', 'in.metadata', 'in.channelNames', 'in.channelData', 'in', '__shading', 'out.channelNames', 'out.channelData', 'out.format', 'out.dataWindow', 'out.metadata', 'out' ] )
def test(self): for useClosure in [False, True]: getRed = GafferOSL.OSLShader() getRed.loadShader("ImageProcessing/InChannel") getRed["parameters"]["channelName"].setValue("R") getGreen = GafferOSL.OSLShader() getGreen.loadShader("ImageProcessing/InChannel") getGreen["parameters"]["channelName"].setValue("G") getBlue = GafferOSL.OSLShader() getBlue.loadShader("ImageProcessing/InChannel") getBlue["parameters"]["channelName"].setValue("B") floatToColor = GafferOSL.OSLShader() floatToColor.loadShader("Conversion/FloatToColor") floatToColor["parameters"]["r"].setInput( getBlue["out"]["channelValue"]) floatToColor["parameters"]["g"].setInput( getGreen["out"]["channelValue"]) floatToColor["parameters"]["b"].setInput( getRed["out"]["channelValue"]) reader = GafferImage.ImageReader() reader["fileName"].setValue( os.path.expandvars( "$GAFFER_ROOT/python/GafferImageTest/images/rgb.100x100.exr" )) shuffle = GafferImage.Shuffle() shuffle["in"].setInput(reader["out"]) shuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("channel")) shuffle["channels"]["channel"]["out"].setValue('unchangedR') shuffle["channels"]["channel"]["in"].setValue('R') image = GafferOSL.OSLImage() image["in"].setInput(shuffle["out"]) # we haven't connected the shader yet, so the node should act as a pass through self.assertEqual(GafferImage.ImageAlgo.image(image["out"]), GafferImage.ImageAlgo.image(shuffle["out"])) # that should all change when we hook up a shader if useClosure: outRGB = GafferOSL.OSLShader() outRGB.loadShader("ImageProcessing/OutLayer") outRGB["parameters"]["layerColor"].setInput( floatToColor["out"]["c"]) imageShader = GafferOSL.OSLShader() imageShader.loadShader("ImageProcessing/OutImage") imageShader["parameters"]["in0"].setInput( outRGB["out"]["layer"]) image["channels"].addChild( Gaffer.NameValuePlug("", GafferOSL.ClosurePlug(), "testClosure")) else: image["channels"].addChild( Gaffer.NameValuePlug("", imath.Color3f(), "testColor")) cs = GafferTest.CapturingSlot(image.plugDirtiedSignal()) def checkDirtiness(expected): self.assertEqual([i[0].fullName() for i in cs], ["OSLImage." + i for i in expected]) del cs[:] if useClosure: image["channels"]["testClosure"]["value"].setInput( imageShader["out"]["out"]) channelsDirtied = [ "channels.testClosure.value", "channels.testClosure" ] else: image["channels"]["testColor"]["value"].setInput( floatToColor["out"]["c"]) channelsDirtied = [ "channels.testColor.value.r", "channels.testColor.value.g", "channels.testColor.value.b", "channels.testColor.value", "channels.testColor" ] checkDirtiness(channelsDirtied + [ "channels", "__shader", "__shading", "__affectedChannels", "out.channelNames", "out.channelData", "out" ]) inputImage = GafferImage.ImageAlgo.image(shuffle["out"]) with Gaffer.ContextMonitor(image["__shading"]) as monitor: self.assertEqual( image["out"].channelNames(), IECore.StringVectorData(["A", "B", "G", "R", "unchangedR"])) # Evaluating channel names only requires evaluating the shading plug if we have a closure self.assertEqual( monitor.combinedStatistics().numUniqueContexts(), 1 if useClosure else 0) # Channels we don't touch should be passed through unaltered for channel, changed in [('B', True), ('G', True), ('R', True), ('A', False), ('unchangedR', False)]: self.assertEqual( image["out"].channelDataHash(channel, imath.V2i( 0, 0)) == shuffle["out"].channelDataHash( channel, imath.V2i(0, 0)), not changed) image["out"].channelData(channel, imath.V2i(0, 0)) # Should only need one shading evaluate for all channels self.assertEqual(monitor.combinedStatistics().numUniqueContexts(), 1) outputImage = GafferImage.ImageAlgo.image(image["out"]) self.assertNotEqual(inputImage, outputImage) self.assertEqual(outputImage["R"], inputImage["B"]) self.assertEqual(outputImage["G"], inputImage["G"]) self.assertEqual(outputImage["B"], inputImage["R"]) # changes in the shader network should signal more dirtiness getGreen["parameters"]["channelName"].setValue("R") checkDirtiness(channelsDirtied + [ "channels", "__shader", "__shading", "__affectedChannels", "out.channelNames", "out.channelData", "out" ]) floatToColor["parameters"]["r"].setInput( getRed["out"]["channelValue"]) checkDirtiness(channelsDirtied + [ "channels", "__shader", "__shading", "__affectedChannels", "out.channelNames", "out.channelData", "out" ]) inputImage = GafferImage.ImageAlgo.image(shuffle["out"]) outputImage = GafferImage.ImageAlgo.image(image["out"]) self.assertEqual(outputImage["R"], inputImage["R"]) self.assertEqual(outputImage["G"], inputImage["R"]) self.assertEqual(outputImage["B"], inputImage["R"]) self.assertEqual(outputImage["A"], inputImage["A"]) self.assertEqual(outputImage["unchangedR"], inputImage["unchangedR"]) image["in"].setInput(None) checkDirtiness([ 'in.format', 'in.dataWindow', 'in.metadata', 'in.deep', 'in.sampleOffsets', 'in.channelNames', 'in.channelData', 'in', '__shading', '__affectedChannels', 'out.channelNames', 'out.channelData', 'out.format', 'out.dataWindow', 'out.metadata', 'out.deep', 'out.sampleOffsets', 'out' ]) image["defaultFormat"]["displayWindow"]["max"]["x"].setValue(200) checkDirtiness([ 'defaultFormat.displayWindow.max.x', 'defaultFormat.displayWindow.max', 'defaultFormat.displayWindow', 'defaultFormat', '__defaultIn.format', '__defaultIn.dataWindow', '__defaultIn', '__shading', '__affectedChannels', 'out.channelNames', 'out.channelData', 'out.format', 'out.dataWindow', 'out' ]) constant = GafferImage.Constant() image["in"].setInput(constant["out"]) checkDirtiness([ 'in.format', 'in.dataWindow', 'in.metadata', 'in.deep', 'in.sampleOffsets', 'in.channelNames', 'in.channelData', 'in', '__shading', '__affectedChannels', 'out.channelNames', 'out.channelData', 'out.format', 'out.dataWindow', 'out.metadata', 'out.deep', 'out.sampleOffsets', 'out' ]) image["in"].setInput(shuffle["out"]) if useClosure: outRGB["parameters"]["layerName"].setValue("newLayer") else: image["channels"][0]["name"].setValue("newLayer") self.assertEqual( image["out"].channelNames(), IECore.StringVectorData([ "A", "B", "G", "R", "newLayer.B", "newLayer.G", "newLayer.R", "unchangedR" ])) for channel in ['B', 'G', 'R', 'A', 'unchangedR']: self.assertEqual( image["out"].channelDataHash(channel, imath.V2i(0, 0)), shuffle["out"].channelDataHash(channel, imath.V2i(0, 0))) self.assertEqual( image["out"].channelData(channel, imath.V2i(0, 0)), shuffle["out"].channelData(channel, imath.V2i(0, 0))) crop = GafferImage.Crop() crop["area"].setValue(imath.Box2i(imath.V2i(0, 0), imath.V2i(0, 0))) crop["in"].setInput(shuffle["out"]) image["in"].setInput(crop["out"]) if useClosure: # When using closures, we can't find out about the new channels being added if the datawindow is # empty self.assertEqual( image["out"].channelNames(), IECore.StringVectorData(["A", "B", "G", "R", "unchangedR"])) else: self.assertEqual( image["out"].channelNames(), IECore.StringVectorData([ "A", "B", "G", "R", "newLayer.B", "newLayer.G", "newLayer.R", "unchangedR" ]))
def testWarpImage(self): def __warpImage(size, distortion, idistortStyle): w = IECore.Box2i(IECore.V2i(0), size - IECore.V2i(1)) image = IECore.ImagePrimitive(w, w) R = IECore.FloatVectorData(size.x * size.y) G = IECore.FloatVectorData(size.x * size.y) for iy in range(size.y): for ix in range(size.x): x = (ix + 0.5) / size.x y = 1 - (iy + 0.5) / size.y if idistortStyle: R[iy * size.x + ix] = distortion * math.sin(y * 8) * size.x G[iy * size.x + ix] = distortion * math.sin(x * 8) * size.y else: R[iy * size.x + ix] = x + distortion * math.sin(y * 8) G[iy * size.x + ix] = y + distortion * math.sin(x * 8) image["R"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, R) image["G"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, G) return image def __dotGrid(size): w = IECore.Box2i(IECore.V2i(0), size - IECore.V2i(1)) image = IECore.ImagePrimitive(w, w) R = IECore.FloatVectorData(size.x * size.y) G = IECore.FloatVectorData(size.x * size.y) B = IECore.FloatVectorData(size.x * size.y) for iy in range(0, size.y): for ix in range(0, size.x): q = max(ix % 16, iy % 16) R[iy * size.x + ix] = q < 1 G[iy * size.x + ix] = q < 4 B[iy * size.x + ix] = q < 8 image["R"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, R) image["G"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, G) image["B"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, B) return image objectToImageSource = GafferImage.ObjectToImage() objectToImageSource["object"].setValue(__dotGrid(IECore.V2i(300))) # TODO - reorder channels of our source image because ObjectToImage outputs in opposite order to # the rest of Gaffer. This probably should be fixed in ObjectToImage, # or we shouldn't depend on channel order to check if images are equal? sourceReorderConstant = GafferImage.Constant() sourceReorderConstant["format"].setValue( GafferImage.Format(300, 300, 1.000)) sourceReorderDelete = GafferImage.DeleteChannels() sourceReorderDelete["channels"].setValue(IECore.StringVectorData(["A" ])) sourceReorderDelete["in"].setInput(sourceReorderConstant["out"]) sourceReorder = GafferImage.CopyChannels() sourceReorder["channels"].setValue("R G B") sourceReorder["in"]["in0"].setInput(sourceReorderDelete["out"]) sourceReorder["in"]["in1"].setInput(objectToImageSource["out"]) objectToImageVector = GafferImage.ObjectToImage() vectorWarp = GafferImage.VectorWarp() vectorWarp["in"].setInput(sourceReorder["out"]) vectorWarp["vector"].setInput(objectToImageVector["out"]) # Test that a warp with no distortion and a box filter reproduces the input objectToImageVector["object"].setValue( __warpImage(IECore.V2i(300), 0, False)) vectorWarp["filter"].setValue("box") self.assertImagesEqual(vectorWarp["out"], sourceReorder["out"], maxDifference=0.00001) # Test that a warp with distortion produces an expected output objectToImageVector["object"].setValue( __warpImage(IECore.V2i(300), 0.2, False)) vectorWarp["filter"].setValue("blackman-harris") # Enable to write out images for visual comparison if False: testWriter = GafferImage.ImageWriter() testWriter["in"].setInput(vectorWarp["out"]) testWriter["fileName"].setValue("/tmp/dotGrid.warped.exr") testWriter["task"].execute() expectedReader = GafferImage.ImageReader() expectedReader["fileName"].setValue( os.path.dirname(__file__) + "/images/dotGrid.warped.exr") # Test that we can get the same result using pixel offsets instead of normalized coordinates objectToImageVector["object"].setValue( __warpImage(IECore.V2i(300), 0.2, True)) vectorWarp["vectorMode"].setValue( GafferImage.VectorWarp.VectorMode.Relative) vectorWarp["vectorUnits"].setValue( GafferImage.VectorWarp.VectorUnits.Pixels) self.assertImagesEqual(vectorWarp["out"], expectedReader["out"], maxDifference=0.0005, ignoreMetadata=True)
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 testParallelGatherTileOrder( self ) : c = GafferImage.Constant() tileOrigins = [] channelTileOrigins = [] def tileFunctor( *args ) : pass def gatherFunctor( image, tileOrigin, tile ) : tileOrigins.append( tileOrigin ) def channelGatherFunctor( image, channelName, tileOrigin, tile ) : channelTileOrigins.append( tileOrigin ) for window in [ # Window not aligned to tile boundaries imath.Box2i( imath.V2i( 2 ), GafferImage.ImagePlug.tileSize() * imath.V2i( 20, 8 ) - imath.V2i( 2 ) ), # Window aligned to tile boundaries imath.Box2i( imath.V2i( 0 ), GafferImage.ImagePlug.tileSize() * imath.V2i( 6, 7 ) ), # Negative origin imath.Box2i( imath.V2i( -GafferImage.ImagePlug.tileSize() ), GafferImage.ImagePlug.tileSize() * imath.V2i( 4, 6 ) ) ] : size = GafferImage.ImagePlug.tileIndex( window.max() - imath.V2i( 1 ) ) - GafferImage.ImagePlug.tileIndex( window.min() ) + imath.V2i( 1 ) numTiles = size.x * size.y for order in GafferImage.ImageAlgo.TileOrder.values.values() : del tileOrigins[:] del channelTileOrigins[:] GafferImage.ImageAlgo.parallelGatherTiles( c["out"], tileFunctor, gatherFunctor, window = window, tileOrder = order ) GafferImage.ImageAlgo.parallelGatherTiles( c["out"], [ "R" ], tileFunctor, channelGatherFunctor, window = window, tileOrder = order ) self.assertEqual( len( tileOrigins ), numTiles ) self.assertEqual( len( channelTileOrigins ), numTiles ) for i in range( 1, len( tileOrigins ) ) : if order == GafferImage.ImageAlgo.TileOrder.TopToBottom : self.assertGreaterEqual( tileOrigins[i-1].y, tileOrigins[i].y ) elif order == GafferImage.ImageAlgo.TileOrder.BottomToTop : self.assertLessEqual( tileOrigins[i-1].y, tileOrigins[i].y ) if order != GafferImage.ImageAlgo.TileOrder.Unordered : self.assertEqual( channelTileOrigins[i], tileOrigins[i] )
def testOverall(self): constant = GafferImage.Constant() constant["color"].setValue(imath.Color4f(0.1, 0.2, 0.3, 0.5)) shuffle = GafferImage.Shuffle() shuffle["in"].setInput(constant["out"]) shuffle["channels"].addChild(shuffle.ChannelPlug("Z", "R")) shuffle["channels"].addChild(shuffle.ChannelPlug("ZBack", "G")) shuffle["enabled"].setValue(False) addDepth = GafferImage.FlatToDeep() addDepth["in"].setInput(shuffle["out"]) addDepth["enabled"].setValue(False) self.assertEqual(addDepth["out"]["channelNames"].getValue(), IECore.StringVectorData(["R", "G", "B", "A"])) addDepth["enabled"].setValue(True) self.assertEqual(addDepth["out"]["channelNames"].getValue(), IECore.StringVectorData(["R", "G", "B", "A", "Z"])) addDepth["zBackMode"].setValue( GafferImage.FlatToDeep.ZBackMode.Thickness) self.assertEqual( addDepth["out"]["channelNames"].getValue(), IECore.StringVectorData(["R", "G", "B", "A", "Z", "ZBack"])) with Gaffer.Context() as c: c["image:tileOrigin"] = imath.V2i(0) c["image:channelName"] = "R" rHash = constant["out"]["channelData"].hash() c["image:channelName"] = "G" gHash = constant["out"]["channelData"].hash() # TEST Z CHANNEL c["image:channelName"] = "Z" tilePixels = GafferImage.ImagePlug.tileSize()**2 initialZHash = addDepth["out"]["channelData"].hash() self.assertEqual(addDepth["out"]["channelData"].getValue(), IECore.FloatVectorData([0] * tilePixels)) addDepth["depth"].setValue(42.0) newZHash = addDepth["out"]["channelData"].hash() self.assertEqual(addDepth["out"]["channelData"].getValue(), IECore.FloatVectorData([42.0] * tilePixels)) self.assertNotEqual(initialZHash, newZHash) addDepth["zMode"].setValue(GafferImage.FlatToDeep.ZMode.Channel) addDepth["zChannel"].setValue("R") self.assertEqual(addDepth["out"]["channelData"].hash(), rHash) self.assertEqual(addDepth["out"]["channelData"].getValue(), IECore.FloatVectorData([0.1] * tilePixels)) addDepth["zChannel"].setValue("G") self.assertEqual(addDepth["out"]["channelData"].hash(), gHash) self.assertEqual(addDepth["out"]["channelData"].getValue(), IECore.FloatVectorData([0.2] * tilePixels)) addDepth["zChannel"].setValue("Q") six.assertRaisesRegex( self, RuntimeError, 'FlatToDeep : Cannot find requested Z channel - no channel "Q" found.', addDepth["out"]["channelData"].hash) six.assertRaisesRegex( self, RuntimeError, 'FlatToDeep : Cannot find requested Z channel - no channel "Q" found.', addDepth["out"]["channelData"].getValue) addDepth["zChannel"].setValue("Z") shuffle["enabled"].setValue(True) self.assertEqual(shuffle["out"]["channelData"].hash(), addDepth["out"]["channelData"].hash()) self.assertEqual(addDepth["out"]["channelData"].getValue(), IECore.FloatVectorData([0.1] * tilePixels)) addDepth["zMode"].setValue(GafferImage.FlatToDeep.ZMode.Constant) addDepth["depth"].setValue(0.0) shuffle["enabled"].setValue(False) # TEST ZBack CHANNEL c["image:channelName"] = "ZBack" initialZBackHash = addDepth["out"]["channelData"].hash() self.assertEqual(addDepth["out"]["channelData"].getValue(), IECore.FloatVectorData([0] * tilePixels)) addDepth["depth"].setValue(42.0) newZBackHash = addDepth["out"]["channelData"].hash() self.assertEqual(addDepth["out"]["channelData"].getValue(), IECore.FloatVectorData([42.0] * tilePixels)) self.assertNotEqual(initialZBackHash, newZBackHash) addDepth["thickness"].setValue(0.09) newerZBackHash = addDepth["out"]["channelData"].hash() self.assertEqual(addDepth["out"]["channelData"].getValue(), IECore.FloatVectorData([42.09] * tilePixels)) self.assertNotEqual(newZBackHash, newerZBackHash) addDepth["zBackMode"].setValue( GafferImage.FlatToDeep.ZBackMode.Channel) addDepth["zBackChannel"].setValue("R") self.assertEqual(addDepth["out"]["channelData"].hash(), rHash) self.assertEqual(addDepth["out"]["channelData"].getValue(), IECore.FloatVectorData([0.1] * tilePixels)) addDepth["zBackChannel"].setValue("G") self.assertEqual(addDepth["out"]["channelData"].hash(), gHash) self.assertEqual(addDepth["out"]["channelData"].getValue(), IECore.FloatVectorData([0.2] * tilePixels)) addDepth["zBackChannel"].setValue("Q") six.assertRaisesRegex( self, RuntimeError, 'FlatToDeep : Cannot find requested ZBack channel - no channel "Q" found.', addDepth["out"]["channelData"].hash) six.assertRaisesRegex( self, RuntimeError, 'FlatToDeep : Cannot find requested ZBack channel - no channel "Q" found.', addDepth["out"]["channelData"].getValue) addDepth["zBackChannel"].setValue("ZBack") shuffle["enabled"].setValue(True) self.assertEqual(shuffle["out"]["channelData"].hash(), addDepth["out"]["channelData"].hash()) self.assertEqual(addDepth["out"]["channelData"].getValue(), IECore.FloatVectorData([0.2] * tilePixels)) addDepth["zBackMode"].setValue( GafferImage.FlatToDeep.ZBackMode.Thickness) self.assertEqual(newerZBackHash, addDepth["out"]["channelData"].hash()) self.assertEqual(addDepth["out"]["channelData"].getValue(), IECore.FloatVectorData([42.09] * tilePixels)) addDepth["zMode"].setValue(GafferImage.FlatToDeep.ZMode.Channel) addDepth["zChannel"].setValue("Z") self.assertNotEqual(newerZBackHash, addDepth["out"]["channelData"].hash()) self.assertEqual(addDepth["out"]["channelData"].getValue(), IECore.FloatVectorData([0.19] * tilePixels))
def testLayerMapping(self): constant1 = GafferImage.Constant() constant1['color'].setValue(IECore.Color4f(0.1, 0.2, 0.3, 0.4)) constant1["format"].setValue(GafferImage.Format(10, 10, 1.000)) metadata1 = GafferImage.ImageMetadata() metadata1["in"].setInput(constant1["out"]) metadata1["metadata"].addMember("test", 1) constant2 = GafferImage.Constant() constant2['color'].setValue(IECore.Color4f(0.2, 0.4, 0.6, 0.8)) constant2["format"].setValue(GafferImage.Format(20, 20, 1.000)) metadata2 = GafferImage.ImageMetadata() metadata2["in"].setInput(constant2["out"]) metadata2["metadata"].addMember("test", 2) switch = GafferImage.ImageSwitch() switch["in"][0].setInput(metadata1["out"]) switch["in"][1].setInput(metadata2["out"]) e = Gaffer.Expression() switch.addChild(e) e.setExpression( 'parent["index"] = context["collect:layerName"] != "A"', "python") collect = GafferImage.CollectImages() collect["in"].setInput(switch["out"]) # Metadata and format are driven by the first layer collect["rootLayers"].setValue(IECore.StringVectorData(['A', 'B'])) self.assertEqual(collect["out"]["format"].getValue(), GafferImage.Format(10, 10, 1)) self.assertEqual(collect["out"]["metadata"].getValue(), IECore.CompoundData({"test": 1})) collect["rootLayers"].setValue(IECore.StringVectorData(['B', 'A'])) self.assertEqual(collect["out"]["format"].getValue(), GafferImage.Format(20, 20, 1)) self.assertEqual(collect["out"]["metadata"].getValue(), IECore.CompoundData({"test": 2})) collect["rootLayers"].setValue(IECore.StringVectorData([])) self.assertEqual( collect["out"]["format"].getValue(), constant1["format"].getDefaultFormat(Gaffer.Context.current())) self.assertEqual(collect["out"]["metadata"].getValue(), IECore.CompoundData()) sampler = GafferImage.ImageSampler("ImageSampler") sampler["pixel"].setValue(IECore.V2f(1, 1)) sampler["channels"].setValue( IECore.StringVectorData(["A.R", "A.G", "A.B", "A.A"])) sampler["image"].setInput(collect["out"]) collect["rootLayers"].setValue(IECore.StringVectorData(['A'])) self.assertEqual(list(collect["out"]["channelNames"].getValue()), ["A.R", "A.G", "A.B", "A.A"]) self.assertEqual(sampler["color"].getValue(), IECore.Color4f(0.1, 0.2, 0.3, 0.4)) # Test simple duplicate collect["rootLayers"].setValue(IECore.StringVectorData(['A', 'A'])) self.assertEqual(list(collect["out"]["channelNames"].getValue()), ["A.R", "A.G", "A.B", "A.A"]) self.assertEqual(sampler["color"].getValue(), IECore.Color4f(0.1, 0.2, 0.3, 0.4)) collect["rootLayers"].setValue(IECore.StringVectorData(['A', 'B'])) self.assertEqual( list(collect["out"]["channelNames"].getValue()), ["A.R", "A.G", "A.B", "A.A", "B.R", "B.G", "B.B", "B.A"]) self.assertEqual(sampler["color"].getValue(), IECore.Color4f(0.1, 0.2, 0.3, 0.4)) sampler["channels"].setValue( IECore.StringVectorData(["B.R", "B.G", "B.B", "B.A"])) self.assertEqual(sampler["color"].getValue(), IECore.Color4f(0.2, 0.4, 0.6, 0.8)) # Test overlapping names take the first layer constant1["layer"].setValue("B") collect["rootLayers"].setValue(IECore.StringVectorData(['A', 'A.B'])) sampler["channels"].setValue( IECore.StringVectorData(["A.B.R", "A.B.G", "A.B.B", "A.B.A"])) self.assertEqual(list(collect["out"]["channelNames"].getValue()), ["A.B.R", "A.B.G", "A.B.B", "A.B.A"]) self.assertEqual(sampler["color"].getValue(), IECore.Color4f(0.1, 0.2, 0.3, 0.4)) collect["rootLayers"].setValue(IECore.StringVectorData(['A.B', 'A'])) self.assertEqual(list(collect["out"]["channelNames"].getValue()), ["A.B.R", "A.B.G", "A.B.B", "A.B.A"]) self.assertEqual(sampler["color"].getValue(), IECore.Color4f(0.2, 0.4, 0.6, 0.8))
def testRenamePromotedImages(self): # Create boxed Catalogue with promoted `images` plug. box = Gaffer.Box() box["catalogue"] = GafferImage.Catalogue() box["catalogue"]["directory"].setValue( os.path.join(self.temporaryDirectory(), "catalogue")) images = Gaffer.PlugAlgo.promote(box["catalogue"]["images"]) # Send 2 images and name them using the promoted plugs. red = GafferImage.Constant() red["format"].setValue(GafferImage.Format(64, 64)) red["color"]["r"].setValue(1) self.sendImage(red["out"], box["catalogue"]) images[-1].setName("Red") green = GafferImage.Constant() green["format"].setValue(GafferImage.Format(64, 64)) green["color"]["g"].setValue(1) self.sendImage(green["out"], box["catalogue"]) images[-1].setName("Green") # Assert that images are accessible under those names. with Gaffer.Context() as c: c["catalogue:imageName"] = "Red" self.assertImagesEqual(box["catalogue"]["out"], red["out"], ignoreMetadata=True) c["catalogue:imageName"] = "Green" self.assertImagesEqual(box["catalogue"]["out"], green["out"], ignoreMetadata=True) # And that invalid names generate errors. with six.assertRaisesRegex(self, RuntimeError, 'Unknown image name "Blue"'): with Gaffer.Context() as c: c["catalogue:imageName"] = "Blue" box["catalogue"]["out"].metadata() # Assert that we can rename the images and get them under the new name. images[0].setName("Crimson") images[1].setName("Emerald") with Gaffer.Context() as c: c["catalogue:imageName"] = "Crimson" self.assertImagesEqual(box["catalogue"]["out"], red["out"], ignoreMetadata=True) c["catalogue:imageName"] = "Emerald" self.assertImagesEqual(box["catalogue"]["out"], green["out"], ignoreMetadata=True) # And that the old names are now invalid. with six.assertRaisesRegex(self, RuntimeError, 'Unknown image name "Red"'): with Gaffer.Context() as c: c["catalogue:imageName"] = "Red" box["catalogue"]["out"].metadata()
def testDeep(self): constantA = GafferImage.Constant() constantA["color"].setValue(imath.Color4f(0.1, 0.2, 0.3, 0.4)) constantB = GafferImage.Constant() constantB["color"].setValue(imath.Color4f(0.01, 0.02, 0.03, 0.04)) constantC = GafferImage.Constant() constantC["color"].setValue(imath.Color4f(0.001, 0.002, 0.003, 0.004)) constantD = GafferImage.Constant() constantD["color"].setValue( imath.Color4f(0.0001, 0.0002, 0.0003, 0.0004)) deepMergeAB = GafferImage.DeepMerge() deepMergeAB["in"][0].setInput(constantA["out"]) deepMergeAB["in"][1].setInput(constantB["out"]) deepMergeCD = GafferImage.DeepMerge() deepMergeCD["in"][0].setInput(constantC["out"]) deepMergeCD["in"][1].setInput(constantD["out"]) switch = Gaffer.Switch() switch.setup(GafferImage.ImagePlug("in", )) switch["in"][0].setInput(deepMergeAB["out"]) switch["in"][1].setInput(deepMergeCD["out"]) switchExpr = Gaffer.Expression() switch.addChild(switchExpr) switchExpr.setExpression( 'parent["index"] = context["collect:layerName"] == "CD"') collect = GafferImage.CollectImages() collect["in"].setInput(switch["out"]) collect["rootLayers"].setValue(IECore.StringVectorData(['AB', 'CD'])) o = imath.V2i(0) self.assertEqual(collect["out"].channelData("AB.R", o), deepMergeAB["out"].channelData("R", o)) self.assertEqual(collect["out"].channelData("AB.G", o), deepMergeAB["out"].channelData("G", o)) self.assertEqual(collect["out"].channelData("AB.B", o), deepMergeAB["out"].channelData("B", o)) self.assertEqual(collect["out"].channelData("AB.A", o), deepMergeAB["out"].channelData("A", o)) self.assertEqual(collect["out"].channelData("CD.R", o), deepMergeCD["out"].channelData("R", o)) self.assertEqual(collect["out"].channelData("CD.G", o), deepMergeCD["out"].channelData("G", o)) self.assertEqual(collect["out"].channelData("CD.B", o), deepMergeCD["out"].channelData("B", o)) self.assertEqual(collect["out"].channelData("CD.A", o), deepMergeCD["out"].channelData("A", o)) self.assertEqual(collect["out"].sampleOffsets(o), deepMergeAB["out"].sampleOffsets(o)) self.assertEqual(collect["out"].dataWindow(), deepMergeAB["out"].dataWindow()) self.assertEqual(collect["out"].deep(), True) self.assertEqual( collect["out"].channelNames(), IECore.StringVectorData([ 'AB.R', 'AB.G', 'AB.B', 'AB.A', 'CD.R', 'CD.G', 'CD.B', 'CD.A' ])) deepMergeAB["enabled"].setValue(False) with self.assertRaisesRegexp( Gaffer.ProcessException, r'Input to CollectImages must be consistent, but it is sometimes deep.*' ) as raised: collect["out"].deep() deepMergeAB["enabled"].setValue(True) deepMergeAB["in"][2].setInput(constantB["out"]) with self.assertRaisesRegexp( Gaffer.ProcessException, r'SampleOffsets on input to CollectImages must match. Pixel 0,0 received both 3 and 2 samples' ) as raised: collect["out"].sampleOffsets(o) offset = GafferImage.Offset() offset["in"].setInput(constantB["out"]) offset["offset"].setValue(imath.V2i(-5, -13)) deepMergeAB["in"][2].setInput(offset["out"]) with self.assertRaisesRegexp( Gaffer.ProcessException, r'DataWindows on deep input to CollectImages must match. Received both -5,-13 -> 1920,1080 and 0,0 -> 1920,1080' ) as raised: collect["out"].dataWindow()
def testFilterDerivatives( self ): # Size of one grid cell subSize = 35 # Each grid cell gets a dot in the middle redDot = GafferImage.Constant() redDot["format"].setValue( GafferImage.Format( 1, 1, 1.000 ) ) redDot["color"].setValue( IECore.Color4f( 10, 0, 0, 1 ) ) redDotCentered = GafferImage.Crop( "Crop" ) redDotCentered["in"].setInput( redDot["out"] ) redDotCentered["area"].setValue( IECore.Box2i( IECore.V2i( -(subSize-1)/2 ), IECore.V2i( (subSize-1)/2 + 1 ) ) ) borderForFilterWidth = 40 sampleRegion = redDotCentered["out"]["dataWindow"].getValue() sampleRegion.min -= IECore.V2i( borderForFilterWidth ) sampleRegion.max += IECore.V2i( borderForFilterWidth ) s = GafferImage.Sampler( redDotCentered["out"], "R", sampleRegion, GafferImage.Sampler.BoundingMode.Black ) filters = GafferImage.FilterAlgo.filterNames() dirs = [ (IECore.V2f(1,0), IECore.V2f(0,1)), (IECore.V2f(5,0), IECore.V2f(0,1)), (IECore.V2f(1,0), IECore.V2f(0,5)), (IECore.V2f(5,0), IECore.V2f(0,5)) ] for angle in range( 0, 91, 15 ): sa = math.sin( angle / 180.0 * math.pi ) ca = math.cos( angle / 180.0 * math.pi ) dirs.append( ( IECore.V2f(ca * 5, sa * 5 ), IECore.V2f(-sa * 3, ca * 3 ) ) ) size = subSize * IECore.V2i( len( dirs ), len( filters ) ) w = IECore.Box2i( IECore.V2i( 0 ), size - IECore.V2i( 1 ) ) parallelogramImage = IECore.ImagePrimitive( w, w ) boxImage = IECore.ImagePrimitive( w, w ) parallelogramR = IECore.FloatVectorData( size[0] * size[1] ) boxR = IECore.FloatVectorData( size[0] * size[1] ) for x_sub, d in enumerate( dirs ): for y_sub, f in enumerate( filters ): for y in range( subSize ): for x in range( subSize ): p = IECore.V2f( x + 0.5, y + 0.5 ) inputDerivatives = GafferImage.FilterAlgo.derivativesToAxisAligned( p, d[0], d[1] ) boxR[ ( y_sub * subSize + y ) * size[0] + x_sub * subSize + x ] = GafferImage.FilterAlgo.sampleBox( s, p, inputDerivatives[0], inputDerivatives[1], f ) parallelogramR[ ( y_sub * subSize + y ) * size[0] + x_sub * subSize + x ] = GafferImage.FilterAlgo.sampleParallelogram( s, p, d[0], d[1], f ) parallelogramImage["R"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, parallelogramR ) boxImage["R"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, boxR ) # Enable to write out images for visual comparison if False: IECore.Writer.create( parallelogramImage, "/tmp/filterDerivativesTestResult.parallelogram.exr" ).write() IECore.Writer.create( boxImage, "/tmp/filterDerivativesTestResult.box.exr" ).write() imageNode = GafferImage.ObjectToImage() imageNode["object"].setValue( parallelogramImage ) expectedImage = GafferImage.ImageReader() expectedImage["fileName"].setValue( self.derivativesReferenceParallelFileName ) self.assertImagesEqual( imageNode["out"], expectedImage["out"], ignoreMetadata = True, maxDifference = 0.000005 ) imageNode["object"].setValue( boxImage ) expectedImage["fileName"].setValue( self.derivativesReferenceBoxFileName ) self.assertImagesEqual( imageNode["out"], expectedImage["out"], ignoreMetadata = True, maxDifference = 0.000005 )
def testFilterDerivatives(self): # Size of one grid cell subSize = 35 # Each grid cell gets a dot in the middle redDot = GafferImage.Constant() redDot["format"].setValue(GafferImage.Format(1, 1, 1.000)) redDot["color"].setValue(imath.Color4f(10, 0, 0, 1)) redDotCentered = GafferImage.Crop("Crop") redDotCentered["in"].setInput(redDot["out"]) redDotCentered["area"].setValue( imath.Box2i(imath.V2i(-(subSize - 1) / 2), imath.V2i((subSize - 1) / 2 + 1))) borderForFilterWidth = 40 sampleRegion = redDotCentered["out"]["dataWindow"].getValue() sampleRegion.setMin(sampleRegion.min() - imath.V2i(borderForFilterWidth)) sampleRegion.setMax(sampleRegion.max() + imath.V2i(borderForFilterWidth)) s = GafferImage.Sampler(redDotCentered["out"], "R", sampleRegion, GafferImage.Sampler.BoundingMode.Black) filters = [ 'box', 'triangle', 'gaussian', 'sharp-gaussian', 'catmull-rom', 'blackman-harris', 'sinc', 'lanczos3', 'radial-lanczos3', 'mitchell', 'bspline', 'disk', 'cubic', 'keys', 'simon', 'rifman', 'smoothGaussian', ] if filters != GafferImage.FilterAlgo.filterNames(): print( "INFO : GafferImageTest.FilterAlgoTest.testFilterDerivatives : " + "Some image filters have not been tested ({}). Consider updating the reference images to account for the newly available filters." .format( list( set(filters).symmetric_difference( set(GafferImage.FilterAlgo.filterNames()))))) dirs = [(imath.V2f(1, 0), imath.V2f(0, 1)), (imath.V2f(5, 0), imath.V2f(0, 1)), (imath.V2f(1, 0), imath.V2f(0, 5)), (imath.V2f(5, 0), imath.V2f(0, 5))] for angle in range(0, 91, 15): sa = math.sin(angle / 180.0 * math.pi) ca = math.cos(angle / 180.0 * math.pi) dirs.append((imath.V2f(ca * 5, sa * 5), imath.V2f(-sa * 3, ca * 3))) size = subSize * imath.V2i(len(dirs), len(filters)) w = imath.Box2i(imath.V2i(0), size - imath.V2i(1)) parallelogramImage = IECoreImage.ImagePrimitive(w, w) boxImage = IECoreImage.ImagePrimitive(w, w) parallelogramR = IECore.FloatVectorData(size[0] * size[1]) boxR = IECore.FloatVectorData(size[0] * size[1]) for x_sub, d in enumerate(dirs): for y_sub, f in enumerate(filters): for y in range(subSize): for x in range(subSize): p = imath.V2f(x + 0.5, y + 0.5) inputDerivatives = GafferImage.FilterAlgo.derivativesToAxisAligned( p, d[0], d[1]) boxR[(y_sub * subSize + y) * size[0] + x_sub * subSize + x] = GafferImage.FilterAlgo.sampleBox( s, p, inputDerivatives[0], inputDerivatives[1], f) parallelogramR[ (y_sub * subSize + y) * size[0] + x_sub * subSize + x] = GafferImage.FilterAlgo.sampleParallelogram( s, p, d[0], d[1], f) parallelogramImage["R"] = parallelogramR boxImage["R"] = boxR # Enable to write out images for visual comparison if False: IECore.Writer.create( parallelogramImage, "/tmp/filterDerivativesTestResult.parallelogram.exr").write() IECore.Writer.create( boxImage, "/tmp/filterDerivativesTestResult.box.exr").write() parallelogramReference = IECore.Reader.create( self.derivativesReferenceParallelFileName).read() boxReference = IECore.Reader.create( self.derivativesReferenceBoxFileName).read() for i in range(len(parallelogramImage["R"])): self.assertAlmostEqual(parallelogramReference["R"][i], parallelogramImage["R"][i], places=5) self.assertAlmostEqual(boxReference["R"][i], boxImage["R"][i], places=5)
def testPixelAspectRatio(self): c = GafferImage.Constant() c["format"].setValue(GafferImage.Format(1000, 1000)) r = GafferImage.Resize() r["in"].setInput(c["out"]) r["format"].setValue(GafferImage.Format(1500, 1000)) for fitMode in r.FitMode.values: r["fitMode"].setValue(fitMode) for inputPixelAspect in (0.5, 1, 2): c["format"]["pixelAspect"].setValue(inputPixelAspect) for outputPixelAspect in (0.5, 1, 2): r["format"]["pixelAspect"].setValue(outputPixelAspect) if fitMode == r.FitMode.Horizontal: self.assertEqual( r["out"]["dataWindow"].getValue().min().x, r["out"] ["format"].getValue().getDisplayWindow().min().x) self.assertEqual( r["out"]["dataWindow"].getValue().max().x, r["out"] ["format"].getValue().getDisplayWindow().max().x) elif fitMode == r.FitMode.Vertical: self.assertEqual( r["out"]["dataWindow"].getValue().min().y, r["out"] ["format"].getValue().getDisplayWindow().min().y) self.assertEqual( r["out"]["dataWindow"].getValue().max().y, r["out"] ["format"].getValue().getDisplayWindow().max().y) if fitMode != r.FitMode.Distort: # All fit modes other than Distort should ensure that the aspect # ratio of the output data window is the same as the aspect ratio # of the input data window. inputDataWindow = r["in"]["dataWindow"].getValue() inputFormat = r["in"]["format"].getValue() inputAspect = (inputDataWindow.size().x ) * inputFormat.getPixelAspect() / ( inputDataWindow.size().y) outputDataWindow = r["out"]["dataWindow"].getValue() outputFormat = r["out"]["format"].getValue() outputAspect = (outputDataWindow.size().x ) * outputFormat.getPixelAspect() / ( outputDataWindow.size().y) # `delta` accounts for the fact that we're comparing integer data windows # which have been expanded to enclose "fractional" pixels. self.assertAlmostEqual(outputAspect, inputAspect, delta=0.01) else: # Distort mode - data window fills output format. self.assertEqual( r["out"]["dataWindow"].getValue(), r["out"]["format"].getValue().getDisplayWindow())
def testGlobals(self): constant = GafferImage.Constant() constant["format"].setValue( GafferImage.Format(IECore.Box2i(IECore.V2i(-10), IECore.V2i(10)))) globals = GafferOSL.OSLShader() globals.loadShader("Utility/Globals") outP = GafferOSL.OSLShader() outP.loadShader("ImageProcessing/OutLayer") outP["parameters"]["layerColor"].setInput(globals["out"]["globalP"]) outU = GafferOSL.OSLShader() outU.loadShader("ImageProcessing/OutChannel") outU["parameters"]["channelName"].setValue("u") outU["parameters"]["channelValue"].setInput(globals["out"]["globalU"]) outV = GafferOSL.OSLShader() outV.loadShader("ImageProcessing/OutChannel") outV["parameters"]["channelName"].setValue("v") outV["parameters"]["channelValue"].setInput(globals["out"]["globalV"]) imageShader = GafferOSL.OSLShader() imageShader.loadShader("ImageProcessing/OutImage") imageShader["parameters"]["in0"].setInput(outP["out"]["layer"]) imageShader["parameters"]["in1"].setInput(outU["out"]["channel"]) imageShader["parameters"]["in2"].setInput(outV["out"]["channel"]) image = GafferOSL.OSLImage() image["in"].setInput(constant["out"]) image["shader"].setInput(imageShader["out"]) displayWindow = image["out"]["format"].getValue().getDisplayWindow() samplerR = GafferImage.Sampler(image["out"], "R", displayWindow) samplerG = GafferImage.Sampler(image["out"], "G", displayWindow) samplerB = GafferImage.Sampler(image["out"], "B", displayWindow) samplerU = GafferImage.Sampler(image["out"], "u", displayWindow) samplerV = GafferImage.Sampler(image["out"], "v", displayWindow) size = IECore.V2f(displayWindow.size()) uvStep = IECore.V2f(1.0) / size uvMin = 0.5 * uvStep for y in range(displayWindow.min.y, displayWindow.max.y): for x in range(displayWindow.min.x, displayWindow.max.x): self.assertEqual(samplerR.sample(x, y), x + 0.5, "Pixel {},{}".format(x, y)) self.assertEqual(samplerG.sample(x, y), y + 0.5, "Pixel {},{}".format(x, y)) self.assertEqual(samplerB.sample(x, y), 0, "Pixel {},{}".format(x, y)) uv = uvMin + uvStep * IECore.V2f( IECore.V2i(x, y) - displayWindow.min) self.assertAlmostEqual(samplerU.sample(x, y), uv.x, delta=0.0000001, msg="Pixel {},{}".format(x, y)) self.assertAlmostEqual(samplerV.sample(x, y), uv.y, delta=0.0000001, msg="Pixel {},{}".format(x, y))
def testBadCachePolicyHang(self): # Using the legacy cache policy for OSLImage.shadingPlug creates a hang due to tbb task stealing, # though it's a bit hard to actually demonstrate constant = GafferImage.Constant() constant["format"].setValue(GafferImage.Format(128, 128, 1.000)) # Need a slow to compute OSL code in order to trigger hang mandelbrotCode = self.mandelbrotNode() # In order to trigger the hang, we need to mix threads which are stuck waiting for an expression which # uses the Standard policy with threads that are actually finishing, so that tbb tries to start up new # threads while we're waiting for the expression result. To do this, we use the "var" context variable # to create two versions of this OSLCode mandelbrotCode["varExpression"] = Gaffer.Expression() mandelbrotCode["varExpression"].setExpression( 'parent.parameters.iterations = 100000 + context( "var", 0 );', "OSL") oslImage = GafferOSL.OSLImage() oslImage["channels"].addChild( Gaffer.NameValuePlug( "", Gaffer.Color3fPlug( "value", defaultValue=imath.Color3f(1, 1, 1), flags=Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ), True, "channel", Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic)) oslImage["in"].setInput(constant["out"]) oslImage["channels"]["channel"]["value"][0].setInput( mandelbrotCode["out"]["outFloat"]) oslImage["channels"]["channel"]["value"][1].setInput( mandelbrotCode["out"]["outFloat"]) oslImage["channels"]["channel"]["value"][2].setInput( mandelbrotCode["out"]["outFloat"]) # This imageStats is use to create non-blocking slow calculations imageStats = GafferImage.ImageStats() imageStats["in"].setInput(oslImage["out"]) imageStats["area"].setValue( imath.Box2i(imath.V2i(0, 0), imath.V2i(64, 64))) # This box does the non-blocking slow calculation, followed by a blocking slow calculation. # This ensures that tasks which do just the non-block calculation will start finishing while # the blocking slow calculation is still running, allowing tbb to try running more threads # on the blocking calcluation, realizing they can't run, and stealing tasks onto those threads # which can hit the Standard policy lock on the expression upstream and deadlock, unless the # OSLImage isolates its threads correctly expressionBox = Gaffer.Box() expressionBox.addChild( Gaffer.FloatVectorDataPlug("inChannelData", defaultValue=IECore.FloatVectorData( []))) expressionBox.addChild(Gaffer.FloatPlug("inStat")) expressionBox.addChild( Gaffer.FloatPlug("out", direction=Gaffer.Plug.Direction.Out)) expressionBox["inChannelData"].setInput(oslImage["out"]["channelData"]) expressionBox["inStat"].setInput(imageStats["average"]["r"]) expressionBox["contextVariables"] = Gaffer.ContextVariables() expressionBox["contextVariables"].setup( Gaffer.FloatVectorDataPlug("in", defaultValue=IECore.FloatVectorData( []))) expressionBox["contextVariables"]["variables"].addChild( Gaffer.NameValuePlug("image:tileOrigin", Gaffer.V2iPlug("value"), True, "member1")) expressionBox["contextVariables"]["variables"].addChild( Gaffer.NameValuePlug("image:channelName", Gaffer.StringPlug("value", defaultValue='R'), True, "member2")) expressionBox["contextVariables"]["variables"].addChild( Gaffer.NameValuePlug("var", Gaffer.IntPlug("value", defaultValue=1), True, "member3")) expressionBox["contextVariables"]["in"].setInput( expressionBox["inChannelData"]) expressionBox["expression"] = Gaffer.Expression() expressionBox["expression"].setExpression( inspect.cleandoc(""" d = parent["contextVariables"]["out"] parent["out"] = d[0] + parent["inStat"] """)) # Create a switch to mix which tasks perform the non-blocking or blocking calculation - we need a mixture # to trigger the hang switch = Gaffer.Switch() switch.setup(Gaffer.IntPlug( "in", defaultValue=0, )) switch["in"][0].setInput(expressionBox["out"]) switch["in"][1].setInput(imageStats["average"]["r"]) switch["switchExpression"] = Gaffer.Expression() switch["switchExpression"].setExpression( 'parent.index = ( stoi( context( "testContext", "0" ) ) % 10 ) > 5;', "OSL") # In order to evaluate this expression a bunch of times at once with different values of "testContext", # we set up a simple scene that can be evaluated with GafferSceneTest.traversScene. # In theory, we could use a simple function that used a parallel_for to evaluate switch["out"], but for # some reason we don't entirely understand, this does not trigger the hang import GafferSceneTest import GafferScene sphere = GafferScene.Sphere() pathFilter = GafferScene.PathFilter() pathFilter["paths"].setValue(IECore.StringVectorData(['/sphere'])) customAttributes = GafferScene.CustomAttributes() customAttributes["attributes"].addChild( Gaffer.NameValuePlug("foo", Gaffer.FloatPlug("value"), True, "member1")) customAttributes["attributes"]["member1"]["value"].setInput( switch["out"]) customAttributes["in"].setInput(sphere["out"]) customAttributes["filter"].setInput(pathFilter["out"]) collectScenes = GafferScene.CollectScenes() collectScenes["in"].setInput(customAttributes["out"]) collectScenes["rootNames"].setValue( IECore.StringVectorData([str(i) for i in range(1000)])) collectScenes["rootNameVariable"].setValue('testContext') # When OSLImage.shadingPlug is not correctly isolated, and grain size on ShadingEngine is smaller than the # image tile size, this fails about 50% of the time. Running it 5 times makes the failure pretty consistent. for i in range(5): Gaffer.ValuePlug.clearCache() Gaffer.ValuePlug.clearHashCache() GafferSceneTest.traverseScene(collectScenes["out"])
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 testDeleteKeepsOrder(self): # Send 4 images to a Catalogue : red, green, blue, yellow script = Gaffer.ScriptNode() script["catalogue"] = GafferImage.Catalogue() script["catalogue"]["directory"].setValue( os.path.join(self.temporaryDirectory(), "catalogue")) script["red"] = GafferImage.Constant() script["red"]["format"].setValue(GafferImage.Format(64, 64)) script["red"]["color"]["r"].setValue(1) self.sendImage(script["red"]["out"], script["catalogue"]) script["catalogue"]["images"][-1].setName("Red") script["green"] = GafferImage.Constant() script["green"]["format"].setValue(GafferImage.Format(64, 64)) script["green"]["color"]["g"].setValue(1) self.sendImage(script["green"]["out"], script["catalogue"]) script["catalogue"]["images"][-1].setName("Green") script["blue"] = GafferImage.Constant() script["blue"]["format"].setValue(GafferImage.Format(64, 64)) script["blue"]["color"]["b"].setValue(1) self.sendImage(script["blue"]["out"], script["catalogue"]) script["catalogue"]["images"][-1].setName("Blue") script["yellow"] = GafferImage.Constant() script["yellow"]["format"].setValue(GafferImage.Format(64, 64)) script["yellow"]["color"].setValue(imath.Color4f(1, 1, 0, 1)) self.sendImage(script["yellow"]["out"], script["catalogue"]) script["catalogue"]["images"][-1].setName("Yellow") script["fileName"].setValue("/tmp/ttt.gfr") script.save() # Check it worked def assertPreconditions(): self.assertEqual(len(script["catalogue"]["images"]), 4) self.assertEqual(script["catalogue"]["images"][0].getName(), "Red") self.assertEqual(script["catalogue"]["images"][1].getName(), "Green") self.assertEqual(script["catalogue"]["images"][2].getName(), "Blue") self.assertEqual(script["catalogue"]["images"][3].getName(), "Yellow") script["catalogue"]["imageIndex"].setValue(0) self.assertImagesEqual(script["catalogue"]["out"], script["red"]["out"], ignoreMetadata=True) script["catalogue"]["imageIndex"].setValue(1) self.assertImagesEqual(script["catalogue"]["out"], script["green"]["out"], ignoreMetadata=True) script["catalogue"]["imageIndex"].setValue(2) self.assertImagesEqual(script["catalogue"]["out"], script["blue"]["out"], ignoreMetadata=True) script["catalogue"]["imageIndex"].setValue(3) self.assertImagesEqual(script["catalogue"]["out"], script["yellow"]["out"], ignoreMetadata=True) with Gaffer.Context(script.context()) as c: c["catalogue:imageName"] = "Red" self.assertImagesEqual(script["catalogue"]["out"], script["red"]["out"], ignoreMetadata=True) c["catalogue:imageName"] = "Green" self.assertImagesEqual(script["catalogue"]["out"], script["green"]["out"], ignoreMetadata=True) c["catalogue:imageName"] = "Blue" self.assertImagesEqual(script["catalogue"]["out"], script["blue"]["out"], ignoreMetadata=True) c["catalogue:imageName"] = "Yellow" self.assertImagesEqual(script["catalogue"]["out"], script["yellow"]["out"], ignoreMetadata=True) assertPreconditions() # Delete green, then blue, then yellow script["catalogue"]["imageIndex"].setValue(0) with Gaffer.UndoScope(script): del script["catalogue"]["images"][1] del script["catalogue"]["images"][1] del script["catalogue"]["images"][1] # Check it worked def assertPostConditions(): self.assertEqual(len(script["catalogue"]["images"]), 1) self.assertEqual(script["catalogue"]["images"][0].getName(), "Red") script["catalogue"]["imageIndex"].setValue(0) self.assertImagesEqual(script["catalogue"]["out"], script["red"]["out"], ignoreMetadata=True) with Gaffer.Context(script.context()) as c: c["catalogue:imageName"] = "Red" self.assertImagesEqual(script["catalogue"]["out"], script["red"]["out"], ignoreMetadata=True) assertPostConditions() # Check that undo and redo work script.undo() assertPreconditions() script.redo() assertPostConditions() script.undo() assertPreconditions()
def testFilterDerivatives( self ): # Size of one grid cell subSize = 35 # Each grid cell gets a dot in the middle redDot = GafferImage.Constant() redDot["format"].setValue( GafferImage.Format( 1, 1, 1.000 ) ) redDot["color"].setValue( imath.Color4f( 10, 0, 0, 1 ) ) redDotCentered = GafferImage.Crop( "Crop" ) redDotCentered["in"].setInput( redDot["out"] ) redDotCentered["area"].setValue( imath.Box2i( imath.V2i( -(subSize-1)/2 ), imath.V2i( (subSize-1)/2 + 1 ) ) ) borderForFilterWidth = 40 sampleRegion = redDotCentered["out"]["dataWindow"].getValue() sampleRegion.setMin( sampleRegion.min() - imath.V2i( borderForFilterWidth ) ) sampleRegion.setMax( sampleRegion.max() + imath.V2i( borderForFilterWidth ) ) s = GafferImage.Sampler( redDotCentered["out"], "R", sampleRegion, GafferImage.Sampler.BoundingMode.Black ) filters = GafferImage.FilterAlgo.filterNames() dirs = [ (imath.V2f(1,0), imath.V2f(0,1)), (imath.V2f(5,0), imath.V2f(0,1)), (imath.V2f(1,0), imath.V2f(0,5)), (imath.V2f(5,0), imath.V2f(0,5)) ] for angle in range( 0, 91, 15 ): sa = math.sin( angle / 180.0 * math.pi ) ca = math.cos( angle / 180.0 * math.pi ) dirs.append( ( imath.V2f(ca * 5, sa * 5 ), imath.V2f(-sa * 3, ca * 3 ) ) ) size = subSize * imath.V2i( len( dirs ), len( filters ) ) w = imath.Box2i( imath.V2i( 0 ), size - imath.V2i( 1 ) ) parallelogramImage = IECoreImage.ImagePrimitive( w, w ) boxImage = IECoreImage.ImagePrimitive( w, w ) parallelogramR = IECore.FloatVectorData( size[0] * size[1] ) boxR = IECore.FloatVectorData( size[0] * size[1] ) for x_sub, d in enumerate( dirs ): for y_sub, f in enumerate( filters ): for y in range( subSize ): for x in range( subSize ): p = imath.V2f( x + 0.5, y + 0.5 ) inputDerivatives = GafferImage.FilterAlgo.derivativesToAxisAligned( p, d[0], d[1] ) boxR[ ( y_sub * subSize + y ) * size[0] + x_sub * subSize + x ] = GafferImage.FilterAlgo.sampleBox( s, p, inputDerivatives[0], inputDerivatives[1], f ) parallelogramR[ ( y_sub * subSize + y ) * size[0] + x_sub * subSize + x ] = GafferImage.FilterAlgo.sampleParallelogram( s, p, d[0], d[1], f ) parallelogramImage["R"] = parallelogramR boxImage["R"] = boxR # Enable to write out images for visual comparison if False: IECore.Writer.create( parallelogramImage, "/tmp/filterDerivativesTestResult.parallelogram.exr" ).write() IECore.Writer.create( boxImage, "/tmp/filterDerivativesTestResult.box.exr" ).write() parallelogramReference = IECore.Reader.create( self.derivativesReferenceParallelFileName ).read() boxReference = IECore.Reader.create( self.derivativesReferenceBoxFileName ).read() for i in range( len( parallelogramImage["R"] ) ): self.assertAlmostEqual( parallelogramReference["R"][i], parallelogramImage["R"][i], places = 5 ) self.assertAlmostEqual( boxReference["R"][i], boxImage["R"][i], places = 5 )