| | | 1 | | using System; |
| | | 2 | | using System.IO; |
| | | 3 | | using System.Threading.Tasks; |
| | | 4 | | using Newtonsoft.Json; |
| | | 5 | | using NLog; |
| | | 6 | | using ReFlex.Core.Common.Components; |
| | | 7 | | using ReFlex.Core.Common.Util; |
| | | 8 | | using ReFlex.Core.Tracking.Interfaces; |
| | | 9 | | using SixLabors.ImageSharp; |
| | | 10 | | using SixLabors.ImageSharp.PixelFormats; |
| | | 11 | | |
| | | 12 | | namespace ReFlex.Sensor.EmulatorModule |
| | | 13 | | { |
| | | 14 | | public class DepthStreamRecorder |
| | | 15 | | { |
| | 2 | 16 | | private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); |
| | | 17 | | |
| | | 18 | | private IDepthCamera _recordingCamera; |
| | | 19 | | |
| | | 20 | | private string _sessionDir; |
| | | 21 | | |
| | | 22 | | private StreamParameter _sessionParams; |
| | | 23 | | |
| | | 24 | | private bool _usePng; |
| | | 25 | | |
| | 26 | 26 | | public string SessionName { get; private set; } |
| | | 27 | | |
| | 130 | 28 | | public bool IsRecording { get; private set; } |
| | | 29 | | |
| | 26 | 30 | | public uint FrameId = 0; |
| | | 31 | | |
| | | 32 | | public event EventHandler<RecordingStateUpdate> RecordingStateUpdated; |
| | | 33 | | |
| | | 34 | | public string StartRecording(IDepthCamera camera, string name, bool usePng = false) |
| | 26 | 35 | | { |
| | 26 | 36 | | if (IsRecording) |
| | 0 | 37 | | { |
| | 0 | 38 | | Logger.Log(LogLevel.Warn, $"Already recording from {_recordingCamera?.ModelDescription} with Config {_re |
| | 0 | 39 | | return ""; |
| | | 40 | | } |
| | | 41 | | |
| | 26 | 42 | | _recordingCamera = camera; |
| | 26 | 43 | | _usePng = usePng; |
| | | 44 | | |
| | | 45 | | // delete recording with existing name |
| | 26 | 46 | | if (RecordingUtils.DeleteRecording(name)) |
| | 0 | 47 | | Logger.Warn($"Existing Recording with name '{name}' has been deleted."); |
| | | 48 | | |
| | 26 | 49 | | var date = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}"; |
| | | 50 | | |
| | 26 | 51 | | var dir = Directory.CreateDirectory($"{RecordingUtils.SavePath}{date}"); |
| | | 52 | | |
| | 26 | 53 | | _sessionDir = dir.FullName; |
| | 26 | 54 | | _sessionParams = new StreamParameter(_recordingCamera.StreamParameter.Width, _recordingCamera.StreamParamete |
| | | 55 | | |
| | 26 | 56 | | _recordingCamera.DepthImageReady += SaveImage; |
| | | 57 | | |
| | 26 | 58 | | Logger.Log(LogLevel.Info, $"Start recording from {_recordingCamera?.ModelDescription} with Config {_recordin |
| | | 59 | | |
| | 26 | 60 | | IsRecording = true; |
| | | 61 | | |
| | 26 | 62 | | SessionName = name; |
| | | 63 | | |
| | 26 | 64 | | RecordingStateUpdated?.Invoke(this, new RecordingStateUpdate(RecordingState.Recording, (int)FrameId, Session |
| | | 65 | | |
| | 26 | 66 | | var result = JsonConvert.SerializeObject(_sessionParams); |
| | | 67 | | |
| | 26 | 68 | | using (var writer = File.CreateText($"{_sessionDir}/{RecordingUtils.ConfigFile}")) |
| | 26 | 69 | | { |
| | 26 | 70 | | writer.Write(result); |
| | 26 | 71 | | } |
| | | 72 | | |
| | 26 | 73 | | return _sessionDir; |
| | 26 | 74 | | } |
| | | 75 | | |
| | | 76 | | public async Task<string> StopRecording() |
| | 26 | 77 | | { |
| | 26 | 78 | | if (!IsRecording) |
| | 0 | 79 | | return ""; |
| | | 80 | | |
| | 26 | 81 | | if (_recordingCamera == null) |
| | 0 | 82 | | { |
| | 0 | 83 | | Logger.Log(LogLevel.Error, $"Cannot {nameof(StopRecording)} for {GetType().Name}: No valid Camera set fo |
| | 0 | 84 | | return ""; |
| | | 85 | | } |
| | | 86 | | |
| | 26 | 87 | | _recordingCamera.DepthImageReady -= SaveImage; |
| | 26 | 88 | | Logger.Log(LogLevel.Info, $"Successfully Recorded {FrameId + 1} frames from {_recordingCamera?.ModelDescript |
| | | 89 | | |
| | 26 | 90 | | IsRecording = false; |
| | | 91 | | |
| | 26 | 92 | | RecordingStateUpdated?.Invoke(this, new RecordingStateUpdate(RecordingState.Stopped, (int) FrameId, SessionN |
| | | 93 | | |
| | 26 | 94 | | FrameId = 0; |
| | | 95 | | |
| | 52 | 96 | | return await Task.Run(() => JsonConvert.SerializeObject(_sessionParams)); |
| | 26 | 97 | | } |
| | | 98 | | |
| | | 99 | | |
| | | 100 | | |
| | | 101 | | private void SaveImage(object sender, ImageByteArray cameraData) |
| | 26 | 102 | | { |
| | 26 | 103 | | var id = $"{FrameId:0000000}"; |
| | | 104 | | |
| | 26 | 105 | | FrameId += 1; |
| | | 106 | | |
| | 26 | 107 | | var extension = _usePng ? "png" : "jpg"; |
| | 26 | 108 | | var fileName = $"{_sessionDir}/{id}.{extension}"; |
| | | 109 | | |
| | | 110 | | |
| | | 111 | | |
| | 52 | 112 | | Task.Run(async () => { |
| | 26 | 113 | | |
| | 26 | 114 | | switch (cameraData.Format) |
| | 26 | 115 | | { |
| | 26 | 116 | | case DepthImageFormat.Greyccale48bpp: |
| | 10 | 117 | | await SaveAsync<Rgb48>(fileName, cameraData); |
| | 10 | 118 | | break; |
| | 26 | 119 | | case DepthImageFormat.Greyscale8bpp: |
| | 6 | 120 | | await SaveAsync<L8>(fileName, cameraData); |
| | 6 | 121 | | break; |
| | 26 | 122 | | case DepthImageFormat.Rgb24bpp: |
| | 26 | 123 | | default: |
| | 10 | 124 | | await SaveAsync<Rgb24>(fileName, cameraData); |
| | 10 | 125 | | break; |
| | 26 | 126 | | } |
| | 52 | 127 | | }).Wait(); |
| | 26 | 128 | | } |
| | | 129 | | |
| | | 130 | | private async Task<bool> SaveAsync<T>(string fileName, ImageByteArray cameraData) where T: unmanaged, IPixel<T> |
| | 26 | 131 | | { |
| | | 132 | | try |
| | 26 | 133 | | { |
| | 26 | 134 | | using (var image = |
| | 26 | 135 | | Image.LoadPixelData<T>(cameraData.ImageData, cameraData.Width, cameraData.Height)) |
| | 26 | 136 | | { |
| | 52 | 137 | | await Task.Run(() => { |
| | 26 | 138 | | |
| | 26 | 139 | | if (fileName.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) |
| | 20 | 140 | | image.SaveAsPng(fileName); |
| | 26 | 141 | | else |
| | 6 | 142 | | image.SaveAsJpeg(fileName); |
| | 52 | 143 | | }); |
| | 26 | 144 | | RecordingStateUpdated?.Invoke(this, new RecordingStateUpdate(RecordingState.Recording, (int)FrameId, |
| | 26 | 145 | | } |
| | 26 | 146 | | } |
| | 0 | 147 | | catch (Exception e) |
| | 0 | 148 | | { |
| | 0 | 149 | | Logger.Log(LogLevel.Error, e, $"{e.GetType().Name} when saving Image to {fileName}: {e.Message}"); |
| | 0 | 150 | | RecordingStateUpdated?.Invoke(this, new RecordingStateUpdate(RecordingState.Faulted, (int)FrameId, Sessi |
| | | 151 | | |
| | 0 | 152 | | return false; |
| | | 153 | | } |
| | | 154 | | |
| | 26 | 155 | | return true; |
| | 26 | 156 | | } |
| | | 157 | | } |
| | | 158 | | } |