Version: Deadline 8.0
BACKGROUND
Draft is a lightweight compositing and video processing tool designed to automate typical post-render tasks such as the creation and processing of QuickTimes, thumbnails and other deliverables in a pipeline. It is implemented as a Python library and is designed to be tightly integrated with Deadline. Draft can also be used as a standalone tool. Read more about Draft here and here.
A good way to get introduced to Draft is through small code snippets, or “recipes”. With this approach, you can simply take the pieces you need and combine them together, tweak the code a bit and create your first Draft script!
In this blog entry, the Top Ten Draft Cookbook Recipes are presented. These recipes have been selected either because they form the basis of most scripts or because they illustrate very useful Draft functionalities. For many more, consult the complete Draft Cookbook.
1. CONVERT AN IMAGE FROM ONE FORMAT TO ANOTHER
This recipe shows how to convert an image from one format to another. This operation is as simple as reading the image in memory and saving it in a new file with the appropriate file extension.
import Draft img = Draft.Image.ReadFromFile( 'inputFile.exr' ) img.WriteToFile( 'outputFile.png' )
2. WRITE AN IMAGE TO FILE WITH A SPECIFIC FILE CHANNEL MAP
When an image is written to file, it might be necessary to minimize the resulting file size. This recipe describes how this can be done by using an image file channel map to specify the bit depth of each image channel when the image is written to file. An image file channel map is represented using a dictionary. Each dictionary entry is made of two strings separated by a colon. The first string corresponds to the channel name, typically ‘R’, ‘G’, ‘B’ or ‘A’, and the second string corresponds to the channel bit depth.
import Draft # Read an image from file img = Draft.Image.ReadFromFile( 'inputFile.exr' ) # Create and set the file channel map fileChannelMap = { 'R':'16f', 'G':'16f', 'B':'16f', 'A':'16f' } img.SetFileChannelMap( fileChannelMap ) # Write the image back to file img.WriteToFile( 'outputFile.exr' )
3. SPECIFY SAVING SETTINGS IN AN IMAGE FILE
One may want to control the compression, the quality and the tile size of an image when it is written to file to meet specific requirements. This recipe lays out how users can specify these properties using an ImageInfo object. An ImageInfo stores those saving settings as properties and can be passed as an additional parameter when writing an image to file.
import Draft # Read an image from file img = Draft.Image.ReadFromFile( 'inputFile.exr' ) # Create a Draft.ImageInfo object and set compression, quality and tileSize imageInfo = Draft.ImageInfo() imageInfo.compression = 'dwaa' imageInfo.quality = 80 imageInfo.tileSize = ( 64, 64 ) # Write the image back to file img.WriteToFile( 'outputFile.exr', imageInfo )
4. APPLY A COLOR TRANSFORM TO AN IMAGE
It is often necessary to adjust the colors in an image to obtain specific results when the image is displayed on a particular device. This recipe illustrates how this can be achieved by applying a color transform. Applying a color transform consists of two simple steps: creating a LUT that will encapsulate your color transform and applying it to your image.
import Draft import copy from DraftASCCDLReader import ReadASCCDL inFile = 'manyTeapots.png' img = Draft.Image.ReadFromFile( inFile ) # Create a Cineon LUT cineonLut = Draft.LUT.CreateCineon() # Apply the LUT to a copy of the original image cineonImg = copy.deepcopy( img ) cineonLut.Apply( cineonImg ) # Write the image back to file cineonImg.WriteToFile( 'manyTeapotsCineonLUTApplied.png' ) # Create an ASCCDL LUT from file ASCCDLLut = ReadASCCDL( 'my_color_correction.cc' ) # Apply the LUT to a copy of the original image ASCCDLImage = copy.deepcopy( img ) ASCCDLLut.Apply( ASCCDLImage ) # Write the image back to file ASCCDLImage.WriteToFile( 'manyTeapotsASCCDLLUTApplied.png' )
The original image is pictured first, the resulting image when applying a cineon LUT second and the resulting image when applying the ASCCDL LUT is last.
5. ADD A SEMI-TRANSPARENT MASK TO AN IMAGE
When modifying an image aspect ratio, some parts of the original image need to be removed. This recipe depicts how to apply a semi-transparent mask to the original image to show exactly which parts of the image will be affected.
import Draft # For the composite operations compOperation = Draft.CompositeOperator.OverCompositeOp # Read an image from file img = Draft.Image.ReadFromFile( 'manyTeapots.png' ) # Store the original image width and height outWidth = img.width outHeight = img.height # Define the desired ratio ratio = 2.35 # Create the mask mask = Draft.Image.CreateImage( outWidth, outHeight ) mask.SetChannel( 'A', 0 ) img.SetChannel( 'A', 1 ) maskRectHeight = int( round( ( outHeight - outWidth / ratio ) / 2 ) ) maskRect = Draft.Image.CreateImage( outWidth, maskRectHeight ) maskRect.SetChannel( 'A', 0.3 ) # The value 0.3 can be adjusted, higher the value, darker the mask mask.CompositeWithAnchor( maskRect, Draft.Anchor.SouthEast, compOperation ) mask.CompositeWithAnchor( maskRect, Draft.Anchor.NorthEast, compOperation ) # Add the mask to the image img.Composite( mask, 0, 0, Draft.CompositeOperator.OverCompositeOp ) # Write the image back to file img.WriteToFile( 'manyTeapotsWithMask.png' )
The original image is pictured on the left, the resulting image is on the right.
6. CREATE A SLATE FRAME
A slate frame is used to display project metadata and can be used to pass along information to a client. Typically, it is inserted at the beginning of a video. This recipe shows how to create such a slate frame by loading a standard background and by adding text to it. The text is added in three steps: creating an AnnotationInfo object that stores properties such as font, point size and colour, creating an image that contains the text, and compositing the text image over top the slate frame background.
import Draft import datetime # For the composite operations compOperation = Draft.CompositeOperator.OverCompositeOp # Read background image from file slateFrame = Draft.Image.ReadFromFile( 'slateBackground.png' ) # Set up the text on the slate frame slateText = [("SHOW", "My Show"), ("SHOT", "119"), ("FRAMES", "1-300"), ("VERSION", "12"), ("ARTIST", "John Doe"), ("DATE", datetime.datetime.now().strftime("%m/%d/%Y") )] # Set text's color and point size annotationInfo = Draft.AnnotationInfo() annotationInfo.Color = Draft.ColorRGBA( 0.0, 0.1, 0.3, 1.0 ) annotationInfo.PointSize = int( slateFrame.height * 0.045 ) # Composite annotations over top the slate frame for i in range( 0, len( slateText ) ): txtImg = Draft.Image.CreateAnnotation( slateText[i][0] + ": ", annotationInfo ) slateFrame.CompositeWithPositionAndAnchor( txtImg, 0.15, 0.7 - (i * 0.06), Draft.Anchor.SouthEast, compOperation ) txtImg = Draft.Image.CreateAnnotation( slateText[i][1], annotationInfo ) slateFrame.CompositeWithPositionAndAnchor( txtImg, 0.15, 0.7 - (i * 0.06), Draft.Anchor.SouthWest, compOperation ) slateFrame.WriteToFile('mySlateFrame.png')
Here’s the result:
7. ADD BURN-INS TO MOVIE FRAMES
One may also want to display basic information, or burn-ins, on each movie frame. Burn-ins can include general information about your project such as the name of the movie, the name of the studio and the current date and can also include information specific to each frame such as the frame number. This recipe describes how to add burn-ins to movie frames. It is done in a very similar way as adding text to a slate frame.
import Draft import datetime # For the composite operations compOperation = Draft.CompositeOperator.OverCompositeOp # Read an image from file img = Draft.Image.ReadFromFile( 'awesomeTeapots.png' ) # Set text's color and point size annotationInfo = Draft.AnnotationInfo() annotationInfo.Color = Draft.ColorRGBA( 1.0, 1.0, 1.0, 1.0 ) annotationInfo.PointSize = int( img.height * 0.045 ) # Add each annotation titleAnnotation = Draft.Image.CreateAnnotation( "Awesome Teapots", annotationInfo ) img.CompositeWithAnchor( titleAnnotation, Draft.Anchor.NorthWest, compOperation ) dateAnnotation = Draft.Image.CreateAnnotation( datetime.datetime.now().strftime("%m/%d/%Y %I:%M %p"), annotationInfo ) img.CompositeWithAnchor( dateAnnotation, Draft.Anchor.NorthEast, compOperation ) studioNameAnnotation = Draft.Image.CreateAnnotation( "VFX.co", annotationInfo ) img.CompositeWithAnchor( studioNameAnnotation, Draft.Anchor.SouthEast, compOperation )
Here’s the result:
8. CREATE A QUICKTIME MOVIE
This recipe illustrates a fundamental post rendering task: creating a video. In order to do that, users first need to create a VideoEncoder object and encode each frame, one after the other. Once all frames have been encoded, the encoding process is finalized and the video is saved to file.
import Draft from DraftParamParser import ReplaceFilenameHashesWithNumber # Create the video encoder. encoder = Draft.VideoEncoder( 'myMovie.mov' ) # Encode each frames for currFrame in range( 1, 200 ): # Read current frame from file currFile = ReplaceFilenameHashesWithNumber( 'movieFrame####.exr', currFrame ) frame = Draft.Image.ReadFromFile( currFile ) # Add current frame to the video. encoder.EncodeNextFrame( frame ) # Finalize and save the resulting video. encoder.FinalizeEncoding()
9. EMBED A TIMECODE IN A VIDEO FILE
In Draft, a Timecode object stores the hours, minutes, seconds and frame number associated to a timecode, as described in SMPTE standard. A timecode is a metadata holding a time reference and is typically used in video editing. This recipe outlines how to embed a timecode when creating a video file from an image sequence, by extracting the timecode embedded in the first frame (if there is one) and injecting it into the VideoEncoder
import Draft from DraftParamParser import ReplaceFilenameHashesWithNumber # Create a Draft.ImageInfo object to extract a Draft.Timecode object imageInfo = Draft.ImageInfo() # Main encoding loop for currFrame in range( 1, 200 ): currFile = ReplaceFilenameHashesWithNumber( 'movieFrame####.exr', currFrame ) frame = Draft.Image.ReadFromFile( currFile, imageInfo ) # If first frame and a timecode is found, specify the timecode parameter when creating the encoder if currFrame == 1: if( imageInfo.timecode ): encoder = Draft.VideoEncoder( 'myMovie.mov', timecode = imageInfo.timecode ) else: encoder = Draft.VideoEncoder( 'myMovie.mov' ) # Add current frame to the video. encoder.EncodeNextFrame( frame ) encoder.FinalizeEncoding()
10. CONCATENATE VIDEO FILES
This recipe shows how to concatenate movie clips to create one single video. This last recipe is very simple but incredibly powerful! Within Deadline, it is used in Quick Draft, behind the scene, to concatenate movie chunks when performing distributed encoding across a rendering farm. For more details, see the section entitled “Distributed Encoding” from this blog entry.
import Draft # Define input and output files inputVideoFile1 = 'movie_clip_1.mov' inputVideoFile2 = 'movie_clip_2.mov' inputVideoFile3 = 'movie_clip_3.mov' outputVideoFile = 'complete_movie.mov' # Concatenate video files Draft.ConcatenateVideoFiles( [ inputVideo1, inputVideo2, inputVideo3 ], outputVideo )
PUTTING IT ALL TOGETHER
Now, let us put together a few of the above recipes to create a complete Draft script. A typical task would be to create a QuickTime movie, add a slate frame, add frame burn-ins, apply a color transform, and add a semi-transparent mask to each frame. A sample Draft script that would do all of the above is as follows.
import Draft import datetime from DraftASCCDLReader import ReadASCCDL # For creating the ASCCDL LUT from DraftParamParser import ReplaceFilenameHashesWithNumber # For reading frames when encoding # Final dimensions outWidth = 1920 outHeight = 1152 # For the composite operations compOperation = Draft.CompositeOperator.OverCompositeOp # Set text's color and point size annotationInfo = Draft.AnnotationInfo() annotationInfo.Color = Draft.ColorRGBA( 0.0, 0.1, 0.3, 1.0 ) annotationInfo.PointSize = int( outHeight * 0.045 ) # Set up the text for the slate frame slateText = [("SHOW", "My Show"), ("SHOT", "119"), ("FRAMES", "1-300"), ("VERSION", "12"), ("ARTIST", "John Doe"), ("DATE", datetime.datetime.now().strftime("%m/%d/%Y") )] # Create the slate frame slateFrame = Draft.Image.ReadFromFile( 'slateBackground.png' ) for i in range( 0, len( slateText ) ): txtImg = Draft.Image.CreateAnnotation( slateText[i][0] + ": ", annotationInfo ) slateFrame.CompositeWithPositionAndAnchor( txtImg, 0.18, 0.7 - (i * 0.06), Draft.Anchor.SouthEast, compOperation ) txtImg = Draft.Image.CreateAnnotation( slateText[i][1], annotationInfo ) slateFrame.CompositeWithPositionAndAnchor( txtImg, 0.18, 0.7 - (i * 0.06), Draft.Anchor.SouthWest, compOperation ) annotationInfo.Color = Draft.ColorRGBA( 1.0, 1.0, 1.0, 1.0 ) titleAnnotation = Draft.Image.CreateAnnotation( "Awesome Teapots", annotationInfo ) dateAnnotation = Draft.Image.CreateAnnotation( datetime.datetime.now().strftime("%m/%d/%Y %I:%M %p"), annotationInfo ) studioNameAnnotation = Draft.Image.CreateAnnotation( "VFX.co", annotationInfo ) # Create an ASCCDL LUT from file ASCCDLLut = ReadASCCDL( 'my_color_correction.cc' ) # Create the semi-transparent mask ratio = 2.35 # The value 2.35 can be adjusted to fit your project's needs maskRectHeight = int( round( ( outHeight - outWidth / ratio ) / 2 ) ) maskRect = Draft.Image.CreateImage( outWidth, maskRectHeight ) maskRect.SetChannel( 'A', 0.3 ) # The value 0.3 can be adjusted, higher the value, darker the mask mask = Draft.Image.CreateImage( outWidth, outHeight ) mask.SetChannel( 'A', 0 ) mask.CompositeWithAnchor( maskRect, Draft.Anchor.SouthEast, compOperation ) mask.CompositeWithAnchor( maskRect, Draft.Anchor.NorthEast, compOperation ) # Initialize the video encoder. encoder = Draft.VideoEncoder( 'MyMovie.mov', width=outWidth, height=outHeight ) # Encode the slate frames at the start of the video numberOfSlateFrames = 12 # Hold for half a second @ 24fps for i in range( 0, numberOfSlateFrames ): encoder.EncodeNextFrame( slateFrame ) # Main encoding loop for currFrame in range( 1, 10 ): currFile = ReplaceFilenameHashesWithNumber( 'movieFrame####.exr', currFrame ) frame = Draft.Image.ReadFromFile( currFile ) # Add burn-ins frame.CompositeWithAnchor( titleAnnotation, Draft.Anchor.NorthWest, compOperation ) frame.CompositeWithAnchor( dateAnnotation, Draft.Anchor.NorthEast, compOperation ) frame.CompositeWithAnchor( studioNameAnnotation, Draft.Anchor.SouthEast, compOperation ) # Apply the ASCCDL LUT ASCCDLLut.Apply( frame ) # Add the semi-transparent mask frame.Composite( mask, 0, 0, compOperation ) # Add the frame to the video. encoder.EncodeNextFrame( frame ) # Finalize and save the resulting video. encoder.FinalizeEncoding()
WRAPUP
In this blog entry, the goal was to keep things simple. For more details on all the available options and additional features, please consult the full Draft Documentation. You can also consult this blog entry to see how you can integrate Draft in your Deadline workflow using Custom Draft.
Comments
0 comments
Article is closed for comments.