sensor_pipeline/node.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | #include "chrono_batch_render/sensor_pipeline/node.h" | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | |||
| 6 | #include "chrono_batch_render/graph/library.h" | ||
| 7 | #include "chrono_batch_render/util/error.h" | ||
| 8 | |||
| 9 | namespace chrono::batch_render::sensor_pipeline { | ||
| 10 | |||
| 11 | namespace { | ||
| 12 | |||
| 13 | 778 | void Validate(const NodeDef& def) { | |
| 14 | 778 | switch (def.kind) { | |
| 15 | 82 | case NodeKind::Source: | |
| 16 | 82 | if (!def.inputs.empty()) { | |
| 17 | ✗ | throw Error("Pipeline node {} is a source, so it takes no signal inputs", def.id); | |
| 18 | } | ||
| 19 | break; | ||
| 20 | 131 | case NodeKind::Stencil: | |
| 21 | 131 | if (def.inputs.empty() || def.radius > kMostStencilRadius) { | |
| 22 | ✗ | throw Error( | |
| 23 | "Pipeline node {} is a stencil, so it needs a signal input and a radius up to {}", | ||
| 24 | ✗ | def.id, kMostStencilRadius); | |
| 25 | } | ||
| 26 | break; | ||
| 27 | 35 | case NodeKind::Reduce: | |
| 28 | 35 | if (def.inputs.empty() || def.outputs.size() != 1 || | |
| 29 | 70 | def.outputs[0].type != SignalType::CaptureStat || def.layout != LayoutRule::Stat) { | |
| 30 | ✗ | throw Error("Pipeline node {} is a reduction, so it takes a signal and outputs one " | |
| 31 | "CaptureStat laid out per capture", | ||
| 32 | ✗ | def.id); | |
| 33 | } | ||
| 34 | break; | ||
| 35 | 46 | case NodeKind::Global: | |
| 36 | 46 | if (def.inputs.size() != 1 || def.outputs.size() != 1 || def.passes == 0 || | |
| 37 | 46 | def.inputs[0].type != def.outputs[0].type) { | |
| 38 | ✗ | throw Error("Pipeline node {} is global, so it takes one signal and outputs one of the " | |
| 39 | "same type over one or more passes", | ||
| 40 | ✗ | def.id); | |
| 41 | } | ||
| 42 | break; | ||
| 43 | case NodeKind::Point: | ||
| 44 | case NodeKind::Compact: | ||
| 45 | break; | ||
| 46 | } | ||
| 47 | 778 | if (!def.state.empty() && def.kind != NodeKind::Point) { | |
| 48 | ✗ | throw Error("Pipeline node {} keeps state, which only point nodes do for now", def.id); | |
| 49 | } | ||
| 50 | 1022 | for (const UniformDef& uniform : def.uniforms) { | |
| 51 | 244 | if (std::ranges::find(uniform.allowed, uniform.fallback) == uniform.allowed.end()) { | |
| 52 | ✗ | throw Error("Pipeline node {}'s uniform {} falls back to {}, which it doesn't allow", | |
| 53 | ✗ | def.id, uniform.name, uniform.fallback); | |
| 54 | } | ||
| 55 | } | ||
| 56 | 778 | } | |
| 57 | |||
| 58 | 9508 | graph::Published<NodeRegistry>& Registry() { | |
| 59 | 9514 | static graph::Published<NodeRegistry> registered(BuiltinNodes()); | |
| 60 | 9508 | return registered; | |
| 61 | } | ||
| 62 | |||
| 63 | } | ||
| 64 | |||
| 65 | 778 | uint32_t NodeRegistry::Add(NodeDef def) { | |
| 66 | 778 | Validate(def); | |
| 67 | 778 | const auto found = m_ids.find(def.id); | |
| 68 | 778 | if (found != m_ids.end()) { | |
| 69 | 3 | m_defs[found->second] = std::move(def); | |
| 70 | 3 | return found->second; | |
| 71 | } | ||
| 72 | 775 | const auto index = uint32_t(m_defs.size()); | |
| 73 | 775 | m_ids.emplace(def.id, index); | |
| 74 | 775 | m_defs.push_back(std::move(def)); | |
| 75 | 775 | return index; | |
| 76 | } | ||
| 77 | |||
| 78 | 631 | uint32_t NodeRegistry::Find(std::string_view id) const { | |
| 79 | 1262 | const auto found = m_ids.find(std::string(id)); | |
| 80 | 1252 | return found == m_ids.end() ? kNoDef : found->second; | |
| 81 | } | ||
| 82 | |||
| 83 | 9498 | std::shared_ptr<const NodeRegistry> SensorPipelineNodes() { | |
| 84 | 9498 | return Registry().Get(); | |
| 85 | } | ||
| 86 | |||
| 87 | 10 | void RegisterSensorPipelineNode(NodeDef def) { | |
| 88 | 20 | Registry().Update([&](NodeRegistry& nodes) { nodes.Add(std::move(def)); }); | |
| 89 | 10 | } | |
| 90 | |||
| 91 | 7 | const NodeRegistry& BuiltinNodes() { | |
| 92 | 6 | static const NodeRegistry registry = [] { | |
| 93 | 6 | NodeRegistry builtins; | |
| 94 | 6 | RegisterBuiltinNodes(builtins); | |
| 95 | 6 | return builtins; | |
| 96 | 7 | }(); | |
| 97 | 7 | return registry; | |
| 98 | } | ||
| 99 | |||
| 100 | 23 | void RegisterBuiltinNodes(NodeRegistry& registry) { | |
| 101 | 115 | registry.Add({.id = "CameraSignal", | |
| 102 | .kind = NodeKind::Source, | ||
| 103 | 23 | .outputs = {{"electronRate", SignalType::ElectronRate}}, | |
| 104 | 23 | .uniforms = {{"layout", {"full", "mosaic"}, "full"}, | |
| 105 | {"precision", {"float", "half"}, "float"}}, | ||
| 106 | .layout = LayoutRule::Detector}); | ||
| 107 | 184 | registry.Add({.id = "Integrate", | |
| 108 | 23 | .inputs = {{"electronRate", SignalType::ElectronRate}}, | |
| 109 | 23 | .outputs = {{"electrons", SignalType::Electrons}}, | |
| 110 | 23 | .parameters = {{"time", 1, {0}}}, | |
| 111 | 23 | .code = {.module = kCameraNodes, .type = "IntegrateNode"}}); | |
| 112 | 276 | registry.Add({.id = "Emva1288", | |
| 113 | 23 | .inputs = {{"electrons", SignalType::Electrons}}, | |
| 114 | 23 | .outputs = {{"electrons", SignalType::Electrons}}, | |
| 115 | 23 | .parameters = {{"time", 1, {0}}}, | |
| 116 | 23 | .uniforms = {{"shot", {"poisson", "gaussian", "off"}, "poisson"}, | |
| 117 | {"fixedPatterns", {"false", "true"}, "true"}, | ||
| 118 | {"clipping", {"clip", "none"}, "clip"}}, | ||
| 119 | 23 | .code = {.module = kCameraNodes, .type = "Emva1288Node"}}); | |
| 120 | 115 | registry.Add({.id = "Adc", | |
| 121 | 23 | .inputs = {{"electrons", SignalType::Electrons}}, | |
| 122 | 23 | .outputs = {{"digital", SignalType::Digital}}, | |
| 123 | 23 | .code = {.module = kCameraNodes, .type = "AdcNode"}}); | |
| 124 | 184 | registry.Add({.id = "Gain", | |
| 125 | 23 | .inputs = {{"digital", SignalType::Digital}}, | |
| 126 | 23 | .outputs = {{"digital", SignalType::Digital}}, | |
| 127 | 23 | .parameters = {{"gain", 1, {1}}}, | |
| 128 | 23 | .code = {.module = kCameraNodes, .type = "GainNode"}}); | |
| 129 | 115 | registry.Add({.id = "ToColor", | |
| 130 | 23 | .inputs = {{"digital", SignalType::Digital}}, | |
| 131 | 23 | .outputs = {{"color", SignalType::LinearColor}}, | |
| 132 | .layout = LayoutRule::Color, | ||
| 133 | 23 | .code = {.module = kCameraNodes, .type = "ToColorNode"}}); | |
| 134 | 161 | registry.Add({.id = "Encode", | |
| 135 | 23 | .inputs = {{"color", SignalType::LinearColor}}, | |
| 136 | 23 | .outputs = {{"display", SignalType::DisplayColor}}, | |
| 137 | 23 | .uniforms = {{"transfer", {"srgb", "rec709"}, "srgb"}}, | |
| 138 | 23 | .code = {.module = kCameraNodes, .type = "EncodeNode"}}); | |
| 139 | 161 | registry.Add({.id = "Demosaic", | |
| 140 | .kind = NodeKind::Stencil, | ||
| 141 | 23 | .inputs = {{"digital", SignalType::Digital}}, | |
| 142 | 23 | .outputs = {{"digital", SignalType::Digital}}, | |
| 143 | 23 | .uniforms = {{"method", {"bilinear", "malvar"}, "bilinear"}}, | |
| 144 | .radius = 2, | ||
| 145 | .layout = LayoutRule::FullChannels, | ||
| 146 | 23 | .code = {.module = kIspNodes, .type = "DemosaicNode"}}); | |
| 147 | 184 | registry.Add({.id = "GaussianPsf", | |
| 148 | .kind = NodeKind::Stencil, | ||
| 149 | 23 | .inputs = {{"electronRate", SignalType::ElectronRate}}, | |
| 150 | 23 | .outputs = {{"electronRate", SignalType::ElectronRate}}, | |
| 151 | 23 | .parameters = {{"sigma", 1, {0.7f}}}, | |
| 152 | .radius = 3, | ||
| 153 | 23 | .code = {.module = kIspNodes, .type = "GaussianPsfNode"}}); | |
| 154 | 276 | registry.Add( | |
| 155 | 23 | {.id = "DiffractionPsf", | |
| 156 | .kind = NodeKind::Stencil, | ||
| 157 | 23 | .inputs = {{"electronRate", SignalType::ElectronRate}}, | |
| 158 | 23 | .outputs = {{"electronRate", SignalType::ElectronRate}}, | |
| 159 | 23 | .parameters = {{"fNumber", 1, {2.8f}}, {"wavelength", 1, {550}}, {"pixelPitch", 1, {3}}}, | |
| 160 | .radius = kMostStencilRadius, | ||
| 161 | 23 | .code = {.module = kIspNodes, .type = "DiffractionPsfNode"}}); | |
| 162 | 230 | registry.Add({.id = "Glare", | |
| 163 | .kind = NodeKind::Global, | ||
| 164 | 23 | .inputs = {{"electronRate", SignalType::ElectronRate}}, | |
| 165 | 23 | .outputs = {{"electronRate", SignalType::ElectronRate}}, | |
| 166 | 23 | .parameters = {{"sigma", 1, {8}}, {"strength", 1, {0.04f}}}, | |
| 167 | .passes = 3, | ||
| 168 | 23 | .code = {.module = kIspNodes, .type = "GlareNode"}}); | |
| 169 | 184 | registry.Add({.id = "Blooming", | |
| 170 | .kind = NodeKind::Global, | ||
| 171 | 23 | .inputs = {{"electrons", SignalType::Electrons}}, | |
| 172 | 23 | .outputs = {{"electrons", SignalType::Electrons}}, | |
| 173 | 23 | .parameters = {{"reach", 1, {32}, ParameterType::Int}}, | |
| 174 | 23 | .code = {.module = kIspNodes, .type = "BloomingNode"}}); | |
| 175 | 230 | registry.Add({.id = "Denoise", | |
| 176 | .kind = NodeKind::Stencil, | ||
| 177 | 23 | .inputs = {{"color", SignalType::LinearColor}}, | |
| 178 | 23 | .outputs = {{"color", SignalType::LinearColor}}, | |
| 179 | 23 | .parameters = {{"sigmaSpace", 1, {1}}, {"sigmaRange", 1, {0.05f}}}, | |
| 180 | .radius = 2, | ||
| 181 | 23 | .code = {.module = kIspNodes, .type = "DenoiseNode"}}); | |
| 182 | 184 | registry.Add({.id = "Vignette", | |
| 183 | 23 | .inputs = {{"electronRate", SignalType::ElectronRate}}, | |
| 184 | 23 | .outputs = {{"electronRate", SignalType::ElectronRate}}, | |
| 185 | 23 | .parameters = {{"strength", 1, {0.3f}}}, | |
| 186 | 23 | .code = {.module = kIspNodes, .type = "VignetteNode"}}); | |
| 187 | 115 | registry.Add({.id = "BlackLevel", | |
| 188 | 23 | .inputs = {{"digital", SignalType::Digital}}, | |
| 189 | 23 | .outputs = {{"digital", SignalType::Digital}}, | |
| 190 | 23 | .code = {.module = kIspNodes, .type = "BlackLevelNode"}}); | |
| 191 | 207 | registry.Add( | |
| 192 | 23 | {.id = "WhiteBalance", | |
| 193 | 23 | .inputs = {{"color", SignalType::LinearColor}, {"stat", SignalType::CaptureStat, true}}, | |
| 194 | 23 | .outputs = {{"color", SignalType::LinearColor}}, | |
| 195 | 23 | .parameters = {{"gains", 3, {1, 1, 1}}}, | |
| 196 | 23 | .code = {.module = kIspNodes, .type = "WhiteBalanceNode"}}); | |
| 197 | 230 | registry.Add({.id = "Meter", | |
| 198 | .kind = NodeKind::Reduce, | ||
| 199 | 23 | .inputs = {{"color", SignalType::LinearColor}}, | |
| 200 | 23 | .outputs = {{"stat", SignalType::CaptureStat}}, | |
| 201 | 23 | .parameters = {{"radius", 1, {0.3f}}}, | |
| 202 | 23 | .uniforms = {{"mode", {"average", "centerWeighted", "spot"}, "average"}}, | |
| 203 | .layout = LayoutRule::Stat, | ||
| 204 | 23 | .code = {.module = kIspNodes, .type = "MeterNode"}}); | |
| 205 | 345 | registry.Add({.id = "AutoExposure", | |
| 206 | 23 | .inputs = {{"color", SignalType::LinearColor}, {"stat", SignalType::CaptureStat}}, | |
| 207 | 23 | .outputs = {{"color", SignalType::LinearColor}}, | |
| 208 | 23 | .parameters = {{"target", 1, {0.18f}}, | |
| 209 | {"speed", 1, {0.5f}}, | ||
| 210 | {"minGain", 1, {1.0f / 64}}, | ||
| 211 | {"maxGain", 1, {64}}}, | ||
| 212 | .state = {1}, | ||
| 213 | 23 | .code = {.module = kIspNodes, .type = "AutoExposureNode"}}); | |
| 214 | 276 | registry.Add( | |
| 215 | 23 | {.id = "Ccm", | |
| 216 | 23 | .inputs = {{"color", SignalType::LinearColor}}, | |
| 217 | 23 | .outputs = {{"color", SignalType::LinearColor}}, | |
| 218 | 23 | .parameters = {{"red", 3, {1, 0, 0}}, {"green", 3, {0, 1, 0}}, {"blue", 3, {0, 0, 1}}}, | |
| 219 | 23 | .code = {.module = kIspNodes, .type = "CcmNode"}}); | |
| 220 | 46 | registry.Add({.id = "LidarReturns", | |
| 221 | .kind = NodeKind::Source, | ||
| 222 | 23 | .outputs = {{"returns", SignalType::Returns}}, | |
| 223 | .layout = LayoutRule::Returns}); | ||
| 224 | 230 | registry.Add({.id = "RangeNoise", | |
| 225 | 23 | .inputs = {{"returns", SignalType::Returns}}, | |
| 226 | 23 | .outputs = {{"returns", SignalType::Returns}}, | |
| 227 | 23 | .parameters = {{"sigma", 1, {0.02f}}, {"sigmaPerMeter", 1, {0}}}, | |
| 228 | 23 | .code = {.module = kLidarNodes, .type = "RangeNoiseNode"}}); | |
| 229 | 230 | registry.Add({.id = "IntensityNoise", | |
| 230 | 23 | .inputs = {{"returns", SignalType::Returns}}, | |
| 231 | 23 | .outputs = {{"returns", SignalType::Returns}}, | |
| 232 | 23 | .parameters = {{"sigma", 1, {0}}, {"relative", 1, {0.05f}}}, | |
| 233 | 23 | .code = {.module = kLidarNodes, .type = "IntensityNoiseNode"}}); | |
| 234 | 230 | registry.Add({.id = "Dropout", | |
| 235 | 23 | .inputs = {{"returns", SignalType::Returns}}, | |
| 236 | 23 | .outputs = {{"returns", SignalType::Returns}}, | |
| 237 | 23 | .parameters = {{"threshold", 1, {0}}, {"rate", 1, {0}}}, | |
| 238 | 23 | .code = {.module = kLidarNodes, .type = "DropoutNode"}}); | |
| 239 | 184 | registry.Add({.id = "RangeQuantize", | |
| 240 | 23 | .inputs = {{"returns", SignalType::Returns}}, | |
| 241 | 23 | .outputs = {{"returns", SignalType::Returns}}, | |
| 242 | 23 | .parameters = {{"step", 1, {0.01f}}}, | |
| 243 | 23 | .code = {.module = kLidarNodes, .type = "RangeQuantizeNode"}}); | |
| 244 | 115 | registry.Add({.id = "ToPointCloud", | |
| 245 | 23 | .inputs = {{"returns", SignalType::Returns}}, | |
| 246 | 23 | .outputs = {{"cloud", SignalType::PointCloud}}, | |
| 247 | 23 | .code = {.module = kLidarNodes, .type = "ToPointCloudNode"}}); | |
| 248 | 46 | registry.Add({.id = "RadarReturns", | |
| 249 | .kind = NodeKind::Source, | ||
| 250 | 23 | .outputs = {{"returns", SignalType::Returns}}, | |
| 251 | .layout = LayoutRule::Returns}); | ||
| 252 | 276 | registry.Add({.id = "RadarNoise", | |
| 253 | 23 | .inputs = {{"returns", SignalType::Returns}}, | |
| 254 | 23 | .outputs = {{"returns", SignalType::Returns}}, | |
| 255 | 23 | .parameters = {{"rangeSigma", 1, {0.05f}}, | |
| 256 | {"velocitySigma", 1, {0.1f}}, | ||
| 257 | {"floor", 1, {0}}}, | ||
| 258 | 23 | .code = {.module = kRadarNodes, .type = "RadarNoiseNode"}}); | |
| 259 | 276 | registry.Add({.id = "Cfar", | |
| 260 | .kind = NodeKind::Stencil, | ||
| 261 | 23 | .inputs = {{"returns", SignalType::Returns}}, | |
| 262 | 23 | .outputs = {{"returns", SignalType::Returns}}, | |
| 263 | 23 | .parameters = {{"guard", 1, {1}, ParameterType::Int}, | |
| 264 | {"training", 1, {2}, ParameterType::Int}, | ||
| 265 | {"falseAlarmRate", 1, {1e-3f}}}, | ||
| 266 | .radius = kCfarRadius, | ||
| 267 | 23 | .code = {.module = kRadarNodes, .type = "CfarNode"}}); | |
| 268 | 161 | registry.Add({.id = "Tone", | |
| 269 | 23 | .inputs = {{"color", SignalType::LinearColor}}, | |
| 270 | 23 | .outputs = {{"color", SignalType::LinearColor}}, | |
| 271 | 23 | .uniforms = {{"curve", {"none", "reinhard", "aces", "agx"}, "none"}}, | |
| 272 | 23 | .code = {.module = kIspNodes, .type = "ToneNode"}}); | |
| 273 | 3013 | } | |
| 274 | |||
| 275 | } | ||
| 276 |