您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
550 行
18 KiB
550 行
18 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using Unity.Simulation;
|
|
|
|
namespace UnityEngine.Perception.GroundTruth.Exporters.Coco
|
|
{
|
|
public class CocoExporter : IDatasetExporter
|
|
{
|
|
bool m_PrettyPrint = true;
|
|
|
|
bool m_ReportingObjectDetection;
|
|
bool m_ReportingKeypoints;
|
|
|
|
bool m_Initialized;
|
|
string m_DirectoryName = string.Empty;
|
|
|
|
string m_RgbCaptureFilename;
|
|
StreamWriter m_RgbCaptureStream;
|
|
|
|
string m_ObjectDetectionFilename;
|
|
StreamWriter m_ObjectDetectionStream;
|
|
Task m_ObjectDetectionWritingTask;
|
|
|
|
string m_KeypointFilename;
|
|
StreamWriter m_KeypointDetectionStream;
|
|
Task m_KeypointDetectionWritingTask;
|
|
|
|
CocoTypes.ObjectDetectionCategories m_ObjectDetectionCategories;
|
|
string m_ObjectDetectionCategoryFilename;
|
|
StreamWriter m_ObjectDetectionCategoryStream;
|
|
Task m_ObjectDetectionCategoryWritingTask;
|
|
|
|
CocoTypes.KeypointCategories m_KeypointCategories;
|
|
string m_KeypointCategoryFilename;
|
|
StreamWriter m_KeypointCategoryStream;
|
|
Task m_KeypointCategoryWritingTask;
|
|
|
|
Guid m_SessionGuid;
|
|
|
|
public string GetRgbCaptureFilename(params(string, object)[] additionalSensorValues)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
public void OnSimulationBegin(string directoryName)
|
|
{
|
|
Debug.Log($"SS - COCO - OnSimBegin");
|
|
m_DirectoryName = directoryName;
|
|
m_DataCaptured = false;
|
|
}
|
|
|
|
async Task AwaitAllWrites()
|
|
{
|
|
Debug.Log("SS - coco - writing");
|
|
|
|
WriteOutCategories();
|
|
|
|
if (m_ObjectDetectionWritingTask != null)
|
|
{
|
|
await m_ObjectDetectionWritingTask;
|
|
await m_ObjectDetectionStream.WriteAsync("]");
|
|
}
|
|
|
|
if (m_KeypointDetectionWritingTask != null)
|
|
{
|
|
await m_KeypointDetectionWritingTask;
|
|
await m_KeypointDetectionStream.WriteAsync("]");
|
|
}
|
|
|
|
if (m_RgbCaptureWritingTask != null)
|
|
{
|
|
await m_RgbCaptureWritingTask;
|
|
await m_RgbCaptureStream.WriteAsync("]");
|
|
}
|
|
|
|
if (m_ObjectDetectionCategoryWritingTask != null)
|
|
{
|
|
await m_ObjectDetectionCategoryWritingTask;
|
|
}
|
|
|
|
if (m_KeypointCategoryWritingTask != null)
|
|
{
|
|
await m_KeypointCategoryWritingTask;
|
|
}
|
|
|
|
m_RgbCaptureStream?.Close();
|
|
m_ObjectDetectionStream?.Close();
|
|
m_ObjectDetectionCategoryStream?.Close();
|
|
m_KeypointDetectionStream?.Close();
|
|
m_KeypointCategoryStream?.Close();
|
|
}
|
|
|
|
|
|
public async void OnSimulationEnd()
|
|
{
|
|
Debug.Log($"SS - COCO - OnSimEnd");
|
|
if (!m_DataCaptured) return;
|
|
|
|
await AwaitAllWrites();
|
|
|
|
if (m_ReportingObjectDetection)
|
|
{
|
|
await WriteObjectDetectionFile();
|
|
}
|
|
|
|
if (m_ReportingKeypoints)
|
|
{
|
|
await WriteKeypointFile();
|
|
}
|
|
|
|
File.Delete(m_RgbCaptureFilename);
|
|
|
|
m_Initialized = false;
|
|
|
|
}
|
|
|
|
void InitializeCaptureFiles()
|
|
{
|
|
if (m_Initialized) return;
|
|
|
|
if (!Directory.Exists(m_DirectoryName))
|
|
Directory.CreateDirectory(m_DirectoryName);
|
|
|
|
m_SessionGuid = Guid.NewGuid();
|
|
|
|
//var prefix = m_DirectoryName + Path.DirectorySeparatorChar + m_SessionGuid;
|
|
|
|
|
|
m_RgbCaptureFilename = Path.Combine(m_DirectoryName, m_SessionGuid + "_coco_captures.json");
|
|
m_RgbCaptureStream = File.CreateText(m_RgbCaptureFilename);
|
|
|
|
m_ObjectDetectionFilename = Path.Combine(m_DirectoryName, m_SessionGuid + "_coco_box_annotations.json");
|
|
if (m_ReportingObjectDetection)
|
|
{
|
|
m_ObjectDetectionStream = File.CreateText(m_ObjectDetectionFilename);
|
|
m_ObjectDetectionCategoryFilename = Path.Combine(m_DirectoryName, m_SessionGuid + "_coco_obj_detection_categories.json");
|
|
}
|
|
|
|
m_KeypointFilename = Path.Combine(m_DirectoryName, m_SessionGuid + "_coco_keypoint_annotations.json");
|
|
if (m_ReportingKeypoints)
|
|
{
|
|
m_KeypointDetectionStream = File.CreateText(m_KeypointFilename);
|
|
m_KeypointCategoryFilename = Path.Combine(m_DirectoryName, m_SessionGuid + "_coco_keypoint_categories.json");
|
|
}
|
|
|
|
m_Initialized = true;
|
|
}
|
|
|
|
static void AggregateFile(string filename, StringBuilder aggregated, bool skipFirstCharacter = false)
|
|
{
|
|
var sr = new StreamReader(filename);
|
|
|
|
var length = (int)sr.BaseStream.Length;
|
|
var start = 0;
|
|
|
|
if (length == 0) return;
|
|
|
|
var buffer = new char[length];
|
|
sr.Read(buffer, start, length);
|
|
|
|
if (skipFirstCharacter)
|
|
{
|
|
length--;
|
|
start++;
|
|
}
|
|
|
|
aggregated.Append(buffer, start, length);
|
|
|
|
sr.Dispose();
|
|
}
|
|
|
|
async Task WriteObjectDetectionFile()
|
|
{
|
|
var stringBuilder = new StringBuilder();
|
|
|
|
stringBuilder.Append("{");
|
|
|
|
// Create the json header
|
|
CreateHeaderInfo(stringBuilder);
|
|
stringBuilder.Append(",");
|
|
|
|
CreateLicenseInfo(stringBuilder);
|
|
|
|
// Read in the contents of the captures
|
|
stringBuilder.Append(",\"images\":");
|
|
AggregateFile(m_RgbCaptureFilename, stringBuilder);
|
|
|
|
// Read in the contents of the object detection
|
|
stringBuilder.Append(",\"annotations\":");
|
|
AggregateFile(m_ObjectDetectionFilename, stringBuilder);
|
|
stringBuilder.Append(",");
|
|
|
|
// Read in the contents of the object detection categories
|
|
AggregateFile(m_ObjectDetectionCategoryFilename, stringBuilder, true);
|
|
|
|
var json = stringBuilder.ToString();
|
|
|
|
if (m_PrettyPrint)
|
|
{
|
|
json = JToken.Parse(json).ToString(Formatting.Indented);
|
|
}
|
|
|
|
Debug.Log($"SS - COCO - writing to path: {m_DirectoryName}, file: coco_object_detection_annotations.json");
|
|
|
|
// Write out the files
|
|
var filename = Path.Combine(m_DirectoryName, "coco_object_detection_annotations.json");
|
|
|
|
Debug.Log($"SS - COCO - file: {filename}");
|
|
|
|
|
|
var cocoStream = File.CreateText(filename);
|
|
await cocoStream.WriteAsync(json);
|
|
cocoStream.Close();
|
|
|
|
Manager.Instance.ConsumerFileProduced(filename);
|
|
|
|
File.Delete(m_ObjectDetectionFilename);
|
|
File.Delete(m_ObjectDetectionCategoryFilename);
|
|
}
|
|
|
|
async Task WriteKeypointFile()
|
|
{
|
|
var stringBuilder = new StringBuilder();
|
|
|
|
stringBuilder.Append("{");
|
|
|
|
// Create the json header
|
|
CreateHeaderInfo(stringBuilder);
|
|
stringBuilder.Append(",");
|
|
|
|
CreateLicenseInfo(stringBuilder);
|
|
|
|
// Read in the contents of the captures
|
|
stringBuilder.Append(",\"images\":");
|
|
AggregateFile(m_RgbCaptureFilename, stringBuilder);
|
|
|
|
// Read in the contents of the object detection
|
|
stringBuilder.Append(",\"annotations\":");
|
|
AggregateFile(m_KeypointFilename, stringBuilder);
|
|
stringBuilder.Append(",");
|
|
|
|
// Read in the contents of the object detection categories
|
|
AggregateFile(m_KeypointCategoryFilename, stringBuilder, true);
|
|
|
|
var json = stringBuilder.ToString();
|
|
|
|
if (m_PrettyPrint)
|
|
{
|
|
json = JToken.Parse(json).ToString(Formatting.Indented);
|
|
}
|
|
|
|
// Write out the files
|
|
var filename = Path.Combine(m_DirectoryName, "coco_keypoint_annotations.json");
|
|
var cocoStream = File.CreateText(filename);
|
|
await cocoStream.WriteAsync(json);
|
|
cocoStream.Dispose();
|
|
|
|
Manager.Instance.ConsumerFileProduced(filename);
|
|
|
|
File.Delete(m_KeypointFilename);
|
|
File.Delete(m_KeypointCategoryFilename);
|
|
}
|
|
|
|
bool m_DataCaptured;
|
|
|
|
void WriteOutCategories()
|
|
{
|
|
// 3 cases
|
|
// 1) Just have object detection
|
|
// 2) Just have keypoint detection
|
|
// 3) Have both, which includes writing out object detection in object detection coco output
|
|
// and merging the object detection & keypoints and writing out that data in the keypoint
|
|
// coco output data
|
|
if (m_ReportingObjectDetection)
|
|
{
|
|
m_ObjectDetectionCategoryStream = File.CreateText(m_ObjectDetectionCategoryFilename);
|
|
var json = JsonUtility.ToJson(m_ObjectDetectionCategories);
|
|
m_ObjectDetectionCategoryWritingTask = m_ObjectDetectionCategoryStream.WriteAsync(json);
|
|
}
|
|
|
|
if (m_ReportingKeypoints)
|
|
{
|
|
// TODO - revisit this, but right now we are going to add the keypoint info to all of the categories,
|
|
// we can get away with this because we only support one set of keypoint definitions per simulation
|
|
// currently.
|
|
|
|
if (m_KeypointCategories.categories.Length < 1) return;
|
|
|
|
m_KeypointCategoryStream = File.CreateText(m_KeypointCategoryFilename);
|
|
|
|
var merged = m_KeypointCategories;
|
|
|
|
if (m_ReportingObjectDetection)
|
|
{
|
|
merged = new CocoTypes.KeypointCategories
|
|
{
|
|
categories = new CocoTypes.KeypointCategory[m_ObjectDetectionCategories.categories.Count]
|
|
};
|
|
|
|
var i = 0;
|
|
foreach (var odc in m_ObjectDetectionCategories.categories)
|
|
{
|
|
merged.categories[i++] = MergeCategories(odc, m_KeypointCategories.categories[0]);
|
|
}
|
|
}
|
|
|
|
var json = JsonUtility.ToJson(merged);
|
|
m_KeypointCategoryWritingTask = m_KeypointCategoryStream.WriteAsync(json);
|
|
}
|
|
}
|
|
|
|
public void OnAnnotationRegistered<TSpec>(Guid annotationId, TSpec[] values)
|
|
{
|
|
if (annotationId.ToString() == BoundingBox2DLabeler.annotationId)
|
|
{
|
|
m_ReportingObjectDetection = true;
|
|
m_ObjectDetectionCategories = new CocoTypes.ObjectDetectionCategories
|
|
{
|
|
categories = new List<CocoTypes.ObjectDetectionCategory>()
|
|
};
|
|
|
|
foreach (var value in values)
|
|
{
|
|
if (value is IdLabelConfig.LabelEntrySpec spec)
|
|
{
|
|
var rec = new CocoTypes.ObjectDetectionCategory
|
|
{
|
|
id = spec.label_id,
|
|
name = spec.label_name,
|
|
supercategory = spec.label_name
|
|
};
|
|
|
|
m_ObjectDetectionCategories.categories.Add(rec);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (annotationId.ToString() == KeypointLabeler.annotationId)
|
|
{
|
|
m_ReportingKeypoints = true;
|
|
if (values[0] is KeypointLabeler.KeypointJson keypointJson)
|
|
{
|
|
m_KeypointCategories = new CocoTypes.KeypointCategories
|
|
{
|
|
categories = new []
|
|
{
|
|
AnnotationHandler.ToKeypointCategory(keypointJson)
|
|
}
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
static string versionEntry = "0.0.1";
|
|
static string descriptionEntry = "Description of dataset";
|
|
static string contributorEntry = "Anonymous";
|
|
static string urlEntry = "Not Set";
|
|
|
|
static void CreateHeaderInfo(StringBuilder stringBuilder)
|
|
{
|
|
stringBuilder.Append("\"info\":");
|
|
|
|
var dateTime = DateTime.Today;
|
|
var info = new CocoTypes.Info
|
|
{
|
|
year = int.Parse(dateTime.ToString("yyyy")),
|
|
version = versionEntry,
|
|
description = descriptionEntry,
|
|
contributor = contributorEntry,
|
|
url = urlEntry,
|
|
date_created = DateTime.Today.ToString("D")
|
|
};
|
|
stringBuilder.Append(JsonUtility.ToJson(info));
|
|
}
|
|
|
|
static void CreateLicenseInfo(StringBuilder stringBuilder)
|
|
{
|
|
var licenses = new CocoTypes.Licenses
|
|
{
|
|
licenses = new[]
|
|
{
|
|
new CocoTypes.License
|
|
{
|
|
id = 0,
|
|
name = "No License",
|
|
url = "Not Set"
|
|
}
|
|
}
|
|
};
|
|
|
|
var tmpJson = JsonUtility.ToJson(licenses);
|
|
|
|
// Remove the start and end '{' from the licenses json
|
|
stringBuilder.Append(tmpJson.Substring(1, tmpJson.Length - 2));
|
|
}
|
|
|
|
bool m_FirstBoxAnnotation = true;
|
|
bool m_FirstKeypointAnnotation = true;
|
|
|
|
bool m_FirstCapture = true;
|
|
Task m_RgbCaptureWritingTask;
|
|
|
|
static CocoTypes.KeypointCategory MergeCategories(CocoTypes.ObjectDetectionCategory od, CocoTypes.KeypointCategory kp)
|
|
{
|
|
return new CocoTypes.KeypointCategory
|
|
{
|
|
id = od.id,
|
|
name = od.name,
|
|
supercategory = od.supercategory,
|
|
keypoints = kp.keypoints,
|
|
skeleton = kp.skeleton
|
|
};
|
|
}
|
|
|
|
public async Task ProcessPendingCaptures(List<SimulationState.PendingCapture> pendingCaptures, SimulationState simState)
|
|
{
|
|
var boxJson = string.Empty;
|
|
var keypointJson = string.Empty;
|
|
|
|
foreach (var cap in pendingCaptures)
|
|
{
|
|
var boxes = new Dictionary<int, CocoTypes.ObjectDetectionAnnotation>();
|
|
|
|
foreach (var annotation in cap.Annotations)
|
|
{
|
|
var tmp = ProcessBoundingBoxAnnotations(annotation.Item2.RawValues);
|
|
|
|
foreach (var box in tmp.Values)
|
|
{
|
|
boxes[box.id] = box;
|
|
|
|
if (m_FirstBoxAnnotation)
|
|
{
|
|
boxJson = "[";
|
|
m_FirstBoxAnnotation = false;
|
|
}
|
|
else
|
|
boxJson += ",";
|
|
|
|
boxJson += JsonUtility.ToJson(box);
|
|
}
|
|
|
|
}
|
|
|
|
foreach (var annotation in cap.Annotations)
|
|
{
|
|
var keypoints = ProcessKeypointAnnotations(annotation.Item2.RawValues, boxes);
|
|
|
|
foreach (var kp in keypoints.Values)
|
|
{
|
|
if (m_FirstKeypointAnnotation)
|
|
{
|
|
keypointJson = "[";
|
|
m_FirstKeypointAnnotation = false;
|
|
}
|
|
else
|
|
keypointJson += ",";
|
|
|
|
keypointJson += JsonUtility.ToJson(kp);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_ObjectDetectionWritingTask != null)
|
|
await m_ObjectDetectionWritingTask;
|
|
|
|
if (boxJson != string.Empty)
|
|
{
|
|
InitializeCaptureFiles();
|
|
m_ObjectDetectionWritingTask = m_ObjectDetectionStream.WriteAsync(boxJson);
|
|
}
|
|
|
|
if (m_KeypointDetectionWritingTask != null)
|
|
await m_KeypointDetectionWritingTask;
|
|
|
|
if (keypointJson != string.Empty)
|
|
{
|
|
InitializeCaptureFiles();
|
|
m_KeypointDetectionWritingTask = m_KeypointDetectionStream.WriteAsync(keypointJson);
|
|
}
|
|
}
|
|
|
|
static Dictionary<int, CocoTypes.ObjectDetectionAnnotation> ProcessBoundingBoxAnnotations(IEnumerable<object> annotations)
|
|
{
|
|
var map = new Dictionary<int, CocoTypes.ObjectDetectionAnnotation>();
|
|
foreach (var annotation in annotations)
|
|
{
|
|
if (annotation is BoundingBox2DLabeler.BoundingBoxValue bbox)
|
|
{
|
|
var coco = CocoTypes.ObjectDetectionAnnotation.FromBoundingBoxValue(bbox);
|
|
map[coco.id] = coco;
|
|
}
|
|
}
|
|
|
|
return map;
|
|
}
|
|
|
|
static Dictionary<int, CocoTypes.KeypointAnnotation> ProcessKeypointAnnotations(IEnumerable<object> annotations, Dictionary<int, CocoTypes.ObjectDetectionAnnotation> boundingBoxes)
|
|
{
|
|
var map = new Dictionary<int, CocoTypes.KeypointAnnotation>();
|
|
|
|
foreach (var annotation in annotations)
|
|
{
|
|
if (annotation is KeypointLabeler.KeypointEntry keypoint)
|
|
{
|
|
var coco = CocoTypes.KeypointAnnotation.FromKeypointValue(keypoint);
|
|
if (boundingBoxes.ContainsKey(coco.id))
|
|
{
|
|
coco.CopyObjectDetectionData(boundingBoxes[coco.id]);
|
|
}
|
|
|
|
map[coco.id] = coco;
|
|
}
|
|
}
|
|
|
|
return map;
|
|
}
|
|
|
|
public async Task OnCaptureReported(int frame, int width, int height, string filename)
|
|
{
|
|
InitializeCaptureFiles();
|
|
|
|
var json = string.Empty;
|
|
|
|
var converted = AnnotationHandler.HandleCameraCapture(frame, width, height, filename);
|
|
if (m_FirstCapture)
|
|
{
|
|
json = "[";
|
|
m_FirstCapture = false;
|
|
}
|
|
else
|
|
json += ",";
|
|
|
|
json += JsonUtility.ToJson(converted);
|
|
|
|
if (m_RgbCaptureWritingTask != null)
|
|
await m_RgbCaptureWritingTask;
|
|
|
|
if (json != string.Empty)
|
|
m_RgbCaptureWritingTask = m_RgbCaptureStream.WriteAsync(json);
|
|
|
|
m_DataCaptured = true;
|
|
}
|
|
}
|
|
}
|