Motion tracking and Pan/Crop - Vegas 16 Pro

mathieu-r wrote on 10/9/2018, 10:19 PM

Hi all,

Is there a way to copy the keyframes computed by the bezier masking / tracking FX into the pan/crop event. Mostly interested by the timeframe and position (x,y) of the keyframes. This could be a very valuable tool for stabilization of the video when the subject needs to be kept in the center of the frame.

Or is there another simple trick available to accomplish such a feat that I'm missing?

Thanks for the help,




musko wrote on 10/10/2018, 3:14 AM

Try use this one [BETA] Pin Image/Video to motion track

mathieu-r wrote on 10/10/2018, 3:46 AM

Thanks Musko

- Had already tried. Either I didn't do it right or it is just not able to accomplish the effect I am trying to get. Basically the whole PiP frame was just following the motion track recorded. So the movement of the tracked item, applied to the same frame, ends up doubling the final x and y translations. It could work if there was a way to use the inverse of the motion track x & y....

Gives me an idea. Maybe I can record the motion track with the video rotated by 180deg, then apply this to movement to the original video... I'm going to try this now and post about the result later.

mathieu-r wrote on 10/10/2018, 7:57 AM

Well, it's a bit of a reach around, but it works! Great for x & y stabilization, not so much for rotational shakes though... Stabilization FX should help with this at this point.

klt wrote on 11/14/2018, 5:33 AM

I was fiddling with for the last 2 days.

I modified the copy motion track script, and it became a script, which does what I really need to.

