def testHashPassThrough(self): constant1 = GafferImage.Constant() constant2 = GafferImage.Constant() ########################################## # Test to see if the input is always passed # through if only the first input is connected. ########################################## merge = GafferImage.DeepMerge() merge["in"][0].setInput(constant1["out"]) for plug in [ "format", "dataWindow", "metadata", "channelNames", "sampleOffsets", "deep" ]: self.assertEqual(constant1["out"][plug].hash(), merge["out"][plug].hash()) ########################################## # Test that if we add a second input and disable # the node the hash still gets passed through. ########################################## merge["in"][1].setInput(constant2["out"]) merge["enabled"].setValue(False) for plug in [ "format", "dataWindow", "metadata", "channelNames", "sampleOffsets", "deep" ]: self.assertEqual(constant1["out"][plug].hash(), merge["out"][plug].hash())
def testOccludeAll( self ) : representativeImage = GafferImage.ImageReader() representativeImage["fileName"].setValue( self.representativeImagePath ) constantNodes = self.__getConstant( 0.1, 0.2, 0.3, 1, -10, -10, imath.V2i( 150, 100 ) ) empty = GafferImage.Empty() empty["format"].setValue( GafferImage.Format( imath.Box2i( imath.V2i( 0 ), imath.V2i( 150, 100 ) ), 1 ) ) deepConstant = GafferImage.DeepMerge() deepConstant["in"][0].setInput( constantNodes[1]["out"] ) deepConstant["in"][1].setInput( empty["out"] ) deepMerge = GafferImage.DeepMerge() deepMerge["in"][0].setInput( representativeImage["out"] ) deepMerge["in"][1].setInput( deepConstant["out"] ) deepState = GafferImage.DeepState() deepState["in"].setInput( deepMerge["out"] ) deepState["pruneOccluded"].setValue( True ) self.assertEqual( GafferImage.ImageAlgo.tiles( constantNodes[1]["out"] ), GafferImage.ImageAlgo.tiles( constantNodes[1]["out"] ) )
def testImageDeepException(self): constant1 = GafferImage.Constant() constant1["color"].setValue(imath.Color4f(1)) constant2 = GafferImage.Constant() constant2["color"].setValue(imath.Color4f(1)) merge = GafferImage.DeepMerge() merge["in"][0].setInput(constant1["out"]) merge["in"][1].setInput(constant2["out"]) with self.assertRaises(RuntimeError): GafferImage.ImageAlgo.image(merge["out"])
def testOutputDeep(self): # Test default deep state when no inputs connected merge1 = GafferImage.DeepMerge() self.assertEqual(merge1["out"]["deep"].getValue(), merge1["out"]["deep"].defaultValue()) # Test that deep state passes through Flat value when one input connected constant1 = GafferImage.Constant() merge1["in"][0].setInput(constant1["out"]) self.assertEqual(merge1["out"]["deep"].getValue(), constant1["out"]["deep"].getValue()) # Test that deep state is Deep when two inputs connected constant2 = GafferImage.Constant() merge1["in"][1].setInput(constant2["out"]) self.assertEqual(merge1["out"]["deep"].getValue(), GafferImage.ImagePlug.Deep.Deep) # Test that deep state passes through Deep value when one input connected merge2 = GafferImage.DeepMerge() merge2["in"][0].setInput(merge1["out"]) self.assertEqual(merge2["out"]["deep"].getValue(), merge1["out"]["deep"].getValue())
def testDataWindow(self): sourceFormat = GafferImage.Format( imath.Box2i(imath.V2i(0), imath.V2i(512)), 1) constant1 = GafferImage.Constant() constant1["format"].setValue(sourceFormat) constant2 = GafferImage.Constant() constant2["format"].setValue(sourceFormat) crop1 = GafferImage.Crop() crop1["in"].setInput(constant1["out"]) crop1["affectDisplayWindow"].setValue(False) crop2 = GafferImage.Crop() crop2["in"].setInput(constant2["out"]) crop2["affectDisplayWindow"].setValue(False) merge = GafferImage.DeepMerge() merge["in"][0].setInput(crop1["out"]) merge["in"][1].setInput(crop2["out"]) for i in range(100): crop1Area = imath.Box2i() crop1Area.extendBy( imath.V2i(int(random.uniform(0, sourceFormat.width())), int(random.uniform(0, sourceFormat.height())))) crop1Area.extendBy( imath.V2i(int(random.uniform(0, sourceFormat.width())), int(random.uniform(0, sourceFormat.height())))) crop1["area"].setValue(crop1Area) crop2Area = imath.Box2i() crop2Area.extendBy( imath.V2i(int(random.uniform(0, sourceFormat.width())), int(random.uniform(0, sourceFormat.height())))) crop2Area.extendBy( imath.V2i(int(random.uniform(0, sourceFormat.width())), int(random.uniform(0, sourceFormat.height())))) crop2["area"].setValue(crop2Area) expectedDataWindow = crop1Area expectedDataWindow.extendBy(crop2Area.min()) expectedDataWindow.extendBy(crop2Area.max()) self.assertEqual(merge["out"]["dataWindow"].getValue(), expectedDataWindow)
def __getMessy( self, values = None, randomValueCount = 5, forceOverlap = False ) : nodes = {} if not values: values = [] for i in range( randomValueCount ) : v = {} for channel in [ "R", "G", "B" ] : v[channel] = round( random.uniform( 0.0, 5.0 ), 2 ) v["A"] = round( random.uniform( 0.0, 1.0 ), 2 ) for channel in [ "Z", "ZBack" ] : v[channel] = round( random.uniform( 0.0, 10.0 ) ) if random.random() > 0.5 : v["ZBack"] = v["Z"] else: v["ZBack"] = max(v["Z"], v["ZBack"]) if forceOverlap: # All samples will overlap 100% - exercise the merge code v["Z"] = 1 v["ZBack"] = 2 values.append( v ) nodes['merge'] = GafferImage.DeepMerge() nodes['constants'] = [] nodes['values'] = values for i, v in enumerate( values ) : c,d = self.__getConstant( **v ) nodes['constants'].append( c ) nodes['constants'].append( d ) nodes['merge']['in'][i].setInput( d["out"] ) self.assertEqual( nodes['merge']["out"]["deep"].getValue(), True ) return nodes
def testExceptionOnDeepData(self): constant1 = GafferImage.Constant() constant1["format"].setValue(GafferImage.Format(1000, 1000)) constant1["color"].setValue(imath.Color4f(1)) constant2 = GafferImage.Constant() constant2["format"].setValue(GafferImage.Format(1000, 1000)) constant2["color"].setValue(imath.Color4f(1)) merge = GafferImage.DeepMerge() merge["in"][0].setInput(constant1["out"]) merge["in"][1].setInput(constant2["out"]) with self.assertRaises(RuntimeError): sampler = GafferImage.Sampler( merge["out"], "R", imath.Box2i(imath.V2i(0), imath.V2i(200)), boundingMode=GafferImage.Sampler.BoundingMode.Black)
def testRealisticReference( self ) : representativeImage = GafferImage.ImageReader() representativeImage["fileName"].setValue( self.representativeImagePath ) offset = GafferImage.Offset() offset["in"].setInput( representativeImage["out"] ) offset["offset"].setValue( imath.V2i( -58, 11 ) ) depthGrade = self.__createDepthGrade() depthGrade["in"].setInput( offset["out"] ) depthGrade["depthOffset"].setValue( -0.9 ) offset2 = GafferImage.Offset() offset2["in"].setInput( representativeImage["out"] ) offset2["offset"].setValue( imath.V2i( -44, -46 ) ) depthGrade2 = self.__createDepthGrade() depthGrade2["in"].setInput( offset2["out"] ) depthGrade2["depthOffset"].setValue( -1.5 ) deepMerge = GafferImage.DeepMerge() deepMerge["in"][-1].setInput( representativeImage["out"] ) deepMerge["in"][-1].setInput( depthGrade["out"] ) deepMerge["in"][-1].setInput( depthGrade2["out"] ) referenceImage = GafferImage.ImageReader() referenceImage["fileName"].setValue( self.mergeReferencePath ) self.__assertDeepStateProcessing( deepMerge["out"], referenceImage["out"], [ 0.002, 0.002, 0.002, 0.0003 ], [ 0.0001, 0.0001, 0.0001, 0.00003 ], 100, 2 ) firstResult = GafferImage.DeepState() firstResult["in"].setInput( deepMerge["out"] ) firstResult["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) giantDepthOffset = self.__createDepthGrade() giantDepthOffset["in"].setInput( deepMerge["out"] ) giantDepthOffset["depthOffset"].setValue( 100000 ) # A large depth offset means we have insufficient floating point precision to represent # some samples, and some samples will collapse together. This will throw off some pixels, # but most pixels should still be close to correct ( and in particular, the alpha channel is independent # of the order of results, so it's fine ) self.__assertDeepStateProcessing( giantDepthOffset["out"], firstResult["out"], [ 0.5, 0.5, 0.5, 0.000002 ], [ 0.0002, 0.0002, 0.0002, 0.00000003 ], 50, 1 )
def testSampleOffsets(self): ts = GafferImage.ImagePlug.tileSize() constant1 = GafferImage.Constant() constant2 = GafferImage.Constant() constant1["format"].setValue( GafferImage.Format(imath.Box2i(imath.V2i(0), imath.V2i(512)), 1)) constant2["format"].setValue( GafferImage.Format(imath.Box2i(imath.V2i(0), imath.V2i(512)), 1)) merge = GafferImage.DeepMerge() merge["in"][0].setInput(constant1["out"]) merge["in"][1].setInput(constant2["out"]) expectedSampleOffsets = IECore.IntVectorData( range(2, ts * ts * 2 + 1, 2)) actualSampleOffsets = merge["out"].sampleOffsets(imath.V2i(0)) self.assertEqual(actualSampleOffsets, expectedSampleOffsets)
def testFlattenedWrite( self ) : c1 = GafferImage.Constant() c1['format'].setValue( GafferImage.Format( 512, 512 ) ) c2 = GafferImage.Constant() c2['format'].setValue( GafferImage.Format( 512, 512 ) ) m = GafferImage.DeepMerge() m['in'][0].setInput( c1["out"] ) m['in'][1].setInput( c2["out"] ) iState = GafferImage.DeepState() iState['in'].setInput( m['out'] ) iState['deepState'].setValue( GafferImage.DeepState.TargetState.Flat ) testFile = self.temporaryDirectory() + "/test.Flat.exr" self.assertFalse( os.path.exists( testFile ) ) w = GafferImage.ImageWriter() w['in'].setInput( iState["out"] ) w["fileName"].setValue( testFile ) with Gaffer.Context() : w.execute()
def testBasics(self): representativeImage = GafferImage.ImageReader() representativeImage["fileName"].setValue(self.representativeImagePath) offset = GafferImage.Offset() offset["in"].setInput(representativeImage["out"]) holdout = GafferImage.DeepHoldout() holdout["in"].setInput(representativeImage["out"]) holdout["holdout"].setInput(offset["out"]) flat = GafferImage.DeepToFlat() flat["in"].setInput(representativeImage["out"]) # For the case of holding out an image by itself, we can find an analytic solution for the # held out alpha. For a composited alpha value A, the held out alpha will be ( 1 - ( 1 - A )^2 ) / 2 # Check that this relationship holds alphaOnlyHoldout = GafferImage.DeleteChannels() alphaOnlyHoldout["in"].setInput(holdout["out"]) alphaOnlyHoldout["mode"].setValue(GafferImage.DeleteChannels.Mode.Keep) alphaOnlyHoldout["channels"].setValue('[A]') complementAndSquare = GafferImage.Grade() complementAndSquare["in"].setInput(flat["out"]) complementAndSquare["channels"].setValue('[A]') complementAndSquare["multiply"].setValue(imath.Color4f(1, 1, 1, -1)) complementAndSquare["offset"].setValue(imath.Color4f(0, 0, 0, 1)) complementAndSquare["gamma"].setValue(imath.Color4f(1, 1, 1, 0.5)) complementAndHalve = GafferImage.Grade() complementAndHalve["in"].setInput(complementAndSquare["out"]) complementAndHalve["channels"].setValue('[A]') complementAndHalve["multiply"].setValue(imath.Color4f(1, 1, 1, -0.5)) complementAndHalve["offset"].setValue(imath.Color4f(0, 0, 0, 0.5)) alphaOnlyReference = GafferImage.DeleteChannels() alphaOnlyReference["in"].setInput(complementAndHalve["out"]) alphaOnlyReference["mode"].setValue( GafferImage.DeleteChannels.Mode.Keep) alphaOnlyReference["channels"].setValue('[A]') self.assertImagesEqual(alphaOnlyHoldout["out"], alphaOnlyReference["out"], maxDifference=1e-6) # For a more complex holdout, we can create a comparison manually using shuffles and a DeepMerge preShuffle = GafferImage.Shuffle() preShuffle["in"].setInput(representativeImage["out"]) preShuffle["channels"].addChild(preShuffle.ChannelPlug( "holdoutR", "R")) preShuffle["channels"].addChild(preShuffle.ChannelPlug( "holdoutG", "G")) preShuffle["channels"].addChild(preShuffle.ChannelPlug( "holdoutB", "B")) preShuffle["channels"].addChild(preShuffle.ChannelPlug( "holdoutA", "A")) manualHoldoutMerge = GafferImage.DeepMerge() manualHoldoutMerge["in"][0].setInput(preShuffle["out"]) manualHoldoutMerge["in"][1].setInput(offset["out"]) manualHoldoutFlatten = GafferImage.DeepToFlat() manualHoldoutFlatten["in"].setInput(manualHoldoutMerge["out"]) postShuffle = GafferImage.Shuffle() postShuffle["in"].setInput(manualHoldoutFlatten["out"]) postShuffle["channels"].addChild( postShuffle.ChannelPlug("R", "holdoutR")) postShuffle["channels"].addChild( postShuffle.ChannelPlug("G", "holdoutG")) postShuffle["channels"].addChild( postShuffle.ChannelPlug("B", "holdoutB")) postShuffle["channels"].addChild( postShuffle.ChannelPlug("A", "holdoutA")) channelCleanup = GafferImage.DeleteChannels() channelCleanup["in"].setInput(postShuffle["out"]) channelCleanup["mode"].setValue(GafferImage.DeleteChannels.Mode.Keep) channelCleanup["channels"].setValue('[RGBAZ]') cropCleanup = GafferImage.Crop() cropCleanup["in"].setInput(channelCleanup["out"]) cropCleanup["area"].setValue( imath.Box2i(imath.V2i(0, 0), imath.V2i(150, 100))) self.assertImagesEqual(holdout["out"], cropCleanup["out"], maxDifference=1e-5) # The way we handle Z is a bit arbitrary, but everything else we should be able to match with # this network with arbitrary inputs holdoutNoZ = GafferImage.DeleteChannels() holdoutNoZ["in"].setInput(holdout["out"]) holdoutNoZ["channels"].setValue('Z') channelCleanup["channels"].setValue('[RGBA]') offset["offset"].setValue(imath.V2i(13, 31)) self.assertImagesEqual(holdoutNoZ["out"], cropCleanup["out"], maxDifference=1e-5) offset["offset"].setValue(imath.V2i(-13, -51)) self.assertImagesEqual(holdoutNoZ["out"], cropCleanup["out"], maxDifference=1e-5) offset["offset"].setValue(imath.V2i(103, -27)) self.assertImagesEqual(holdoutNoZ["out"], cropCleanup["out"], maxDifference=1e-5)
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 testDeepMix(self): representativeDeepImage = GafferImage.ImageReader() representativeDeepImage["fileName"].setValue( self.representativeDeepImagePath) # Easier to compare colors if we clamp - this requires unpremulting unpremultiply = GafferImage.Unpremultiply() unpremultiply["in"].setInput(representativeDeepImage["out"]) clamp = GafferImage.Grade() clamp["in"].setInput(unpremultiply["out"]) clamp["whiteClamp"].setValue(True) premultiply = GafferImage.Premultiply() premultiply["in"].setInput(clamp["out"]) # Create a deep image containing a mixture of samples from two offset copies, where # the "offsetted" channel contains a mask showing which of the samples come from # the offsetted copy offset = GafferImage.Offset() offset["in"].setInput(premultiply["out"]) offset["offset"].setValue(imath.V2i(33, -25)) addOffsetMarker = GafferImage.Shuffle() addOffsetMarker["channels"].addChild( GafferImage.Shuffle.ChannelPlug("offsetted", "__white")) addOffsetMarker["in"].setInput(offset["out"]) deepMerge = GafferImage.DeepMerge() deepMerge["in"].addChild( GafferImage.ImagePlug( "in2", flags=Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, )) deepMerge["in"]["in0"].setInput(addOffsetMarker["out"]) deepMerge["in"]["in1"].setInput(premultiply["out"]) gradeBlack = GafferImage.Grade() gradeBlack["in"].setInput(deepMerge["out"]) gradeBlack["channels"].setValue('[RGBA]') gradeBlack["multiply"].setValue(imath.Color4f(0, 0, 0, 0)) mix = GafferImage.Mix("mix") mix["in"]["in0"].setInput(deepMerge["out"]) mix["in"]["in1"].setInput(gradeBlack["out"]) mix["mask"].setInput(deepMerge["out"]) mix["maskChannel"].setValue('offsetted') mixedFlat = GafferImage.DeepToFlat() mixedFlat["in"].setInput(mix["out"]) mergeRef = GafferImage.DeepToFlat() mergeRef["in"].setInput(deepMerge["out"]) startRef = GafferImage.DeepToFlat() startRef["in"].setInput(premultiply["out"]) startDiff = GafferImage.Merge() startDiff["in"]["in0"].setInput(startRef["out"]) startDiff["in"]["in1"].setInput(mixedFlat["out"]) startDiff["operation"].setValue(GafferImage.Merge.Operation.Difference) startDiffStats = GafferImage.ImageStats("startDiffStats") startDiffStats["in"].setInput(startDiff["out"]) startDiffStats["area"].setValue(mix["out"].dataWindow()) mergedDiff = GafferImage.Merge() mergedDiff["in"]["in0"].setInput(mergeRef["out"]) mergedDiff["in"]["in1"].setInput(mixedFlat["out"]) mergedDiff["operation"].setValue( GafferImage.Merge.Operation.Difference) mergedDiffStats = GafferImage.ImageStats("mergedDiffStats") mergedDiffStats["in"].setInput(mergedDiff["out"]) mergedDiffStats["area"].setValue(mix["out"].dataWindow()) # With mix set to 0, the mix outputs the left input with everything mix["mix"].setValue(0.0) self.assertEqual(mergedDiffStats["average"].getValue(), imath.Color4f(0)) self.assertEqual(mergedDiffStats["max"].getValue(), imath.Color4f(0)) self.assertGreater(startDiffStats["average"].getValue()[3], 0.2) self.assertGreater(startDiffStats["max"].getValue()[3], 0.999) for i in range(3): self.assertGreater(startDiffStats["average"].getValue()[i], 0.1) self.assertGreater(startDiffStats["max"].getValue()[i], 0.8) # With mix set to 1, the mix blends the offsetted samples to 0, and outputs just the non-offset # samples mix["mix"].setValue(1.0) for i in range(4): self.assertAlmostEqual(startDiffStats["average"].getValue()[i], 0) self.assertAlmostEqual(startDiffStats["max"].getValue()[i], 0, places=5) self.assertGreater(mergedDiffStats["average"].getValue()[3], 0.2) self.assertGreater(mergedDiffStats["max"].getValue()[3], 0.999) for i in range(3): self.assertGreater(mergedDiffStats["average"].getValue()[i], 0.1) self.assertGreater(mergedDiffStats["max"].getValue()[i], 0.8) # With the mix in between, the result should be a bit closer to both than the farthest # result at either extreme mix["mix"].setValue(0.75) self.assertLess(startDiffStats["average"].getValue()[3], 0.16) self.assertLess(startDiffStats["max"].getValue()[3], 0.8) self.assertLess(mergedDiffStats["average"].getValue()[3], 0.16) self.assertLess(mergedDiffStats["max"].getValue()[3], 0.8) for i in range(3): self.assertLess(startDiffStats["average"].getValue()[i], 0.08) self.assertLess(startDiffStats["max"].getValue()[i], 0.7) self.assertLess(mergedDiffStats["average"].getValue()[i], 0.08) self.assertLess(mergedDiffStats["max"].getValue()[i], 0.7)
def testMissingChannels( self ) : # Create some messy data representativeImage = GafferImage.ImageReader() representativeImage["fileName"].setValue( self.representativeImagePath ) offset = GafferImage.Offset() offset["in"].setInput( representativeImage["out"] ) offset["offset"].setValue( imath.V2i( 29, 9 ) ) deepMerge = GafferImage.DeepMerge() deepMerge["in"][0].setInput( representativeImage["out"] ) deepMerge["in"][1].setInput( offset["out"] ) referenceFlatten = GafferImage.DeepState() referenceFlatten["in"].setInput( deepMerge["out"] ) referenceFlatten["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) deleteChannels = GafferImage.DeleteChannels() deleteChannels["in"].setInput( deepMerge["out"] ) self.__assertDeepStateProcessing( deleteChannels["out"], referenceFlatten["out"], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], 100, 0.45 ) # Having no ZBack should be equivalent to having ZBack = Z deleteChannels["channels"].setValue( "ZBack" ) referenceNoZBack = GafferImage.Shuffle() referenceNoZBack["in"].setInput( deepMerge["out"] ) referenceNoZBack["channels"].addChild( referenceNoZBack.ChannelPlug( "ZBack", "Z" ) ) referenceFlatten["in"].setInput( referenceNoZBack["out"] ) self.__assertDeepStateProcessing( deleteChannels["out"], referenceFlatten["out"], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], 100, 0.45 ) # Removing A results in all samples just getting summed. deleteChannels["channels"].setValue( "A" ) referenceNoAlphaA = GafferImage.DeleteChannels() referenceNoAlphaA["in"].setInput( representativeImage["out"] ) referenceNoAlphaA["channels"].setValue( "A" ) referenceFlattenA = GafferImage.DeepState() referenceFlattenA["in"].setInput( referenceNoAlphaA["out"] ) referenceFlattenA["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) referenceNoAlphaB = GafferImage.DeleteChannels() referenceNoAlphaB["in"].setInput( offset["out"] ) referenceNoAlphaB["channels"].setValue( "A" ) referenceFlattenB = GafferImage.DeepState() referenceFlattenB["in"].setInput( referenceNoAlphaB["out"] ) referenceFlattenB["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) referenceSum = GafferImage.Merge() referenceSum['operation'].setValue( GafferImage.Merge.Operation.Add ) referenceSum['in'][0].setInput( referenceFlattenA["out"] ) referenceSum['in'][1].setInput( referenceFlattenB["out"] ) self.__assertDeepStateProcessing( deleteChannels["out"], referenceSum["out"], [ 3e-6, 3e-6, 3e-6, 10 ], [ 2e-8, 2e-8, 2e-8, 10 ], 0, 0 ) deleteChannels["channels"].setValue( "A ZBack" ) self.__assertDeepStateProcessing( deleteChannels["out"], referenceSum["out"], [ 3e-6, 3e-6, 3e-6, 10 ], [ 2e-8, 2e-8, 2e-8, 10 ], 0, 0 ) deleteChannels["channels"].setValue( "A Z ZBack" ) self.__assertDeepStateProcessing( deleteChannels["out"], referenceSum["out"], [ 3e-6, 3e-6, 3e-6, 10 ], [ 2e-8, 2e-8, 2e-8, 10 ], 0, 0 ) # Having no Z should be equivalent to having all the samples composited in their current order deleteChannels["channels"].setValue( "Z ZBack" ) try: import GafferOSL except: raise unittest.SkipTest( "Could not load GafferOSL, skipping DeepState missing alpha test" ) outZIndexCode = GafferOSL.OSLCode( "OSLCode" ) outZIndexCode["out"].addChild( Gaffer.FloatPlug( "output1", direction = Gaffer.Plug.Direction.Out ) ) outZIndexCode["code"].setValue( 'output1 = P[2];' ) replaceDepths = GafferOSL.OSLImage( "OSLImage" ) replaceDepths["in"].setInput( deepMerge["out"] ) replaceDepths["channels"].addChild( Gaffer.NameValuePlug( "Z", Gaffer.FloatPlug( "value" ), True ) ) replaceDepths["channels"].addChild( Gaffer.NameValuePlug( "ZBack", Gaffer.FloatPlug( "value" ), True ) ) replaceDepths["channels"][0]["value"].setInput( outZIndexCode["out"]["output1"] ) replaceDepths["channels"][1]["value"].setInput( outZIndexCode["out"]["output1"] ) referenceFlatten["in"].setInput( replaceDepths["out"] ) self.__assertDeepStateProcessing( deleteChannels["out"], referenceFlatten["out"], [ 0, 0, 0, 10 ], [ 0, 0, 0, 10 ], 100, 0.45 ) deleteChannels["channels"].setValue( "[Z]" ) # Removing just Z has the same effect self.__assertDeepStateProcessing( deleteChannels["out"], referenceFlatten["out"], [ 0, 0, 0, 10 ], [ 0, 0, 0, 10 ], 100, 0.45 )
def testMoreArbitraryOffsets( self ) : representativeImage = GafferImage.ImageReader() representativeImage["fileName"].setValue( self.representativeImagePath ) crop = GafferImage.Crop() crop["in"].setInput( representativeImage["out"] ) offset = GafferImage.Offset() offset["in"].setInput( crop["out"] ) depthGrade = self.__createDepthGrade() depthGrade["in"].setInput( offset["out"] ) deepMerge = GafferImage.DeepMerge() deepMerge["in"][-1].setInput( representativeImage["out"] ) deepMerge["in"][-1].setInput( depthGrade["out"] ) firstResult = GafferImage.DeepState() firstResult["in"].setInput( deepMerge["out"] ) firstResult["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) deepMergeBackwards = GafferImage.DeepMerge() deepMergeBackwards["in"][-1].setInput( depthGrade["out"] ) deepMergeBackwards["in"][-1].setInput( representativeImage["out"] ) giantDepthOffset = self.__createDepthGrade() giantDepthOffset["in"].setInput( deepMerge["out"] ) giantDepthOffset["depthOffset"].setValue( 100000 ) flatRepresentative = GafferImage.DeepState() flatRepresentative["in"].setInput( representativeImage["out"] ) flatRepresentative["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) flatCrop = GafferImage.Crop() flatCrop["in"].setInput( flatRepresentative["out"] ) flatCrop["area"].setInput( crop["area"] ) flatOffset = GafferImage.Offset() flatOffset["in"].setInput( flatCrop["out"] ) flatOffset["offset"].setInput( offset["offset"] ) flatOver = GafferImage.Merge() flatOver['operation'].setValue( GafferImage.Merge.Operation.Over ) flatOver['in'][0].setInput( flatRepresentative["out"] ) flatOver['in'][1].setInput( flatOffset["out"] ) for curOffset, curDepthOffset, curCrop in [ [ imath.V2i( 0, 0 ), 0, None ], [ imath.V2i( 1, 1 ), 0.0001, None ], [ imath.V2i( 4, 6 ), 0.2, None ], [ imath.V2i( 10, 14 ), 0.3, None ], [ imath.V2i( 0, 0 ), 0, imath.Box2i( imath.V2i( 11, 17 ), imath.V2i( 101, 93 ) ) ], [ imath.V2i( 10, 14 ), 0.3, imath.Box2i( imath.V2i( 19, 23 ), imath.V2i( 91, 73 ) ) ], ]: offset["offset"].setValue( curOffset ) depthGrade["depthOffset"].setValue( curDepthOffset ) if curCrop: crop["area"].setValue( curCrop ) else: crop["area"].setValue( imath.Box2i( imath.V2i( 0, 0 ), imath.V2i( 150, 100 ) ) ) expectedMaxPrune = 4 if curDepthOffset == 0 or curCrop else 100 expectedAveragePrune = 0.1 if curDepthOffset == 0 or curCrop else 0.45 # Comparing to a flat over isn't very accurate - we need a big tolerance. But we can make sure it # didn't explode, and we can check the alpha accurately. self.__assertDeepStateProcessing( deepMerge["out"], flatOver["out"], [ 8, 8, 8, 0.000003 ], [ 0.09, 0.09, 0.09, 0.0000002 ], expectedMaxPrune, expectedAveragePrune ) # Switching the order we merge in should have no impact self.__assertDeepStateProcessing( deepMergeBackwards["out"], firstResult["out"], [ 0.000002, 0.000002, 0.000002, 0 ], [ 0.00000002, 0.00000002, 0.00000002, 0 ], expectedMaxPrune, expectedAveragePrune ) # As in prev test, this is large enough to cause precision problems, but things should still mostly work self.__assertDeepStateProcessing( giantDepthOffset["out"], firstResult["out"], [ 0.7, 0.7, 0.7, 0.000004 ], [ 0.002, 0.002, 0.002, 0.0000002 ], min( expectedMaxPrune, 20 ), expectedAveragePrune )
def testPracticalTransparencyPrune( self ) : representativeImage = GafferImage.ImageReader() representativeImage["fileName"].setValue( self.representativeImagePath ) empty = GafferImage.Empty() empty["format"].setValue( GafferImage.Format( imath.Box2i( imath.V2i( 0 ), imath.V2i( 150, 100 ) ), 1 ) ) properlyLabelledIn = GafferImage.DeepMerge() properlyLabelledIn["in"][0].setInput( representativeImage["out"] ) properlyLabelledIn["in"][1].setInput( empty["out"] ) # The representative image from Arnold actually contains overlaps one floating point epsilon in width. # Get rid of those, and then we can see the sampleCounts change in a predictable way actuallyTidyIn = GafferImage.DeepState() actuallyTidyIn["in"].setInput( properlyLabelledIn["out"] ) prune = GafferImage.DeepState() prune["in"].setInput( actuallyTidyIn["out"] ) prune["pruneTransparent"].setValue( True ) flatRef = GafferImage.DeepState() flatRef["in"].setInput( actuallyTidyIn["out"] ) flatRef["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) flatPrune = GafferImage.DeepState() flatPrune["in"].setInput( prune["out"] ) flatPrune["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) diff = GafferImage.Merge() diff['operation'].setValue( GafferImage.Merge.Operation.Difference ) diff['in'][0].setInput( flatPrune["out"] ) diff['in'][1].setInput( flatRef["out"] ) diffStats = GafferImage.ImageStats() diffStats["in"].setInput( diff["out"] ) diffStats["area"].setValue( diff["out"].dataWindow() ) origCounts = GafferImage.DeepSampleCounts() origCounts["in"].setInput( actuallyTidyIn["out"] ) prunedCounts = GafferImage.DeepSampleCounts() prunedCounts["in"].setInput( prune["out"] ) diffCounts = GafferImage.Merge() diffCounts["operation"].setValue( GafferImage.Merge.Operation.Subtract ) diffCounts["in"][0].setInput( origCounts["out"] ) diffCounts["in"][1].setInput( prunedCounts["out"] ) diffCountsStats = GafferImage.ImageStats() diffCountsStats["in"].setInput( diffCounts["out"] ) diffCountsStats["area"].setValue( diffCounts["out"].dataWindow() ) self.assertEqual( diffCountsStats["max"].getValue()[0], 0 ) # For some reason, our test data from Arnold has a bunch of transparent pixels to start with, # so we've got some stuff to throw out self.assertLess( diffCountsStats["min"].getValue()[0], -20 ) self.assertLess( diffCountsStats["min"].getValue()[0], -0.26 ) # We've got some moderate error introduced by discarding transparent. Why is it so large? # Looks like those transparent pixels are slightly emissive for i in range( 4 ): self.assertLessEqual( diffStats["max"].getValue()[i], [0.00001,0.00001,0.00001,0][i] ) self.assertGreaterEqual( diffStats["max"].getValue()[i], [0.000001,0.000001,0.000001,0][i] ) # By premulting/unpremulting, we zero out samples with no alpha premultiply = GafferImage.Premultiply() premultiply["in"].setInput( actuallyTidyIn["out"] ) unpremultiply = GafferImage.Unpremultiply() unpremultiply["in"].setInput( premultiply["out"] ) flatRef["in"].setInput( unpremultiply["out"] ) prune["in"].setInput( unpremultiply["out"] ) # That gets us a couple extra digits of matching - this is more like what we would expect based # on floating point precision for i in range( 4 ): self.assertLessEqual( diffStats["max"].getValue()[i], [0.0000005,0.0000005,0.0000005,0][i] ) # But now lets hack it to really make some transparent pixels grade = GafferImage.Grade() grade["in"].setInput( actuallyTidyIn["out"] ) grade["channels"].setValue( '[A]' ) grade["multiply"]["a"].setValue( 1.5 ) grade["offset"]["a"].setValue( -0.5 ) premultiply['in'].setInput( grade["out"] ) # Now we can kill lots of samples self.assertEqual( diffCountsStats["max"].getValue()[0], 0 ) self.assertLess( diffCountsStats["min"].getValue()[0], -200 ) self.assertLess( diffCountsStats["min"].getValue()[0], -10 ) # And the flattened results still match closely for i in range( 4 ): self.assertLessEqual( diffStats["max"].getValue()[i], [0.0000005,0.0000005,0.0000005,0.0000002][i] )
def testChannelData(self): ts = GafferImage.ImagePlug.tileSize() constant1 = GafferImage.Constant() constant1["format"].setValue( GafferImage.Format(imath.Box2i(imath.V2i(0), imath.V2i(512)), 1)) constant1["color"].setValue(imath.Color4f(0.25, 0.5, 1.0, 0.5)) addDepth1 = GafferImage.FlatToDeep() addDepth1["in"].setInput(constant1["out"]) addDepth1["depth"].setValue(10.0) constant2 = GafferImage.Constant() constant2["format"].setValue( GafferImage.Format(imath.Box2i(imath.V2i(0), imath.V2i(512)), 1)) constant2["color"].setValue(imath.Color4f(2.0, 3.0, 4.0, 1.0)) addDepth2 = GafferImage.FlatToDeep() addDepth2["in"].setInput(constant2["out"]) addDepth2["depth"].setValue(20.0) merge = GafferImage.DeepMerge() merge["in"][0].setInput(addDepth1["out"]) merge["in"][1].setInput(addDepth2["out"]) expectedChannelData = {} expectedChannelData["R"] = IECore.FloatVectorData([0.25, 2.0] * ts * ts) expectedChannelData["G"] = IECore.FloatVectorData([0.5, 3.0] * ts * ts) expectedChannelData["B"] = IECore.FloatVectorData([1.0, 4.0] * ts * ts) expectedChannelData["A"] = IECore.FloatVectorData([0.5, 1.0] * ts * ts) expectedChannelData["Z"] = IECore.FloatVectorData([10.0, 20.0] * ts * ts) for channelName in expectedChannelData: actualChannelData = merge["out"].channelData( channelName, imath.V2i(0)) self.assertEqual(actualChannelData, expectedChannelData[channelName]) addDepth1["zBackMode"].setValue( GafferImage.FlatToDeep.ZBackMode.Thickness) addDepth1["thickness"].setValue(2.0) expectedChannelData["ZBack"] = IECore.FloatVectorData([12.0, 20.0] * ts * ts) for channelName in expectedChannelData: actualChannelData = merge["out"].channelData( channelName, imath.V2i(0)) self.assertEqual(actualChannelData, expectedChannelData[channelName]) addDepth2["zBackMode"].setValue( GafferImage.FlatToDeep.ZBackMode.Thickness) addDepth2["thickness"].setValue(0.1) expectedChannelData["ZBack"] = IECore.FloatVectorData([12.0, 20.1] * ts * ts) for channelName in expectedChannelData: actualChannelData = merge["out"].channelData( channelName, imath.V2i(0)) self.assertEqual(actualChannelData, expectedChannelData[channelName])
def __testMergedDifferentDataWindows(self, ensureOverlap=False): ts = GafferImage.ImagePlug.tileSize() tileCount = 2 values1 = { "R": 0.25, "G": 0.5, "B": 1.0, "A": 0.5, "Z": 10.0, "ZBack": 12.0 } values2 = { "R": 2.0, "G": 3.0, "B": 4.0, "A": 1.0, "Z": 20.0, "ZBack": 20.0 } sourceFormat = GafferImage.Format( imath.Box2i(imath.V2i(0), imath.V2i(ts * tileCount)), 1) constant1 = GafferImage.Constant() constant1["format"].setValue(sourceFormat) constant1["color"].setValue( imath.Color4f(values1["R"], values1["G"], values1["B"], values1["A"])) addDepth1 = GafferImage.FlatToDeep() addDepth1["in"].setInput(constant1["out"]) addDepth1["depth"].setValue(values1["Z"]) addDepth1["zBackMode"].setValue( GafferImage.FlatToDeep.ZBackMode.Thickness) addDepth1["thickness"].setValue(values1["ZBack"] - values1["Z"]) constant2 = GafferImage.Constant() constant2["format"].setValue(sourceFormat) constant2["color"].setValue( imath.Color4f(values2["R"], values2["G"], values2["B"], values2["A"])) addDepth2 = GafferImage.FlatToDeep() addDepth2["in"].setInput(constant2["out"]) addDepth2["depth"].setValue(values2["Z"]) addDepth2["zBackMode"].setValue( GafferImage.FlatToDeep.ZBackMode.Thickness) addDepth2["thickness"].setValue(values2["ZBack"] - values2["Z"]) crop1 = GafferImage.Crop() crop1["in"].setInput(addDepth1["out"]) crop1["affectDisplayWindow"].setValue(False) crop2 = GafferImage.Crop() crop2["in"].setInput(addDepth2["out"]) crop2["affectDisplayWindow"].setValue(False) merge = GafferImage.DeepMerge() merge["in"][0].setInput(crop1["out"]) merge["in"][1].setInput(crop2["out"]) for i in range(10): crop1Area = imath.Box2i() crop1Area.extendBy( imath.V2i(int(random.uniform(0, sourceFormat.width())), int(random.uniform(0, sourceFormat.height())))) crop1Area.extendBy( imath.V2i(int(random.uniform(0, sourceFormat.width())), int(random.uniform(0, sourceFormat.height())))) crop2Area = imath.Box2i() crop2Area.extendBy( imath.V2i(int(random.uniform(0, sourceFormat.width())), int(random.uniform(0, sourceFormat.height())))) crop2Area.extendBy( imath.V2i(int(random.uniform(0, sourceFormat.width())), int(random.uniform(0, sourceFormat.height())))) # If we want to ensure that the two crop areas overlap, extend the second one to a random point # within the first one's area if ensureOverlap and not GafferImage.BufferAlgo.intersects( crop1Area, crop2Area): crop2Area.extendBy( imath.V2i( int( random.uniform(crop1Area.min().x, crop1Area.max().x)), int( random.uniform(crop1Area.min().y, crop1Area.max().y)))) crop1["area"].setValue(crop1Area) crop2["area"].setValue(crop2Area) for tileX in range(tileCount): for tileY in range(tileCount): tileOrigin = imath.V2i(tileX * ts, tileY * ts) sampleOffsets = merge["out"].sampleOffsets(tileOrigin) self.assertEqual( sampleOffsets, self.__getExpectedSampleOffsets( tileOrigin, crop1Area, crop2Area)) for channelName in values1.keys(): channelData = merge["out"].channelData( channelName, tileOrigin) self.assertEqual( channelData, self.__getExpectedChannelData( tileOrigin, crop1Area, values1[channelName], crop2Area, values2[channelName]))
def testChannelRequest(self): ts = GafferImage.ImagePlug.tileSize() a = GafferImage.Constant() a["color"].setValue(imath.Color4f(0.1, 0.2, 0.3, 0.4)) addDepthA = GafferImage.FlatToDeep() addDepthA["in"].setInput(a["out"]) addDepthA["depth"].setValue(2.0) addDepthA["zBackMode"].setValue( GafferImage.FlatToDeep.ZBackMode.Thickness) addDepthA["thickness"].setValue(1.0) ad = GafferImage.DeleteChannels() ad["in"].setInput(addDepthA["out"]) ad["mode"].setValue(GafferImage.DeleteChannels.Mode.Delete) ad["channels"].setValue(IECore.StringVectorData(["G", "Z", "ZBack"])) b = GafferImage.Constant() b["color"].setValue(imath.Color4f(0.5, 0.6, 0.7, 0.8)) addDepthB = GafferImage.FlatToDeep() addDepthB["in"].setInput(b["out"]) addDepthB["depth"].setValue(4.0) addDepthB["zBackMode"].setValue( GafferImage.FlatToDeep.ZBackMode.Thickness) addDepthB["thickness"].setValue(1.0) bd = GafferImage.DeleteChannels() bd["in"].setInput(addDepthB["out"]) bd["mode"].setValue(GafferImage.DeleteChannels.Mode.Delete) bd["channels"].setValue(IECore.StringVectorData(["R", "A"])) merge = GafferImage.DeepMerge() merge["in"][0].setInput(ad["out"]) merge["in"][1].setInput(bd["out"]) ad["enabled"].setValue(False) bd["enabled"].setValue(False) expectedChannelData = {} expectedChannelData["R"] = IECore.FloatVectorData([0.1, 0.5] * ts * ts) expectedChannelData["G"] = IECore.FloatVectorData([0.2, 0.6] * ts * ts) expectedChannelData["B"] = IECore.FloatVectorData([0.3, 0.7] * ts * ts) expectedChannelData["A"] = IECore.FloatVectorData([0.4, 0.8] * ts * ts) expectedChannelData["Z"] = IECore.FloatVectorData([2.0, 4.0] * ts * ts) expectedChannelData["ZBack"] = IECore.FloatVectorData([3.0, 5.0] * ts * ts) for channelName in expectedChannelData: actualChannelData = merge["out"].channelData( channelName, imath.V2i(0)) self.assertEqual(actualChannelData, expectedChannelData[channelName]) ad["enabled"].setValue(True) bd["enabled"].setValue(True) expectedChannelData = {} expectedChannelData["R"] = IECore.FloatVectorData([0.1, 0.0] * ts * ts) expectedChannelData["G"] = IECore.FloatVectorData([0.0, 0.6] * ts * ts) expectedChannelData["B"] = IECore.FloatVectorData([0.3, 0.7] * ts * ts) expectedChannelData["A"] = IECore.FloatVectorData([0.4, 0.0] * ts * ts) expectedChannelData["Z"] = IECore.FloatVectorData([0.0, 4.0] * ts * ts) expectedChannelData["ZBack"] = IECore.FloatVectorData([0.0, 5.0] * ts * ts) for channelName in expectedChannelData: actualChannelData = merge["out"].channelData( channelName, imath.V2i(0)) self.assertEqual(actualChannelData, expectedChannelData[channelName])