diff --git a/Splitter-UI/Models/PreviewData.cs b/Splitter-UI/Models/PreviewData.cs index ea76d07..9d41762 100644 --- a/Splitter-UI/Models/PreviewData.cs +++ b/Splitter-UI/Models/PreviewData.cs @@ -3,13 +3,13 @@ public class PreviewData { public Avalonia.Media.Imaging.Bitmap? Frame { get; } - public IReadOnlyList DetectedBoxes { get; } + public IReadOnlyList DetectedBoxes { get; } public Rect? CropRect { get; } public Point2f GravitateTo { get; } public TimeSpan Position { get; } public int? Rotate { get; } - public PreviewData(Avalonia.Media.Imaging.Bitmap? frame, IReadOnlyList boxes, Rect? crop, Point2f gravitateTo, TimeSpan position, int? rotate) + public PreviewData(Avalonia.Media.Imaging.Bitmap? frame, IReadOnlyList boxes, Rect? crop, Point2f gravitateTo, TimeSpan position, int? rotate) { Frame = frame; DetectedBoxes = boxes; diff --git a/Splitter-UI/Program.cs b/Splitter-UI/Program.cs index dd8f420..7bc20f8 100644 --- a/Splitter-UI/Program.cs +++ b/Splitter-UI/Program.cs @@ -39,19 +39,16 @@ internal sealed class Program services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(x => new SingleThreadedDetector(x.GetRequiredService())); - services.AddSingleton(x => new SingleThreadedDetector(x.GetRequiredService())); - services.AddSingleton(x => new SingleThreadedDetector(x.GetRequiredService())); + services.AddKeyedSingleton("face", (x,_) => new SingleThreadedDetector(x.GetRequiredService())); + services.AddKeyedSingleton("body", (x,_) => new SingleThreadedDetector(x.GetRequiredService())); + services.AddKeyedSingleton("none", (x,_) => new SingleThreadedDetector(x.GetRequiredService())); services.AddSingleton(x => new SingleThreadedEmbeddingExtractor(x.GetRequiredService())); - services.AddSingleton>( x => detectorName => + services.AddSingleton>(x => detectorName => x.GetKeyedService(detectorName) ?? new DummyDetector()); + services.AddSingleton>(x => detectorName => { - return detectorName switch - { - "face" => x.GetRequiredService>(), - "body" => x.GetRequiredService>(), - "none" => x.GetRequiredService>(), - _ => new DummyDetector() - }; + var detectorFactory = x.GetRequiredService>(); + var extractor = x.GetRequiredService(); + return new ObjectTracker(detectorFactory(detectorName), extractor); }); services.AddSingleton(); services.AddSingleton(); diff --git a/Splitter-UI/ViewModels/InspectorPaneViewModel.cs b/Splitter-UI/ViewModels/InspectorPaneViewModel.cs index 726faf8..7aba1b5 100644 --- a/Splitter-UI/ViewModels/InspectorPaneViewModel.cs +++ b/Splitter-UI/ViewModels/InspectorPaneViewModel.cs @@ -30,22 +30,7 @@ public partial class InspectorPaneViewModel : ObservableObject return; foreach (JobViewModel job in Files.Where(x => !ReferenceEquals(x, Selected))) - { - job.Detect = Selected.Detect; - job.Rotate = Selected.Rotate; - job.CropText = Selected.CropText; - job.ForceFixed = Selected.ForceFixed; - job.GravitateText = Selected.GravitateText; - job.Mask = Selected.Mask; - job.OutputFolder = Selected.OutputFolder; - job.OverrideTargetDuration = Selected.OverrideTargetDuration; - job.PassthroughText = Selected.PassthroughText; - job.Enhance = Selected.Enhance; - - job.ParametersList.Clear(); - foreach (var param in Selected.ParametersList) - job.ParametersList.Add(param); - } + job.CopyFrom(Selected); } public IRelayCommand RotateLeftCommand { get; } diff --git a/Splitter-UI/ViewModels/JobViewModel.cs b/Splitter-UI/ViewModels/JobViewModel.cs index 493c542..f192fa7 100644 --- a/Splitter-UI/ViewModels/JobViewModel.cs +++ b/Splitter-UI/ViewModels/JobViewModel.cs @@ -28,7 +28,7 @@ public partial class JobViewModel : ObservableObject private readonly IThumbnailService _thumbnails; private readonly DispatcherTimer _debounceTimer; - private readonly Func _detectorFactory; + private readonly Func _trackerFactory; private readonly ILogger _log; public string FileName => Path.GetFileName(Job.InputFile); @@ -220,6 +220,19 @@ public partial class JobViewModel : ObservableObject } } + public ulong? DetectId + { + get => Job.DetectId; + set + { + if (DetectId == value) + return; + Job.DetectId = value; + OnPropertyChanged(); + Task.Run(CreatePreview); + } + } + public double? OverrideTargetDuration { get => Job.OverrideTargetDuration; @@ -231,11 +244,12 @@ public partial class JobViewModel : ObservableObject OnPropertyChanged(); } } - public JobViewModel(SingleJob job, IThumbnailService thumbnails, Func detectorFactory, ILogger log) + + public JobViewModel(SingleJob job, IThumbnailService thumbnails, Func trackerFactory, ILogger log) { Job = job; _thumbnails = thumbnails; - _detectorFactory = detectorFactory; + _trackerFactory = trackerFactory; _log = log; ParametersList.Add(new ParameterEntry("DropoutToleranceFrames" , "")); @@ -271,6 +285,12 @@ public partial class JobViewModel : ObservableObject _debounceTimer.Tick += DebounceTimerTick; } + public void CopyFrom(JobViewModel src) + { + Job.CopyFrom(src.Job); + OnPropertyChanged(string.Empty); // Refresh all properties + } + public async Task CreatePreview() { if ( Probe == null) @@ -289,7 +309,7 @@ public partial class JobViewModel : ObservableObject Preview = new PreviewData(frame, [], null, Job.GravitateTo, pos, Job.Rotate); - var detector = _detectorFactory(Job.Detect ?? ""); + var tracker = _trackerFactory(Job.Detect ?? ""); var j = new SingleTask ( Job : Job, @@ -301,31 +321,26 @@ public partial class JobViewModel : ObservableObject SegmentLength : 1, // 1 second segment for detection ProcessorFactory: _ => throw new NotImplementedException() ); - var detections = detector.DetectAll(j, frame.ToMatContinuous()); + + var (detections, primaryDetection) = tracker.SelectTrackedObject(j, frame.ToMatContinuous(), j.Job.GravitateTo); Rect? crop = null; - if (detections.Count > 0) - { - var primaryDetection = detections - .OrderByDescending(d => d.Box.Height * d.Box.Width) - .FirstOrDefault(); + var w = Probe.Width; + var h = Probe.Height; - var w = Probe.Width; - var h = Probe.Height; + var cropWidth = Job.Crop?.width ?? CommandLine.DefaultW; + var cropHeight = Job.Crop?.height ?? CommandLine.DefaultH; - var cropWidth = Job.Crop?.width ?? CommandLine.DefaultW; - var cropHeight = Job.Crop?.height ?? CommandLine.DefaultH; + var p = primaryDetection?.Center ?? new Point2f(w * Job.GravitateTo.X, h * Job.GravitateTo.Y); - var cx = primaryDetection.Center.X - cropWidth / 2f; - var cy = primaryDetection.Center.Y - cropHeight / 2f; + var cx = p.X - cropWidth / 2f; + var cy = p.Y - cropHeight / 2f; - var r = new Rect(cx, cy, cropWidth, cropHeight); + var r = new Rect(cx, cy, cropWidth, cropHeight); - crop = ClampCrop(r, w, h); - } + crop = ClampCrop(r, w, h); - var boxes = detections.Select(x => x.Box).ToList(); - Preview = new PreviewData(frame, boxes, crop, Job.GravitateTo, pos, Job.Rotate); + Preview = new PreviewData(frame, detections ?? [], crop, Job.GravitateTo, pos, Job.Rotate); } catch (Exception ex) { diff --git a/Splitter-UI/ViewModels/PreviewPaneViewModel.cs b/Splitter-UI/ViewModels/PreviewPaneViewModel.cs index 8a45d73..58c1b2e 100644 --- a/Splitter-UI/ViewModels/PreviewPaneViewModel.cs +++ b/Splitter-UI/ViewModels/PreviewPaneViewModel.cs @@ -35,6 +35,18 @@ public partial class PreviewPaneViewModel : ObservableObject } } + public ulong? TrackedId + { + get => Selected?.DetectId; + set + { + if (Selected == null) + return; + Selected.DetectId = value; + OnPropertyChanged(nameof(TrackedId)); + } + } + partial void OnSelectedChanged(JobViewModel? oldValue, JobViewModel? newValue) { if (oldValue != null) @@ -46,6 +58,8 @@ public partial class PreviewPaneViewModel : ObservableObject OnPropertyChanged(nameof(Preview)); OnPropertyChanged(nameof(Sar)); OnPropertyChanged(nameof(Rotate)); + OnPropertyChanged(nameof(TrackedId)); + OnPropertyChanged(nameof(DetectAbove)); } private void SelectedPropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/Splitter-UI/Views/InspectorPane.axaml b/Splitter-UI/Views/InspectorPane.axaml index 3b0cd7f..6e5d301 100644 --- a/Splitter-UI/Views/InspectorPane.axaml +++ b/Splitter-UI/Views/InspectorPane.axaml @@ -9,6 +9,21 @@ x:DataType="vm:InspectorPaneViewModel"> + + +