2018-06-24 02:56:03 -05:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2018-07-02 23:31:16 -05:00
|
|
|
|
using System.Collections.ObjectModel;
|
2018-06-24 02:56:03 -05:00
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using csmic;
|
|
|
|
|
|
|
|
|
|
namespace butterflow_ui
|
|
|
|
|
{
|
|
|
|
|
|
2018-07-03 18:59:28 -05:00
|
|
|
|
/// <summary> The butterflow options configuration. Contians all the options necessary to run butterflow and process a video. </summary>
|
2018-06-24 02:56:03 -05:00
|
|
|
|
[Serializable]
|
2018-07-02 23:31:16 -05:00
|
|
|
|
public class OptionsConfiguration : PropertyChangedAlerter
|
2018-06-24 02:56:03 -05:00
|
|
|
|
{
|
|
|
|
|
#region Members
|
|
|
|
|
|
2018-07-01 17:02:50 -05:00
|
|
|
|
/// <summary> An interpreter used to ensure numeric input is correctly calculated. </summary>
|
2018-06-24 02:56:03 -05:00
|
|
|
|
private InputInterpreter interpreter = new InputInterpreter();
|
2018-07-03 18:59:28 -05:00
|
|
|
|
/// <summary> The aspect ratio used for calculating heights when the aspect ratio is locked. </summary>
|
|
|
|
|
private decimal aspectRatio = 0;
|
2018-06-24 02:56:03 -05:00
|
|
|
|
|
|
|
|
|
private string playbackRate;
|
|
|
|
|
private bool keepAudio;
|
|
|
|
|
private int width;
|
|
|
|
|
private int height;
|
2018-07-01 17:02:50 -05:00
|
|
|
|
private bool keepAspectRatio;
|
2018-06-24 03:29:34 -05:00
|
|
|
|
private bool losslessQuality;
|
2018-07-03 18:59:28 -05:00
|
|
|
|
private bool smoothMotion;
|
|
|
|
|
private bool lockAspectRatio;
|
2018-07-01 17:02:50 -05:00
|
|
|
|
private string videoInput;
|
|
|
|
|
private string videoOutput;
|
2018-07-03 18:59:28 -05:00
|
|
|
|
private bool fastPyramid;
|
|
|
|
|
private decimal pyramidScale;
|
|
|
|
|
private int levels;
|
|
|
|
|
private int windowSize;
|
|
|
|
|
private int iterations;
|
|
|
|
|
private int pixelNeighborhood;
|
|
|
|
|
private decimal smoothDerivativeStandardDeviation;
|
2018-07-02 23:31:16 -05:00
|
|
|
|
private ObservableCollection<ButterflowSubregion> subregions = new ObservableCollection<ButterflowSubregion>();
|
2018-06-24 02:56:03 -05:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Properties
|
|
|
|
|
|
2018-07-01 17:02:50 -05:00
|
|
|
|
/// <summary> Gets the command line output given the current configuration. </summary>
|
|
|
|
|
/// <value> The command line output. </value>
|
|
|
|
|
public string CommandLineOutput
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return ToButterflowArguments();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-24 02:56:03 -05:00
|
|
|
|
/// <summary> Gets or sets the playback rate. </summary>
|
|
|
|
|
/// <value> The playback rate. </value>
|
|
|
|
|
public string PlaybackRate
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.playbackRate;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
this.playbackRate = value;
|
2018-07-02 23:31:16 -05:00
|
|
|
|
OnPropertyChanged();
|
2018-06-24 02:56:03 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Gets or sets a value indicating whether the keep audio. </summary>
|
|
|
|
|
/// <value> True if keep audio, false if not. </value>
|
|
|
|
|
public bool KeepAudio
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.keepAudio;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
this.keepAudio = value;
|
2018-07-02 23:31:16 -05:00
|
|
|
|
OnPropertyChanged();
|
2018-06-24 02:56:03 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-03 18:59:28 -05:00
|
|
|
|
/// <summary> Gets or sets a value indicating whether the butterflow should be turned toward smooth motion. </summary>
|
|
|
|
|
/// <value> True if tuned toward smooth motion, false if not. </value>
|
|
|
|
|
public bool SmoothMotion
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.smoothMotion;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
this.smoothMotion = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Gets or sets a value indicating whether to lock aspect ratio of the video. </summary>
|
|
|
|
|
/// <value> True if locking aspect ratio of the video, false if not. </value>
|
|
|
|
|
public bool LockAspectRatio
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.lockAspectRatio;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value)
|
|
|
|
|
{
|
|
|
|
|
this.aspectRatio = Convert.ToDecimal(this.height) / Convert.ToDecimal(this.width);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.lockAspectRatio = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-01 17:02:50 -05:00
|
|
|
|
/// <summary> Gets or sets the width of the video output. </summary>
|
|
|
|
|
/// <value> The width of the video output. </value>
|
2018-06-24 02:56:03 -05:00
|
|
|
|
public string Width
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.width.ToString();
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
2018-07-03 18:59:28 -05:00
|
|
|
|
var oldWidth = this.width;
|
|
|
|
|
|
2018-06-24 02:56:03 -05:00
|
|
|
|
interpreter.Interpret(value);
|
|
|
|
|
this.width = interpreter.Int;
|
2018-07-03 18:59:28 -05:00
|
|
|
|
|
2018-07-02 23:31:16 -05:00
|
|
|
|
OnPropertyChanged();
|
2018-07-03 18:59:28 -05:00
|
|
|
|
|
|
|
|
|
if (this.lockAspectRatio)
|
|
|
|
|
{
|
|
|
|
|
interpreter.Interpret(string.Format("{0} * {1}", this.aspectRatio, this.width));
|
|
|
|
|
this.height = interpreter.Int;
|
|
|
|
|
|
|
|
|
|
OnPropertyChanged("Height");
|
|
|
|
|
}
|
2018-06-24 02:56:03 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-01 17:02:50 -05:00
|
|
|
|
/// <summary> Gets or sets the height of the video output. </summary>
|
|
|
|
|
/// <value> The height of the video output. </value>
|
2018-06-24 02:56:03 -05:00
|
|
|
|
public string Height
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.height.ToString();
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
interpreter.Interpret(value);
|
|
|
|
|
this.height = interpreter.Int;
|
2018-07-02 23:31:16 -05:00
|
|
|
|
OnPropertyChanged();
|
2018-06-24 02:56:03 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-01 17:02:50 -05:00
|
|
|
|
/// <summary> Gets or sets a value indicating whether the keep aspect ratio of the input video file for the output video file. </summary>
|
|
|
|
|
/// <value> True if keep aspect ratio, false if not. </value>
|
|
|
|
|
public bool KeepAspectRatio
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.keepAspectRatio;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
this.keepAspectRatio = value;
|
2018-07-02 23:31:16 -05:00
|
|
|
|
OnPropertyChanged();
|
2018-07-01 17:02:50 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-24 03:29:34 -05:00
|
|
|
|
/// <summary> Gets or sets a value indicating whether the result is rendered in lossless quality. </summary>
|
|
|
|
|
/// <value> True if lossless quality is selected, false if not. </value>
|
|
|
|
|
public bool LosslessQuality
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.losslessQuality;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
this.losslessQuality = value;
|
2018-07-02 23:31:16 -05:00
|
|
|
|
OnPropertyChanged();
|
2018-06-24 03:29:34 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-01 17:02:50 -05:00
|
|
|
|
/// <summary> Gets or sets the video input file path. </summary>
|
|
|
|
|
/// <value> The video input file path. </value>
|
|
|
|
|
public string VideoInput
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.videoInput;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
this.videoInput = value;
|
2018-07-02 23:31:16 -05:00
|
|
|
|
OnPropertyChanged();
|
2018-07-01 17:02:50 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Gets or sets the video output file path. </summary>
|
|
|
|
|
/// <value> The video output file path. </value>
|
|
|
|
|
public string VideoOutput
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.videoOutput;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
this.videoOutput = value;
|
2018-07-02 23:31:16 -05:00
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-03 18:59:28 -05:00
|
|
|
|
/// <summary> Gets or sets a value indicating whether to use fast pyramids. </summary>
|
|
|
|
|
/// <value> True if using fast pyramids, false if not. </value>
|
|
|
|
|
public bool FastPyramid
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.fastPyramid;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
this.fastPyramid = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Gets or sets the pyramid scale factor. </summary>
|
|
|
|
|
/// <value> The pyramid scale factor. </value>
|
|
|
|
|
public string PyramidScale
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.pyramidScale.ToString();
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
interpreter.Interpret(value);
|
|
|
|
|
this.pyramidScale = interpreter.Decimal;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Gets or sets the number of pyramid layers. </summary>
|
|
|
|
|
/// <value> The number of pyramid layers. </value>
|
|
|
|
|
public string Levels
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.levels.ToString();
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
interpreter.Interpret(value);
|
|
|
|
|
this.levels = interpreter.Int;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Gets or sets the size of the windowing average. </summary>
|
|
|
|
|
/// <value> The size of the windowing average. </value>
|
|
|
|
|
public string WindowSize
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.windowSize.ToString();
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
interpreter.Interpret(value);
|
|
|
|
|
this.windowSize = interpreter.Int;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Gets or sets the number of iterations at each pyramid level. </summary>
|
|
|
|
|
/// <value> The number of iterations at each pyramid level. </value>
|
|
|
|
|
public string Iterations
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.iterations.ToString();
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
interpreter.Interpret(value);
|
|
|
|
|
this.iterations = interpreter.Int;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Gets or sets the size of the pixel neighborhood. </summary>
|
|
|
|
|
/// <value> The size of the pixel neighborhood. </value>
|
|
|
|
|
public string PixelNeighborhood
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.pixelNeighborhood.ToString();
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
interpreter.Interpret(value);
|
|
|
|
|
this.pixelNeighborhood = interpreter.Int;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Gets or sets the standard deviation of smooth derivatives. </summary>
|
|
|
|
|
/// <value> The standard deviation of smooth derivatives. </value>
|
|
|
|
|
public string SmoothDerivativeStandardDeviation
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.smoothDerivativeStandardDeviation.ToString();
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
interpreter.Interpret(value);
|
|
|
|
|
this.smoothDerivativeStandardDeviation = interpreter.Decimal;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-02 23:31:16 -05:00
|
|
|
|
/// <summary> Gets or sets the subregions of the video on which to work. </summary>
|
|
|
|
|
/// <value> The subregions of the video. </value>
|
|
|
|
|
public ObservableCollection<ButterflowSubregion> Subregions
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.subregions;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
this.subregions = value;
|
|
|
|
|
OnPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Contructors
|
|
|
|
|
|
|
|
|
|
/// <summary> Default constructor. </summary>
|
|
|
|
|
public OptionsConfiguration()
|
|
|
|
|
{
|
|
|
|
|
AddConstantCallProperty("CommandLineOutput");
|
|
|
|
|
|
|
|
|
|
this.subregions.CollectionChanged += SubregionsCollectionChanged;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SubregionsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
|
|
|
|
{
|
2018-07-03 18:59:28 -05:00
|
|
|
|
if (e.NewItems != null)
|
2018-07-02 23:31:16 -05:00
|
|
|
|
{
|
2018-07-03 18:59:28 -05:00
|
|
|
|
foreach (ButterflowSubregion newItem in e.NewItems)
|
2018-07-02 23:31:16 -05:00
|
|
|
|
{
|
|
|
|
|
newItem.PropertyChanged += SubregionPropertyChanged;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-03 18:59:28 -05:00
|
|
|
|
if (e.OldItems != null)
|
2018-07-02 23:31:16 -05:00
|
|
|
|
{
|
2018-07-03 18:59:28 -05:00
|
|
|
|
foreach (ButterflowSubregion oldItem in e.OldItems)
|
2018-07-02 23:31:16 -05:00
|
|
|
|
{
|
|
|
|
|
oldItem.PropertyChanged -= SubregionPropertyChanged;
|
|
|
|
|
}
|
2018-07-01 17:02:50 -05:00
|
|
|
|
}
|
2018-07-02 23:31:16 -05:00
|
|
|
|
|
|
|
|
|
OnPropertyChanged("CommandLineOutput");
|
2018-07-01 17:02:50 -05:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-24 02:56:03 -05:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Methods
|
|
|
|
|
|
2018-07-02 23:31:16 -05:00
|
|
|
|
private void SubregionPropertyChanged(object sender, PropertyChangedEventArgs e)
|
2018-06-24 02:56:03 -05:00
|
|
|
|
{
|
2018-07-02 23:31:16 -05:00
|
|
|
|
OnPropertyChanged("CommandLineOutput");
|
2018-07-01 17:02:50 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Converts this object to a butterflow options. </summary>
|
|
|
|
|
/// <returns> This object as a string. </returns>
|
|
|
|
|
public string ToButterflowArguments()
|
|
|
|
|
{
|
|
|
|
|
var stringBuilder = new StringBuilder("-v "); //Verbose
|
|
|
|
|
|
2018-07-02 23:31:16 -05:00
|
|
|
|
if (this.KeepAspectRatio)
|
2018-07-01 17:02:50 -05:00
|
|
|
|
{
|
|
|
|
|
stringBuilder.AppendFormat("-vs {0}:-1 ", this.Width);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
stringBuilder.AppendFormat("-vs {0}:{1} ", this.Width, this.Height);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-02 23:31:16 -05:00
|
|
|
|
if (!string.IsNullOrWhiteSpace(this.PlaybackRate)) stringBuilder.AppendFormat("-r {0} ", this.PlaybackRate);
|
2018-07-01 17:02:50 -05:00
|
|
|
|
if (this.KeepAudio) stringBuilder.Append("-audio ");
|
|
|
|
|
if (this.LosslessQuality) stringBuilder.Append("-l ");
|
|
|
|
|
|
|
|
|
|
stringBuilder.AppendFormat("\"{0}\"", this.VideoInput);
|
|
|
|
|
|
2018-07-02 23:31:16 -05:00
|
|
|
|
if (this.Subregions.Any())
|
|
|
|
|
{
|
|
|
|
|
foreach (var anon in this.Subregions.Select((sr, index) => new { Index = index, Subregion = sr }))
|
|
|
|
|
{
|
|
|
|
|
string format = "ss\\.fff";
|
|
|
|
|
|
|
|
|
|
if (anon.Index > 0)
|
|
|
|
|
{
|
|
|
|
|
stringBuilder.Append(":");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (anon.Subregion.Start.TotalHours > 1)
|
|
|
|
|
{
|
|
|
|
|
format = "h\\:m\\:s\\.fff";
|
|
|
|
|
}
|
|
|
|
|
else if (anon.Subregion.Start.TotalMinutes > 1)
|
|
|
|
|
{
|
|
|
|
|
format = "m\\:s\\.fff";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stringBuilder.AppendFormat("a={0},b={1},{2}={3}", anon.Subregion.Start.ToString(format), anon.Subregion.ToEnd ? "end" : anon.Subregion.End.ToString(format), anon.Subregion.SubregionType, anon.Subregion.Value);
|
|
|
|
|
}
|
|
|
|
|
stringBuilder.Append(" ");
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-01 17:02:50 -05:00
|
|
|
|
return stringBuilder.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary> Returns a string that represents the current object. </summary>
|
|
|
|
|
/// <returns> A string that represents the current object. </returns>
|
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
|
|
|
|
return ToButterflowArguments();
|
2018-06-24 02:56:03 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|