def testMismatchedDataWindow( self ) : # Set up a situation where we're copying channels # from an image with a smaller data window than the # primary input. main = self.__constantLayer( "", IECore.Color4f( 1 ), size = IECore.V2i( 64 ) ) diffuse = self.__constantLayer( "diffuse", IECore.Color4f( 0.5 ), size = IECore.V2i( 60 ) ) copy = GafferImage.CopyChannels() copy["in"][0].setInput( main["out"] ) copy["in"][1].setInput( diffuse["out"] ) copy["channels"].setValue( "*" ) # Format should be taken from the primary input, and data window should be the union # of both input data windows. self.assertEqual( copy["out"]["format"].getValue(), main["out"]["format"].getValue() ) self.assertEqual( copy["out"]["dataWindow"].getValue(), main["out"]["dataWindow"].getValue() ) # And CopyChannels must take care to properly fill in with # black any missing areas from the second input. for channel in ( "R", "G", "B", "A" ) : diffuseDW = diffuse["out"]["dataWindow"].getValue() copyDW = copy["out"]["dataWindow"].getValue() sampler = GafferImage.Sampler( copy["out"], "diffuse." + channel, copyDW ) for x in range( copyDW.min.x, copyDW.max.x ) : for y in range( copyDW.min.y, copyDW.max.y ) : if GafferImage.BufferAlgo.contains( diffuseDW, IECore.V2i( x, y ) ) : self.assertEqual( sampler.sample( x, y ), 0.5 ) else : self.assertEqual( sampler.sample( x, y ), 0 )
def testMultipleLayers(self): main = GafferImage.Constant() main["color"].setValue(imath.Color4f(1, 0.5, 0.25, 1)) diffuse = GafferImage.Constant() diffuse["color"].setValue(imath.Color4f(0.25, 0.5, 0.75, 1)) diffuse["layer"].setValue("diffuse") m = GafferImage.CopyChannels() m["in"][0].setInput(main["out"]) m["in"][1].setInput(diffuse["out"]) m["channels"].setValue("*") cdl = GafferImage.CDL() cdl["in"].setInput(m["out"]) self.assertImagesEqual(cdl["out"], m["out"]) mainCDLSampler = GafferImage.ImageSampler() mainCDLSampler["image"].setInput(cdl["out"]) mainCDLSampler["pixel"].setValue(imath.V2f(0.5)) mainCDLSampler["channels"].setValue( IECore.StringVectorData(["R", "G", "B", "A"])) diffuseCDLSampler = GafferImage.ImageSampler() diffuseCDLSampler["image"].setInput(cdl["out"]) diffuseCDLSampler["pixel"].setValue(imath.V2f(0.5)) diffuseCDLSampler["channels"].setValue( IECore.StringVectorData(["diffuse." + x for x in "RGBA"])) self.assertEqual(mainCDLSampler["color"].getValue(), main["color"].getValue()) self.assertEqual(diffuseCDLSampler["color"].getValue(), diffuse["color"].getValue()) cdl["saturation"].setValue(0.5) self.assertNotEqual(mainCDLSampler["color"].getValue(), main["color"].getValue()) self.assertEqual(diffuseCDLSampler["color"].getValue(), diffuse["color"].getValue()) cdl["channels"].setValue("*[RGB]") self.assertNotEqual(mainCDLSampler["color"].getValue(), main["color"].getValue()) self.assertNotEqual(diffuseCDLSampler["color"].getValue(), diffuse["color"].getValue()) self.assertNotEqual(mainCDLSampler["color"].hash(), diffuseCDLSampler["color"].hash()) self.assertNotEqual(mainCDLSampler["color"].getValue(), diffuseCDLSampler["color"].getValue()) cdl["channels"].setValue("diffuse.[RGB]") self.assertEqual(mainCDLSampler["color"].getValue(), main["color"].getValue()) self.assertNotEqual(diffuseCDLSampler["color"].getValue(), diffuse["color"].getValue())
def testChannelsPlug( self ) : main = self.__constantLayer( "", IECore.Color4f( 1, 0.5, 0.25, 1 ) ) diffuse = self.__constantLayer( "diffuse", IECore.Color4f( 0, 0.25, 0.5, 1 ) ) copy = GafferImage.CopyChannels() copy["in"][0].setInput( main["out"] ) copy["in"][1].setInput( diffuse["out"] ) copy["channels"].setValue( "diffuse.R" ) self.assertEqual( copy["out"]["channelNames"].getValue(), IECore.StringVectorData( [ "R", "G", "B", "A", "diffuse.R" ] ), ) copy["channels"].setValue( "diffuse.R diffuse.B" ) self.assertEqual( copy["out"]["channelNames"].getValue(), IECore.StringVectorData( [ "R", "G", "B", "A", "diffuse.R", "diffuse.B" ] ), ) copy["channels"].setValue( "diffuse.*" ) self.assertEqual( copy["out"]["channelNames"].getValue(), IECore.StringVectorData( [ "R", "G", "B", "A", "diffuse.R", "diffuse.G", "diffuse.B", "diffuse.A" ] ), )
def testAffectsChannelNames( self ) : c1 = GafferImage.Constant() c2 = GafferImage.Constant() copy = GafferImage.CopyChannels() copy["in"][0].setInput( c1["out"] ) copy["in"][1].setInput( c2["out"] ) cs = GafferTest.CapturingSlot( copy.plugDirtiedSignal() ) c2["layer"].setValue( "diffuse" ) self.assertTrue( copy["out"]["channelNames"] in [ x[0] for x in cs ] ) del cs[:] copy["channels"].setValue( "diffuse.R" ) self.assertTrue( copy["out"]["channelNames"] in [ x[0] for x in cs ] )
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 testPerLayerExpression(self): script = Gaffer.ScriptNode() script["c1"] = GafferImage.Constant() script["c1"]["color"].setValue(imath.Color4f(1)) script["c2"] = GafferImage.Constant() script["c2"]["color"].setValue(imath.Color4f(1)) script["c2"]["layer"].setValue("B") script["copyChannels"] = GafferImage.CopyChannels() script["copyChannels"]["in"][0].setInput(script["c1"]["out"]) script["copyChannels"]["in"][1].setInput(script["c2"]["out"]) script["copyChannels"]["channels"].setValue("*") script["grade"] = GafferImage.Grade() script["grade"]["in"].setInput(script["copyChannels"]["out"]) script["grade"]["channels"].setValue("*") script["expression"] = Gaffer.Expression() script["expression"].setExpression( inspect.cleandoc(""" import GafferImage layerName = GafferImage.ImageAlgo.layerName( context["image:channelName" ] ) parent["grade"]["gain"] = imath.Color4f( 1 if layerName == "B" else 0.5 ) """)) sampler = GafferImage.ImageSampler() sampler["image"].setInput(script["grade"]["out"]) sampler["pixel"].setValue(imath.V2f(10.5)) sampler["channels"].setValue( IECore.StringVectorData(["R", "G", "B", "A"])) self.assertEqual(sampler["color"].getValue(), imath.Color4f(0.5)) sampler["channels"].setValue( IECore.StringVectorData(["B.R", "B.G", "B.B", "B.A"])) self.assertEqual(sampler["color"].getValue(), imath.Color4f(1))
def test( self ) : # Set up a copy where the main layer comes from the first # input and the diffuse layer is copied in from a second input. main = self.__constantLayer( "", IECore.Color4f( 1, 0.5, 0.25, 1 ) ) diffuse = self.__constantLayer( "diffuse", IECore.Color4f( 0, 0.25, 0.5, 1 ) ) copy = GafferImage.CopyChannels() copy["in"][0].setInput( main["out"] ) copy["in"][1].setInput( diffuse["out"] ) copy["channels"].setValue( "*" ) # Check that our new image has all the expected channels. self.assertEqual( copy["out"]["channelNames"].getValue(), IECore.StringVectorData( [ "R", "G", "B", "A", "diffuse.R", "diffuse.G", "diffuse.B", "diffuse.A" ] ), ) # Check that each channel is a perfect pass-through from the # relevant input, sharing the same entries in the cache. for constant in ( main, diffuse ) : for channel in ( "R", "G", "B", "A" ) : if constant["layer"].getValue() : channel = constant["layer"].getValue() + "." + channel self.assertEqual( copy["out"].channelDataHash( channel, IECore.V2i( 0 ) ), constant["out"].channelDataHash( channel, IECore.V2i( 0 ) ), ) self.assertTrue( copy["out"].channelData( channel, IECore.V2i( 0 ), _copy = False ).isSame( constant["out"].channelData( channel, IECore.V2i( 0 ), _copy = False ) ) )
def testMerging(self): allFilter = GafferScene.PathFilter() allFilter["paths"].setValue(IECore.StringVectorData(['/...'])) plane = GafferScene.Plane() plane["divisions"].setValue(imath.V2i(20, 20)) # Assign a basic gradient shader uvGradientCode = GafferOSL.OSLCode() uvGradientCode["out"].addChild( Gaffer.Color3fPlug("out", direction=Gaffer.Plug.Direction.Out)) uvGradientCode["code"].setValue('out = color( u, v, 0.5 );') shaderAssignment = GafferScene.ShaderAssignment() shaderAssignment["in"].setInput(plane["out"]) shaderAssignment["filter"].setInput(allFilter["out"]) shaderAssignment["shader"].setInput(uvGradientCode["out"]["out"]) # Set up a random id from 0 - 3 on each face randomCode = GafferOSL.OSLCode() randomCode["out"].addChild( Gaffer.IntPlug("randomId", direction=Gaffer.Plug.Direction.Out)) randomCode["code"].setValue( 'randomId = int(cellnoise( P * 100 ) * 4);') outInt = GafferOSL.OSLShader() outInt.loadShader("ObjectProcessing/OutInt") outInt["parameters"]["name"].setValue('randomId') outInt["parameters"]["value"].setInput(randomCode["out"]["randomId"]) outObject = GafferOSL.OSLShader() outObject.loadShader("ObjectProcessing/OutObject") outObject["parameters"]["in0"].setInput( outInt["out"]["primitiveVariable"]) oSLObject = GafferOSL.OSLObject() oSLObject["in"].setInput(shaderAssignment["out"]) oSLObject["filter"].setInput(allFilter["out"]) oSLObject["shader"].setInput(outObject["out"]) oSLObject["interpolation"].setValue(2) # Create 4 meshes by picking each of the 4 ids deleteContextVariables = Gaffer.DeleteContextVariables() deleteContextVariables.setup(GafferScene.ScenePlug()) deleteContextVariables["variables"].setValue('collect:rootName') deleteContextVariables["in"].setInput(oSLObject["out"]) pickCode = GafferOSL.OSLCode() pickCode["parameters"].addChild(Gaffer.IntPlug("targetId")) pickCode["out"].addChild( Gaffer.IntPlug("cull", direction=Gaffer.Plug.Direction.Out)) pickCode["code"].setValue( 'int randomId; getattribute( "randomId", randomId ); cull = randomId != targetId;' ) expression = Gaffer.Expression() pickCode.addChild(expression) expression.setExpression( 'parent.parameters.targetId = stoi( context( "collect:rootName", "0" ) );', "OSL") outInt1 = GafferOSL.OSLShader() outInt1.loadShader("ObjectProcessing/OutInt") outInt1["parameters"]["name"].setValue('deleteFaces') outInt1["parameters"]["value"].setInput(pickCode["out"]["cull"]) outObject1 = GafferOSL.OSLShader() outObject1.loadShader("ObjectProcessing/OutObject") outObject1["parameters"]["in0"].setInput( outInt1["out"]["primitiveVariable"]) oSLObject1 = GafferOSL.OSLObject() oSLObject1["in"].setInput(deleteContextVariables["out"]) oSLObject1["filter"].setInput(allFilter["out"]) oSLObject1["shader"].setInput(outObject1["out"]) oSLObject1["interpolation"].setValue(2) deleteFaces = GafferScene.DeleteFaces() deleteFaces["in"].setInput(oSLObject1["out"]) deleteFaces["filter"].setInput(allFilter["out"]) collectScenes = GafferScene.CollectScenes() collectScenes["in"].setInput(deleteFaces["out"]) collectScenes["rootNames"].setValue( IECore.StringVectorData(['0', '1', '2', '3'])) collectScenes["sourceRoot"].setValue('/plane') # First variant: bake everything, covering the whole 1001 UDIM customAttributes1 = GafferScene.CustomAttributes() customAttributes1["attributes"].addMember( 'bake:fileName', IECore.StringData( '${bakeDirectory}/complete/<AOV>/<AOV>.<UDIM>.exr')) customAttributes1["in"].setInput(collectScenes["out"]) # Second vaiant: bake just 2 of the 4 meshes, leaving lots of holes that will need filling pruneFilter = GafferScene.PathFilter() pruneFilter["paths"].setValue(IECore.StringVectorData(['/2', '/3'])) prune = GafferScene.Prune() prune["in"].setInput(collectScenes["out"]) prune["filter"].setInput(pruneFilter["out"]) customAttributes2 = GafferScene.CustomAttributes() customAttributes2["attributes"].addMember( 'bake:fileName', IECore.StringData( '${bakeDirectory}/incomplete/<AOV>/<AOV>.<UDIM>.exr')) customAttributes2["in"].setInput(prune["out"]) # Third variant: bake everything, but with one mesh at a higher resolution customAttributes3 = GafferScene.CustomAttributes() customAttributes3["attributes"].addMember( 'bake:fileName', IECore.StringData( '${bakeDirectory}/mismatch/<AOV>/<AOV>.<UDIM>.exr')) customAttributes3["in"].setInput(collectScenes["out"]) pathFilter2 = GafferScene.PathFilter() pathFilter2["paths"].setValue(IECore.StringVectorData(['/2'])) customAttributes = GafferScene.CustomAttributes() customAttributes["attributes"].addMember('bake:resolution', IECore.IntData(200)) customAttributes["filter"].setInput(pathFilter2["out"]) customAttributes["in"].setInput(customAttributes3["out"]) # Merge the 3 variants mergeGroup = GafferScene.Group() mergeGroup["in"][-1].setInput(customAttributes["out"]) mergeGroup["in"][-1].setInput(customAttributes1["out"]) mergeGroup["in"][-1].setInput(customAttributes2["out"]) arnoldTextureBake = GafferArnold.ArnoldTextureBake() arnoldTextureBake["in"].setInput(mergeGroup["out"]) arnoldTextureBake["filter"].setInput(allFilter["out"]) arnoldTextureBake["bakeDirectory"].setValue(self.temporaryDirectory() + '/bakeMerge/') arnoldTextureBake["defaultResolution"].setValue(128) # We want to check the intermediate results arnoldTextureBake["cleanupIntermediateFiles"].setValue(False) # Dispatch the bake script = Gaffer.ScriptNode() script.addChild(arnoldTextureBake) dispatcher = GafferDispatch.LocalDispatcher() dispatcher["jobsDirectory"].setValue(self.temporaryDirectory()) dispatcher.dispatch([arnoldTextureBake]) # Check results imageReader = GafferImage.ImageReader() outLayer = GafferOSL.OSLShader() outLayer.loadShader("ImageProcessing/OutLayer") outLayer["parameters"]["layerColor"].setInput( uvGradientCode["out"]["out"]) outImage = GafferOSL.OSLShader() outImage.loadShader("ImageProcessing/OutImage") outImage["parameters"]["in0"].setInput(outLayer["out"]["layer"]) oSLImage = GafferOSL.OSLImage() oSLImage["in"].setInput(imageReader["out"]) oSLImage["shader"].setInput(outImage["out"]) merge3 = GafferImage.Merge() merge3["in"]["in0"].setInput(oSLImage["out"]) merge3["in"]["in1"].setInput(imageReader["out"]) merge3["operation"].setValue(10) edgeDetect = self.SimpleEdgeDetect() edgeDetect["in"].setInput(imageReader["out"]) edgeStats = GafferImage.ImageStats() edgeStats["in"].setInput(edgeDetect["out"]) refDiffStats = GafferImage.ImageStats() refDiffStats["in"].setInput(merge3["out"]) oneLayerReader = GafferImage.ImageReader() grade = GafferImage.Grade() grade["in"].setInput(oneLayerReader["out"]) grade["channels"].setValue('[A]') grade["blackPoint"].setValue(imath.Color4f(0, 0, 0, 0.999899983)) copyChannels = GafferImage.CopyChannels() copyChannels["in"]["in0"].setInput(merge3["out"]) copyChannels["in"]["in1"].setInput(grade["out"]) copyChannels["channels"].setValue('[A]') premultiply = GafferImage.Premultiply() premultiply["in"].setInput(copyChannels["out"]) refDiffCoveredStats = GafferImage.ImageStats() refDiffCoveredStats["in"].setInput(premultiply["out"]) # We are testing 3 different cases: # complete : Should be an exact match. # incomplete : Expect some mild variance of slopes and some error, because we have to # reconstruct a lot of missing data. # mismatch : We should get a larger image, sized to the highest override on any mesh. # Match won't be as perfect, because we're combining source images at # different resolutions for name, expectedSize, maxEdge, maxRefDiff, maxMaskedDiff in [ ("complete", 128, 0.01, 0.000001, 0.000001), ("incomplete", 128, 0.05, 0.15, 0.000001), ("mismatch", 200, 0.01, 0.01, 0.01) ]: imageReader["fileName"].setValue(self.temporaryDirectory() + "/bakeMerge/" + name + "/beauty/beauty.1001.tx") oneLayerReader["fileName"].setValue(self.temporaryDirectory() + "/bakeMerge/" + name + "/beauty/beauty.1001.exr") self.assertEqual(imageReader["out"]["format"].getValue().width(), expectedSize) self.assertEqual(imageReader["out"]["format"].getValue().height(), expectedSize) edgeStats["area"].setValue( imath.Box2i(imath.V2i(1), imath.V2i(expectedSize - 1))) refDiffStats["area"].setValue( imath.Box2i(imath.V2i(1), imath.V2i(expectedSize - 1))) refDiffCoveredStats["area"].setValue( imath.Box2i(imath.V2i(0), imath.V2i(expectedSize))) # Blue channel is constant, so everything should line up perfectly self.assertEqual(0, edgeStats["max"].getValue()[2]) self.assertEqual(0, refDiffStats["max"].getValue()[2]) self.assertEqual(0, refDiffCoveredStats["max"].getValue()[2]) for i in range(2): # Make sure we've got actual data, by checking that we have some error ( we're not expecting # to perfectly reconstruct the gradient when the input is incomplete ) self.assertGreater(edgeStats["max"].getValue()[i], 0.005) if name == "incomplete": self.assertGreater(edgeStats["max"].getValue()[i], 0.03) self.assertGreater(refDiffStats["max"].getValue()[i], 0.06) self.assertLess(edgeStats["max"].getValue()[i], maxEdge) self.assertLess(refDiffStats["max"].getValue()[i], maxRefDiff) self.assertLess(refDiffCoveredStats["max"].getValue()[i], maxMaskedDiff)
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 testWarpImage(self): def __warpImage(size, distortion, idistortStyle): w = IECore.Box2i(IECore.V2i(0), size - IECore.V2i(1)) image = IECore.ImagePrimitive(w, w) R = IECore.FloatVectorData(size.x * size.y) G = IECore.FloatVectorData(size.x * size.y) for iy in range(size.y): for ix in range(size.x): x = (ix + 0.5) / size.x y = 1 - (iy + 0.5) / size.y if idistortStyle: R[iy * size.x + ix] = distortion * math.sin(y * 8) * size.x G[iy * size.x + ix] = distortion * math.sin(x * 8) * size.y else: R[iy * size.x + ix] = x + distortion * math.sin(y * 8) G[iy * size.x + ix] = y + distortion * math.sin(x * 8) image["R"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, R) image["G"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, G) return image def __dotGrid(size): w = IECore.Box2i(IECore.V2i(0), size - IECore.V2i(1)) image = IECore.ImagePrimitive(w, w) R = IECore.FloatVectorData(size.x * size.y) G = IECore.FloatVectorData(size.x * size.y) B = IECore.FloatVectorData(size.x * size.y) for iy in range(0, size.y): for ix in range(0, size.x): q = max(ix % 16, iy % 16) R[iy * size.x + ix] = q < 1 G[iy * size.x + ix] = q < 4 B[iy * size.x + ix] = q < 8 image["R"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, R) image["G"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, G) image["B"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Vertex, B) return image objectToImageSource = GafferImage.ObjectToImage() objectToImageSource["object"].setValue(__dotGrid(IECore.V2i(300))) # TODO - reorder channels of our source image because ObjectToImage outputs in opposite order to # the rest of Gaffer. This probably should be fixed in ObjectToImage, # or we shouldn't depend on channel order to check if images are equal? sourceReorderConstant = GafferImage.Constant() sourceReorderConstant["format"].setValue( GafferImage.Format(300, 300, 1.000)) sourceReorderDelete = GafferImage.DeleteChannels() sourceReorderDelete["channels"].setValue(IECore.StringVectorData(["A" ])) sourceReorderDelete["in"].setInput(sourceReorderConstant["out"]) sourceReorder = GafferImage.CopyChannels() sourceReorder["channels"].setValue("R G B") sourceReorder["in"]["in0"].setInput(sourceReorderDelete["out"]) sourceReorder["in"]["in1"].setInput(objectToImageSource["out"]) objectToImageVector = GafferImage.ObjectToImage() vectorWarp = GafferImage.VectorWarp() vectorWarp["in"].setInput(sourceReorder["out"]) vectorWarp["vector"].setInput(objectToImageVector["out"]) # Test that a warp with no distortion and a box filter reproduces the input objectToImageVector["object"].setValue( __warpImage(IECore.V2i(300), 0, False)) vectorWarp["filter"].setValue("box") self.assertImagesEqual(vectorWarp["out"], sourceReorder["out"], maxDifference=0.00001) # Test that a warp with distortion produces an expected output objectToImageVector["object"].setValue( __warpImage(IECore.V2i(300), 0.2, False)) vectorWarp["filter"].setValue("blackman-harris") # Enable to write out images for visual comparison if False: testWriter = GafferImage.ImageWriter() testWriter["in"].setInput(vectorWarp["out"]) testWriter["fileName"].setValue("/tmp/dotGrid.warped.exr") testWriter["task"].execute() expectedReader = GafferImage.ImageReader() expectedReader["fileName"].setValue( os.path.dirname(__file__) + "/images/dotGrid.warped.exr") # Test that we can get the same result using pixel offsets instead of normalized coordinates objectToImageVector["object"].setValue( __warpImage(IECore.V2i(300), 0.2, True)) vectorWarp["vectorMode"].setValue( GafferImage.VectorWarp.VectorMode.Relative) vectorWarp["vectorUnits"].setValue( GafferImage.VectorWarp.VectorUnits.Pixels) self.assertImagesEqual(vectorWarp["out"], expectedReader["out"], maxDifference=0.0005, ignoreMetadata=True)
def __init__(self, name="ColoriseSHO"): GafferImage.ImageProcessor.__init__(self, name) self["show"] = Gaffer.IntPlug(defaultValue=0, minValue=0, maxValue=3) # Channel colorising merge = GafferImage.Merge() self["__Merge"] = merge merge["operation"].setValue(0) outputSwitch = Gaffer.Switch() self["__Switch_output"] = outputSwitch outputSwitch.setup(self["out"]) outputSwitch["index"].setInput(self["show"]) outputSwitch["in"][0].setInput(merge["out"]) for channel in GafferAstro.NarrowbandChannels: self["source%s" % channel] = Gaffer.StringPlug( defaultValue='%s.input' % channel) self["range%s" % channel] = Gaffer.V2fPlug(defaultValue=imath.V2f(0, 1)) self["map%s" % channel] = Gaffer.SplinefColor4fPlug( defaultValue=self.__mapDefaults[channel]) self["saturation%s" % channel] = Gaffer.FloatPlug(defaultValue=1.0, minValue=0.0) self["multiply%s" % channel] = Gaffer.FloatPlug(defaultValue=1.0) self["gamma%s" % channel] = Gaffer.FloatPlug(defaultValue=1.0) colorise = GafferAstro.Colorise() self["__Colorise_%s" % channel] = colorise colorise["in"].setInput(self["in"]) colorise["channel"].setInput(self["source%s" % channel]) colorise["mapEnabled"].setValue(True) colorise["range"].setInput(self["range%s" % channel]) colorise["enabled"].setInput(colorise["channel"]) # Work around issue where setInput doesn't sync the number of knots colorise["map"].setValue(self["map%s" % channel].getValue()) colorise["map"].setInput(self["map%s" % channel]) cdl = GafferImage.CDL() self["__CDL_%s" % channel] = cdl cdl["in"].setInput(colorise["out"]) cdl["saturation"].setInput(self["saturation%s" % channel]) grade = GafferImage.Grade() self["__Grade_%s" % channel] = grade grade["in"].setInput(cdl["out"]) grade["multiply"].gang() grade["multiply"]["r"].setInput(self["multiply%s" % channel]) grade["gamma"].gang() grade["gamma"]["r"].setInput(self["gamma%s" % channel]) merge["in"][len(merge["in"]) - 1].setInput(grade["out"]) outputSwitch["in"][len(outputSwitch["in"]) - 1].setInput( grade["out"]) self["saturation"] = Gaffer.FloatPlug(defaultValue=1.0, minValue=0.0) self["blackPoint"] = Gaffer.FloatPlug(defaultValue=0.0) self["whitePoint"] = Gaffer.FloatPlug(defaultValue=1.0) self["multiply"] = Gaffer.Color4fPlug( defaultValue=imath.Color4f(1, 1, 1, 1)) self["gamma"] = Gaffer.FloatPlug(defaultValue=1.0, minValue=0.0) outputCdl = GafferImage.CDL() self["__CDL_output"] = outputCdl outputCdl["in"].setInput(outputSwitch["out"]) outputCdl["saturation"].setInput(self["saturation"]) outputGrade = GafferImage.Grade() self["__Grade_output"] = outputGrade outputGrade["in"].setInput(outputCdl["out"]) outputGrade["blackPoint"].gang() outputGrade["blackPoint"]["r"].setInput(self["blackPoint"]) outputGrade["whitePoint"].gang() outputGrade["whitePoint"]["r"].setInput(self["whitePoint"]) outputGrade["multiply"].setInput(self["multiply"]) outputGrade["gamma"].gang() outputGrade["gamma"]["r"].setInput(self["gamma"]) copyChannels = GafferImage.CopyChannels() self["__CopyChannels"] = copyChannels copyChannels["in"][0].setInput(self["in"]) copyChannels["in"][1].setInput(outputGrade["out"]) copyChannels["channels"].setValue("*") self["out"].setInput(copyChannels["out"])
def __init__(self, name="MultiStarnet"): GafferDispatch.TaskNode.__init__(self, name) self["fileName"] = Gaffer.StringPlug(defaultValue='${channel}.tif', ) self["in"] = GafferImage.ImagePlug("in") scale = GafferAstro.Scale() self["__Scale"] = scale scale["in"].setInput(self["in"]) starnet = GafferAstro.Starnet() self["__Starnet"] = starnet starnet["in"].setInput(scale["out"]) starnet["fileName"].setInput(self["fileName"]) Gaffer.PlugAlgo.promote(starnet["dataType"]) resize = GafferImage.Resize() self["__Resize"] = resize resize["in"].setInput(starnet["out"]) scaleExpression = Gaffer.Expression() self["__ScaleExpression"] = scaleExpression scaleExpression.setExpression( inspect.cleandoc(""" import GafferImage enabled = parent["__Scale"]["factor"] != 1 parent["__Scale"]["enabled"] = enabled inFormat = parent["__Scale"]["in"]["format"] parent["__Resize"]["format"] = GafferImage.Format( inFormat.width(), inFormat.height(), 1.000 ) parent["__Resize"]["enabled"] = enabled """), "python") spreadsheet = Gaffer.Spreadsheet() self["__Spreadsheet"] = spreadsheet spreadsheet["selector"].setValue("${channel}") Gaffer.Metadata.registerValue(spreadsheet["rows"], "spreadsheet:defaultRowVisible", False, persistent=False) Gaffer.Metadata.registerValue(spreadsheet["rows"], "spreadsheet:columnsNeedSerialisation", False, persistent=False) spreadsheet["rows"].addColumn(Gaffer.StringPlug("source"), "source") starnet["channels"].setInput(spreadsheet["out"]["source"]) Gaffer.Metadata.registerValue( spreadsheet["rows"].defaultRow()["cells"]["source"]["value"], "plugValueWidget:type", "GafferUI.PresetsPlugValueWidget", persistent=False) Gaffer.Metadata.registerValue( spreadsheet["rows"].defaultRow()["cells"]["source"]["value"], "presetsPlugValueWidget:allowCustom", True, persistent=False) Gaffer.Metadata.registerValue( MultiStarnet, "rows.*.cells.source.value", "presetNames", lambda plug: IECore.StringVectorData(_channelNames(plug))) Gaffer.Metadata.registerValue( MultiStarnet, "rows.*.cells.source.value", "presetValues", lambda plug: IECore.StringVectorData(_channelNames(plug))) spreadsheet["rows"].addColumn( Gaffer.FloatPlug("scale", minValue=0, defaultValue=1), "scale") scale["factor"].setInput(spreadsheet["out"]["scale"]) promotedRowsPlug = Gaffer.PlugAlgo.promote(spreadsheet["rows"]) Gaffer.Metadata.registerValue(promotedRowsPlug, "spreadsheet:columnsNeedSerialisation", False, persistent=False) wedge = GafferDispatch.Wedge() self["__Wedge"] = wedge wedge["variable"].setValue("channel") wedge["mode"].setValue(5) wedge["strings"].setInput(spreadsheet["activeRowNames"]) wedge["preTasks"]["preTask0"].setInput(starnet["task"]) collect = GafferAstro.CollectChannels() self["__Collect"] = collect collect["in"].setInput(resize["out"]) collect["channelVariable"].setValue("channel") collect["channels"].setInput(spreadsheet["activeRowNames"]) copy = GafferImage.CopyChannels() self["__Copy"] = copy copy["channels"].setValue("*") copy["in"][0].setInput(self["in"]) copy["in"][1].setInput(collect["out"]) self["out"] = GafferImage.ImagePlug( direction=Gaffer.Plug.Direction.Out) self["out"].setInput(copy["out"]) self["task"].setInput(wedge["task"])
def __init__(self, name="MultiPixInsight"): GafferDispatch.TaskNode.__init__(self, name) self["fileName"] = Gaffer.StringPlug(defaultValue='${channel}.xisf', ) self["in"] = GafferImage.ImagePlug("in") pixInsight = GafferAstro.PixInsight() self["__PixInsight"] = pixInsight pixInsight["in"].setInput(self["in"]) pixInsight["fileName"].setInput(self["fileName"]) Gaffer.PlugAlgo.promote(pixInsight["dataType"]) spreadsheet = Gaffer.Spreadsheet() self["__Spreadsheet"] = spreadsheet spreadsheet["selector"].setValue("${channel}") Gaffer.Metadata.registerValue(spreadsheet["rows"], "spreadsheet:columnsNeedSerialisation", False, persistent=False) spreadsheet["rows"].addColumn(Gaffer.StringPlug("source"), "source") pixInsight["channels"].setInput(spreadsheet["out"]["source"]) Gaffer.Metadata.registerValue( spreadsheet["rows"].defaultRow()["cells"]["source"]["value"], "plugValueWidget:type", "GafferUI.PresetsPlugValueWidget", persistent=False) Gaffer.Metadata.registerValue( spreadsheet["rows"].defaultRow()["cells"]["source"]["value"], "presetsPlugValueWidget:allowCustom", True, persistent=False) Gaffer.Metadata.registerValue( MultiPixInsight, "rows.*.cells.source.value", "presetNames", lambda plug: IECore.StringVectorData(_channelNames(plug))) Gaffer.Metadata.registerValue( MultiPixInsight, "rows.*.cells.source.value", "presetValues", lambda plug: IECore.StringVectorData(_channelNames(plug))) spreadsheet["rows"].addColumn(pixInsight["pixScript"]) Gaffer.MetadataAlgo.copy( pixInsight["pixScript"], spreadsheet["rows"].defaultRow()["cells"]["pixScript"]["value"], exclude="spreadsheet:columnName layout:* deletable") pixInsight["pixScript"].setInput(spreadsheet["out"]["pixScript"]) promotedRowsPlug = Gaffer.PlugAlgo.promote(spreadsheet["rows"]) Gaffer.Metadata.registerValue(promotedRowsPlug, "spreadsheet:columnsNeedSerialisation", False, persistent=False) Gaffer.PlugAlgo.promote(pixInsight["variables"]) wedge = GafferDispatch.Wedge() self["__Wedge"] = wedge wedge["variable"].setValue("channel") wedge["mode"].setValue(5) wedge["strings"].setInput(spreadsheet["activeRowNames"]) wedge["preTasks"]["preTask0"].setInput(pixInsight["task"]) collect = GafferAstro.CollectChannels() self["__Collect"] = collect collect["in"].setInput(pixInsight["out"]) collect["channelVariable"].setValue("channel") collect["channels"].setInput(spreadsheet["activeRowNames"]) copy = GafferImage.CopyChannels() self["__Copy"] = copy copy["channels"].setValue("*") copy["in"][0].setInput(self["in"]) copy["in"][1].setInput(collect["out"]) self["out"] = GafferImage.ImagePlug( direction=Gaffer.Plug.Direction.Out) self["out"].setInput(copy["out"]) self["task"].setInput(wedge["task"])