def __assertDeepStateProcessing( self, deepPlug, flatReferencePlug, refMaxTolerance, refAverageTolerance, expectedMaxPruning, expectedAveragePruning ) : # When testing on complex data, we can test that we are close to our reference # We can also check that the different sequences of computation supported by DeepState # all give identical results. There are different code paths through the node depending on # the input and output state, and by switching between flattening in one step, vs sorting # or tidying before flattening, we can test all these paths. oneStepFlatten = GafferImage.DeepState() oneStepFlatten["in"].setInput( deepPlug ) oneStepFlatten["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) twoStepASort = GafferImage.DeepState() twoStepASort["in"].setInput( deepPlug ) twoStepASort["deepState"].setValue( GafferImage.DeepState.TargetState.Sorted ) twoStepAFlatten = GafferImage.DeepState( "TwoStepAFlatten" ) twoStepAFlatten["in"].setInput( twoStepASort["out"] ) twoStepAFlatten["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) twoStepBTidy = GafferImage.DeepState() twoStepBTidy["in"].setInput( deepPlug ) twoStepBTidy["deepState"].setValue( GafferImage.DeepState.TargetState.Tidy ) twoStepBFlatten = GafferImage.DeepState( "TwoStepBFlatten" ) twoStepBFlatten["in"].setInput( twoStepBTidy["out"] ) twoStepBFlatten["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) threeStepSort = GafferImage.DeepState() threeStepSort["in"].setInput( deepPlug ) threeStepSort["deepState"].setValue( GafferImage.DeepState.TargetState.Sorted ) threeStepTidy = GafferImage.DeepState() threeStepTidy["in"].setInput( threeStepSort["out"] ) threeStepTidy["deepState"].setValue( GafferImage.DeepState.TargetState.Tidy ) threeStepFlatten = GafferImage.DeepState( "ThreeStepFlatten") threeStepFlatten["in"].setInput( threeStepTidy["out"] ) threeStepFlatten["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) pruneOne = GafferImage.DeepState() pruneOne["in"].setInput( deepPlug ) pruneOne["deepState"].setValue( GafferImage.DeepState.TargetState.Tidy ) pruneOne["pruneOccluded"].setValue( True ) pruneOne["occludedThreshold"].setValue( 1.0 ) pruneOneFlatten = GafferImage.DeepState( "PruneOneFlatten" ) pruneOneFlatten["in"].setInput( pruneOne["out"] ) pruneOneFlatten["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) prunePointNine = GafferImage.DeepState() prunePointNine["in"].setInput( deepPlug ) prunePointNine["deepState"].setValue( GafferImage.DeepState.TargetState.Tidy ) prunePointNine["pruneOccluded"].setValue( True ) prunePointNine["occludedThreshold"].setValue( 0.9 ) prunePointNineFlatten = GafferImage.DeepState( "PrunePointNineFlatten" ) prunePointNineFlatten["in"].setInput( prunePointNine["out"] ) prunePointNineFlatten["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) diff = GafferImage.Merge() diff['operation'].setValue( GafferImage.Merge.Operation.Difference ) diff['in'][0].setInput( oneStepFlatten["out"] ) diff['in'][1].setInput( flatReferencePlug ) stats = GafferImage.ImageStats() stats["in"].setInput( diff["out"] ) stats["area"].setValue( diff["out"].dataWindow() ) m = stats["max"].getValue() a = stats["average"].getValue() for i in range( 4 ): self.assertLessEqual( m[i], refMaxTolerance[i], "%s Channel : Max deviation from reference too high" % "RGBA"[i] ) self.assertLessEqual( a[i], refAverageTolerance[i], "%s Channel : Average deviation from reference too high" % "RGBA"[i] ) for method in [ twoStepAFlatten, twoStepBFlatten, threeStepFlatten, pruneOneFlatten, prunePointNineFlatten ]: diff["in"][1].setInput( method["out"] ) if method in [ pruneOneFlatten, prunePointNineFlatten ]: # Pruning does require merging alpha at the back before alpha at the front, so floating point # imprecision in alpha is introduced self.assertLess( stats["max"].getValue(), imath.Color4f( 0.000006, 0.000007, 0.000006, 0.000001 ), "Compute method %s does not match single stage flatten" % method.getName() ) else: # Without pruning, the alpha is processed identically self.assertLess( stats["max"].getValue(), imath.Color4f( 0.000007, 0.000007, 0.000007, 0.0000006 ), "Compute method %s does not match single stage flatten" % method.getName() ) tidyCounts = GafferImage.DeepSampleCounts() tidyCounts["in"].setInput( twoStepBTidy["out"] ) pruneOneCounts = GafferImage.DeepSampleCounts() pruneOneCounts["in"].setInput( pruneOne["out"] ) pruneOneDiff = GafferImage.Merge() pruneOneDiff["operation"].setValue( GafferImage.Merge.Operation.Subtract ) pruneOneDiff["in"][0].setInput( tidyCounts["out"] ) pruneOneDiff["in"][1].setInput( pruneOneCounts["out"] ) pruneOneStats = GafferImage.ImageStats() pruneOneStats["in"].setInput( pruneOneDiff["out"] ) pruneOneStats["area"].setValue( diff["out"].dataWindow() ) prunePointNineCounts = GafferImage.DeepSampleCounts() prunePointNineCounts["in"].setInput( prunePointNine["out"] ) prunePointNineDiff = GafferImage.Merge() prunePointNineDiff["operation"].setValue( GafferImage.Merge.Operation.Subtract ) prunePointNineDiff["in"][0].setInput( tidyCounts["out"] ) prunePointNineDiff["in"][1].setInput( prunePointNineCounts["out"] ) prunePointNineStats = GafferImage.ImageStats() prunePointNineStats["in"].setInput( prunePointNineDiff["out"] ) prunePointNineStats["area"].setValue( diff["out"].dataWindow() ) # All of tests contain some pixels where no samples can be pruned # ( But the number of samples should definitely never increase ) self.assertEqual( pruneOneStats["max"].getValue()[0], 0 ) self.assertEqual( prunePointNineStats["max"].getValue()[0], 0 ) # These are rough heuristics, but make sure we are pruning something, and that the .9 threshold # prunes more. self.assertLessEqual( pruneOneStats["min"].getValue()[0], -expectedMaxPruning ) self.assertLessEqual( prunePointNineStats["min"].getValue()[0], -expectedMaxPruning ) self.assertLessEqual( pruneOneStats["average"].getValue()[0], -expectedAveragePruning ) self.assertLessEqual( prunePointNineStats["average"].getValue()[0], pruneOneStats["average"].getValue()[0] * 2 ) # Assert that we can repeat an operation on data where it has already been applied, and nothing happens resort = GafferImage.DeepState() resort["in"].setInput( threeStepSort["out"] ) resort["deepState"].setValue( GafferImage.DeepState.TargetState.Sorted ) self.assertImagesEqual( resort["out"], threeStepSort["out"] ) reprunePointNine = GafferImage.DeepState() reprunePointNine["in"].setInput( prunePointNine["out"] ) reprunePointNine["deepState"].setValue( GafferImage.DeepState.TargetState.Tidy ) reprunePointNine["pruneOccluded"].setValue( True ) reprunePointNine["occludedThreshold"].setValue( 0.9 ) self.assertImagesEqual( reprunePointNine["out"], prunePointNine["out"] ) reflatten = GafferImage.DeepState() reflatten["in"].setInput( oneStepFlatten["out"] ) reflatten["deepState"].setValue( GafferImage.DeepState.TargetState.Flat ) self.assertImagesEqual( reflatten["out"], oneStepFlatten["out"] )
def testBasics( self ): representativeImage = GafferImage.ImageReader() representativeImage["fileName"].setValue( self.representativeImagePath ) flatImage = GafferImage.ImageReader() flatImage["fileName"].setValue( self.flatImagePath ) rc = GafferImage.DeepRecolor() rc["in"].setInput( representativeImage["out"] ) rc["colorSource"].setInput( flatImage["out"] ) representativeSampleCounts = GafferImage.DeepSampleCounts() representativeSampleCounts["in"].setInput( representativeImage["out"] ) rcSampleCounts = GafferImage.DeepSampleCounts() rcSampleCounts["in"].setInput( rc["out"] ) # Make sure we keep all the deep samples self.assertImagesEqual( representativeSampleCounts["out"], rcSampleCounts["out"] ) rcFlat = GafferImage.DeepToFlat() rcFlat["in"].setInput( rc["out"] ) representativeFlat = GafferImage.DeepToFlat() representativeFlat["in"].setInput( representativeImage["out"] ) unpremult = GafferImage.Unpremultiply() unpremult["in"].setInput( flatImage["out"] ) flatCombine = GafferImage.CopyChannels() flatCombine["in"][0].setInput( representativeFlat["out"] ) flatCombine["in"][1].setInput( unpremult["out"] ) flatCombine["channels"].setValue( "[RGB]" ) premult = GafferImage.Premultiply() premult["in"].setInput( flatCombine["out"] ) # Make sure that the default recolor matches the flat value of premulting by the deep alpha self.assertImagesEqual( rcFlat["out"], premult["out"], maxDifference = 1e-6 ) compare = GafferImage.Merge() compare["in"][0].setInput( rcFlat["out"] ) compare["in"][1].setInput( premult["out"] ) compare["operation"].setValue( GafferImage.Merge.Operation.Difference ) compareStats = GafferImage.ImageStats() compareStats["in"].setInput( compare["out"] ) compareStats["area"].setValue( imath.Box2i( imath.V2i( 0, 0 ), imath.V2i( 150, 100 ) ) ) m = compareStats["max"].getValue() a = compareStats["average"].getValue() for i in range( 4 ): self.assertLessEqual( m[i], 1e-6 ) self.assertLessEqual( a[i], 1e-8 ) rc["useColorSourceAlpha"].setValue( True ) m = compareStats["max"].getValue() a = compareStats["average"].getValue() for i in range( 3 ): self.assertGreater( m[i], 0.4 ) self.assertGreater( a[i], 0.0001 ) # Make sure that using useColorSourceAlpha basically matches the original color source after # flattening. ( It's not exact because of a few pixels with zero samples in the deep which # we can't do anything with ) compare["in"][1].setInput( flatImage["out"] ) m = compareStats["max"].getValue() a = compareStats["average"].getValue() for i in range( 4 ): self.assertLessEqual( m[i], 0.5 ) self.assertLessEqual( a[i], 0.0003 )
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] )