fix inference UI and annotation saving

This commit is contained in:
Alex Bezdieniezhnykh
2025-01-30 12:33:24 +02:00
parent 62623b7123
commit e7afa96a0b
6 changed files with 47 additions and 163 deletions
+37 -159
View File
@@ -10,7 +10,6 @@ using Azaion.Annotator.DTO;
using Azaion.Common.Database;
using Azaion.Common.DTO;
using Azaion.Common.DTO.Config;
using Azaion.Common.DTO.Queue;
using Azaion.Common.Events;
using Azaion.Common.Extensions;
using Azaion.Common.Services;
@@ -45,6 +44,9 @@ public partial class Annotator
private ObservableCollection<DetectionClass> AnnotationClasses { get; set; } = new();
private bool _suspendLayout;
public bool FollowAI = false;
public bool IsInferenceNow = false;
private readonly TimeSpan _thresholdBefore = TimeSpan.FromMilliseconds(100);
private readonly TimeSpan _thresholdAfter = TimeSpan.FromMilliseconds(300);
@@ -53,7 +55,6 @@ public partial class Annotator
public ObservableCollection<MediaFileInfo> FilteredMediaFiles { get; set; } = new();
public IntervalTree<TimeSpan, Annotation> TimedAnnotations { get; set; } = new();
private AutodetectDialog _autoDetectDialog = new() { Topmost = true };
public Annotator(
IConfigUpdater configUpdater,
@@ -166,7 +167,12 @@ public partial class Annotator
}
};
LvFiles.MouseDoubleClick += async (_, _) => await _mediator.Publish(new AnnotatorControlEvent(PlaybackControlEnum.Play));
LvFiles.MouseDoubleClick += async (_, _) =>
{
if (IsInferenceNow)
FollowAI = false;
await _mediator.Publish(new AnnotatorControlEvent(PlaybackControlEnum.Play));
};
LvClasses.SelectionChanged += (_, _) =>
{
@@ -224,6 +230,8 @@ public partial class Annotator
public void OpenAnnotationResult(AnnotationResult res)
{
if (IsInferenceNow)
FollowAI = false;
_mediaPlayer.SetPause(true);
Editor.RemoveAllAnns();
_mediaPlayer.Time = (long)res.Annotation.Time.TotalMilliseconds;
@@ -399,9 +407,9 @@ public partial class Annotator
Process.Start("explorer.exe", "/select,\"" + mediaFileInfo.Path +"\"");
}
public void SeekTo(long timeMilliseconds)
public void SeekTo(long timeMilliseconds, bool setPause = true)
{
_mediaPlayer.SetPause(true);
_mediaPlayer.SetPause(setPause);
_mediaPlayer.Time = timeMilliseconds;
VideoSlider.Value = _mediaPlayer.Position * 100;
@@ -446,6 +454,8 @@ public partial class Annotator
private void PlayClick(object sender, RoutedEventArgs e)
{
if (IsInferenceNow)
FollowAI = false;
_mediator.Publish(new AnnotatorControlEvent(_mediaPlayer.CanPause ? PlaybackControlEnum.Pause : PlaybackControlEnum.Play));
}
@@ -478,8 +488,14 @@ public partial class Annotator
private (TimeSpan Time, List<Detection> Detections)? _previousDetection;
public async void AutoDetect(object sender, RoutedEventArgs e)
public void AutoDetect(object sender, RoutedEventArgs e)
{
if (IsInferenceNow)
{
FollowAI = true;
return;
}
if (LvFiles.Items.IsEmpty)
return;
if (LvFiles.SelectedIndex == -1)
@@ -487,28 +503,14 @@ public partial class Annotator
var mct = new CancellationTokenSource();
var token = mct.Token;
Dispatcher.Invoke(() => Editor.ResetBackground());
_autoDetectDialog = new AutodetectDialog
{
Topmost = true,
Owner = this
};
_autoDetectDialog.Closing += (_, _) =>
{
mct.Cancel();
_mediaPlayer.SeekTo(TimeSpan.Zero);
Editor.RemoveAllAnns();
};
_autoDetectDialog.Top = Height - _autoDetectDialog.Height - 80;
_autoDetectDialog.Left = 5;
_autoDetectDialog.Log("Ініціалізація AI...");
IsInferenceNow = true;
FollowAI = true;
_ = Task.Run(async () =>
{
var mediaInfo = Dispatcher.Invoke(() => (MediaFileInfo)LvFiles.SelectedItem);
while (mediaInfo != null)
while (mediaInfo != null && !token.IsCancellationRequested)
{
await Dispatcher.Invoke(async () =>
{
@@ -516,11 +518,11 @@ public partial class Annotator
await ReloadAnnotations();
});
await _inferenceService.RunInference(mediaInfo.Path, async (annotationImage, ct) =>
await _inferenceService.RunInference(mediaInfo.Path, async annotationImage =>
{
annotationImage.OriginalMediaName = mediaInfo.FName;
await ProcessDetection(annotationImage, ct);
}, token);
await ProcessDetection(annotationImage);
});
mediaInfo = Dispatcher.Invoke(() =>
{
@@ -533,156 +535,32 @@ public partial class Annotator
}
Dispatcher.Invoke(() =>
{
_autoDetectDialog.Close();
_mediaPlayer.Stop();
LvFiles.Items.Refresh();
IsInferenceNow = false;
});
}, token);
_autoDetectDialog.ShowDialog();
Dispatcher.Invoke(() => Editor.ResetBackground());
}
// private async Task DetectImage(MediaFileInfo mediaInfo, CancellationTokenSource manualCancellationSource, CancellationToken token)
// {
// try
// {
// var fName = Path.GetFileNameWithoutExtension(mediaInfo.Path);
// var stream = new FileStream(mediaInfo.Path, FileMode.Open);
// var detections = await _aiDetector.Detect(fName, stream, token);
// await ProcessDetection((TimeSpan.FromMilliseconds(0), stream), Path.GetExtension(mediaInfo.Path), detections, token);
// if (detections.Count != 0)
// mediaInfo.HasAnnotations = true;
// }
// catch (Exception e)
// {
// _logger.LogError(e, e.Message);
// await manualCancellationSource.CancelAsync();
// }
// }
// private async Task DetectVideo(MediaFileInfo mediaInfo, CancellationTokenSource manualCancellationSource, CancellationToken token)
// {
// var prevSeekTime = 0.0;
// await foreach (var timeframe in _vlcFrameExtractor.ExtractFrames(mediaInfo.Path, token))
// {
// Console.WriteLine($"Detect time: {timeframe.Time}");
// try
// {
// var fName = _formState.GetTimeName(timeframe.Time);
// var detections = await _aiDetector.Detect(fName, timeframe.Stream, token);
//
// var isValid = IsValidDetection(timeframe.Time, detections);
// Console.WriteLine($"Detection time: {timeframe.Time}");
//
// var log = string.Join(Environment.NewLine, detections.Select(det =>
// $"{_appConfig.AnnotationConfig.DetectionClassesDict[det.ClassNumber].Name}: " +
// $"xy=({det.CenterX:F2},{det.CenterY:F2}), " +
// $"size=({det.Width:F2}, {det.Height:F2}), " +
// $"prob: {det.Probability:F1}%"));
//
// log = $"Detection time: {timeframe.Time}, Valid: {isValid}. {Environment.NewLine} {log}";
// Dispatcher.Invoke(() => _autoDetectDialog.Log(log));
//
// if (timeframe.Time.TotalMilliseconds > prevSeekTime + 250)
// {
// Dispatcher.Invoke(() => SeekTo(timeframe.Time));
// prevSeekTime = timeframe.Time.TotalMilliseconds;
// if (!isValid) //Show frame anyway
// {
// Dispatcher.Invoke(() =>
// {
// Editor.RemoveAllAnns();
// Editor.Background = new ImageBrush
// {
// ImageSource = timeframe.Stream.OpenImage()
// };
// });
// }
// }
//
// if (!isValid)
// continue;
//
// mediaInfo.HasAnnotations = true;
// await ProcessDetection(timeframe, ".jpg", detections, token);
// await timeframe.Stream.DisposeAsync();
// }
// catch (Exception ex)
// {
// _logger.LogError(ex, ex.Message);
// await manualCancellationSource.CancelAsync();
// }
// }
// }
// private bool IsValidDetection(TimeSpan time, List<Detection> detections)
// {
// // No AI detection, forbid
// if (detections.Count == 0)
// return false;
//
// // Very first detection, allow
// if (!_previousDetection.HasValue)
// return true;
//
// var prev = _previousDetection.Value;
//
// // Time between detections is >= than Frame Recognition Seconds, allow
// if (time >= prev.Time.Add(TimeSpan.FromSeconds(_appConfig.AIRecognitionConfig.FrameRecognitionSeconds)))
// return true;
//
// // Detection is earlier than previous + FrameRecognitionSeconds.
// // Look to the detections more in detail
//
// // More detected objects, allow
// if (detections.Count > prev.Detections.Count)
// return true;
//
// foreach (var det in detections)
// {
// var point = new Point(det.CenterX, det.CenterY);
// var closestObject = prev.Detections
// .Select(p => new
// {
// Point = p,
// Distance = point.SqrDistance(new Point(p.CenterX, p.CenterY))
// })
// .OrderBy(x => x.Distance)
// .First();
//
// // Closest object is farther than Tracking distance confidence, hence it's a different object, allow
// if (closestObject.Distance > _appConfig.AIRecognitionConfig.TrackingDistanceConfidence)
// return true;
//
// // Since closest object within distance confidence, then it is tracking of the same object. Then if recognition probability for the object > increase from previous
// if (det.Probability >= closestObject.Point.Probability + _appConfig.AIRecognitionConfig.TrackingProbabilityIncrease)
// return true;
// }
//
// return false;
// }
private async Task ProcessDetection(AnnotationImage annotationImage, CancellationToken token = default)
private async Task ProcessDetection(AnnotationImage annotationImage)
{
await Dispatcher.Invoke(async () =>
{
try
{
var annotation = await _annotationService.SaveAnnotation(annotationImage, token);
Editor.Background = new ImageBrush { ImageSource = await annotation.ImagePath.OpenImage() };
Editor.RemoveAllAnns();
ShowAnnotations(annotation, true);
var annotation = await _annotationService.SaveAnnotation(annotationImage);
AddAnnotation(annotation);
if (FollowAI)
SeekTo(annotationImage.Milliseconds, false);
var log = string.Join(Environment.NewLine, annotation.Detections.Select(det =>
$"{_appConfig.AnnotationConfig.DetectionClassesDict[det.ClassNumber].Name}: " +
$"xy=({det.CenterX:F2},{det.CenterY:F2}), " +
$"size=({det.Width:F2}, {det.Height:F2}), " +
$"prob: {det.Probability:F1}%"));
$"prob: {det.Probability*100:F0}%"));
Dispatcher.Invoke(() => _autoDetectDialog.Log(log));
Dispatcher.Invoke(() => StatusHelp.Text = log);
}
catch (Exception e)
{