GafferHQ / gaffer

Gaffer is a node-based application for lookdev, lighting and automation
http://www.gafferhq.org
BSD 3-Clause "New" or "Revised" License
951 stars 205 forks source link

ImageTransform : boundary conditions for subpixel operations #4366

Open brendanholt opened 2 years ago

brendanholt commented 2 years ago

Some of our clients require specific transform nudges to the frame when following a camera extraction workflow (going from a full-sized plate to 1920x1080 editorial framing). Gaffer's image transform filtering results in some border effects on the frame edge since it’s surrounded by black pixels.

e.g. Translating an image by -5.5 pixels in X results in a dark band on the right-hand border.

Currently our workaround is to mirror a copy of the image along the affected edge before doing the transform, then crop off the excess.

Could you please add some boundary conditions (duplicate/mirror/wrap or whatever matches the behavior of other nodes) to ImageTransform to help automatically suppress those artifacts?

danieldresser-ie commented 2 years ago

Discussed with Brendan, and it sounds like this network addresses his issue. Not sure if this is something that is worth building into core. The far more complicated case would be if you wanted to address borders that were more than a fractional pixel, which would require outputting a different data window.

import GafferImage
import imath

Gaffer.Metadata.registerValue( parent, "serialiser:milestoneVersion", 0, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:majorVersion", 60, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:minorVersion", 77, persistent=False )
Gaffer.Metadata.registerValue( parent, "serialiser:patchVersion", 0, persistent=False )

__children = {}

__children["ImageTransform"] = GafferImage.ImageTransform( "ImageTransform" )
parent.addChild( __children["ImageTransform"] )
__children["ImageTransform"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["Unpremultiply"] = GafferImage.Unpremultiply( "Unpremultiply" )
parent.addChild( __children["Unpremultiply"] )
__children["Unpremultiply"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["DeleteChannels"] = GafferImage.DeleteChannels( "DeleteChannels" )
parent.addChild( __children["DeleteChannels"] )
__children["DeleteChannels"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["Shuffle"] = GafferImage.Shuffle( "Shuffle" )
parent.addChild( __children["Shuffle"] )
__children["Shuffle"]["channels"].addChild( GafferImage.Shuffle.ChannelPlug( "channel", flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["Shuffle"].addChild( Gaffer.V2fPlug( "__uiPosition", defaultValue = imath.V2f( 0, 0 ), flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic, ) )
__children["ImageTransform"]["in"].setInput( __children["Shuffle"]["out"] )
__children["ImageTransform"]["transform"]["translate"].setValue( imath.V2f( 0.5, 0.5 ) )
__children["ImageTransform"]["__uiPosition"].setValue( imath.V2f( 247.807846, 32.1879387 ) )
__children["Unpremultiply"]["in"].setInput( __children["ImageTransform"]["out"] )
__children["Unpremultiply"]["channels"].setValue( '*' )
__children["Unpremultiply"]["alphaChannel"].setValue( '__borderMask' )
__children["Unpremultiply"]["__uiPosition"].setValue( imath.V2f( 247.798447, 24.0238876 ) )
__children["DeleteChannels"]["in"].setInput( __children["Unpremultiply"]["out"] )
__children["DeleteChannels"]["channels"].setValue( '__borderMask' )
__children["DeleteChannels"]["__uiPosition"].setValue( imath.V2f( 247.798447, 15.859849 ) )
__children["Shuffle"]["channels"]["channel"]["out"].setValue( '__borderMask' )
__children["Shuffle"]["channels"]["channel"]["in"].setValue( '__white' )
__children["Shuffle"]["__uiPosition"].setValue( imath.V2f( 247.816574, 40.3520012 ) )

del __children