/// <inheritdoc/> public override void Initialize(GridBuffer buffer) { base.Initialize(buffer); // Helper GridBuffer, channels: // 0 - proximity lookup // 1 - kernel value (max, dilated area) // 2 - buffer value (max, dilated area) m_AuxGrid = new GridBuffer(3, buffer.Width, buffer.Height); m_Kernels = new List <Point> [c_MaxRadius]; for (int radius = 1; radius <= c_MaxRadius; radius++) { int i = radius - 1; m_Kernels[i] = new List <Point>(radius * radius * 4); // TBD for (int x = -radius; x <= radius; x++) { for (int y = -radius; y <= radius; y++) { float value = 1 - new Vector2(x, y).magnitude / radius; if (value > 0) { m_Kernels[i].Add(new Point { x = x, y = y, value = value }); } } } } }
/// <summary> /// Writes additional value to stored grid positions, /// using either the originally added positions /// or the positions calculated in <see cref="Process"/>. /// </summary> /// <param name="buffer"><see cref="GridBuffer"/> used by the sensor</param> /// <param name="channel">Grid channel index</param> /// <param name="value">The value to write</param> public virtual void Write(GridBuffer buffer, int channel, float value) { foreach (Vector2Int pos in GridPositions) { buffer.Write(channel, pos, value); } }
/// <inheritdoc/> public override void Initialize(GridBuffer buffer) { base.Initialize(buffer); m_Samples = new float[ Mathf.CeilToInt(buffer.Width / 2f), Mathf.CeilToInt(buffer.Height / 2f)]; }
/// <inheritdoc/> public override void Process(GridBuffer buffer, int channel) { // Side length of squares around points required for filling // Width x Height, IF positions were distributed evenly. float sparseness = Mathf.Sqrt(Width * Height / (float)GridPositions.Count); if (sparseness > 1) { sparseness *= c_Multiplier; foreach (Vector2Int pGrid in GridPositions) { int xGrid = pGrid.x; int yGrid = pGrid.y; float proximity = m_AuxGrid.Read(0, xGrid, yGrid); // Get matching kernel size for current proximity and point sparseness. var kernel = m_Kernels[Mathf.Min(Mathf.RoundToInt(sparseness * proximity), c_MaxRadius - 1)]; float bufferValue = buffer.Read(channel, xGrid, yGrid); foreach (var pKernel in kernel) { int xDilate = xGrid + pKernel.x; int yDilate = yGrid + pKernel.y; // TryRead -> Dilation area might go beyond grid size. if (m_AuxGrid.TryRead(1, xDilate, yDilate, out float kernelValue)) { // Occlusion, 3D specific: // Write maximum kernel value if already set. m_AuxGrid.Write(1, xDilate, yDilate, Mathf.Max(pKernel.value, kernelValue)); // Write maximum buffer value if already set. m_AuxGrid.Write(2, xDilate, yDilate, Mathf.Max(m_AuxGrid.Read(2, xDilate, yDilate), bufferValue)); Expand(xDilate, yDilate); } } } // Expanded area. for (int x = m_xMin; x <= m_xMax; x++) { for (int y = m_yMin; y <= m_yMax; y++) { if (m_AuxGrid.Read(1, x, y) > c_Threshold) { // Copy back dilated buffer values. buffer.Write(channel, x, y, m_AuxGrid.Read(2, x, y)); // Store for later Write(buffer, channel, value) call. GridPositions.Add(new Vector2Int(x, y)); } } } } // else: keep original positions, they're dense enough. }
/// <summary> /// Enables drawer. /// </summary> /// <param name="channelData"><see cref="DebugChannelData"/> instance</param> /// <param name="buffer"><see cref="GridBuffer"/> instance</param> public void Enable(MonoBehaviour context, DebugChannelData channelData, GridBuffer buffer) { ChannelData = channelData; Buffer = buffer; GridSizeRatio = buffer.Height / (float)buffer.Width; Context = context; m_RepaintOnFirstUpdate = true; m_StopRepaintCoroutine ??= StopRepaintCoroutine(); IsEnabled = true; IsStandby = false; }
/// <summary> /// Creates a <see cref="GridSensor"/> instance. /// </summary> /// <param name="buffer">The <see cref="GridBuffer"/> instance to wrap</param> /// <param name="compressionType">The <see cref="SensorCompressionType"/> /// to apply to the generated image</param> /// <param name="observationType">The <see cref="ObservationType"/> /// (default or goal signal) of the sensor</param> /// <param name="name">Name of the sensor</param> public GridSensor( string name, GridBuffer buffer, SensorCompressionType compressionType, ObservationType observationType) { m_Name = name; buffer.GetShape().Validate(); m_GridBuffer = buffer; m_CompressionType = compressionType; HandleCompressionType(); m_ObservationSpec = ObservationSpec.Visual( m_GridBuffer.Height, m_GridBuffer.Width, m_GridBuffer.NumChannels, observationType); }
/// <summary> /// <see cref="PointModifier"/> factory. /// </summary> /// <param name="settings"><see cref="IEncodingSettings"/></param> /// <param name="buffer"><see cref="GridBuffer"/> used by the sensor</param> /// <returns>Dictionary of <see cref="PointModifiers"/>s by tag</returns> public static IDictionary <string, PointModifier> CreateModifiers( IEncodingSettings settings, GridBuffer buffer) { var modifiers = new Dictionary <string, PointModifier>(); foreach (string tag in settings.DetectableTags) { PointModifier modifier = settings.GetPointModifierType(tag) switch { PointModifierType.Dilation => new PointModDilation(), PointModifierType.Downsampling => new PointModDownsampling(), PointModifierType.OrthogonalFill => new PointModOrthogonalFill(), PointModifierType.DiagonalFill => new PointModDiagonalFill(), _ => new PointModNone(), }; modifier.Initialize(buffer); modifiers.Add(tag, modifier); } return(modifiers); }
/// <inheritdoc/> public override void Process(GridBuffer buffer, int channel) { int xCenter = (m_xMin + m_xMax) / 2; int yCenter = (m_yMin + m_yMax) / 2; float tmpValue = 0; for (int x = m_xMin; x <= xCenter; x++) { int xLeft = Mathf.Max(m_xMin, x - 1); for (int y = m_yMin; y <= yCenter; y++) { float bufferValue = buffer.Read(channel, x, y); if (bufferValue == 0) { int yTop = Mathf.Max(m_yMin, y - 1); bufferValue = Mathf.Max( buffer.Read(channel, xLeft, y), buffer.Read(channel, x, yTop), buffer.Read(channel, xLeft, yTop)); if (bufferValue > 0) { buffer.Write(channel, x, y, bufferValue); tmpValue = bufferValue; // Store for later Write(buffer, channel, value) call. GridPositions.Add(new Vector2Int(x, y)); } } else { // Occlusion, 3D specific: // Write maximum buffer value if already set. buffer.Write(channel, x, y, Mathf.Max(tmpValue, bufferValue)); } } } tmpValue = 0; for (int x = m_xMax; x > xCenter; x--) { int xRight = Mathf.Min(m_xMax, x + 1); for (int y = m_yMin; y <= yCenter; y++) { float bufferValue = buffer.Read(channel, x, y); if (bufferValue == 0) { int yTop = Mathf.Max(m_yMin, y - 1); bufferValue = Mathf.Max( buffer.Read(channel, xRight, y), buffer.Read(channel, x, yTop), buffer.Read(channel, xRight, yTop)); if (bufferValue > 0) { buffer.Write(channel, x, y, bufferValue); tmpValue = bufferValue; // Store for later Write(buffer, channel, value) call. GridPositions.Add(new Vector2Int(x, y)); } } else { // Occlusion, 3D specific: // Write maximum buffer value if already set. buffer.Write(channel, x, y, Mathf.Max(tmpValue, bufferValue)); } } } tmpValue = 0; for (int x = m_xMin; x <= xCenter; x++) { int xLeft = Mathf.Max(m_xMin, x - 1); for (int y = m_yMax; y > yCenter; y--) { float bufferValue = buffer.Read(channel, x, y); if (bufferValue == 0) { int yBottom = Mathf.Min(m_yMax, y + 1); bufferValue = Mathf.Max( buffer.Read(channel, xLeft, y), buffer.Read(channel, x, yBottom), buffer.Read(channel, xLeft, yBottom)); if (bufferValue > 0) { buffer.Write(channel, x, y, bufferValue); tmpValue = bufferValue; // Store for later Write(buffer, channel, value) call. GridPositions.Add(new Vector2Int(x, y)); } } else { // Occlusion, 3D specific: // Write maximum buffer value if already set. buffer.Write(channel, x, y, Mathf.Max(tmpValue, bufferValue)); } } } tmpValue = 0; for (int x = m_xMax; x > xCenter; x--) { int xRight = Mathf.Min(m_xMax, x + 1); for (int y = m_yMax; y > yCenter; y--) { float bufferValue = buffer.Read(channel, x, y); if (bufferValue == 0) { int yBottom = Mathf.Min(m_yMax, y + 1); bufferValue = Mathf.Max( buffer.Read(channel, xRight, y), buffer.Read(channel, x, yBottom), buffer.Read(channel, xRight, yBottom)); if (bufferValue > 0) { buffer.Write(channel, x, y, bufferValue); tmpValue = bufferValue; // Store for later Write(buffer, channel, value) call. GridPositions.Add(new Vector2Int(x, y)); } } else { // Occlusion, 3D specific: // Write maximum buffer value if already set. buffer.Write(channel, x, y, Mathf.Max(tmpValue, bufferValue)); } } } }
/// <summary> /// Initializes the <see cref="PointModifier"/>. /// </summary> /// <param name="buffer"><see cref="GridBuffer"/> used by the sensor</param> public virtual void Initialize(GridBuffer buffer) { // TODO Capacity? https://stackoverflow.com/a/23071206 GridPositions = new HashSet <Vector2Int>(); }
/// <summary> /// Processes a specific channel of the <see cref="GridBuffer"/>. /// </summary> /// <param name="buffer"><see cref="GridBuffer"/> used by the sensor</param> /// <param name="channel">Grid channel index</param> public virtual void Process(GridBuffer buffer, int channel) { }
/// <inheritdoc/> public override void Process(GridBuffer buffer, int channel) { // Side length of squares around points required for filling // Width x Height, IF positions were distributed evenly. float sparseness = Mathf.Sqrt(Width * Height / (float)GridPositions.Count); if (sparseness > 1) { // Make downsample area a bit larger than // sparseness value in order to prevent gaps. int size = Mathf.RoundToInt(sparseness) + 2; // TBD pad // Bottom/left offset. int xOffset = m_xMin - (size - Width % size) / 2; int yOffset = m_yMin - (size - Height % size) / 2; int nx = 0, ny = 0; foreach (Vector2Int point in GridPositions) { // Downsampling: grid pos -> sample pos. int xSample = (point.x - xOffset) / size; int ySample = (point.y - yOffset) / size; nx = Mathf.Max(nx, xSample); ny = Mathf.Max(ny, ySample); m_Samples[xSample, ySample] = Mathf.Max( m_Samples[xSample, ySample], buffer.Read(channel, point)); } // Replace grid points with squares (size x size). GridPositions.Clear(); for (int xSample = 0; xSample <= nx; xSample++) { for (int ySample = 0; ySample <= ny; ySample++) { float sampleValue = m_Samples[xSample, ySample]; if (sampleValue > 0) { // Upscaling: sample pos -> grid pos. int xGrid = xOffset + xSample * size; int yGrid = yOffset + ySample * size; for (int x = 0; x < size; x++) { for (int y = 0; y < size; y++) { Vector2Int gridPos = new Vector2Int(xGrid + x, yGrid + y); // TryRead -> Square might go beyond grid size. if (buffer.TryRead(channel, gridPos, out float bufferValue)) { // Occlusion, 3D specific: // Write maximum buffer value if already set. buffer.Write(channel, gridPos, Mathf.Max(bufferValue, sampleValue)); // Store for later Write(buffer, channel, value) call. GridPositions.Add(gridPos); } } } } } } } // else: keep original positions, they're dense enough. }