It's now my "Copy motion track to pancrop.cs":

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using ScriptPortal.Vegas;public class EntryPoint
    public void FromVegas(Vegas vegas)
        myVegas = vegas;        VideoEvent bezierTrack = null;
        List<OFXParameter> locationTracking = null;        List<TrackEvent> trackEvents = FindAllSelectedEventsUnderCursor();        if (trackEvents.Count < 2)
            MessageBox.Show(missingSelectionString, "No selected event", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            bool hasBezierFx = false;
            foreach (VideoEvent trackEvent in trackEvents)
                List<Effect> fxList = new List<Effect>(trackEvent.Effects);
                Effect bezierFX = fxList.Find(x => (x.PlugIn.UniqueID == "{Svfx:com.sonycreativesoftware:bzmasking}"));
                hasBezierFx = bezierFX != null;                if (hasBezierFx)
                    List<OFXParameter> ofxList = new List<OFXParameter>(bezierFX.OFXEffect.Parameters);
                    List<OFXParameter> param = ofxList.FindAll(x => (x.Name.Contains("Location")));                    locationTracking = param.FindAll(x =>
                        OFXDouble2DParameter parameter = (OFXDouble2DParameter) x;
                        return parameter.Keyframes.Count > 0;
                    });                    if (param != null)
                        bezierTrack = trackEvent;
            }            if (!hasBezierFx)
                MessageBox.Show(missingTrackingDataString, "VEGAS B\u00E9zier Masking not applied",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }        if (locationTracking == null)
            MessageBox.Show(missingTrackingDataString, "No tracking data", MessageBoxButtons.OK,
                int maskNumber = 0;
                foreach (VideoEvent trackEvent in trackEvents)
                    Timecode startDiff = trackEvent.Start - bezierTrack.Start;
                    // List<OFXParameter> ofxList = new List<OFXParameter>(pipFX.OFXEffect.Parameters);
                    // OFXParameter param = ofxList.Find(x => (x.Name == "Location"));
                                        // if (param != null)
                    // {
                         OFXDouble2DParameter locationTrackingParam = (OFXDouble2DParameter) locationTracking[0];
                        if (locationTracking.Count > 1)
                            List<string> listLocation = new List<string>();
                            locationTracking.ForEach(x => listLocation.Add(x.Name));
                            using (MultiMaskDialog multiMaskPrompt = new MultiMaskDialog(listLocation))
                                maskNumber = multiMaskPrompt.Mask;
                                locationTrackingParam = (OFXDouble2DParameter) locationTracking[maskNumber];
                        // OFXDouble2DParameter pipLocation = (OFXDouble2DParameter) param;                        Timecode relativeCursorTime = myVegas.Transport.CursorPosition - bezierTrack.Start;
                        // OFXDouble2D initLocationAtCursorPosition = pipLocation.GetValueAtTime(relativeCursorTime);
                        OFXDouble2D locationAtCursorPosition =
                            locationTrackingParam.GetValueAtTime(relativeCursorTime);                        // OFXDouble2D diff = new OFXDouble2D
                        // {
                            // X = locationAtCursorPosition.X - initLocationAtCursorPosition.X,
                            // Y = locationAtCursorPosition.Y - initLocationAtCursorPosition.Y
                        // };                        // pipLocation.Keyframes.Clear();
                        double Rotation = trackEvent.VideoMotion.Keyframes[0].Rotation ;
                        VideoMotionBounds InitialBounds = trackEvent.VideoMotion.Keyframes[0].Bounds;
                        VideoKeyframeType Type = trackEvent.VideoMotion.Keyframes[0].Type ;
                        Single Smooth = trackEvent.VideoMotion.Keyframes[0].Smoothness ;
                        double Width  = InitialBounds.TopRight.X   - InitialBounds.TopLeft.X;
                        double Height = InitialBounds.BottomLeft.Y - InitialBounds.TopLeft.Y;
                        double CenterX = trackEvent.VideoMotion.Keyframes[0].Center.X;
                        double CenterY = trackEvent.VideoMotion.Keyframes[0].Center.Y;
                        OFXDouble2D Initialcenter = locationTrackingParam.Keyframes[0].Value;
                        double InitialDiffX = (CenterX - 1920 * Initialcenter.X );
                        double InitialDiffY = (CenterY - 1080 * (1-Initialcenter.Y));
                        foreach (var keyframe in locationTrackingParam.Keyframes)
                            VideoMotionKeyframe key1 = new VideoMotionKeyframe(Project.ActiveProject, keyframe.Time); // - startDiff);
                            trackEvent.VideoMotion.Keyframes.Add(key1) ;
                            key1.Type = Type ;
                            key1.Smoothness = Smooth;
                            VideoMotionBounds newbounds = new VideoMotionBounds(InitialBounds.TopLeft, InitialBounds.TopRight, InitialBounds.BottomRight, InitialBounds.BottomLeft);
                            OFXDouble2D tmpValue = keyframe.Value;
                            float X = (float) (1920 * tmpValue.X + InitialDiffX);
                            float Y = (float) (1080 * (1- tmpValue.Y) + InitialDiffY);
                            newbounds.TopRight.X = (float) (X + Width / 2);
                            newbounds.TopLeft.X = (float) (X - Width / 2);
                            newbounds.BottomLeft.X = newbounds.TopLeft.X ;
                            newbounds.BottomRight.X = newbounds.TopRight.X ;
                            newbounds.TopLeft.Y = (float) ( Y - Height / 2 ) ;
                            newbounds.BottomLeft.Y = (float) ( Y + Height / 2 ) ;
                            newbounds.TopRight.Y = newbounds.TopLeft.Y ;
                            newbounds.BottomRight.Y = newbounds.BottomLeft.Y;
                            key1.Bounds = newbounds ;
                            key1.Center.X = X;
                             key1.Center.Y = Y;
                MessageBox.Show("Done with copying location from Mask " + (maskNumber + 1) + ".");
    }    /// <summary>
    /// Returns the first selected event that's under the cursor
    /// </summary>
    /// <returns>The first selected event or null if no event selected</returns>
    List<TrackEvent> FindAllSelectedEventsUnderCursor()
        List<TrackEvent> selectedEventsAcrossTracks = new List<TrackEvent>();        foreach (Track track in myVegas.Project.Tracks)
            foreach (TrackEvent trackEvent in track.Events)
                if (trackEvent.Selected && trackEvent.Start <= myVegas.Transport.CursorPosition &&
                    trackEvent.End >= myVegas.Transport.CursorPosition)
        }        return selectedEventsAcrossTracks;
    }    Vegas myVegas;    const string missingSelectionString = "Please select the video event to which you've applied tracking and the overlapping video event on a different track that holds the media you want to pin to the tracking.";    const string missingTrackingDataString = "You must first apply the B\u00E9zier masking plug-in to one of the selected events and add motion tracking data for any mask.";    const string missingPipFxString = "You must first add the Picture in Picture plug-in to the event that you want to pin to the motion tracked event.";
}public class MultiMaskDialog : Form
    #region Windows Form Designer generated code    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
        this.cbox_mask = new System.Windows.Forms.ComboBox();
        this.btn_okay = new System.Windows.Forms.Button();
        this.label = new System.Windows.Forms.Label();
        // cbox_mask
        this.cbox_mask.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
        | System.Windows.Forms.AnchorStyles.Right)));
        this.cbox_mask.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
        this.cbox_mask.FormattingEnabled = true;
        this.cbox_mask.Location = new System.Drawing.Point(13, 34);
        this.cbox_mask.Name = "cbox_mask";
        this.cbox_mask.Size = new System.Drawing.Size(260, 21);
        this.cbox_mask.TabIndex = 0;
        // btn_okay
        this.btn_okay.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
        this.btn_okay.Location = new System.Drawing.Point(197, 61);
        this.btn_okay.Name = "btn_okay";
        this.btn_okay.Size = new System.Drawing.Size(75, 23);
        this.btn_okay.TabIndex = 1;
        this.btn_okay.Text = "Ok";
        this.btn_okay.UseVisualStyleBackColor = true;
        this.btn_okay.Click += new System.EventHandler(this.btn_okay_Click);
        // label1
        this.label.AutoSize = true;
        this.label.Location = new System.Drawing.Point(13, 13);
        this.label.Name = "label";
        this.label.Size = new System.Drawing.Size(170, 13);
        this.label.TabIndex = 2;
        this.label.Text = "Please choose your tracked mask:";
        // Form1
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(285, 102);
        this.ControlBox = false;
        this.MaximizeBox = false;
        this.MinimizeBox = false;
        this.Name = "Form";
        this.ShowIcon = false;
        this.ShowInTaskbar = false;
        this.Text = "Multiple masks found";
        this.TopMost = true;
        this.PerformLayout();    }
        //..................................    private System.Windows.Forms.ComboBox cbox_mask;
    private System.Windows.Forms.Button btn_okay;
    private System.Windows.Forms.Label label;    //..................................    private int maskNumber = 0;    public MultiMaskDialog(List<string> maskList)
        maskList.ForEach(x =>
            int accumulate = int.Parse(x.Split('_')[1]) + 1;
            cbox_mask.Items.Add("Mask " + accumulate);
        cbox_mask.SelectedIndex = 0;
    }    private void btn_okay_Click(object sender, EventArgs e)
        string[] maskNumberStr = cbox_mask.SelectedItem.ToString().Split(' ');
        if (maskNumberStr.Length > 1)
            maskNumber = int.Parse(maskNumberStr[1]) - 1;
    }    public int Mask
        get { return maskNumber; }

