mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 10:46:30 +00:00
add MediaHash. Step1
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
@@ -21,6 +22,7 @@ using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using Size = System.Windows.Size;
|
||||
using IntervalTree;
|
||||
using LinqToDB;
|
||||
using LinqToDB.Data;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MediaPlayer = LibVLCSharp.Shared.MediaPlayer;
|
||||
@@ -40,7 +42,6 @@ public partial class Annotator
|
||||
private readonly ILogger<Annotator> _logger;
|
||||
private readonly IDbFactory _dbFactory;
|
||||
private readonly IInferenceService _inferenceService;
|
||||
private readonly IInferenceClient _inferenceClient;
|
||||
|
||||
private bool _suspendLayout;
|
||||
private bool _gpsPanelVisible;
|
||||
@@ -52,14 +53,15 @@ public partial class Annotator
|
||||
private readonly TimeSpan _thresholdBefore = TimeSpan.FromMilliseconds(50);
|
||||
private readonly TimeSpan _thresholdAfter = TimeSpan.FromMilliseconds(150);
|
||||
|
||||
public ObservableCollection<MediaFileInfo> AllMediaFiles { get; set; } = new();
|
||||
private ObservableCollection<MediaFileInfo> FilteredMediaFiles { get; set; } = new();
|
||||
public Dictionary<string, MediaFileInfo> MediaFilesDict = new();
|
||||
public ObservableCollection<MediaFile> AllMediaFiles { get; set; } = new();
|
||||
private ObservableCollection<MediaFile> FilteredMediaFiles { get; set; } = new();
|
||||
public Dictionary<string, MediaFile> MediaFilesDict = new();
|
||||
|
||||
public IntervalTree<TimeSpan, Annotation> TimedAnnotations { get; set; } = new();
|
||||
public string MainTitle { get; set; }
|
||||
|
||||
public CameraConfig Camera => _appConfig?.CameraConfig ?? new CameraConfig();
|
||||
private static readonly Guid ReloadTaskId = Guid.NewGuid();
|
||||
|
||||
public Annotator(
|
||||
IConfigUpdater configUpdater,
|
||||
@@ -86,7 +88,6 @@ public partial class Annotator
|
||||
_logger = logger;
|
||||
_dbFactory = dbFactory;
|
||||
_inferenceService = inferenceService;
|
||||
_inferenceClient = inferenceClient;
|
||||
|
||||
// Ensure bindings (e.g., Camera) resolve immediately
|
||||
DataContext = this;
|
||||
@@ -294,9 +295,14 @@ public partial class Annotator
|
||||
TimedAnnotations.Clear();
|
||||
Editor.RemoveAllAnns();
|
||||
|
||||
var mediaHash = _formState.CurrentMedia?.Hash;
|
||||
var mediaName = _formState.CurrentMedia?.Name;
|
||||
|
||||
var annotations = await _dbFactory.Run(async db =>
|
||||
await db.Annotations.LoadWith(x => x.Detections)
|
||||
.Where(x => x.OriginalMediaName == _formState.MediaName)
|
||||
.Where(x =>
|
||||
(!string.IsNullOrEmpty(mediaHash) && x.MediaHash == mediaHash) ||
|
||||
(x.MediaHash == null && x.OriginalMediaName == mediaName))
|
||||
.OrderBy(x => x.Time)
|
||||
.ToListAsync(token: _mainCancellationSource.Token));
|
||||
|
||||
@@ -304,7 +310,6 @@ public partial class Annotator
|
||||
_formState.AnnotationResults.Clear();
|
||||
foreach (var ann in annotations)
|
||||
{
|
||||
// Duplicate for speed
|
||||
TimedAnnotations.Add(ann.Time.Subtract(_thresholdBefore), ann.Time.Add(_thresholdAfter), ann);
|
||||
_formState.AnnotationResults.Add(ann);
|
||||
}
|
||||
@@ -344,55 +349,109 @@ public partial class Annotator
|
||||
_formState.AnnotationResults.Insert(index, annotation);
|
||||
}
|
||||
|
||||
private async Task ReloadFiles()
|
||||
public void ReloadFilesThrottled()
|
||||
{
|
||||
ThrottleExt.Throttle(async () =>
|
||||
{
|
||||
await ReloadFiles();
|
||||
}, ReloadTaskId, TimeSpan.FromSeconds(4));
|
||||
}
|
||||
|
||||
public async Task ReloadFiles()
|
||||
{
|
||||
var dir = new DirectoryInfo(_appConfig?.DirectoriesConfig.VideosDirectory ?? Constants.DEFAULT_VIDEO_DIR);
|
||||
if (!dir.Exists)
|
||||
return;
|
||||
|
||||
var videoFiles = dir.GetFiles((_appConfig?.AnnotationConfig.VideoFormats ?? Constants.DefaultVideoFormats)
|
||||
.ToArray()).Select(x =>
|
||||
{
|
||||
var media = new Media(_libVlc, x.FullName);
|
||||
media.Parse();
|
||||
var fInfo = new MediaFileInfo
|
||||
{
|
||||
Name = x.Name,
|
||||
Path = x.FullName,
|
||||
MediaType = MediaTypes.Video
|
||||
};
|
||||
media.ParsedChanged += (_, _) => fInfo.Duration = TimeSpan.FromMilliseconds(media.Duration);
|
||||
return fInfo;
|
||||
}).ToList();
|
||||
var folderFiles = dir.GetFiles(Constants.DefaultVideoFormats.Concat(Constants.DefaultImageFormats).ToArray())
|
||||
.Select(x => x.FullName)
|
||||
.Select(x => new MediaFile(x))
|
||||
.GroupBy(x => x.Hash)
|
||||
.ToDictionary(x => x.Key, v => v.First());
|
||||
|
||||
var imageFiles = dir.GetFiles((_appConfig?.AnnotationConfig.ImageFormats ?? Constants.DefaultImageFormats).ToArray())
|
||||
.Select(x => new MediaFileInfo
|
||||
{
|
||||
Name = x.Name,
|
||||
Path = x.FullName,
|
||||
MediaType = MediaTypes.Image
|
||||
});
|
||||
var allFiles = videoFiles.Concat(imageFiles).ToList();
|
||||
//sync with db
|
||||
var dbFiles = await _dbFactory.Run(async db =>
|
||||
await db.MediaFiles
|
||||
.Where(x => folderFiles.ContainsKey(x.Hash))
|
||||
.ToDictionaryAsync(x => x.Hash));
|
||||
var newFiles = folderFiles
|
||||
.Where(x => !dbFiles.ContainsKey(x.Key))
|
||||
.Select(x => x.Value)
|
||||
.ToList();
|
||||
if (newFiles.Count > 0)
|
||||
await _dbFactory.RunWrite(async db => await db.BulkCopyAsync(newFiles));
|
||||
|
||||
var allFileNames = allFiles.Select(x => x.FName).ToList();
|
||||
var allFiles = dbFiles.Select(x => x.Value)
|
||||
.Concat(newFiles)
|
||||
.ToList();
|
||||
|
||||
var labelsDict = await _dbFactory.Run(async db =>
|
||||
await db.Annotations
|
||||
.GroupBy(x => x.OriginalMediaName)
|
||||
.Where(x => allFileNames.Contains(x.Key))
|
||||
.Select(x => x.Key)
|
||||
.ToDictionaryAsync(x => x, x => x));
|
||||
|
||||
foreach (var mediaFile in allFiles)
|
||||
mediaFile.HasAnnotations = labelsDict.ContainsKey(mediaFile.FName);
|
||||
|
||||
AllMediaFiles = new ObservableCollection<MediaFileInfo>(allFiles);
|
||||
await SyncAnnotations(allFiles);
|
||||
|
||||
AllMediaFiles = new ObservableCollection<MediaFile>(allFiles);
|
||||
MediaFilesDict = AllMediaFiles.GroupBy(x => x.Name)
|
||||
.ToDictionary(gr => gr.Key, gr => gr.First());
|
||||
var selectedIndex = LvFiles.SelectedIndex;
|
||||
LvFiles.ItemsSource = AllMediaFiles;
|
||||
LvFiles.SelectedIndex = selectedIndex;
|
||||
DataContext = this;
|
||||
}
|
||||
|
||||
private async Task SyncAnnotations(List<MediaFile> allFiles)
|
||||
{
|
||||
var hashes = allFiles.Select(x => x.Hash).ToList();
|
||||
var filenames = allFiles.Select(x => x.Name).ToList();
|
||||
var nameHashMap = allFiles.ToDictionary(x => x.Name.ToFName(), x => x.Hash);
|
||||
await _dbFactory.RunWrite(async db =>
|
||||
{
|
||||
var hashedAnnotations = await db.Annotations
|
||||
.Where(a => hashes.Contains(a.MediaHash))
|
||||
.ToDictionaryAsync(x => x.Name);
|
||||
|
||||
var fileNameAnnotations = await db.Annotations
|
||||
.Where(a => filenames.Contains(a.OriginalMediaName))
|
||||
.ToDictionaryAsync(x => x.Name);
|
||||
|
||||
var toUpdate = fileNameAnnotations
|
||||
.Where(a => !hashedAnnotations.ContainsKey(a.Key))
|
||||
.Select(a => new { a.Key, MediaHash = nameHashMap.GetValueOrDefault(a.Value.OriginalMediaName) ?? "" })
|
||||
.ToList();
|
||||
|
||||
if (toUpdate.Count > 0)
|
||||
{
|
||||
var caseBuilder = new StringBuilder("UPDATE Annotations SET MediaHash = CASE Name ");
|
||||
var parameters = new List<DataParameter>();
|
||||
|
||||
for (int i = 0; i < toUpdate.Count; i++)
|
||||
{
|
||||
caseBuilder.Append($"WHEN @name{i} THEN @hash{i} ");
|
||||
parameters.Add(new DataParameter($"@name{i}", toUpdate[i].Key, DataType.NVarChar));
|
||||
parameters.Add(new DataParameter($"@hash{i}", toUpdate[i].MediaHash, DataType.NVarChar));
|
||||
}
|
||||
|
||||
caseBuilder.Append("END WHERE Name IN (");
|
||||
caseBuilder.Append(string.Join(", ", Enumerable.Range(0, toUpdate.Count).Select(i => $"@name{i}")));
|
||||
caseBuilder.Append(")");
|
||||
|
||||
await db.ExecuteAsync(caseBuilder.ToString(), parameters.ToArray());
|
||||
}
|
||||
var annotationMediaHashes = hashedAnnotations
|
||||
.GroupBy(x => x.Value.MediaHash)
|
||||
.Select(x => x.Key)
|
||||
.ToList();
|
||||
|
||||
var annotationMediaNames = fileNameAnnotations
|
||||
.GroupBy(x => x.Value.OriginalMediaName)
|
||||
.Select(x => x.Key)
|
||||
.ToList();
|
||||
|
||||
await db.MediaFiles
|
||||
.Where(m => annotationMediaHashes.Contains(m.Hash) ||
|
||||
annotationMediaNames.Contains(m.Name))
|
||||
.Set(m => m.Status, MediaStatus.Confirmed)
|
||||
.UpdateAsync();
|
||||
});
|
||||
}
|
||||
|
||||
private void OnFormClosed(object? sender, EventArgs e)
|
||||
{
|
||||
_mainCancellationSource.Cancel();
|
||||
@@ -406,11 +465,11 @@ public partial class Annotator
|
||||
|
||||
private void OpenContainingFolder(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var mediaFileInfo = (sender as MenuItem)?.DataContext as MediaFileInfo;
|
||||
var mediaFileInfo = (sender as MenuItem)?.DataContext as MediaFile;
|
||||
if (mediaFileInfo == null)
|
||||
return;
|
||||
|
||||
Process.Start("explorer.exe", "/select,\"" + mediaFileInfo.Path +"\"");
|
||||
Process.Start("explorer.exe", "/select,\"" + mediaFileInfo.MediaUrl +"\"");
|
||||
}
|
||||
|
||||
public void SeekTo(long timeMilliseconds, bool setPause = true)
|
||||
@@ -446,8 +505,8 @@ public partial class Annotator
|
||||
|
||||
private void TbFilter_OnTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
FilteredMediaFiles = new ObservableCollection<MediaFileInfo>(AllMediaFiles.Where(x => x.Name.ToLower().Contains(TbFilter.Text.ToLower())).ToList());
|
||||
MediaFilesDict = FilteredMediaFiles.ToDictionary(x => x.FName);
|
||||
FilteredMediaFiles = new ObservableCollection<MediaFile>(AllMediaFiles.Where(x => x.Name.ToLower().Contains(TbFilter.Text.ToLower())).ToList());
|
||||
MediaFilesDict = FilteredMediaFiles.ToDictionary(x => x.Name);
|
||||
LvFiles.ItemsSource = FilteredMediaFiles;
|
||||
LvFiles.ItemsSource = FilteredMediaFiles;
|
||||
}
|
||||
@@ -515,7 +574,7 @@ public partial class Annotator
|
||||
|
||||
var files = (FilteredMediaFiles.Count == 0 ? AllMediaFiles : FilteredMediaFiles)
|
||||
.Skip(LvFiles.SelectedIndex)
|
||||
.Select(x => x.Path)
|
||||
.Select(x => x.MediaUrl)
|
||||
.ToList();
|
||||
if (files.Count == 0)
|
||||
return;
|
||||
@@ -566,15 +625,15 @@ public partial class Annotator
|
||||
|
||||
private void DeleteMedia(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var mediaFileInfo = (sender as MenuItem)?.DataContext as MediaFileInfo;
|
||||
var mediaFileInfo = (sender as MenuItem)?.DataContext as MediaFile;
|
||||
if (mediaFileInfo == null)
|
||||
return;
|
||||
DeleteMedia(mediaFileInfo);
|
||||
}
|
||||
|
||||
public void DeleteMedia(MediaFileInfo mediaFileInfo)
|
||||
public void DeleteMedia(MediaFile mediaFile)
|
||||
{
|
||||
var obj = mediaFileInfo.MediaType == MediaTypes.Image
|
||||
var obj = mediaFile.MediaType == MediaTypes.Image
|
||||
? "цю картинку "
|
||||
: "це відео ";
|
||||
var result = MessageBox.Show($"Видалити {obj}?",
|
||||
@@ -582,8 +641,8 @@ public partial class Annotator
|
||||
if (result != MessageBoxResult.Yes)
|
||||
return;
|
||||
|
||||
File.Delete(mediaFileInfo.Path);
|
||||
AllMediaFiles.Remove(mediaFileInfo);
|
||||
File.Delete(mediaFile.MediaUrl);
|
||||
AllMediaFiles.Remove(mediaFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ public class AnnotatorEventHandler(
|
||||
var focusedElement = FocusManager.GetFocusedElement(mainWindow);
|
||||
if (focusedElement is ListViewItem item)
|
||||
{
|
||||
if (item.DataContext is not MediaFileInfo mediaFileInfo)
|
||||
if (item.DataContext is not MediaFile mediaFileInfo)
|
||||
return;
|
||||
mainWindow.DeleteMedia(mediaFileInfo);
|
||||
}
|
||||
@@ -245,7 +245,7 @@ public class AnnotatorEventHandler(
|
||||
{
|
||||
if (mainWindow.LvFiles.SelectedItem == null)
|
||||
return;
|
||||
var mediaInfo = (MediaFileInfo)mainWindow.LvFiles.SelectedItem;
|
||||
var mediaInfo = (MediaFile)mainWindow.LvFiles.SelectedItem;
|
||||
|
||||
if (formState.CurrentMedia == mediaInfo)
|
||||
return; //already loaded
|
||||
@@ -261,12 +261,12 @@ public class AnnotatorEventHandler(
|
||||
//need to wait a bit for correct VLC playback event handling
|
||||
await Task.Delay(100, ct);
|
||||
mediaPlayer.Stop();
|
||||
mediaPlayer.Play(new Media(libVlc, mediaInfo.Path));
|
||||
mediaPlayer.Play(new Media(libVlc, mediaInfo.MediaUrl));
|
||||
}
|
||||
else
|
||||
{
|
||||
formState.BackgroundTime = TimeSpan.Zero;
|
||||
var image = await mediaInfo.Path.OpenImage();
|
||||
var image = await mediaInfo.MediaUrl.OpenImage();
|
||||
formState.CurrentMediaSize = new Size(image.PixelWidth, image.PixelHeight);
|
||||
mainWindow.Editor.SetBackground(image);
|
||||
mediaPlayer.Stop();
|
||||
@@ -281,11 +281,10 @@ public class AnnotatorEventHandler(
|
||||
return;
|
||||
|
||||
var time = formState.BackgroundTime ?? TimeSpan.FromMilliseconds(mediaPlayer.Time);
|
||||
var timeName = formState.MediaName.ToTimeName(time);
|
||||
var timeName = formState.CurrentMedia.Name.ToTimeName(time);
|
||||
var isVideo = formState.CurrentMedia.MediaType == MediaTypes.Video;
|
||||
var imgPath = Path.Combine(dirConfig.Value.ImagesDirectory, $"{timeName}{Constants.JPG_EXT}");
|
||||
|
||||
formState.CurrentMedia.HasAnnotations = mainWindow.Editor.CurrentDetections.Count != 0;
|
||||
var annotations = await SaveAnnotationInner(imgPath, cancellationToken);
|
||||
if (isVideo)
|
||||
{
|
||||
@@ -297,7 +296,7 @@ public class AnnotatorEventHandler(
|
||||
// var annGrid = mainWindow.DgAnnotations;
|
||||
// annGrid.SelectedIndex = Math.Min(annGrid.Items.Count, annGrid.SelectedIndex + 1);
|
||||
// mainWindow.OpenAnnotationResult((AnnotationResult)annGrid.SelectedItem);
|
||||
|
||||
|
||||
mainWindow.Editor.SetBackground(null);
|
||||
formState.BackgroundTime = null;
|
||||
}
|
||||
@@ -310,17 +309,19 @@ public class AnnotatorEventHandler(
|
||||
mainWindow.Editor.RemoveAllAnns();
|
||||
}
|
||||
|
||||
private async Task<List<Annotation>> SaveAnnotationInner(string imgPath, CancellationToken cancellationToken = default)
|
||||
private async Task<List<Annotation>> SaveAnnotationInner(string imgPath, CancellationToken ct = default)
|
||||
{
|
||||
var canvasDetections = mainWindow.Editor.CurrentDetections.Select(x => x.ToCanvasLabel()).ToList();
|
||||
|
||||
var source = (mainWindow.Editor.BackgroundImage.Source as BitmapSource)!;
|
||||
var mediaSize = new Size(source.PixelWidth, source.PixelHeight);
|
||||
var annotationsResult = new List<Annotation>();
|
||||
var time = formState.BackgroundTime ?? TimeSpan.FromMilliseconds(mediaPlayer.Time);
|
||||
|
||||
if (!File.Exists(imgPath))
|
||||
{
|
||||
if (mediaSize.FitSizeForAI())
|
||||
await source.SaveImage(imgPath, cancellationToken);
|
||||
await source.SaveImage(imgPath, ct);
|
||||
else
|
||||
{
|
||||
//Tiling
|
||||
@@ -331,17 +332,16 @@ public class AnnotatorEventHandler(
|
||||
.ToList();
|
||||
|
||||
//2. Split to frames
|
||||
var results = TileProcessor.Split(formState.CurrentMediaSize, detectionCoords, cancellationToken);
|
||||
var results = TileProcessor.Split(formState.CurrentMediaSize, detectionCoords, ct);
|
||||
|
||||
//3. Save each frame as a separate annotation
|
||||
foreach (var res in results)
|
||||
{
|
||||
var time = TimeSpan.Zero;
|
||||
var annotationName = $"{formState.MediaName}{Constants.SPLIT_SUFFIX}{res.Tile.Width}_{res.Tile.Left:0000}_{res.Tile.Top:0000}!".ToTimeName(time);
|
||||
var annotationName = $"{formState.CurrentMedia?.Name ?? ""}{Constants.SPLIT_SUFFIX}{res.Tile.Width}_{res.Tile.Left:0000}_{res.Tile.Top:0000}!".ToTimeName(time);
|
||||
|
||||
var tileImgPath = Path.Combine(dirConfig.Value.ImagesDirectory, $"{annotationName}{Constants.JPG_EXT}");
|
||||
var bitmap = new CroppedBitmap(source, new Int32Rect((int)res.Tile.Left, (int)res.Tile.Top, (int)res.Tile.Width, (int)res.Tile.Height));
|
||||
await bitmap.SaveImage(tileImgPath, cancellationToken);
|
||||
await bitmap.SaveImage(tileImgPath, ct);
|
||||
|
||||
var frameSize = new Size(res.Tile.Width, res.Tile.Height);
|
||||
var detections = res.Detections
|
||||
@@ -349,18 +349,17 @@ public class AnnotatorEventHandler(
|
||||
.Select(x => new Detection(annotationName, new YoloLabel(x, frameSize)))
|
||||
.ToList();
|
||||
|
||||
annotationsResult.Add(await annotationService.SaveAnnotation(formState.MediaName, annotationName, time, detections, token: cancellationToken));
|
||||
annotationsResult.Add(await annotationService.SaveAnnotation(formState.CurrentMediaHash, formState.CurrentMediaName, annotationName, time, detections, token: ct));
|
||||
}
|
||||
return annotationsResult;
|
||||
}
|
||||
}
|
||||
|
||||
var timeImg = formState.BackgroundTime ?? TimeSpan.FromMilliseconds(mediaPlayer.Time);
|
||||
var annName = formState.MediaName.ToTimeName(timeImg);
|
||||
var annName = (formState.CurrentMedia?.Name ?? "").ToTimeName(time);
|
||||
var currentDetections = canvasDetections.Select(x =>
|
||||
new Detection(annName, new YoloLabel(x, mainWindow.Editor.RenderSize, mediaSize)))
|
||||
.ToList();
|
||||
var annotation = await annotationService.SaveAnnotation(formState.MediaName, annName, timeImg, currentDetections, token: cancellationToken);
|
||||
var annotation = await annotationService.SaveAnnotation(formState.CurrentMediaHash, formState.CurrentMediaName, annName, time, currentDetections, token: ct);
|
||||
return [annotation];
|
||||
}
|
||||
|
||||
@@ -383,15 +382,7 @@ public class AnnotatorEventHandler(
|
||||
.Select(x => x.Value).ToList();
|
||||
mainWindow.TimedAnnotations.Remove(timedAnnotationsToRemove);
|
||||
|
||||
if (formState.AnnotationResults.Count == 0)
|
||||
{
|
||||
var media = mainWindow.AllMediaFiles.FirstOrDefault(x => x.Name == formState.CurrentMedia?.Name);
|
||||
if (media != null)
|
||||
{
|
||||
media.HasAnnotations = false;
|
||||
mainWindow.LvFiles.Items.Refresh();
|
||||
}
|
||||
}
|
||||
mainWindow.ReloadFilesThrottled();
|
||||
});
|
||||
|
||||
await dbFactory.DeleteAnnotations(notification.AnnotationNames, ct);
|
||||
@@ -412,7 +403,8 @@ public class AnnotatorEventHandler(
|
||||
}
|
||||
|
||||
//Only validators can send Delete to the queue
|
||||
if (!notification.FromQueue && api.CurrentUser.Role.IsValidator())
|
||||
var currentUser = await api.GetCurrentUserAsync();
|
||||
if (!notification.FromQueue && currentUser.Role.IsValidator())
|
||||
await producer.SendToInnerQueue(notification.AnnotationNames, AnnotationStatus.Deleted, ct);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -426,9 +418,8 @@ public class AnnotatorEventHandler(
|
||||
{
|
||||
mainWindow.Dispatcher.Invoke(() =>
|
||||
{
|
||||
|
||||
var mediaInfo = (MediaFileInfo)mainWindow.LvFiles.SelectedItem;
|
||||
if ((mediaInfo?.FName ?? "") == e.Annotation.OriginalMediaName)
|
||||
var mediaInfo = (MediaFile)mainWindow.LvFiles.SelectedItem;
|
||||
if ((mediaInfo?.Name ?? "") == e.Annotation.OriginalMediaName)
|
||||
mainWindow.AddAnnotation(e.Annotation);
|
||||
|
||||
var log = string.Join(Environment.NewLine, e.Annotation.Detections.Select(det =>
|
||||
@@ -441,7 +432,7 @@ public class AnnotatorEventHandler(
|
||||
|
||||
var media = mainWindow.MediaFilesDict.GetValueOrDefault(e.Annotation.OriginalMediaName);
|
||||
if (media != null)
|
||||
media.HasAnnotations = true;
|
||||
mainWindow.ReloadFilesThrottled();
|
||||
|
||||
mainWindow.LvFiles.Items.Refresh();
|
||||
mainWindow.StatusHelp.Text = log;
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using Azaion.Common;
|
||||
using Azaion.Common.Database;
|
||||
using Azaion.Common.DTO;
|
||||
using Azaion.Common.DTO.Config;
|
||||
@@ -18,7 +19,7 @@ namespace Azaion.Annotator.Controls;
|
||||
public partial class MapMatcher : UserControl
|
||||
{
|
||||
private AppConfig _appConfig = null!;
|
||||
List<MediaFileInfo> _allMediaFiles = new();
|
||||
List<MediaFile> _allMediaFiles = new();
|
||||
public Dictionary<int, Annotation> Annotations = new();
|
||||
private string _currentDir = null!;
|
||||
private IGpsMatcherService _gpsMatcherService = null!;
|
||||
@@ -63,11 +64,11 @@ public partial class MapMatcher : UserControl
|
||||
|
||||
private void OpenContainingFolder(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var mediaFileInfo = (sender as MenuItem)?.DataContext as MediaFileInfo;
|
||||
var mediaFileInfo = (sender as MenuItem)?.DataContext as MediaFile;
|
||||
if (mediaFileInfo == null)
|
||||
return;
|
||||
|
||||
Process.Start("explorer.exe", "/select,\"" + mediaFileInfo.Path +"\"");
|
||||
Process.Start("explorer.exe", "/select,\"" + mediaFileInfo.MediaUrl +"\"");
|
||||
}
|
||||
|
||||
private async void OpenGpsTilesFolderClick(object sender, RoutedEventArgs e)
|
||||
@@ -86,16 +87,16 @@ public partial class MapMatcher : UserControl
|
||||
TbGpsMapFolder.Text = dlg.FileName;
|
||||
_currentDir = dlg.FileName;
|
||||
var dir = new DirectoryInfo(dlg.FileName);
|
||||
var mediaFiles = dir.GetFiles(_appConfig.AnnotationConfig.ImageFormats.ToArray())
|
||||
.Select(x => new MediaFileInfo
|
||||
var mediaFiles = dir.GetFiles(Constants.DefaultImageFormats.ToArray())
|
||||
.Select(x => new MediaFile
|
||||
{
|
||||
Name = x.Name,
|
||||
Path = x.FullName,
|
||||
MediaUrl = x.FullName,
|
||||
MediaType = MediaTypes.Image
|
||||
}).ToList();
|
||||
|
||||
_allMediaFiles = mediaFiles;
|
||||
GpsFiles.ItemsSource = new ObservableCollection<MediaFileInfo>(_allMediaFiles);
|
||||
GpsFiles.ItemsSource = new ObservableCollection<MediaFile>(_allMediaFiles);
|
||||
|
||||
Annotations = mediaFiles.Select((x, i) => (i, new Annotation
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user