def testChannelRequest(self): a = GafferImage.Constant() a["color"].setValue(imath.Color4f(0.1, 0.2, 0.3, 0.4)) ad = GafferImage.DeleteChannels() ad["in"].setInput(a["out"]) ad["mode"].setValue(GafferImage.DeleteChannels.Mode.Delete) ad["channels"].setValue("R") b = GafferImage.Constant() b["color"].setValue(imath.Color4f(1.0, 0.3, 0.1, 0.2)) bd = GafferImage.DeleteChannels() bd["in"].setInput(b["out"]) bd["mode"].setValue(GafferImage.DeleteChannels.Mode.Delete) bd["channels"].setValue("G") merge = GafferImage.Merge() merge["in"][0].setInput(ad["out"]) merge["in"][1].setInput(bd["out"]) merge["operation"].setValue(GafferImage.Merge.Operation.Add) sampler = GafferImage.ImageSampler() sampler["image"].setInput(merge["out"]) sampler["pixel"].setValue(imath.V2f(10)) self.assertAlmostEqual(sampler["color"]["r"].getValue(), 0.0 + 1.0) self.assertAlmostEqual(sampler["color"]["g"].getValue(), 0.2 + 0.0) self.assertAlmostEqual(sampler["color"]["b"].getValue(), 0.3 + 0.1) self.assertAlmostEqual(sampler["color"]["a"].getValue(), 0.4 + 0.2)
def testChannelRequest( self ) : a = GafferImage.Constant() a["color"].setValue( IECore.Color4f( 0.1, 0.2, 0.3, 0.4 ) ) ad = GafferImage.DeleteChannels() ad["in"].setInput( a["out"] ) ad["mode"].setValue( GafferImage.DeleteChannels.Mode.Delete ) ad["channels"].setValue( IECore.StringVectorData( [ "R" ] ) ) b = GafferImage.Constant() b["color"].setValue( IECore.Color4f( 1.0, 0.3, 0.1, 0.2 ) ) bd = GafferImage.DeleteChannels() bd["in"].setInput( b["out"] ) bd["mode"].setValue( GafferImage.DeleteChannels.Mode.Delete ) bd["channels"].setValue( IECore.StringVectorData( [ "G" ] ) ) m = GafferImage.Constant() m["color"].setValue( IECore.Color4f( 0.5 ) ) mix = GafferImage.Mix() mix["in"][0].setInput( ad["out"] ) mix["in"][1].setInput( bd["out"] ) mix["mask"].setInput( m["out"] ) sampler = GafferImage.ImageSampler() sampler["image"].setInput( mix["out"] ) sampler["pixel"].setValue( IECore.V2f( 10 ) ) self.assertAlmostEqual( sampler["color"]["r"].getValue(), ( 0.0 + 1.0 ) * 0.5 ) self.assertAlmostEqual( sampler["color"]["g"].getValue(), ( 0.2 + 0.0 ) * 0.5 ) self.assertAlmostEqual( sampler["color"]["b"].getValue(), ( 0.3 + 0.1 ) * 0.5 ) self.assertAlmostEqual( sampler["color"]["a"].getValue(), ( 0.4 + 0.2 ) * 0.5 )
def testDriverChannel(self): rRaw = GafferImage.ImageReader() rRaw["fileName"].setValue( os.path.dirname(__file__) + "/images/circles.exr") r = GafferImage.Grade() r["in"].setInput(rRaw["out"]) # Trim off the noise in the blacks so that areas with no visible color are actually flat r["blackPoint"].setValue(imath.Color4f(0.03)) masterMedian = GafferImage.Median() masterMedian["in"].setInput(r["out"]) masterMedian["radius"].setValue(imath.V2i(2)) masterMedian["masterChannel"].setValue("G") expected = GafferImage.ImageReader() expected["fileName"].setValue( os.path.dirname(__file__) + "/images/circlesGreenMedian.exr") # Note that in this expected image, the green channel is nicely medianed, and red and blue are completely # unchanged in areas where there is no green. In areas where red and blue overlap with a noisy green, # they get a bit scrambled. This is why in practice, you would use something like luminance, rather # than just the green channel self.assertImagesEqual(masterMedian["out"], expected["out"], ignoreMetadata=True, maxDifference=0.0005) defaultMedian = GafferImage.Median() defaultMedian["in"].setInput(r["out"]) defaultMedian["radius"].setValue(imath.V2i(2)) masterMedianSingleChannel = GafferImage.DeleteChannels() masterMedianSingleChannel["in"].setInput(masterMedian["out"]) masterMedianSingleChannel["mode"].setValue( GafferImage.DeleteChannels.Mode.Keep) defaultMedianSingleChannel = GafferImage.DeleteChannels() defaultMedianSingleChannel["in"].setInput(defaultMedian["out"]) defaultMedianSingleChannel["mode"].setValue( GafferImage.DeleteChannels.Mode.Keep) for c in ["R", "G", "B"]: masterMedian["masterChannel"].setValue(c) masterMedianSingleChannel["channels"].setValue(c) defaultMedianSingleChannel["channels"].setValue(c) # When we look at just the channel being used as the master, it matches a default median not using # a master self.assertImagesEqual(masterMedianSingleChannel["out"], defaultMedianSingleChannel["out"])
def testDirtyPropagation(self): r = GafferImage.ImageReader() r["fileName"].setValue(self.checkerFile) c = GafferImage.DeleteChannels() c["in"].setInput(r["out"]) cs = GafferTest.CapturingSlot(c.plugDirtiedSignal()) self.assertEqual(c["mode"].getValue(), GafferImage.DeleteChannels.Mode.Delete) c["mode"].setValue(GafferImage.DeleteChannels.Mode.Keep) dirtiedPlugs = set([x[0].relativeName(x[0].node()) for x in cs]) self.assertEqual(len(dirtiedPlugs), 3) self.assertTrue("mode" in dirtiedPlugs) self.assertTrue("out" in dirtiedPlugs) self.assertTrue("out.channelNames" in dirtiedPlugs) cs = GafferTest.CapturingSlot(c.plugDirtiedSignal()) c["channels"].setValue("R") dirtiedPlugs = set([x[0].relativeName(x[0].node()) for x in cs]) self.assertEqual(len(dirtiedPlugs), 3) self.assertTrue("channels" in dirtiedPlugs) self.assertTrue("out" in dirtiedPlugs) self.assertTrue("out.channelNames" in dirtiedPlugs)
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 testCollaboratePerf(self): # Test an expensive OSLImage, with many output tiles depending on the same input tiles, # which should give TaskCollaborate a chance to show some benefit constant = GafferImage.Constant() constant["format"].setValue(GafferImage.Format(128, 128)) deleteChannels = GafferImage.DeleteChannels("DeleteChannels") deleteChannels["in"].setInput(constant["out"]) deleteChannels["mode"].setValue(GafferImage.DeleteChannels.Mode.Keep) deleteChannels["channels"].setValue('R') mandelbrotCode = self.mandelbrotNode() mandelbrotCode["parameters"]["iterations"].setValue(500000) oslImage = GafferOSL.OSLImage() oslImage["in"].setInput(deleteChannels["out"]) oslImage["channels"].addChild( Gaffer.NameValuePlug("R", Gaffer.FloatPlug("value"), True, "channel")) oslImage["channels"]["channel"]["value"].setInput( mandelbrotCode["out"]["outFloat"]) resize = GafferImage.Resize() resize["in"].setInput(oslImage["out"]) resize["format"].setValue( GafferImage.Format(imath.Box2i(imath.V2i(0), imath.V2i(2048)), 1)) # We use a resize because it pulls the input tiles repeatedly, we don't want to spend time on resizing # pixels, so use a fast filter resize["filter"].setValue('box') with GafferTest.TestRunner.PerformanceScope(): GafferImage.ImageAlgo.image(resize["out"])
def __test(fileName, size, filter): inputFileName = os.path.dirname(__file__) + "/images/" + fileName reader = GafferImage.ImageReader() reader["fileName"].setValue(inputFileName) inSize = reader["out"]["format"].getValue().getDisplayWindow( ).size() inSize = imath.V2f(inSize.x, inSize.y) deleteChannels = GafferImage.DeleteChannels() deleteChannels["mode"].setValue(1) deleteChannels["channels"].setValue(IECore.StringVectorData(['R'])) deleteChannels["in"].setInput(reader["out"]) scale = imath.V2f(size.x, size.y) / inSize resample = GafferImage.Resample() resample["in"].setInput(deleteChannels["out"]) resample["matrix"].setValue(imath.M33f().scale(scale)) resample["filter"].setValue(filter) resample["boundingMode"].setValue( GafferImage.Sampler.BoundingMode.Clamp) crop = GafferImage.Crop() crop["in"].setInput(resample["out"]) crop["area"].setValue(imath.Box2i(imath.V2i(0), size)) borderForFilterWidth = 60 sampleRegion = reader["out"]["dataWindow"].getValue() sampleRegion.setMin(sampleRegion.min() - imath.V2i(borderForFilterWidth)) sampleRegion.setMax(sampleRegion.max() + imath.V2i(borderForFilterWidth)) s = GafferImage.Sampler(reader["out"], "R", sampleRegion, GafferImage.Sampler.BoundingMode.Clamp) resampledS = GafferImage.Sampler( resample["out"], "R", resample["out"]["dataWindow"].getValue()) for y in range(size.y): for x in range(size.x): resampled = resampledS.sample(x, y) self.assertAlmostEqual( resampled, GafferImage.FilterAlgo.sampleBox( s, imath.V2f(x + 0.5, y + 0.5) / scale, max(1.0 / scale[0], 1.0), max(1.0 / scale[1], 1.0), filter), places=5) self.assertAlmostEqual( resampled, GafferImage.FilterAlgo.sampleParallelogram( s, imath.V2f(x + 0.5, y + 0.5) / scale, imath.V2f(1.0 / scale[0], 0), imath.V2f(0, 1.0 / scale[1]), filter), places=5)
def testPassThrough(self): i = GafferImage.ImageReader() i["fileName"].setValue(self.checkerFile) d = GafferImage.DeleteChannels() d["in"].setInput(i["out"]) d["mode"].setValue(d.Mode.Keep) d["channels"].setValue("R") self.assertEqual(i["out"]["format"].hash(), d["out"]["format"].hash()) self.assertEqual(i["out"]["dataWindow"].hash(), d["out"]["dataWindow"].hash()) self.assertEqual(i["out"]["metadata"].hash(), d["out"]["metadata"].hash()) self.assertEqual(i["out"]["format"].getValue(), d["out"]["format"].getValue()) self.assertEqual(i["out"]["dataWindow"].getValue(), d["out"]["dataWindow"].getValue()) self.assertEqual(i["out"]["metadata"].getValue(), d["out"]["metadata"].getValue()) context = Gaffer.Context() context["image:tileOrigin"] = imath.V2i(0) with context: for c in ["G", "B", "A"]: context["image:channelName"] = c self.assertEqual(i["out"]["channelData"].hash(), d["out"]["channelData"].hash()) self.assertEqual(i["out"]["channelData"].getValue(), d["out"]["channelData"].getValue())
def testBlankScanlines( self ) : # create a wide image constant = GafferImage.Constant() constant["color"].setValue( IECore.Color4f( 0.5, 0.5, 0.5, 1 ) ) constant["format"].setValue( GafferImage.Format( IECore.Box2i( IECore.V2i( 0 ), IECore.V2i( 3000, 1080 ) ), 1. ) ) # fit it such that we have several tiles of blank lines on top (and bottom) resize = GafferImage.Resize() resize["in"].setInput( constant["out"] ) resize["fitMode"].setValue( GafferImage.Resize.FitMode.Horizontal ) # write to a file format that requires consecutive scanlines writer = GafferImage.ImageWriter() writer["in"].setInput( resize["out"] ) writer["fileName"].setValue( "{0}/blankScanlines.jpg".format( self.temporaryDirectory() ) ) writer["task"].execute() # ensure we wrote the file successfully reader = GafferImage.ImageReader() reader["fileName"].setInput( writer["fileName"] ) cleanOutput = GafferImage.DeleteChannels() cleanOutput["in"].setInput( writer["in"] ) cleanOutput["channels"].setValue( "A" ) self.assertImagesEqual( reader["out"], cleanOutput["out"], ignoreMetadata=True, ignoreDataWindow=True, maxDifference=0.05 )
def testMultipartRead( self ) : rgbReader = GafferImage.OpenImageIOReader() rgbReader["fileName"].setValue( self.offsetDataWindowFileName ) compareDelete = GafferImage.DeleteChannels() compareDelete["in"].setInput( rgbReader["out"] ) # This test multipart file contains a "rgb" subimage, an "rgba" subimage, and a "depth" subimage, with # one channel named "Z" ( copied from the green channel of our reference image. # It was created using this command: # > oiiotool rgb.100x100.exr --attrib "oiio:subimagename" rgb -ch "R,G,B" rgb.100x100.exr --attrib "oiio:subimagename" rgba rgb.100x100.exr --attrib "oiio:subimagename" depth --ch "G" --chnames "Z" --siappendall -o multipart.exr multipartReader = GafferImage.OpenImageIOReader() multipartReader["fileName"].setValue( self.multipartFileName ) multipartShuffle = GafferImage.Shuffle() multipartShuffle["in"].setInput( multipartReader["out"] ) multipartDelete = GafferImage.DeleteChannels() multipartDelete["in"].setInput( multipartShuffle["out"] ) multipartDelete['channels'].setValue( "*.*" ) self.assertEqual( set( multipartReader["out"]["channelNames"].getValue() ), set([ "rgba.R", "rgba.G", "rgba.B", "rgba.A", "rgb.R", "rgb.G", "rgb.B", "depth.Z" ]) ) multipartShuffle["channels"].clearChildren() multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug( "R", "rgba.R" ) ) multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug( "G", "rgba.G" ) ) multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug( "B", "rgba.B" ) ) multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug( "A", "rgba.A" ) ) self.assertImagesEqual( compareDelete["out"], multipartDelete["out"], ignoreMetadata = True ) multipartShuffle["channels"].clearChildren() multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug( "R", "rgb.R" ) ) multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug( "G", "rgb.G" ) ) multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug( "B", "rgb.B" ) ) compareDelete['channels'].setValue( "A" ) self.assertImagesEqual( compareDelete["out"], multipartDelete["out"], ignoreMetadata = True ) multipartShuffle["channels"].clearChildren() multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug( "G", "depth.Z" ) ) compareDelete['channels'].setValue( "R B A" ) self.assertImagesEqual( compareDelete["out"], multipartDelete["out"], ignoreMetadata = True )
def __init__(self, name="DeleteAlpha"): GafferImage.ImageProcessor.__init__(self, name) self["__deleteChannels"] = GafferImage.DeleteChannels() self["__deleteChannels"]["in"].setInput(self["in"]) self["__deleteChannels"]["enabled"].setInput(self["enabled"]) self["__deleteChannels"]["channels"].setValue("A") self["out"].setInput(self["__deleteChannels"]["out"])
def testModePlug(self): n = GafferImage.DeleteChannels() self.assertEqual(n["mode"].defaultValue(), n.Mode.Delete) self.assertEqual(n["mode"].getValue(), n.Mode.Delete) n["mode"].setValue(n.Mode.Keep) self.assertEqual(n["mode"].getValue(), n.Mode.Keep) self.assertEqual(n["mode"].minValue(), n.Mode.Delete) self.assertEqual(n["mode"].maxValue(), n.Mode.Keep)
def __init__(self, name="DeleteAlpha"): GafferImage.ImageProcessor.__init__(self, name) self["__deleteChannels"] = GafferImage.DeleteChannels() self["__deleteChannels"]["in"].setInput(self["in"]) self["__deleteChannels"]["enabled"].setInput(self["enabled"]) self["__deleteChannels"]["channels"].setValue( IECore.StringVectorData(["A"])) self["out"].setInput(self["__deleteChannels"]["out"])
def testKeepChannels(self): r = GafferImage.ImageReader() r["fileName"].setValue(self.checkerFile) c = GafferImage.DeleteChannels() c["in"].setInput(r["out"]) c["mode"].setValue( GafferImage.DeleteChannels.Mode.Keep) # Keep selected channels c["channels"].setValue("G A") self.assertEqual(c["out"]["channelNames"].getValue(), IECore.StringVectorData(["G", "A"]))
def testChannelExistsBindings( self ) : # Test that both forms of binding to channelExists return the same # value c = GafferImage.Constant() d = GafferImage.DeleteChannels() d["in"].setInput( c["out"] ) d["mode"].setValue( GafferImage.DeleteChannels.Mode.Delete ) d["channels"].setValue( IECore.StringVectorData( [ "R", "A" ] ) ) for chan in [ "R", "G", "B", "A" ] : self.assertEqual( GafferImage.ImageAlgo.channelExists( d["out"], chan ), GafferImage.ImageAlgo.channelExists( d["out"]["channelNames"].getValue(), chan ) )
def testHashChanged(self): r = GafferImage.ImageReader() r["fileName"].setValue(self.checkerFile) c = GafferImage.DeleteChannels() c["in"].setInput(r["out"]) c["mode"].setValue( GafferImage.DeleteChannels.Mode.Keep) # Keep selected channels c["channels"].setValue(" ".join(r["out"]["channelNames"].getValue())) h = c["out"]["channelNames"].hash() c["channels"].setValue("R B") h2 = c["out"]["channelNames"].hash() self.assertNotEqual(h, h2)
def testImage(self): r = GafferImage.ImageReader() r["fileName"].setValue(self.checkerFile) d = GafferImage.DeleteChannels() d["in"].setInput(r["out"]) d["mode"].setValue(d.Mode.Keep) d["channels"].setValue("R") ri = GafferImage.ImageAlgo.image(r["out"]) di = GafferImage.ImageAlgo.image(d["out"]) self.assertEqual(set(ri.keys()), set(["R", "G", "B", "A"])) self.assertEqual(di.keys(), ["R"]) self.assertEqual(di["R"], ri["R"])
def testImage(self): r = GafferImage.ImageReader() r["fileName"].setValue(self.checkerFile) d = GafferImage.DeleteChannels() d["in"].setInput(r["out"]) d["mode"].setValue(d.Mode.Keep) d["channels"].setValue(IECore.StringVectorData(["R"])) ri = r["out"].image() di = d["out"].image() self.assertEqual(set(ri.keys()), set(["R", "G", "B", "A"])) self.assertEqual(di.keys(), ["R"]) self.assertEqual(di["R"].data, ri["R"].data)
def testChannelExists( self ) : c = GafferImage.Constant() d = GafferImage.DeleteChannels() d["in"].setInput( c["out"] ) d["mode"].setValue( GafferImage.DeleteChannels.Mode.Delete ) d["channels"].setValue( IECore.StringVectorData( [] ) ) self.assertTrue( GafferImage.ImageAlgo.channelExists( d["out"], "R" ) ) self.assertTrue( GafferImage.ImageAlgo.channelExists( d["out"], "G" ) ) self.assertTrue( GafferImage.ImageAlgo.channelExists( d["out"], "B" ) ) self.assertTrue( GafferImage.ImageAlgo.channelExists( d["out"], "A" ) ) for chan in [ "R", "G", "B", "A" ] : d["channels"].setValue( IECore.StringVectorData( [ chan ] ) ) self.assertFalse( GafferImage.ImageAlgo.channelExists( d["out"], chan ) )
def testUnsupportedMultipartRead(self): rgbReader = GafferImage.OpenImageIOReader() rgbReader["fileName"].setValue(self.offsetDataWindowFileName) compareShuffle = GafferImage.Shuffle() compareShuffle["in"].setInput(rgbReader["out"]) compareShuffle["channels"].clearChildren() compareShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("rgba.R", "R")) compareShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("rgba.G", "G")) compareShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("rgba.B", "B")) compareShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("rgba.A", "A")) compareDelete = GafferImage.DeleteChannels() compareDelete["in"].setInput(compareShuffle["out"]) compareDelete["channels"].setValue("R G B A") # This test multipart file contains a "rgba" subimage, and a second subimage with a # differing data window. The second part can currently not be loaded, because Gaffer images # have a single data window for the whole image. # # In the future, should we union the data windows? Are subimages with differing data windows common? # This would probably happen with stereo images, but we should probably put work into handling stereo # images differently - with a context variable to control which eye we get, rather than loading everything # as channels. # # It was created using this command: # > oiiotool rgb.100x100.exr --attrib "oiio:subimagename" rgba checkerboard.100x100.exr --attrib "oiio:subimagename" fullDataWindow --siappendall -o unsupportedMultipart.exr multipartReader = GafferImage.OpenImageIOReader() multipartReader["fileName"].setValue(self.unsupportedMultipartFileName) # When we compare to the single part comparison file, the image will come out the same, because # the second part is ignored - and we should get a message about it being ignored with IECore.CapturingMessageHandler() as mh: self.assertImagesEqual(compareDelete["out"], multipartReader["out"], ignoreMetadata=True) self.assertEqual(len(mh.messages), 1) self.assertTrue( mh.messages[0].message.startswith("Ignoring subimage 1 of "))
def testSimpleLayers(self): expected = GafferImage.ImageReader() expected["fileName"].setValue(self.layersPath) constant = GafferImage.Constant() constant["format"].setValue(GafferImage.Format(10, 10, 1.000)) constant["color"].setValue(IECore.Color4f(1, 0, 0, 1)) crop = GafferImage.Crop() crop["affectDisplayWindow"].setValue(False) crop["in"].setInput(constant["out"]) delete = GafferImage.DeleteChannels() delete["channels"].setValue("A") delete["in"].setInput(crop["out"]) collect = GafferImage.CollectImages() collect["rootLayers"].setValue(IECore.StringVectorData(['0', '1', '2'])) collect["in"].setInput(delete["out"]) e = Gaffer.Expression() crop.addChild(e) e.setExpression( inspect.cleandoc(""" layer = context.get( "collect:layerName", None ) if layer: o = IECore.V2i(2, 2 ) * ( 1 + int( layer ) ) area = IECore.Box2i( o, IECore.V2i( 1, 1 ) + o ) else: area = IECore.Box2i( IECore.V2i( 3, 1 ), IECore.V2i( 4, 2 ) ) parent["area"] = area """), "python") copyChannels = GafferImage.CopyChannels() copyChannels["channels"].setValue("*") copyChannels["in"][0].setInput(crop["out"]) copyChannels["in"][1].setInput(collect["out"]) self.assertImagesEqual(copyChannels["out"], expected["out"], ignoreMetadata=True)
def testChannelDataHash(self): r = GafferImage.ImageReader() r["fileName"].setValue(self.checkerFile) d = GafferImage.DeleteChannels() d["in"].setInput(r["out"]) d["mode"].setValue(GafferImage.DeleteChannels.Mode.Delete) d["channels"].setValue("R") # the channels that are passed through should have identical hashes to the input, # so they can share cache entries. self.assertEqual(r["out"].channelDataHash("G", imath.V2i(0)), d["out"].channelDataHash("G", imath.V2i(0))) self.assertEqual(r["out"].channelDataHash("B", imath.V2i(0)), d["out"].channelDataHash("B", imath.V2i(0))) self.assertEqual(r["out"].channelDataHash("A", imath.V2i(0)), d["out"].channelDataHash("A", imath.V2i(0)))
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 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])
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 testMultipartRead(self): rgbReader = GafferImage.OpenImageIOReader() rgbReader["fileName"].setValue(self.offsetDataWindowFileName) compareDelete = GafferImage.DeleteChannels() compareDelete["in"].setInput(rgbReader["out"]) # This test multipart file contains a "customRgb" subimage, a "customRgba" subimage, # and a "customDepth" subimage, with one channel named "Z" ( copied from the green # channel of our reference image. ) # We don't use the subimage names "rgb", "rgba" or "depth", because we want to look # at channels which don't get automatically mapped to the default channel names. # ( see testDefaultChannelsMultipartRead for that ) # The test file was created using this command: # > oiiotool rgb.100x100.exr --attrib "oiio:subimagename" customRgb -ch "R,G,B" rgb.100x100.exr --attrib "oiio:subimagename" customRgba rgb.100x100.exr --attrib "oiio:subimagename" customDepth --ch "G" --chnames "Z" --siappendall -o multipart.exr multipartReader = GafferImage.OpenImageIOReader() multipartReader["fileName"].setValue(self.multipartFileName) multipartShuffle = GafferImage.Shuffle() multipartShuffle["in"].setInput(multipartReader["out"]) multipartDelete = GafferImage.DeleteChannels() multipartDelete["in"].setInput(multipartShuffle["out"]) multipartDelete['channels'].setValue("*.*") self.assertEqual( set(multipartReader["out"]["channelNames"].getValue()), set([ "customRgba.R", "customRgba.G", "customRgba.B", "customRgba.A", "customRgb.R", "customRgb.G", "customRgb.B", "customDepth.Z" ])) multipartShuffle["channels"].clearChildren() multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("R", "customRgba.R")) multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("G", "customRgba.G")) multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("B", "customRgba.B")) multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("A", "customRgba.A")) self.assertImagesEqual(compareDelete["out"], multipartDelete["out"], ignoreMetadata=True) multipartShuffle["channels"].clearChildren() multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("R", "customRgb.R")) multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("G", "customRgb.G")) multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("B", "customRgb.B")) compareDelete['channels'].setValue("A") self.assertImagesEqual(compareDelete["out"], multipartDelete["out"], ignoreMetadata=True) multipartShuffle["channels"].clearChildren() multipartShuffle["channels"].addChild( GafferImage.Shuffle.ChannelPlug("G", "customDepth.Z")) compareDelete['channels'].setValue("R B A") self.assertImagesEqual(compareDelete["out"], multipartDelete["out"], ignoreMetadata=True)
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 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 __test( fileName, size, filter ) : inputFileName = os.path.dirname( __file__ ) + "/images/" + fileName reader = GafferImage.ImageReader() reader["fileName"].setValue( inputFileName ) inSize = reader["out"]["format"].getValue().getDisplayWindow().size() inSize = IECore.V2f( inSize.x, inSize.y ) deleteChannels = GafferImage.DeleteChannels() deleteChannels["mode"].setValue( 1 ) deleteChannels["channels"].setValue( IECore.StringVectorData( [ 'R' ] ) ) deleteChannels["in"].setInput( reader["out"] ) scale = IECore.V2f( size.x, size.y ) / inSize resample = GafferImage.Resample() resample["in"].setInput( deleteChannels["out"] ) resample["matrix"].setValue( IECore.M33f().scale( scale ) ) resample["filter"].setValue( filter ) resample["boundingMode"].setValue( GafferImage.Sampler.BoundingMode.Clamp ) crop = GafferImage.Crop() crop["in"].setInput( resample["out"] ) crop["area"].setValue( IECore.Box2i( IECore.V2i( 0 ), size ) ) borderForFilterWidth = 60 sampleRegion = reader["out"]["dataWindow"].getValue() sampleRegion.min -= IECore.V2i( borderForFilterWidth ) sampleRegion.max += IECore.V2i( borderForFilterWidth ) s = GafferImage.Sampler( reader["out"], "R", sampleRegion, GafferImage.Sampler.BoundingMode.Clamp ) w = IECore.Box2i( IECore.V2i( 0 ), size - IECore.V2i( 1 ) ) boxImage = IECore.ImagePrimitive( w, w ) parallelImage = IECore.ImagePrimitive( w, w ) boxR = IECore.FloatVectorData( size.x * size.y ) parallelR = IECore.FloatVectorData( size.x * size.y ) for y in range( size.y ): for x in range( size.x ): boxR[ ( size.y - 1 - y ) * size.x + x ] = GafferImage.FilterAlgo.sampleBox( s, IECore.V2f( x + 0.5, y + 0.5 ) / scale, max( 1.0 / scale[0], 1.0 ), max( 1.0 / scale[1], 1.0 ), filter ) parallelR[ ( size.y - 1 - y ) * size.x + x ] = GafferImage.FilterAlgo.sampleParallelogram( s, IECore.V2f( x + 0.5, y + 0.5 ) / scale, IECore.V2f( 1.0 / scale[0], 0), IECore.V2f( 0, 1.0 / scale[1]), filter ) boxImage["R"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, boxR ) parallelImage["R"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, parallelR ) # Enable to write out images for visual comparison if False: tempDirectory = "/tmp" expectedFileName = tempDirectory + "/%s_%dx%d_%s_expected.exr" % ( os.path.splitext( fileName )[0], size.x, size.y, filter ) expectedWriter = GafferImage.ImageWriter() expectedWriter["in"].setInput( crop["out"] ) expectedWriter["fileName"].setValue( expectedFileName ) expectedWriter["task"].execute() outputFileName = tempDirectory + "/%s_%dx%d_%s.box.exr" % ( os.path.splitext( fileName )[0], size.x, size.y, filter ) IECore.Writer.create( boxImage, outputFileName ).write() outputFileName = tempDirectory + "/%s_%dx%d_%s.parallel.exr" % ( os.path.splitext( fileName )[0], size.x, size.y, filter ) IECore.Writer.create( parallelImage, outputFileName ).write() imageNode = GafferImage.ObjectToImage() imageNode["object"].setValue( boxImage ) self.assertImagesEqual( crop["out"], imageNode["out"], maxDifference = 0.000011, ignoreMetadata = True ) imageNode["object"].setValue( parallelImage ) self.assertImagesEqual( crop["out"], imageNode["out"], maxDifference = 0.000011, ignoreMetadata = True )