One of the most popular video-related tasks is the artistic replacement of frames. This is called transition effect. It is widely used when it is necessary to combine two different parts of a movie.
Let's imagine that we have a set of bitmaps and we want to create a video where one bitmap replaces another in artistic way. In order to do it we need to perform the following steps:
The result video will be looking as follows:
Frame #0 | Frame #10 | Frame #20 |
Frame #30 | Frame #40 | Frame #50 |
Let's take a look into the code.
''' <summary> ''' Generates an AVI file from a number of static image files with the stardard ''' stripe-based transition effect. ''' </summary> ''' <param name="inputFilenames">An array of filenames to get frames from. ''' There are should be at least two items in this array.</param> ''' <param name="outputFilename">Output file name.</param> ''' <param name="intermediateFrameCount">Speed of the effect ''' (number of frames used to replace the previous image by the current one).</param> Public Shared Sub CreatingTransitionEffects_Standard(ByVal inputFilenames As String(), _ ByVal outputFilename As String, _ ByVal intermediateFrameCount As Integer) ' Create and open the writer object. Dim writer As New Aurigma.GraphicsMill.Codecs.AviWriter(outputFilename) Dim frame As New Aurigma.GraphicsMill.Codecs.AviFrame Dim tempBitmap As New Aurigma.GraphicsMill.Bitmap ' Initialize transition: specify a number of intermediate frames and ' the object which should produce the transition effect. frame.IntermediateFrameCount = intermediateFrameCount frame.Transition = New Aurigma.GraphicsMill.Codecs.AviStripesTransition( _ Aurigma.GraphicsMill.Codecs.AviStripesTransitionMode.HorizontalIn, 10) ' Iterate each bitmap in the input array and add it to the writer. ' When a frame is added to the writer, transition frames are automatically ' calculated. Dim I As Integer For I = 0 To inputFilenames.Length - 1 tempBitmap.Load(inputFilenames(I)) frame.SetBitmap(tempBitmap) writer.AddFrame(frame) Next ' Clean up. writer.Close() End Sub
/// <summary> /// Generates an AVI file from a number of static image files with the /// stardard stripe-based transition effect. /// </summary> /// <param name="inputFilenames">An array of filenames to get frames from. /// There are should be at least two items in this array.</param> /// <param name="outputFilename">Output file name.</param> /// <param name="intermediateFrameCount">Speed of the effect /// (number of frames used to replace the previous image by the current one).</param> public static void CreatingTransitionEffects_Standard(string[] inputFilenames, string outputFilename, int intermediateFrameCount) { // Create and open the writer object. Aurigma.GraphicsMill.Codecs.AviWriter writer = new Aurigma.GraphicsMill.Codecs.AviWriter(); writer.Open(outputFilename); Aurigma.GraphicsMill.Codecs.AviFrame frame = new Aurigma.GraphicsMill.Codecs.AviFrame(); Aurigma.GraphicsMill.Bitmap tempBitmap = new Aurigma.GraphicsMill.Bitmap(); // Initialize transition: specify a number of intermediate frames and // the object which should produce the transition effect. frame.IntermediateFrameCount = intermediateFrameCount; frame.Transition = new Aurigma.GraphicsMill.Codecs.AviStripesTransition( Aurigma.GraphicsMill.Codecs.AviStripesTransitionMode.HorizontalIn, 10); // Iterate each bitmap in the input array and add it to the writer. // When a frame is added to the writer, transition frames are automatically // calculated. for (int i=0;i<inputFilenames.Length;i++) { tempBitmap.Load(inputFilenames[i]); frame.SetBitmap(tempBitmap); writer.AddFrame(frame); } // Clean up. writer.Close(); }
You may find that that AVI Processor Add-on does not provide enough transition effects. Fortunately, you can easily create custom effects.
To create your own transition effect, you should use the AviCustomTransition class. It can used in two ways: via DrawTransition event and by inheriting from this class and overriding the OnDrawTransition(Bitmap, Bitmap, Bitmap, Int32, Int32) method.
In the first case you just add the DrawTransition event handler, which takes a number of arguments that enable you to produce an intermediate frame: two input "key" frames (bitmaps), current and total number of intermediate frame, output bitmap. Based on these arguments you blend key bitmaps and return the necessary intermediate frame this way.
In the second case you create new class derived from the AviCustomTransition and implement the OnDrawTransition(Bitmap, Bitmap, Bitmap, Int32, Int32) method. It has the same arguments as the DrawTransition event.
There is no difference between these two cases - you may use any one which is more convenient. The first method requires to write less code, but the second one gives you more control - in particular you can add new properties to your custom class and add extra effect parameters this way.
Let's see how to create a transition which changes frames by resizing the previous frame:
Frame #0 | Frame #10 | Frame #20 |
Frame #30 | Frame #40 | Frame #50 |
Here is a code which produces this result:
''' <summary> ''' Generates an AVI file from a number of static image files with the custom ''' transition effect which resizes a previous frame and draws it in the center ''' of the next one. ''' </summary> ''' <param name="inputFilenames">An array of filenames to get frames from. ''' There are should be at least two items in this array.</param> ''' <param name="outputFilename">Output file name.</param> ''' <param name="intermediateFrameCount">Speed of the effect ''' (number of frames used to replace the previous image by the current one).</param> Public Shared Sub CreatingTransitionEffects_Custom(ByVal inputFilenames As String(), _ ByVal outputFilename As String, _ ByVal intermediateFrameCount As Integer) ' Create and open the writer object. Dim writer As New Aurigma.GraphicsMill.Codecs.AviWriter(outputFilename) Dim tempBitmap As New Aurigma.GraphicsMill.Bitmap Dim frame As New Aurigma.GraphicsMill.Codecs.AviFrame ' Create transition object and add the event handler which will apply the effect. Dim transition As New Aurigma.GraphicsMill.Codecs.AviCustomTransition AddHandler transition.DrawTransition, AddressOf transition_DrawTransition ' Initialize transition: specify a number of intermediate frames and ' the transition object. frame.IntermediateFrameCount = intermediateFrameCount frame.Transition = transition ' Iterate each bitmap in the input array and add it to the writer. ' When a frame is added to the writer, transition frames are automatically ' calculated. Dim I As Integer For I = 0 To inputFilenames.Length - 1 tempBitmap.Load(inputFilenames(I)) frame.SetBitmap(tempBitmap) writer.AddFrame(frame) Next ' Clean up. writer.Close() End Sub ''' <summary> ''' This event handler applies the custom transition effect. This transition zooms the ''' previous frame out. ''' </summary> ''' <param name="sender">Event sender.</param> ''' <param name="e">Event arguments.</param> Private Shared Sub transition_DrawTransition(ByVal sender As Object, _ ByVal e As Aurigma.GraphicsMill.Codecs.AviDrawTransitionEventArgs) ' Calculate ratio of the next frame "presence". Dim ratio As Single = CSng(e.IntermediateFrameIndex) / CSng(e.IntermediateFrameCount) ' Draw the next frame first. e.NextBitmap.Draw(e.ResultBitmap, _ 0, 0, 0, 0, 0, 0, 0, 0, _ Aurigma.GraphicsMill.Transforms.CombineMode.Copy, 1.0F, _ Aurigma.GraphicsMill.Transforms.InterpolationMode.HighSpeed) ' Draw zoomed previous frame. Zoom factor depends on the ratio variable. ' When ratio = 0, the previous frame completely overlaps the next frame. ' When ratio = 1, the previous frame disappears. e.PreviousBitmap.Draw(e.ResultBitmap, _ ratio * e.NextBitmap.Width / 2.0F, _ ratio * e.NextBitmap.Height / 2.0F, _ (1.0F - ratio) * e.NextBitmap.Width, _ (1.0F - ratio) * e.NextBitmap.Height, _ 0, 0, 0, 0, _ Aurigma.GraphicsMill.Transforms.CombineMode.Copy, _ 1.0F, Aurigma.GraphicsMill.Transforms.InterpolationMode.MediumQuality) End Sub
/// <summary> /// Generates an AVI file from a number of static image files with the custom /// transition effect which resizes a previous frame and draws it in the center /// of the next one. /// </summary> /// <param name="inputFilenames">An array of filenames to get frames from. /// There are should be at least two items in this array.</param> /// <param name="outputFilename">Output file name.</param> /// <param name="intermediateFrameCount">Speed of the effect /// (number of frames used to replace the previous image by the current one).</param> public static void CreatingTransitionEffects_Custom(string[] inputFilenames, string outputFilename, int intermediateFrameCount) { // Create and open the writer object. Aurigma.GraphicsMill.Codecs.AviWriter writer = new Aurigma.GraphicsMill.Codecs.AviWriter(); writer.Open(outputFilename); Aurigma.GraphicsMill.Bitmap tempBitmap = new Aurigma.GraphicsMill.Bitmap(); Aurigma.GraphicsMill.Codecs.AviFrame frame = new Aurigma.GraphicsMill.Codecs.AviFrame(); // Create transition object and add the event handler which will apply the effect. Aurigma.GraphicsMill.Codecs.AviCustomTransition transition = new Aurigma.GraphicsMill.Codecs.AviCustomTransition(); transition.DrawTransition += new Aurigma.GraphicsMill.Codecs.AviDrawTransitionEventHandler( transition_DrawTransition); // Initialize transition: specify a number of intermediate frames and // the transition object. frame.IntermediateFrameCount = intermediateFrameCount; frame.Transition = transition; // Iterate each bitmap in the input array and add it to the writer. // When a frame is added to the writer, transition frames are automatically // calculated. for (int i=0;i<inputFilenames.Length;i++) { tempBitmap.Load(inputFilenames[i]); frame.SetBitmap(tempBitmap); writer.AddFrame(frame); } // Clean up. writer.Close(); } /// <summary> /// This event handler applies the custom transition effect. This transition zooms /// the previous frame out. /// </summary> /// <param name="sender">Event sender.</param> /// <param name="e">Event arguments.</param> private static void transition_DrawTransition(object sender, Aurigma.GraphicsMill.Codecs.AviDrawTransitionEventArgs e) { // Calculate ratio of the next frame "presence". float ratio = (float)e.IntermediateFrameIndex / e.IntermediateFrameCount; // Draw the next frame first. e.NextBitmap.Draw(e.ResultBitmap, 0, 0, 0, 0, 0, 0, 0, 0, Aurigma.GraphicsMill.Transforms.CombineMode.Copy, 1.0f, Aurigma.GraphicsMill.Transforms.InterpolationMode.HighSpeed); // Draw zoomed previous frame. Zoom factor depends on the ratio variable. // When ratio = 0, the previous frame completely overlaps the next frame. // When ratio = 1, the previous frame disappears. e.PreviousBitmap.Draw(e.ResultBitmap, ratio * e.NextBitmap.Width / 2.0f, ratio * e.NextBitmap.Height / 2.0f, (1.0f - ratio) * e.NextBitmap.Width, (1.0f - ratio) * e.NextBitmap.Height, 0, 0, 0, 0, Aurigma.GraphicsMill.Transforms.CombineMode.Copy, 1.0f, Aurigma.GraphicsMill.Transforms.InterpolationMode.MediumQuality); }