// ReSharper disable once InvalidXmlDocComment /// <summary> /// Compute RenderedObjectInfo for each visible object in the given instance segmentation image. /// InstanceSegmentationRawData should be the raw data from a texture filled by <see cref="InstanceSegmentationUrpPass"/> or <see cref="InstanceSegmentationPass"/> /// using the same LabelingConfiguration that was passed into this object. /// </summary> /// <param name="instanceSegmentationRawData">The raw instance segmentation image.</param> /// <param name="stride">Stride of the image data. Should be equal to the width of the image.</param> /// <param name="boundingBoxOrigin">Whether bounding boxes should be top-left or bottom-right-based.</param> /// <param name="renderedObjectInfos">When this method returns, filled with RenderedObjectInfo entries for each object visible in the frame.</param> /// <param name="perLabelEntryObjectCount">When the method returns, filled with a NativeArray with the count of objects for each entry in <see cref="LabelingConfiguration.LabelEntries"/> in the LabelingConfiguration passed into the constructor.</param> /// <param name="allocator">The allocator to use for allocating renderedObjectInfos and perLabelEntryObjectCount.</param> public void Compute(NativeArray <uint> instanceSegmentationRawData, int stride, BoundingBoxOrigin boundingBoxOrigin, out NativeArray <RenderedObjectInfo> renderedObjectInfos, out NativeArray <uint> perLabelEntryObjectCount, Allocator allocator) { const int jobCount = 24; var height = instanceSegmentationRawData.Length / stride; //special math to round up var rowsPerJob = height / jobCount; var rowRemainder = height % jobCount; var handles = new NativeArray <JobHandle>(jobCount, Allocator.Temp); var jobBoundingBoxLists = new NativeList <Object1DSpan> [jobCount]; using (s_LabelJobs.Auto()) { for (int row = 0, jobIndex = 0; row < height; row += rowsPerJob, jobIndex++) { jobBoundingBoxLists[jobIndex] = new NativeList <Object1DSpan>(10, Allocator.TempJob); var rowsThisJob = math.min(height - row, rowsPerJob); if (jobIndex < rowRemainder) { rowsThisJob++; } handles[jobIndex] = new ComputeHistogramPerRowJob { segmentationImageData = new NativeSlice <uint>(instanceSegmentationRawData, row * stride, stride * rowsThisJob), width = stride, rowStart = row, rows = rowsThisJob, boundingBoxes = jobBoundingBoxLists[jobIndex] }.Schedule(); if (jobIndex < rowRemainder) { row++; } } JobHandle.CompleteAll(handles); } perLabelEntryObjectCount = new NativeArray <uint>(m_LabelingConfiguration.LabelEntries.Count, allocator); var boundingBoxMap = new NativeHashMap <int, RenderedObjectInfo>(100, Allocator.Temp); using (s_LabelMerge.Auto()) { foreach (var boundingBoxList in jobBoundingBoxLists) { if (!boundingBoxList.IsCreated) { continue; } foreach (var info1D in boundingBoxList) { var objectInfo = new RenderedObjectInfo { boundingBox = new Rect(info1D.left, info1D.row, info1D.right - info1D.left + 1, 1), instanceId = info1D.instanceId, pixelCount = info1D.right - info1D.left + 1 }; if (boundingBoxMap.TryGetValue(info1D.instanceId, out var info)) { objectInfo.boundingBox = Rect.MinMaxRect( math.min(info.boundingBox.xMin, objectInfo.boundingBox.xMin), math.min(info.boundingBox.yMin, objectInfo.boundingBox.yMin), math.max(info.boundingBox.xMax, objectInfo.boundingBox.xMax), math.max(info.boundingBox.yMax, objectInfo.boundingBox.yMax)); objectInfo.pixelCount += info.pixelCount; } boundingBoxMap[info1D.instanceId] = objectInfo; } } var keyValueArrays = boundingBoxMap.GetKeyValueArrays(Allocator.Temp); renderedObjectInfos = new NativeArray <RenderedObjectInfo>(keyValueArrays.Keys.Length, allocator); for (var i = 0; i < keyValueArrays.Keys.Length; i++) { var instanceId = keyValueArrays.Keys[i]; if (m_InstanceIdToLabelEntryIndexLookup.Length <= instanceId) { continue; } var labelIndex = m_InstanceIdToLabelEntryIndexLookup[instanceId]; var labelId = m_LabelingConfiguration.LabelEntries[labelIndex].id; perLabelEntryObjectCount[labelIndex]++; var renderedObjectInfo = keyValueArrays.Values[i]; var boundingBox = renderedObjectInfo.boundingBox; if (boundingBoxOrigin == BoundingBoxOrigin.TopLeft) { var y = height - boundingBox.yMax; boundingBox = new Rect(boundingBox.x, y, boundingBox.width, boundingBox.height); } renderedObjectInfos[i] = new RenderedObjectInfo { instanceId = instanceId, labelId = labelId, boundingBox = boundingBox, pixelCount = renderedObjectInfo.pixelCount }; } keyValueArrays.Dispose(); } boundingBoxMap.Dispose(); foreach (var rowBoundingBox in jobBoundingBoxLists) { if (rowBoundingBox.IsCreated) { rowBoundingBox.Dispose(); } } handles.Dispose(); }
// ReSharper disable once InvalidXmlDocComment /// <summary> /// Compute RenderedObjectInfo for each visible object in the given instance segmentation image. /// InstanceSegmentationRawData should be the raw data from a texture filled by <see cref="InstanceSegmentationUrpPass"/> or <see cref="InstanceSegmentationPass"/> /// using the same LabelingConfiguration that was passed into this object. /// </summary> /// <param name="instanceSegmentationRawData">The raw instance segmentation image.</param> /// <param name="stride">Stride of the image data. Should be equal to the width of the image.</param> /// <param name="boundingBoxOrigin">Whether bounding boxes should be top-left or bottom-right-based.</param> /// <param name="renderedObjectInfos">When this method returns, filled with RenderedObjectInfo entries for each object visible in the frame.</param> /// <param name="allocator">The allocator to use for allocating renderedObjectInfos and perLabelEntryObjectCount.</param> public void Compute(NativeArray <Color32> instanceSegmentationRawData, int stride, BoundingBoxOrigin boundingBoxOrigin, out NativeArray <RenderedObjectInfo> renderedObjectInfos, Allocator allocator) { const int jobCount = 24; var height = instanceSegmentationRawData.Length / stride; //special math to round up var rowsPerJob = height / jobCount; var rowRemainder = height % jobCount; var handles = new NativeArray <JobHandle>(jobCount, Allocator.Temp); var jobBoundingBoxLists = new NativeList <Object1DSpan> [jobCount]; using (s_LabelJobs.Auto()) { for (int row = 0, jobIndex = 0; row < height; row += rowsPerJob, jobIndex++) { jobBoundingBoxLists[jobIndex] = new NativeList <Object1DSpan>(10, Allocator.TempJob); var rowsThisJob = math.min(height - row, rowsPerJob); if (jobIndex < rowRemainder) { rowsThisJob++; } handles[jobIndex] = new ComputeHistogramPerRowJob { segmentationImageData = new NativeSlice <Color32>(instanceSegmentationRawData, row * stride, stride * rowsThisJob), width = stride, rowStart = row, rows = rowsThisJob, boundingBoxes = jobBoundingBoxLists[jobIndex] }.Schedule(); if (jobIndex < rowRemainder) { row++; } } JobHandle.CompleteAll(handles); } var boundingBoxMap = new NativeHashMap <uint, RenderedObjectInfo>(100, Allocator.Temp); using (s_LabelMerge.Auto()) { foreach (var boundingBoxList in jobBoundingBoxLists) { if (!boundingBoxList.IsCreated) { continue; } foreach (var info1D in boundingBoxList) { var objectInfo = new RenderedObjectInfo { boundingBox = new Rect(info1D.left, info1D.row, info1D.right - info1D.left + 1, 1), instanceId = info1D.instanceId, pixelCount = info1D.right - info1D.left + 1 }; if (boundingBoxMap.TryGetValue(info1D.instanceId, out var info)) { objectInfo.boundingBox = Rect.MinMaxRect( math.min(info.boundingBox.xMin, objectInfo.boundingBox.xMin), math.min(info.boundingBox.yMin, objectInfo.boundingBox.yMin), math.max(info.boundingBox.xMax, objectInfo.boundingBox.xMax), math.max(info.boundingBox.yMax, objectInfo.boundingBox.yMax)); objectInfo.pixelCount += info.pixelCount; } boundingBoxMap[info1D.instanceId] = objectInfo; } } var keyValueArrays = boundingBoxMap.GetKeyValueArrays(Allocator.Temp); renderedObjectInfos = new NativeArray <RenderedObjectInfo>(keyValueArrays.Keys.Length, allocator); for (var i = 0; i < keyValueArrays.Keys.Length; i++) { var color = InstanceIdToColorMapping.GetColorFromPackedColor(keyValueArrays.Keys[i]); if (InstanceIdToColorMapping.TryGetInstanceIdFromColor(color, out var instanceId)) { var renderedObjectInfo = keyValueArrays.Values[i]; var boundingBox = renderedObjectInfo.boundingBox; if (boundingBoxOrigin == BoundingBoxOrigin.TopLeft) { var y = height - boundingBox.yMax; boundingBox = new Rect(boundingBox.x, y, boundingBox.width, boundingBox.height); } renderedObjectInfos[i] = new RenderedObjectInfo { instanceId = instanceId, boundingBox = boundingBox, pixelCount = renderedObjectInfo.pixelCount, instanceColor = color }; } else { Debug.LogError($"Could not generate instance ID for object, ID exceeded maximum ID"); } } keyValueArrays.Dispose(); } boundingBoxMap.Dispose(); foreach (var rowBoundingBox in jobBoundingBoxLists) { if (rowBoundingBox.IsCreated) { rowBoundingBox.Dispose(); } } handles.Dispose(); }