switcher dataset explorer

lat lon -> geopoint
correct location for gps if small keypoints number
This commit is contained in:
Alex Bezdieniezhnykh
2025-06-23 20:47:28 +03:00
parent c5e72669c5
commit 253f811125
31 changed files with 469 additions and 192 deletions
+5 -5
View File
@@ -59,7 +59,7 @@ public class AnnotationService : IAnnotationService
Task.Run(async () => await InitQueueConsumer()).Wait();
}
private async Task InitQueueConsumer(CancellationToken cancellationToken = default)
private async Task InitQueueConsumer(CancellationToken token = default)
{
if (!_api.CurrentUser.Role.IsValidator())
return;
@@ -79,7 +79,7 @@ public class AnnotationService : IAnnotationService
OffsetSpec = new OffsetTypeOffset(offsets.AnnotationsOffset),
MessageHandler = async (_, _, context, message) =>
{
await _messageProcessingSemaphore.WaitAsync(cancellationToken);
await _messageProcessingSemaphore.WaitAsync(token);
try
{
var email = (string)message.ApplicationProperties[nameof(User.Email)]!;
@@ -101,15 +101,15 @@ public class AnnotationService : IAnnotationService
msg.Role,
msg.Email,
context.Offset,
token: cancellationToken);
token: token);
}
else
{
var msg = MessagePackSerializer.Deserialize<AnnotationBulkMessage>(message.Data.Contents);
if (annotationStatus == AnnotationStatus.Validated)
await ValidateAnnotations(msg.AnnotationNames.ToList(), true, cancellationToken);
await ValidateAnnotations(msg.AnnotationNames.ToList(), true, token);
if (annotationStatus == AnnotationStatus.Deleted)
await _mediator.Publish(new AnnotationsDeletedEvent(msg.AnnotationNames.ToList(), fromQueue:true), cancellationToken);
await _mediator.Publish(new AnnotationsDeletedEvent(msg.AnnotationNames.ToList(), fromQueue:true), token);
}
}
+34 -7
View File
@@ -1,18 +1,45 @@
using Azaion.Common.DTO;
using MediatR;
namespace Azaion.Common.Services;
public enum MatchTypeEnum
{
None = -1,
MatchTypeSingle = 0,
MatchTypeStitched = 1,
MatchTypeOpticalFlow = 2,
MatchTypeInterpolated = 3,
MatchTypeFailure = 4
}
public class GPSMatcherResultEvent : INotification
{
public int Index { get; set; }
public string Image { get; set; } = null!;
public double Latitude { get; set; }
public double Longitude { get; set; }
public int KeyPoints { get; set; }
public int Rotation { get; set; }
public string MatchType { get; set; } = null!;
public int Index { get; set; }
public string Image { get; set; } = null!;
public GeoPoint GeoPoint { get; set; } = null!;
public int KeyPoints { get; set; }
public MatchTypeEnum MatchType { get; set; }
}
public class GPSMatcherResultProcessedEvent : GPSMatcherResultEvent
{
public GeoPoint ProcessedGeoPoint { get; set; } = null!;
public GPSMatcherResultProcessedEvent() { }
public GPSMatcherResultProcessedEvent(GPSMatcherResultEvent gpsMatcherResultEvent, GeoPoint processedGeoPoint)
{
Index = gpsMatcherResultEvent.Index;
Image = gpsMatcherResultEvent.Image;
GeoPoint = gpsMatcherResultEvent.GeoPoint;
KeyPoints = gpsMatcherResultEvent.KeyPoints;
MatchType = gpsMatcherResultEvent.MatchType;
ProcessedGeoPoint = processedGeoPoint;
}
}
public class GPSMatcherJobAcceptedEvent : INotification {}
public class GPSMatcherFinishedEvent : INotification {}
+31 -15
View File
@@ -1,46 +1,51 @@
using System.IO;
using Azaion.Common.DTO;
using Azaion.Common.DTO.Config;
using Azaion.Common.Extensions;
using MediatR;
using Microsoft.Extensions.Options;
namespace Azaion.Common.Services;
public interface IGpsMatcherService
{
Task RunGpsMatching(string userRouteDir, double initialLatitude, double initialLongitude, CancellationToken detectToken = default);
Task RunGpsMatching(string userRouteDir, GeoPoint geoPoint, CancellationToken detectToken = default);
void StopGpsMatching();
Task SetGpsResult(GPSMatcherResultEvent result, CancellationToken detectToken = default);
Task FinishGPS(GPSMatcherFinishedEvent notification, CancellationToken cancellationToken);
}
public class GpsMatcherService(IGpsMatcherClient gpsMatcherClient, ISatelliteDownloader satelliteTileDownloader, IOptions<DirectoriesConfig> dirConfig) : IGpsMatcherService
public class GpsMatcherService(IGpsMatcherClient gpsMatcherClient,
ISatelliteDownloader satelliteTileDownloader,
IOptions<DirectoriesConfig> dirConfig,
IOptions<GpsDeniedConfig> gpsDeniedConfig,
IMediator mediator) : IGpsMatcherService
{
private readonly DirectoriesConfig _dirConfig = dirConfig.Value;
private const int ZOOM_LEVEL = 18;
private const int POINTS_COUNT = 10;
private const int DISTANCE_BETWEEN_POINTS_M = 100;
private const double SATELLITE_RADIUS_M = DISTANCE_BETWEEN_POINTS_M * (POINTS_COUNT + 1);
private const int MAX_AVG_POINTS = 2;
private string _routeDir = "";
private string _userRouteDir = "";
private List<string> _allRouteImages = new();
private Dictionary<string, int> _currentRouteImages = new();
private double _currentLat;
private double _currentLon;
private GeoPoint _lastGeoPoint = new();
private CancellationToken _detectToken;
private int _currentIndex;
private readonly Queue<Direction> _directions = new();
public async Task RunGpsMatching(string userRouteDir, double initialLatitude, double initialLongitude, CancellationToken detectToken = default)
public async Task RunGpsMatching(string userRouteDir, GeoPoint initGeoPoint, CancellationToken detectToken = default)
{
_routeDir = Path.Combine(SecurityConstants.EXTERNAL_GPS_DENIED_FOLDER, _dirConfig.GpsRouteDirectory);
_userRouteDir = userRouteDir;
_allRouteImages = Directory.GetFiles(userRouteDir)
.OrderBy(x => x).ToList();
_currentLat = initialLatitude;
_currentLon = initialLongitude;
_lastGeoPoint = initGeoPoint;
_detectToken = detectToken;
await StartMatchingRound(0);
}
@@ -63,12 +68,11 @@ public class GpsMatcherService(IGpsMatcherClient gpsMatcherClient, ISatelliteDow
})
.ToDictionary(x => x.Filename, x => x.Index);
await satelliteTileDownloader.GetTiles(_currentLat, _currentLon, SATELLITE_RADIUS_M, ZOOM_LEVEL, _detectToken);
await satelliteTileDownloader.GetTiles(_lastGeoPoint, SATELLITE_RADIUS_M, ZOOM_LEVEL, _detectToken);
await gpsMatcherClient.StartMatching(new StartMatchingEvent
{
ImagesCount = POINTS_COUNT,
Latitude = _currentLat,
Longitude = _currentLon,
GeoPoint = _lastGeoPoint,
SatelliteImagesDir = _dirConfig.GpsSatDirectory,
RouteDir = _dirConfig.GpsRouteDirectory
});
@@ -83,9 +87,21 @@ public class GpsMatcherService(IGpsMatcherClient gpsMatcherClient, ISatelliteDow
{
_currentIndex = _currentRouteImages[result.Image];
_currentRouteImages.Remove(result.Image);
_currentLat = result.Latitude;
_currentLon = result.Longitude;
await Task.CompletedTask;
if (result.KeyPoints > gpsDeniedConfig.Value.MinKeyPoints)
{
var direction = _lastGeoPoint.DirectionTo(result.GeoPoint);
_directions.Enqueue(direction);
if (_directions.Count > MAX_AVG_POINTS)
_directions.Dequeue();
_lastGeoPoint = result.GeoPoint;
}
else
{
var direction = new Direction(_directions.Average(x => x.Distance), _directions.Average(x => x.Azimuth));
_lastGeoPoint = _lastGeoPoint.GoDirection(direction);
}
await mediator.Publish(new GPSMatcherResultProcessedEvent(result, _lastGeoPoint), detectToken);
}
public async Task FinishGPS(GPSMatcherFinishedEvent notification, CancellationToken cancellationToken)
+12 -14
View File
@@ -18,17 +18,16 @@ public interface IGpsMatcherClient : IDisposable
public class StartMatchingEvent
{
public string RouteDir { get; set; } = null!;
public string SatelliteImagesDir { get; set; } = null!;
public int ImagesCount { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public int Altitude { get; set; } = 400;
public double CameraSensorWidth { get; set; } = 23.5;
public double CameraFocalLength { get; set; } = 24;
public string RouteDir { get; set; } = null!;
public string SatelliteImagesDir { get; set; } = null!;
public int ImagesCount { get; set; }
public GeoPoint GeoPoint { get; set; } = null!;
public int Altitude { get; set; } = 400;
public double CameraSensorWidth { get; set; } = 23.5;
public double CameraFocalLength { get; set; } = 24;
public override string ToString() =>
$"{RouteDir},{SatelliteImagesDir},{ImagesCount},{Latitude},{Longitude},{Altitude},{CameraSensorWidth},{CameraFocalLength}";
$"{RouteDir},{SatelliteImagesDir},{ImagesCount},{GeoPoint.Lat},{GeoPoint.Lon},{Altitude},{CameraSensorWidth},{CameraFocalLength}";
}
public class GpsMatcherClient : IGpsMatcherClient
@@ -59,7 +58,6 @@ public class GpsMatcherClient : IGpsMatcherClient
catch (Exception e)
{
_logger.LogError(e, e.ToString());
throw;
}
_requestAddress = $"tcp://{gpsConfig.Value.ZeroMqHost}:{gpsConfig.Value.ZeroMqPort}";
@@ -93,7 +91,7 @@ public class GpsMatcherClient : IGpsMatcherClient
break;
default:
var parts = str.Split(',');
if (parts.Length != 5)
if (parts.Length != 6)
throw new Exception("Matching Result Failed");
var filename = Path.GetFileNameWithoutExtension(parts[1]);
@@ -101,9 +99,9 @@ public class GpsMatcherClient : IGpsMatcherClient
{
Index = int.Parse(parts[0]),
Image = filename,
Latitude = double.Parse(parts[2]),
Longitude = double.Parse(parts[3]),
MatchType = parts[4]
GeoPoint = new GeoPoint(double.Parse(parts[2]), double.Parse(parts[3])),
KeyPoints = int.Parse(parts[4]),
MatchType = Enum.TryParse<MatchTypeEnum>(parts[5], out var type) ? type : MatchTypeEnum.None
});
break;
}
@@ -54,7 +54,6 @@ public class InferenceClient : IInferenceClient
catch (Exception e)
{
_logger.LogError(e, e.Message);
throw;
}
_dealer.Options.Identity = Encoding.UTF8.GetBytes(_clientId.ToString("N"));
+15 -8
View File
@@ -20,7 +20,7 @@ namespace Azaion.Common.Services;
public interface ISatelliteDownloader
{
Task GetTiles(double latitude, double longitude, double radiusM, int zoomLevel, CancellationToken token = default);
Task GetTiles(GeoPoint geoPoint, double radiusM, int zoomLevel, CancellationToken token = default);
}
public class SatelliteDownloader(
@@ -45,21 +45,28 @@ public class SatelliteDownloader(
private readonly string _apiKey = mapConfig.Value.ApiKey;
private readonly string _satDirectory = Path.Combine(SecurityConstants.EXTERNAL_GPS_DENIED_FOLDER, directoriesConfig.Value.GpsSatDirectory);
public async Task GetTiles(double centerLat, double centerLon, double radiusM, int zoomLevel, CancellationToken token = default)
public async Task GetTiles(GeoPoint centerGeoPoint, double radiusM, int zoomLevel, CancellationToken token = default)
{
await mediator.Publish(new SetStatusTextEvent($"Завантажується супутникові зображення по координатах: центр: lat: {centerLat:F3} lon: {centerLon:F3} квадрат {radiusM}м * {radiusM}м, zoom: {zoomLevel}..."), token);
await mediator.Publish(new SetStatusTextEvent(
$"Завантажуються супутникові зображення по координатах: центр: " +
$"lat: {centerGeoPoint.Lat:F3} lon: {centerGeoPoint.Lon:F3} квадрат {radiusM}м * {radiusM}м, zoom: {zoomLevel}..."), token);
//empty Satellite directory
if (Directory.Exists(_satDirectory))
Directory.Delete(_satDirectory, true);
Directory.CreateDirectory(_satDirectory);
var downloadTilesResult = await DownloadTiles(centerLat, centerLon, radiusM, zoomLevel, token);
var dtRes = await DownloadTiles(centerGeoPoint, radiusM, zoomLevel, token);
await mediator.Publish(new SetStatusTextEvent("Завершено! Склеюється в 1 зображення..."), token);
var image = ComposeTiles(downloadTilesResult.Tiles, token);
var image = ComposeTiles(dtRes.Tiles, token);
if (image == null)
return;
// Save big map. Uncomment when MapHandler with custom pick images would be ready
// var outputFilename = Path.Combine(_satDirectory,
// $"map_tl_{dtRes.LatMax:F6}_{dtRes.LonMin:F6}_br_{dtRes.LatMin:F6}_{dtRes.LonMax:F6}.tif"
// );
// await image.SaveAsTiffAsync(outputFilename, token);
await mediator.Publish(new SetStatusTextEvent("Розбиття на малі зображення для опрацювання..."), token);
await SplitToTiles(image, downloadTilesResult, token);
await SplitToTiles(image, dtRes, token);
}
private async Task SplitToTiles(Image<Rgba32> image, DownloadTilesResult bounds, CancellationToken token = default)
@@ -178,9 +185,9 @@ public class SatelliteDownloader(
}
}
private async Task<DownloadTilesResult> DownloadTiles(double centerLat, double centerLon, double radiusM, int zoomLevel, CancellationToken token = default)
private async Task<DownloadTilesResult> DownloadTiles(GeoPoint centerGeoPoint, double radiusM, int zoomLevel, CancellationToken token = default)
{
var (latMin, latMax, lonMin, lonMax) = GeoUtils.GetBoundingBox(centerLat, centerLon, radiusM);
var (latMin, latMax, lonMin, lonMax) = GeoUtils.GetBoundingBox(centerGeoPoint, radiusM);
var (xMin, yMin) = GeoUtils.WorldToTilePos(latMax, lonMin, zoomLevel); // Top-left corner
var (xMax, yMax) = GeoUtils.WorldToTilePos(latMin, lonMax, zoomLevel); // Bottom-right corner