Video resolution (1920*1080) is hardcoded, so there's still some work with it.

But I'm very pleased with it already. I often get recorded speech with badly composed picture, which then I have to crop and track in post. This script will save my hundreds of working hours :)

In this case I need to follow only X movement, so I start another script, the X version.

Difference is only on line 135:

float Y = (float) CenterY;


And a small video, how it worked:

Last changed by klt on 11/14/2018, 5:37 AM, changed a total of 1 times.

Camera: JVC GY-HM600

Desktop: AMD Ryzen 5 1600, 16GB RAM (dual channel 2400 MHz) - Videocard: Radeon R9 380 2GB

Laptop: i5 5200u, 8GB RAM (1600MHz single channel) Videocard: integrated HD5500

mathieu-r wrote on 11/16/2018, 1:08 AM

Very cool, I'll have to give it a test run sometime soon... a bit busy with other things now. Cheers!

klt wrote on 11/16/2018, 1:36 AM

I hope you'll like it.

Remember to set up your cropping in the target event before you run the script. ;)


Former user wrote on 2/23/2019, 8:33 AM

Great work @klt

The native motion tracking allows up to 5 masks. How do I choose which mask to copy using this script?

klt wrote on 2/23/2019, 9:42 AM

Thanks@Former user

For keep it simple, it takes only the first mask.

The line:

 int maskNumber = 0;

chooses it.

Feel free to modify the script in any way you like 🙂



Rocky-Ciasulli wrote on 3/4/2019, 4:21 PM

I am currently working on a project where I would be able to use this "Copy motion track to pancrop" script, however when I tried to run it on my project, I received the below error message. (attached a picture and copied and pasted below)

Does anyone have any idea why I might have received this error, or otherwise have any advice for me? Any information would be greatly appreciated, thank you very much.

C:\Program Files\VEGAS\VEGAS Pro 16.0\Script Menu\Copy Motion Track to pancrop.cs(213) : Invalid token '{' in class, struct, or interface member declaration
C:\Program Files\VEGAS\VEGAS Pro 16.0\Script Menu\Copy Motion Track to pancrop.cs(214) : Method must have a return type
C:\Program Files\VEGAS\VEGAS Pro 16.0\Script Menu\Copy Motion Track to pancrop.cs(215) : Invalid token '(' in class, struct, or interface member declaration
C:\Program Files\VEGAS\VEGAS Pro 16.0\Script Menu\Copy Motion Track to pancrop.cs(216) : Invalid token '(' in class, struct, or interface member declaration
C:\Program Files\VEGAS\VEGAS Pro 16.0\Script Menu\Copy Motion Track to pancrop.cs(216) : Invalid token '=>' in class, struct, or interface member declaration
C:\Program Files\VEGAS\VEGAS Pro 16.0\Script Menu\Copy Motion Track to pancrop.cs(219) : Invalid token '(' in class, struct, or interface member declaration
C:\Program Files\VEGAS\VEGAS Pro 16.0\Script Menu\Copy Motion Track to pancrop.cs(219) : Invalid token ')' in class, struct, or interface member declaration
C:\Program Files\VEGAS\VEGAS Pro 16.0\Script Menu\Copy Motion Track to pancrop.cs(220) : A namespace cannot directly contain members such as fields or methods
C:\Program Files\VEGAS\VEGAS Pro 16.0\Script Menu\Copy Motion Track to pancrop.cs(222) : Type or namespace definition, or end-of-file expected

xberk wrote on 3/4/2019, 4:41 PM

klt fixed the problem in another thread with a different download .. here's how ..

klt wrote on 2/25/2019, 10:05 PM

Seems your preferred text editors -Notepad?- inserts unwanted linebreaks, wordrwraps on paste.

Now I remember someone also had a problem with this and I uploaded the zipped scripts, but I forgot I did it.

Sorry for being so forgetful.

klt wrote on 3/4/2019, 11:54 PM

Thank you mr @xberk 😀



That another thread is here:

There you will find a somewhat updated version of this script, and also some examples about how I think it is useful, and how I use it.