/// <summary> /// Exports a stream of IMU readings. /// </summary> /// <param name="source">The source stream of IMU readings.</param> /// <param name="directory">The directory in which to persist.</param> /// <param name="name">The name for the source stream.</param> /// <param name="outputPath">The output path.</param> /// <param name="streamWritersToClose">The collection of stream writers to be closed.</param> internal static void Export(this IProducer <Vector3D> source, string directory, string name, string outputPath, List <StreamWriter> streamWritersToClose) { var filePath = DataExporter.EnsurePathExists(Path.Combine(outputPath, directory, $"{name}.txt")); var file = File.CreateText(filePath); streamWritersToClose.Add(file); source .Do( (vector, envelope) => { file.WriteLine($"{envelope.OriginatingTime.ToText()}\t{vector.ToText()}"); }, DeliveryPolicy.SynchronousOrThrottle); }
/// <summary> /// Exports a stream of encoded image camera views. /// </summary> /// <param name="source">The source stream of encoded image camera views.</param> /// <param name="name">The name for the source stream.</param> /// <param name="outputPath">The output path.</param> /// <param name="streamWritersToClose">The collection of stream writers to be closed.</param> internal static void Export(this IProducer <EncodedImageCameraView> source, string name, string outputPath, List <StreamWriter> streamWritersToClose) { var timingFilePath = DataExporter.EnsurePathExists(Path.Combine(outputPath, name, $"Timing.txt")); var timingFile = File.CreateText(timingFilePath); streamWritersToClose.Add(timingFile); var poseFilePath = DataExporter.EnsurePathExists(Path.Combine(outputPath, name, $"Pose.txt")); var poseFile = File.CreateText(poseFilePath); streamWritersToClose.Add(poseFile); var intrinsicsFilePath = DataExporter.EnsurePathExists(Path.Combine(outputPath, name, $"Intrinsics.txt")); var intrinsicsFile = File.CreateText(intrinsicsFilePath); streamWritersToClose.Add(intrinsicsFile); var imageCounter = 0; source.Do( (eicv, envelope) => { var buffer = eicv.ViewedObject.Resource.GetBuffer(); var isPng = buffer.Length >= 8 && buffer[0] == 0x89 && // look for PNG header buffer[1] == 0x50 && // see https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header buffer[2] == 0x4e && // P buffer[3] == 0x47 && // N buffer[4] == 0x0d && // G buffer[5] == 0x0a && buffer[6] == 0x1a && buffer[7] == 0x0a; var extension = isPng ? "png" : "jpg"; var videoImagesPath = DataExporter.EnsurePathExists(Path.Combine(outputPath, name, $"{imageCounter:000000}.{extension}")); using var videoImageFile = File.Create(videoImagesPath); videoImageFile.Write(buffer, 0, eicv.ViewedObject.Resource.Size); timingFile.WriteLine($"{imageCounter}\t{envelope.OriginatingTime.ToText()}"); poseFile.WriteLine($"{envelope.OriginatingTime.ToText()}\t{eicv.CameraPose.ToText()}"); if (imageCounter == 0) { intrinsicsFile.WriteLine(eicv.CameraIntrinsics.ToText()); } imageCounter++; }, DeliveryPolicy.SynchronousOrThrottle); }
/// <summary> /// Main entry point. /// </summary> /// <param name="args">Command-line arguments.</param> /// <returns>Command-line status.</returns> public static int Main(string[] args) { Console.WriteLine($"HoloLensCaptureExporter Tool"); try { return(Parser.Default.ParseArguments <Verbs.ExportCommand>(args) .MapResult( (Verbs.ExportCommand command) => DataExporter.Run(command), DisplayParseErrors)); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); return(-1); } }
/// <summary> /// Exports a stream of depth image camera views. /// </summary> /// <param name="source">The source stream of depth image camera views.</param> /// <param name="name">The name for the source stream.</param> /// <param name="outputPath">The output path.</param> /// <param name="streamWritersToClose">The collection of stream writers to be closed.</param> internal static void Export(this IProducer <DepthImageCameraView> source, string name, string outputPath, List <StreamWriter> streamWritersToClose) { var timingFilePath = DataExporter.EnsurePathExists(Path.Combine(outputPath, name, $"Timing.txt")); var timingFile = File.CreateText(timingFilePath); streamWritersToClose.Add(timingFile); var poseFilePath = DataExporter.EnsurePathExists(Path.Combine(outputPath, name, $"Pose.txt")); var poseFile = File.CreateText(poseFilePath); streamWritersToClose.Add(poseFile); var intrinsicsFilePath = DataExporter.EnsurePathExists(Path.Combine(outputPath, name, $"Intrinsics.txt")); var intrinsicsFile = File.CreateText(intrinsicsFilePath); streamWritersToClose.Add(intrinsicsFile); var depthImageCounter = 0; source .Encode(new DepthImageToPngStreamEncoder(), DeliveryPolicy.SynchronousOrThrottle) .Do( (edicv, envelope) => { var buffer = edicv.ViewedObject.Resource.GetBuffer(); var depthImagesPath = DataExporter.EnsurePathExists(Path.Combine(outputPath, name, $"{depthImageCounter:000000}.png")); using var depthImageFile = File.Create(depthImagesPath); depthImageFile.Write(buffer, 0, buffer.Length); timingFile.WriteLine($"{depthImageCounter}\t{envelope.OriginatingTime.ToText()}"); poseFile.WriteLine($"{envelope.OriginatingTime.ToText()}\t{edicv.CameraPose.ToText()}"); if (depthImageCounter == 0) { intrinsicsFile.WriteLine(edicv.CameraIntrinsics.ToText()); } depthImageCounter++; }, DeliveryPolicy.SynchronousOrThrottle); }
/// <summary> /// Exports a stream of calibration maps. /// </summary> /// <param name="source">The source stream of calibration maps.</param> /// <param name="directory">The directory in which to persist.</param> /// <param name="name">The name for the source stream.</param> /// <param name="outputPath">The output path.</param> /// <param name="streamWritersToClose">The collection of stream writers to be closed.</param> internal static void Export(this IProducer <CalibrationPointsMap> source, string directory, string name, string outputPath, List <StreamWriter> streamWritersToClose) { var filePath = DataExporter.EnsurePathExists(Path.Combine(outputPath, directory, $"{name}.txt")); var file = File.CreateText(filePath); streamWritersToClose.Add(file); source .Do( (map, envelope) => { var result = new StringBuilder(); result.Append($"{envelope.OriginatingTime.ToText()}\t"); result.Append($"{map.Width.ToText()}\t"); result.Append($"{map.Height.ToText()}\t"); foreach (var point in map.CameraUnitPlanePoints) { result.Append($"{point.ToText()}\t"); } file.WriteLine(result.ToString().TrimEnd('\t')); }); }
/// <summary> /// Exports a stream of scene objects. /// </summary> /// <param name="source">The source stream of scene objects.</param> /// <param name="name">The name for the source stream.</param> /// <param name="outputPath">The output path.</param> /// <param name="streamWritersToClose">The collection of stream writers to be closed.</param> internal static void Export(this IProducer <SceneObjectCollection> source, string name, string outputPath, List <StreamWriter> streamWritersToClose) { void ExportScene(IProducer <SceneObjectCollection.SceneObject> sceneObject, string sceneName) { void BuildRectangle(Rectangle3D?rect, StringBuilder sb) { void BuildPoint(Point3D point, StringBuilder sb) { sb.Append($"{point.ToText()}\t"); } if (rect.HasValue) { var r = rect.Value; BuildPoint(r.TopLeft, sb); BuildPoint(r.TopRight, sb); BuildPoint(r.BottomLeft, sb); BuildPoint(r.BottomRight, sb); } else { var nan = double.NaN.ToText(); for (var i = 0; i < 8; i++) { sb.Append($"{nan}\t"); } } } var path = Path.Combine(outputPath, name, sceneName); void ExportMeshes(List <Mesh3D> meshes, string directory, string name) { for (var i = 0; i < meshes.Count; i++) { // .obj file format: https://en.wikipedia.org/wiki/Wavefront_.obj_file var mesh = meshes[i]; var meshFile = File.CreateText(DataExporter.EnsurePathExists(Path.Combine(path, directory, name, $"Mesh{i}.obj"))); meshFile.Write($"# {directory} {name}"); foreach (var v in mesh.Vertices) { meshFile.Write($"\nv {v.X.ToText()} {v.Y.ToText()} {v.Z.ToText()}"); } var indices = mesh.TriangleIndices; for (var j = 0; j < indices.Length; j += 3) { meshFile.WriteLine($"\nf {indices[j] + 1} {indices[j + 1] + 1} {indices[j + 2] + 1}"); } meshFile.Close(); } } var rectanglesFile = File.CreateText(DataExporter.EnsurePathExists(Path.Combine(path, $"Rectangles.txt"))); var meshesFile = File.CreateText(DataExporter.EnsurePathExists(Path.Combine(path, $"Meshes.txt"))); streamWritersToClose.Add(rectanglesFile); streamWritersToClose.Add(meshesFile); sceneObject .Do( (s, envelope) => { var originatingTime = $"{envelope.OriginatingTime.ToText()}"; var result = new StringBuilder(); result.Append(originatingTime).Append('\t'); for (var i = 0; i < s.Rectangles.Count; i++) { BuildRectangle(s.Rectangles[i], result); BuildRectangle(s.PlacementRectangles.Count > 0 ? s.PlacementRectangles[i] : null, result); } rectanglesFile.WriteLine(result.ToString().TrimEnd('\t')); meshesFile.WriteLine($"{originatingTime}\t{s.Meshes.Count}\t{s.ColliderMeshes.Count}"); ExportMeshes(s.Meshes, nameof(SceneObjectCollection.SceneObject.Meshes), originatingTime); ExportMeshes(s.ColliderMeshes, nameof(SceneObjectCollection.SceneObject.ColliderMeshes), originatingTime); }); } ExportScene(source.Select(s => s.Background), nameof(SceneObjectCollection.Background)); ExportScene(source.Select(s => s.Ceiling), nameof(SceneObjectCollection.Ceiling)); ExportScene(source.Select(s => s.Floor), nameof(SceneObjectCollection.Floor)); ExportScene(source.Select(s => s.Inferred), nameof(SceneObjectCollection.Inferred)); ExportScene(source.Select(s => s.Platform), nameof(SceneObjectCollection.Platform)); ExportScene(source.Select(s => s.Unknown), nameof(SceneObjectCollection.Unknown)); ExportScene(source.Select(s => s.Wall), nameof(SceneObjectCollection.Wall)); ExportScene(source.Select(s => s.World), nameof(SceneObjectCollection.World)); }