def testSeparableCodePath(self): # Make sure that there is no inordinate difference between the special optimized code # when rotation is 0 and the general code checker1 = GafferImage.Checkerboard() checker1["size"].setValue(imath.V2f(35, 121)) checker1["transform"]["translate"].setValue( imath.V2f(214.849503, -199.025101)) checker1["transform"]["scale"].setValue( imath.V2f(7.1413002, 0.640600026)) checker2 = GafferImage.Checkerboard() checker2["size"].setValue(imath.V2f(35, 121)) checker2["transform"]["translate"].setValue( imath.V2f(214.849503, -199.025101)) checker2["transform"]["scale"].setValue( imath.V2f(7.1413002, 0.640600026)) checker2["transform"]["rotate"].setValue(1e-6) diff = GafferImage.Merge() diff["in"][0].setInput(checker2["out"]) diff["in"][1].setInput(checker1["out"]) diff["operation"].setValue(GafferImage.Merge.Operation.Difference) stats = GafferImage.ImageStats() stats["in"].setInput(diff["out"]) stats["area"].setValue( imath.Box2i(imath.V2i(0, 0), imath.V2i(1920, 1080))) self.assertGreater(stats["max"].getValue()[0], 0) # Should produce some change self.assertLess(stats["max"].getValue()[0], 1e-4) # But nothing visible self.assertLess(stats["average"].getValue()[0], 1e-6)
def testLayerAffectsChannelNames(self): c = GafferImage.Checkerboard() cs = GafferTest.CapturingSlot(c.plugDirtiedSignal()) c["layer"].setValue("diffuse") self.assertTrue(c["out"]["channelNames"] in set([x[0] for x in cs]))
def testFormatDependencies(self): c = GafferImage.Checkerboard() self.assertEqual( c.affects(c["format"]["pixelAspect"]), [c["out"]["format"]], ) self.assertEqual(c.affects(c["format"]["displayWindow"]["min"]["x"]), [c["out"]["format"], c["out"]["dataWindow"]]) self.assertEqual( c.affects(c["colorA"]["r"]), [c["out"]["channelData"]], ) self.assertEqual( c.affects(c["colorB"]["r"]), [c["out"]["channelData"]], ) self.assertEqual( c.affects(c["size"]["x"]), [c["out"]["channelData"]], )
def mergePerf(self, operation, mismatch): r = GafferImage.Checkerboard("Checkerboard") r["format"].setValue(GafferImage.Format(4096, 3112, 1.000)) # Make the size of the checkerboard not a perfect multiple of tile size # in case we ever fix Checkerboard to notice when tiles are repeated # and return an identical hash ( which would invalidate this performance # test ) r["size"].setValue(imath.V2f(64.01)) alphaShuffle = GafferImage.Shuffle() alphaShuffle["in"].setInput(r["out"]) alphaShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("A", "R")) transform = GafferImage.Offset() transform["in"].setInput(alphaShuffle["out"]) if mismatch: transform["offset"].setValue(imath.V2i(4000, 3000)) else: transform["offset"].setValue(imath.V2i(26, 42)) merge = GafferImage.Merge() merge["operation"].setValue(operation) merge["in"][0].setInput(alphaShuffle["out"]) merge["in"][1].setInput(transform["out"]) # Precache upstream network, we're only interested in the performance of Merge GafferImageTest.processTiles(alphaShuffle["out"]) GafferImageTest.processTiles(transform["out"]) with GafferTest.TestRunner.PerformanceScope(): GafferImageTest.processTiles(merge["out"])
def testGlobalConvenienceMethods(self): checker = GafferImage.Checkerboard() metadata = GafferImage.ImageMetadata() metadata["in"].setInput(checker["out"]) metadata["metadata"].addChild(Gaffer.NameValuePlug("test", 10)) self.assertEqual(metadata["out"].format(), metadata["out"]["format"].getValue()) self.assertEqual(metadata["out"].formatHash(), metadata["out"]["format"].hash()) self.assertEqual(metadata["out"].dataWindow(), metadata["out"]["dataWindow"].getValue()) self.assertEqual(metadata["out"].dataWindowHash(), metadata["out"]["dataWindow"].hash()) self.assertEqual(metadata["out"].channelNames(), metadata["out"]["channelNames"].getValue()) self.assertFalse(metadata["out"].channelNames().isSame( metadata["out"]["channelNames"].getValue(_copy=False))) self.assertTrue(metadata["out"].channelNames(_copy=False).isSame( metadata["out"]["channelNames"].getValue(_copy=False))) self.assertEqual(metadata["out"].channelNamesHash(), metadata["out"]["channelNames"].hash()) self.assertEqual(metadata["out"].metadata(), metadata["out"]["metadata"].getValue()) self.assertFalse(metadata["out"].metadata().isSame( metadata["out"]["metadata"].getValue(_copy=False))) self.assertTrue(metadata["out"].metadata(_copy=False).isSame( metadata["out"]["metadata"].getValue(_copy=False))) self.assertEqual(metadata["out"].metadataHash(), metadata["out"]["metadata"].hash())
def testChannelNamesHash(self): c = GafferImage.Checkerboard() h1 = c["out"]["channelNames"].hash() c["colorA"].setValue(imath.Color4f(1, 0.5, 0.25, 1)) h2 = c["out"]["channelNames"].hash() self.assertEqual(h1, h2)
def testEnableBehaviour(self): c = GafferImage.Checkerboard() self.assertTrue(c.enabledPlug().isSame(c["enabled"])) self.assertEqual(c.correspondingInput(c["out"]), None) self.assertEqual(c.correspondingInput(c["colorA"]), None) self.assertEqual(c.correspondingInput(c["colorB"]), None) self.assertEqual(c.correspondingInput(c["size"]), None) self.assertEqual(c.correspondingInput(c["format"]), None)
def testFormatHash(self): # Check that the data hash change when the format does. c = GafferImage.Checkerboard() c["format"].setValue(GafferImage.Format(2048, 1156, 1.)) h1 = c["out"].channelData("R", imath.V2i(0)).hash() c["format"].setValue(GafferImage.Format(1920, 1080, 1.)) h2 = c["out"].channelData("R", imath.V2i(0)).hash() self.assertEqual(h1, h2)
def testSerialisationWithZeroAlpha(self): s = Gaffer.ScriptNode() s["c"] = GafferImage.Checkerboard() s["c"]["colorA"].setValue(imath.Color4f(0, 1, 0, 0)) s2 = Gaffer.ScriptNode() s2.execute(s.serialise()) self.assertEqual(s2["c"]["colorA"].getValue(), imath.Color4f(0, 1, 0, 0))
def testTranslationPerformance(self): checker = GafferImage.Checkerboard() checker["format"].setValue(GafferImage.Format(3000, 3000)) transform = GafferImage.ImageTransform() transform["in"].setInput(checker["out"]) transform["transform"]["translate"].setValue(imath.V2f(2.2)) with GafferTest.TestRunner.PerformanceScope(): GafferImageTest.processTiles(transform["out"])
def testUpsizingPerformance(self): checker = GafferImage.Checkerboard() checker["format"].setValue(GafferImage.Format(1000, 1000)) transform = GafferImage.ImageTransform() transform["in"].setInput(checker["out"]) transform["transform"]["scale"].setValue(imath.V2f(3)) with GafferTest.TestRunner.PerformanceScope(): GafferImageTest.processTiles(transform["out"])
def testRotationAndScalingPerformance(self): checker = GafferImage.Checkerboard() checker["format"].setValue(GafferImage.Format(3000, 3000)) transform = GafferImage.ImageTransform() transform["in"].setInput(checker["out"]) transform["transform"]["pivot"].setValue(imath.V2f(1500)) transform["transform"]["rotate"].setValue(2.5) transform["transform"]["scale"].setValue(imath.V2f(0.75)) with GafferTest.TestRunner.PerformanceScope(): GafferImageTest.processTiles(transform["out"])
def testFilterWidth(self): checkerboard = GafferImage.Checkerboard() checkerboard["format"].setValue(GafferImage.Format(128, 128)) checkerboard["size"]["x"].setValue(1) checkerboard["size"]["y"].setValue(1) checkerboard["transform"]["scale"]["x"].setValue(200) checkerboard["transform"]["scale"]["y"].setValue(200) sampler = GafferImage.ImageSampler() sampler["image"].setInput(checkerboard["out"]) sampler["pixel"].setValue(imath.V2f(101, 54)) self.assertAlmostEqual(checkerboard["colorB"].getValue().r, sampler["color"].getValue().r, delta=0.001)
def testConcatenationPerformance1(self): checker = GafferImage.Checkerboard() checker["format"].setValue(GafferImage.Format(3000, 3000)) transform1 = GafferImage.ImageTransform("Transform1") transform1["in"].setInput(checker["out"]) transform1["transform"]["pivot"].setValue(imath.V2f(1500)) transform1["transform"]["rotate"].setValue(2.5) transform2 = GafferImage.ImageTransform("Transform2") transform2["in"].setInput(transform1["out"]) transform2["transform"]["translate"].setValue(imath.V2f(10)) with GafferTest.TestRunner.PerformanceScope(): GafferImageTest.processTiles(transform2["out"])
def testMonitorParallelProcessTiles(self): numTilesX = 50 numTilesY = 50 c = GafferImage.Checkerboard() c["format"].setValue( GafferImage.Format( numTilesX * GafferImage.ImagePlug.tileSize(), numTilesY * GafferImage.ImagePlug.tileSize(), )) with Gaffer.PerformanceMonitor() as m: GafferImageTest.processTiles(c["out"]) self.assertEqual( m.plugStatistics(c["out"]["channelData"]).computeCount, numTilesX * numTilesY * 4)
def testExpectedResult(self): checkerboard = GafferImage.Checkerboard() checkerboard["format"].setValue(GafferImage.Format(128, 128)) checkerboard["size"]["x"].setValue(1) checkerboard["size"]["y"].setValue(1) checkerboard["transform"]["rotate"].setValue(45) checkerboard["transform"]["scale"]["x"].setValue(200) checkerboard["transform"]["scale"]["y"].setValue(10) reader = GafferImage.ImageReader() reader["fileName"].setValue( os.path.dirname(__file__) + "/images/GafferChecker.exr") self.assertImagesEqual(checkerboard["out"], reader["out"], ignoreMetadata=True, maxDifference=0.001)
def testChannelData(self): checkerboard = GafferImage.Checkerboard() checkerboard["format"].setValue( GafferImage.Format(imath.Box2i(imath.V2i(0), imath.V2i(511)), 1)) checkerboard["colorA"].setValue(imath.Color4f(0.1, 0.25, 0.5, 1)) for i, channel in enumerate(["R", "G", "B", "A"]): channelData = checkerboard["out"].channelData( channel, imath.V2i(0)) self.assertEqual( len(channelData), checkerboard["out"].tileSize() * checkerboard["out"].tileSize()) expectedValue = checkerboard["colorA"][i].getValue() s = GafferImage.Sampler( checkerboard["out"], channel, checkerboard["out"]["dataWindow"].getValue()) self.assertEqual(s.sample(12, 12), expectedValue) self.assertEqual(s.sample(72, 72), expectedValue)
def testValuesIdentical(self): checkerboard = GafferImage.Checkerboard() checkerboard["format"].setValue(GafferImage.Format(1024, 1024, 1)) checkerboard["size"].setValue(imath.V2f(14)) offset = GafferImage.Offset() offset["in"].setInput(checkerboard["out"]) offset["offset"].setValue(imath.V2i(7)) merge = GafferImage.Merge() merge["in"][0].setInput(checkerboard["out"]) merge["in"][1].setInput(offset["out"]) merge["operation"].setValue(GafferImage.Merge.Operation.Difference) imageStats = GafferImage.ImageStats() imageStats["in"].setInput(merge["out"]) imageStats["area"].setValue(imath.Box2i(imath.V2i(7), imath.V2i(1024))) self.assertLess(imageStats["max"][0].getValue(), 2e-5) self.assertLess(imageStats["average"][0].getValue(), 5e-7)
def testFilterWidth(self): checkerboard = GafferImage.Checkerboard() checkerboard["format"].setValue(GafferImage.Format(128, 128)) checkerboard["size"]["x"].setValue(1) checkerboard["size"]["y"].setValue(1) checkerboard["transform"]["scale"]["x"].setValue(200) checkerboard["transform"]["scale"]["y"].setValue(200) sampler = GafferImage.ImageSampler() sampler["image"].setInput(checkerboard["out"]) # Pixels on the border could be classified as within the filter width due to floating point precision, # and could have floating point error sampler["pixel"].setValue(imath.V2f(101, 54)) self.assertAlmostEqual(checkerboard["colorB"].getValue().r, sampler["color"].getValue().r, delta=0.0000001) # Pixels on the interior of a checker must be exact sampler["pixel"].setValue(imath.V2f(102, 54)) self.assertEqual(checkerboard["colorB"].getValue().r, sampler["color"].getValue().r)
def testDisabledAndNonConcatenating(self): checker = GafferImage.Checkerboard() checker["format"].setValue(GafferImage.Format(200, 200)) t1 = GafferImage.ImageTransform() t1["in"].setInput(checker["out"]) t1["transform"]["translate"]["x"].setValue(10) t2 = GafferImage.ImageTransform() t2["in"].setInput(t1["out"]) t2["transform"]["translate"]["x"].setValue(10) t3 = GafferImage.ImageTransform() t3["in"].setInput(t2["out"]) t3["transform"]["translate"]["x"].setValue(10) self.assertEqual(t3["out"]["dataWindow"].getValue().min().x, 30) t2["concatenate"].setValue(False) self.assertEqual(t3["out"]["dataWindow"].getValue().min().x, 30) t2["enabled"].setValue(False) self.assertEqual(t3["out"]["dataWindow"].getValue().min().x, 20)
def nodeMenuCreateCommand(menu): checkerboard = GafferImage.Checkerboard() checkerboard["size"].gang() return checkerboard
def testConcatenation(self): # Identical transformation chains, but one # with concatenation broken by a Blur node. # # checker # | # deleteChannels # /\ # / \ # tc1 t1 # | | # tc2 blur # | # t2 checker = GafferImage.Checkerboard() checker["format"].setValue(GafferImage.Format(200, 200)) deleteChannels = GafferImage.DeleteChannels() deleteChannels["in"].setInput(checker["out"]) deleteChannels["channels"].setValue("A") tc1 = GafferImage.ImageTransform() tc1["in"].setInput(deleteChannels["out"]) tc1["filter"].setValue("gaussian") tc2 = GafferImage.ImageTransform() tc2["in"].setInput(tc1["out"]) tc2["filter"].setInput(tc1["filter"]) t1 = GafferImage.ImageTransform() t1["in"].setInput(deleteChannels["out"]) t1["transform"].setInput(tc1["transform"]) t1["filter"].setInput(tc1["filter"]) blur = GafferImage.Blur() blur["in"].setInput(t1["out"]) t2 = GafferImage.ImageTransform() t2["in"].setInput(blur["out"]) t2["transform"].setInput(tc2["transform"]) t2["filter"].setInput(tc1["filter"]) # The blur doesn't do anything except # break concatentation. Check that tc2 # is practically identical to t2 for # a range of transforms. for i in range(0, 10): random.seed(i) translate1 = imath.V2f(random.uniform(-100, 100), random.uniform(-100, 100)) rotate1 = random.uniform(-360, 360) scale1 = imath.V2f(random.uniform(-2, 2), random.uniform(-2, 2)) tc1["transform"]["translate"].setValue(translate1) tc1["transform"]["rotate"].setValue(rotate1) tc1["transform"]["scale"].setValue(scale1) translate2 = imath.V2f(random.uniform(-100, 100), random.uniform(-100, 100)) rotate2 = random.uniform(-360, 360) scale2 = imath.V2f(random.uniform(-2, 2), random.uniform(-2, 2)) tc2["transform"]["translate"].setValue(translate2) tc2["transform"]["rotate"].setValue(rotate2) tc2["transform"]["scale"].setValue(scale2) # The `maxDifference` here is surprisingly high, but visual checks # show that it is legitimate : differences in filtering are that great. # The threshold is still significantly lower than the differences between # checker tiles, so does guarantee that tiles aren't getting out of alignment. self.assertImagesEqual(tc2["out"], t2["out"], maxDifference=0.11, ignoreDataWindow=True)
def testPassthroughs(self): ts = GafferImage.ImagePlug.tileSize() checkerboardB = GafferImage.Checkerboard() checkerboardB["format"]["displayWindow"].setValue( imath.Box2i(imath.V2i(0), imath.V2i(4096))) checkerboardA = GafferImage.Checkerboard() checkerboardA["format"]["displayWindow"].setValue( imath.Box2i(imath.V2i(0), imath.V2i(4096))) checkerboardA["size"].setValue(imath.V2f(5)) cropB = GafferImage.Crop() cropB["in"].setInput(checkerboardB["out"]) cropB["area"].setValue( imath.Box2i(imath.V2i(ts * 0.5), imath.V2i(ts * 4.5))) cropB["affectDisplayWindow"].setValue(False) cropA = GafferImage.Crop() cropA["in"].setInput(checkerboardA["out"]) cropA["area"].setValue( imath.Box2i(imath.V2i(ts * 2.5), imath.V2i(ts * 6.5))) cropA["affectDisplayWindow"].setValue(False) merge = GafferImage.Merge() merge["in"][0].setInput(cropB["out"]) merge["in"][1].setInput(cropA["out"]) merge["operation"].setValue(8) sampleTileOrigins = { "insideBoth": imath.V2i(ts * 3, ts * 3), "outsideBoth": imath.V2i(ts * 5, ts), "outsideEdgeB": imath.V2i(ts, 0), "insideB": imath.V2i(ts, ts), "internalEdgeB": imath.V2i(ts * 4, ts), "internalEdgeA": imath.V2i(ts * 5, ts * 2), "insideA": imath.V2i(ts * 5, ts * 5), "outsideEdgeA": imath.V2i(ts * 6, ts * 5) } for opName, onlyA, onlyB in [("Atop", "black", "passB"), ("Divide", "operate", "black"), ("Out", "passA", "black"), ("Multiply", "black", "black"), ("Over", "passA", "passB"), ("Subtract", "passA", "operate"), ("Difference", "operate", "operate")]: op = getattr(GafferImage.Merge.Operation, opName) merge["operation"].setValue(op) results = {} for name, tileOrigin in sampleTileOrigins.items(): # We want to check the value pass through code independently # of the hash passthrough code, which we can do by dropping # the value cached and evaluating values first Gaffer.ValuePlug.clearCache() with Gaffer.Context() as c: c["image:tileOrigin"] = tileOrigin c["image:channelName"] = "R" data = merge["out"]["channelData"].getValue(_copy=False) if data.isSame( GafferImage.ImagePlug.blackTile(_copy=False)): computeMode = "black" elif data.isSame( cropB["out"]["channelData"].getValue(_copy=False)): computeMode = "passB" elif data.isSame( cropA["out"]["channelData"].getValue(_copy=False)): computeMode = "passA" else: computeMode = "operate" h = merge["out"]["channelData"].hash() if h == GafferImage.ImagePlug.blackTile().hash(): hashMode = "black" elif h == cropB["out"]["channelData"].hash(): hashMode = "passB" elif h == cropA["out"]["channelData"].hash(): hashMode = "passA" else: hashMode = "operate" self.assertEqual(hashMode, computeMode) results[name] = hashMode self.assertEqual(results["insideBoth"], "operate") self.assertEqual(results["outsideBoth"], "black") self.assertEqual(results["outsideEdgeB"], onlyB) self.assertEqual(results["insideB"], onlyB) self.assertEqual(results["outsideEdgeA"], onlyA) self.assertEqual(results["insideA"], onlyA) if onlyA == "black" or onlyB == "black": self.assertEqual(results["internalEdgeB"], onlyB) self.assertEqual(results["internalEdgeA"], onlyA) else: self.assertEqual(results["internalEdgeB"], "operate") self.assertEqual(results["internalEdgeA"], "operate")
def runBoundaryCorrectness(self, scale): testMerge = GafferImage.Merge() subImageNodes = [] for checkSize, col, bound in [ (2, (0.672299981, 0.672299981, 0), ((11, 7), (61, 57))), (4, (0.972599983, 0.493499994, 1), ((9, 5), (59, 55))), (6, (0.310799986, 0.843800008, 1), ((0, 21), (1024, 41))), (8, (0.958999991, 0.672299981, 0.0296), ((22, 0), (42, 1024))), (10, (0.950900018, 0.0899000019, 0.235499993), ((7, 10), (47, 50))), ]: checkerboard = GafferImage.Checkerboard() checkerboard["format"].setValue( GafferImage.Format(1024 * scale, 1024 * scale, 1.000)) checkerboard["size"].setValue(imath.V2f(checkSize * scale)) checkerboard["colorA"].setValue( imath.Color4f(0.1 * col[0], 0.1 * col[1], 0.1 * col[2], 0.3)) checkerboard["colorB"].setValue( imath.Color4f(0.5 * col[0], 0.5 * col[1], 0.5 * col[2], 0.7)) crop = GafferImage.Crop("Crop") crop["in"].setInput(checkerboard["out"]) crop["area"].setValue( imath.Box2i( imath.V2i(scale * bound[0][0], scale * bound[0][1]), imath.V2i(scale * bound[1][0], scale * bound[1][1]))) crop["affectDisplayWindow"].setValue(False) subImageNodes.append(checkerboard) subImageNodes.append(crop) testMerge["in"][-1].setInput(crop["out"]) testMerge["expression"] = Gaffer.Expression() testMerge["expression"].setExpression( 'parent["operation"] = context[ "loop:index" ]') inverseScale = GafferImage.ImageTransform() inverseScale["in"].setInput(testMerge["out"]) inverseScale["filter"].setValue("box") inverseScale["transform"]["scale"].setValue(imath.V2f(1.0 / scale)) crop1 = GafferImage.Crop() crop1["in"].setInput(inverseScale["out"]) crop1["area"].setValue(imath.Box2i(imath.V2i(0, 0), imath.V2i(64, 64))) loopInit = GafferImage.Constant() loopInit["format"].setValue(GafferImage.Format(896, 64, 1.000)) loopInit["color"].setValue(imath.Color4f(0)) loopOffset = GafferImage.Offset() loopOffset["in"].setInput(crop1["out"]) loopOffset["expression"] = Gaffer.Expression() loopOffset["expression"].setExpression( 'parent["offset"]["x"] = 64 * context[ "loop:index" ]') loopMerge = GafferImage.Merge() loopMerge["in"][1].setInput(loopOffset["out"]) loop = Gaffer.Loop() loop.setup(GafferImage.ImagePlug("in", )) loop["iterations"].setValue(14) loop["in"].setInput(loopInit["out"]) loop["next"].setInput(loopMerge["out"]) loopMerge["in"][0].setInput(loop["previous"]) # Uncomment for debug #imageWriter = GafferImage.ImageWriter( "ImageWriter" ) #imageWriter["in"].setInput( loop["out"] ) #imageWriter['openexr']['dataType'].setValue( "float" ) #imageWriter["fileName"].setValue( "/tmp/mergeBoundaries.exr" ) #imageWriter.execute() reader = GafferImage.ImageReader() reader["fileName"].setValue(self.mergeBoundariesRefPath) self.assertImagesEqual(loop["out"], reader["out"], ignoreMetadata=True, maxDifference=1e-5 if scale > 1 else 0)