PythonAPI fixes, rework PixelReader.

This commit is contained in:
Marcel Pi 2024-02-28 15:56:26 +01:00
parent 74d19eece2
commit 3856c97f15
12 changed files with 443 additions and 94 deletions

View File

@ -69,7 +69,7 @@ set (PNG_TOOLS OFF)
set (PNG_BUILD_ZLIB ON)
set (ZLIB_INCLUDE_DIR ${zlib_SOURCE_DIR})
if (WIN32)
set (ZLIB_LIBRARY ${zlib_BINARY_DIR}/zlibstatic.lib)
set (ZLIB_LIBRARY ${zlib_BINARY_DIR}/zlibstatic${CARLA_DEBUG_AFFIX}.lib)
else ()
set (ZLIB_LIBRARY ${zlib_BINARY_DIR}/libz.a)
endif ()

View File

@ -1,6 +1,7 @@
cmake_minimum_required (VERSION 3.20.0)
cmake_policy (SET CMP0097 NEW)
cmake_policy (SET CMP0091 NEW)
cmake_policy (SET CMP0077 OLD)
set (CARLA_VERSION_MAJOR 0)
@ -62,34 +63,14 @@ else ()
set (LIB_EXT .a)
endif ()
set (CARLA_DEFAULT_DEPENDENCY_ARGS)
if (CMAKE_C_COMPILER)
list (
APPEND CARLA_DEFAULT_DEPENDENCY_ARGS
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
)
endif ()
if (CMAKE_INSTALL_PREFIX)
list (
APPEND CARLA_DEFAULT_DEPENDENCY_ARGS
-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
)
endif ()
if (CMAKE_CXX_COMPILER)
list (
APPEND CARLA_DEFAULT_DEPENDENCY_ARGS
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
)
endif ()
if (CMAKE_BUILD_TYPE)
list (
APPEND CARLA_DEFAULT_DEPENDENCY_ARGS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
)
if (WIN32)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set (CARLA_DEBUG_AFFIX d)
set (CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDebugDLL")
else ()
set (CARLA_DEBUG_AFFIX )
set (CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
endif ()
endif ()
set (
@ -102,11 +83,12 @@ if (WIN32)
list (APPEND CARLA_COMMON_DEFINITIONS _WIN32_WINNT=0x0601) # --- Windows 10 ---
list (APPEND CARLA_COMMON_DEFINITIONS _CRT_SECURE_NO_WARNINGS)
list (APPEND CARLA_COMMON_DEFINITIONS HAVE_SNPRINTF)
list (APPEND CARLA_COMMON_DEFINITIONS _USE_MATH_DEFINES)
endif ()
set (CARLA_EXCEPTION_DEFINITIONS)
if (ENABLE_EXCEPTIONS)
# Nothing
else ()
list (APPEND CARLA_EXCEPTION_DEFINITIONS ASIO_NO_EXCEPTIONS)
list (APPEND CARLA_EXCEPTION_DEFINITIONS BOOST_NO_EXCEPTIONS)
@ -114,8 +96,6 @@ else ()
list (APPEND CARLA_EXCEPTION_DEFINITIONS PUGIXML_NO_EXCEPTIONS)
endif ()
add_compile_definitions (_USE_MATH_DEFINES)
include (${CARLA_WORKSPACE_PATH}/CMake/CarlaDependencies.cmake)
if (BUILD_CARLA_CLIENT OR BUILD_CARLA_SERVER)

View File

@ -33,5 +33,12 @@ ADepthCamera::ADepthCamera(const FObjectInitializer &ObjectInitializer)
void ADepthCamera::PostPhysTick(UWorld *World, ELevelTick TickType, float DeltaSeconds)
{
TRACE_CPUPROFILER_EVENT_SCOPE(ADepthCamera::PostPhysTick);
FPixelReader::SendPixelsInRenderThread<ADepthCamera, FColor>(*this);
ImageUtil::ReadSensorImageDataAsyncFColor(*this, [this](
TArrayView<const FColor> Pixels,
FIntPoint Size) -> bool
{
SendImageDataToClient(*this, Pixels);
return true;
});
}

View File

@ -4,22 +4,26 @@
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include "Carla/Sensor/ImageUtil.h"
#include "Runtime/RHI/Public/RHISurfaceDataConversion.h"
#include "Carla/Carla.h"
#include <Carla/Sensor/ImageUtil.h>
#include <Carla/Sensor/ShaderBasedSensor.h>
#include <Carla/Carla.h>
#include <Runtime/RHI/Public/RHISurfaceDataConversion.h>
#include <Runtime/ImageWriteQueue/Public/ImageWriteQueue.h>
#include <HighResScreenshot.h>
#include <RHIGPUReadback.h>
namespace ImageUtil
{
void DecodePixelsByFormat(
void* PixelData,
int32 SourcePitch,
FIntPoint SourceExtent,
FIntPoint DestinationExtent,
EPixelFormat Format,
FReadSurfaceDataFlags Flags,
TArrayView<FLinearColor> Out)
bool DecodePixelsByFormat(
const void* PixelData,
int32 SourcePitch,
FIntPoint SourceExtent,
FIntPoint DestinationExtent,
EPixelFormat Format,
FReadSurfaceDataFlags Flags,
TArrayView<FLinearColor> Out)
{
SourcePitch *= GPixelFormats[Format].BlockBytes;
auto OutPixelCount = DestinationExtent.X * DestinationExtent.Y;
@ -69,19 +73,19 @@ namespace ImageUtil
break;
default:
UE_LOG(LogCarla, Warning, TEXT("Unsupported format %llu"), (unsigned long long)Format);
check(false);
break;
return false;
}
return true;
}
void DecodePixelsByFormat(
void* PixelData,
int32 SourcePitch,
FIntPoint SourceExtent,
FIntPoint DestinationExtent,
EPixelFormat Format,
FReadSurfaceDataFlags Flags,
TArrayView<FColor> Out)
bool DecodePixelsByFormat(
const void* PixelData,
int32 SourcePitch,
FIntPoint SourceExtent,
FIntPoint DestinationExtent,
EPixelFormat Format,
FReadSurfaceDataFlags Flags,
TArrayView<FColor> Out)
{
SourcePitch *= GPixelFormats[Format].BlockBytes;
auto OutPixelCount = DestinationExtent.X * DestinationExtent.Y;
@ -137,8 +141,202 @@ namespace ImageUtil
break;
default:
UE_LOG(LogCarla, Warning, TEXT("Unsupported format %llu"), (unsigned long long)Format);
check(false);
break;
return false;
}
return true;
}
bool ReadImageData(
UTextureRenderTarget2D& RenderTarget,
TArray<FColor>& Out)
{
check(IsInGameThread());
auto Resource = RenderTarget.GameThread_GetRenderTargetResource();
FReadSurfaceDataFlags ReadFlags(RCM_UNorm);
ReadFlags.SetLinearToGamma(true);
return Resource->ReadPixels(Out, ReadFlags);
}
bool ReadImageData(
UTextureRenderTarget2D& RenderTarget,
TArray64<FColor>& Out)
{
auto Resource = RenderTarget.GameThread_GetRenderTargetResource();
FReadSurfaceDataFlags ReadFlags(RCM_UNorm);
ReadFlags.SetLinearToGamma(true);
Out.SetNum(RenderTarget.GetSurfaceWidth() * RenderTarget.GetSurfaceHeight());
return Resource->ReadPixelsPtr(Out.GetData(), ReadFlags);
}
TUniquePtr<TImagePixelData<FColor>> ReadImageData(
UTextureRenderTarget2D& RenderTarget)
{
const auto Size = FIntPoint(
RenderTarget.GetSurfaceWidth(),
RenderTarget.GetSurfaceHeight());
auto PixelData = MakeUnique<TImagePixelData<FColor>>(Size);
ReadImageData(RenderTarget, PixelData->Pixels);
return TUniquePtr<TImagePixelData<FColor>>();
}
TFuture<bool> SaveImageData(
UTextureRenderTarget2D& RenderTarget,
const FStringView& Path)
{
return SaveImageData(ReadImageData(RenderTarget), Path);
}
TFuture<bool> SaveImageData(
TUniquePtr<TImagePixelData<FColor>> Data,
const FStringView& Path)
{
auto& HighResScreenshotConfig = GetHighResScreenshotConfig();
auto ImageTask = MakeUnique<FImageWriteTask>();
ImageTask->PixelData = MoveTemp(Data);
ImageTask->Filename = Path;
ImageTask->Format = EImageFormat::PNG;
ImageTask->CompressionQuality = (int32)EImageCompressionQuality::Default;
ImageTask->bOverwriteFile = true;
ImageTask->PixelPreProcessors.Add(TAsyncAlphaWrite<FColor>(255));
return HighResScreenshotConfig.ImageWriteQueue->Enqueue(MoveTemp(ImageTask));
}
static void ReadImageDataAsyncCommand(
UTextureRenderTarget2D& RenderTarget,
ReadImageDataAsyncCallback&& Callback)
{
static thread_local auto RenderQueryPool = RHICreateRenderQueryPool(RQT_AbsoluteTime);
auto& CmdList = FRHICommandListImmediate::Get();
auto Resource = static_cast<FTextureRenderTarget2DResource*>(
RenderTarget.Resource);
auto Texture = Resource->GetRenderTargetTexture();
if (Texture == nullptr)
return;
auto Readback = MakeUnique<FRHIGPUTextureReadback>(TEXT("ReadImageData-Readback"));
auto Size = Texture->GetSizeXY();
auto Format = Texture->GetFormat();
auto ResolveRect = FResolveRect();
Readback->EnqueueCopy(CmdList, Texture, ResolveRect);
auto Query = RenderQueryPool->AllocateQuery();
CmdList.EndRenderQuery(Query.GetQuery());
CmdList.ImmediateFlush(EImmediateFlushType::FlushRHIThread);
uint64 DeltaTime;
RHIGetRenderQueryResult(Query.GetQuery(), DeltaTime, true);
Query.ReleaseQuery();
AsyncTask(ENamedThreads::HighTaskPriority, [
Readback = MoveTemp(Readback),
Callback = std::move(Callback),
Size, Format]
{
while (!Readback->IsReady())
std::this_thread::yield();
int32 RowPitch, BufferHeight;
auto Mapping = Readback->Lock(RowPitch, &BufferHeight);
if (Mapping != nullptr)
Callback(Mapping, RowPitch, BufferHeight, Format, Size);
Readback->Unlock();
});
}
bool ReadImageDataAsync(
UTextureRenderTarget2D& RenderTarget,
ReadImageDataAsyncCallback&& Callback)
{
if (IsInRenderingThread())
{
ReadImageDataAsyncCommand(
RenderTarget,
std::move(Callback));
}
else
{
ENQUEUE_RENDER_COMMAND(ReadImageDataAsyncCmd)([
&RenderTarget,
Callback = std::move(Callback)
](auto& CmdList) mutable
{
ReadImageDataAsyncCommand(
RenderTarget,
std::move(Callback));
});
}
return true;
}
bool ReadSensorImageDataAsync(
AShaderBasedSensor& Sensor,
ReadImageDataAsyncCallback&& Callback)
{
TArray<FColor> Pixels;
auto RenderTarget = Sensor.GetCaptureRenderTarget();
if (RenderTarget == nullptr)
return false;
return ReadImageDataAsync(
*RenderTarget,
std::move(Callback));
}
bool ReadImageDataAsyncFColor(
UTextureRenderTarget2D& RenderTarget,
ReadImageDataAsyncCallbackFColor&& Callback)
{
return ReadImageDataAsync(RenderTarget, [Callback = std::move(Callback)](
const void* Mapping,
size_t RowPitch,
size_t BufferHeight,
EPixelFormat Format,
FIntPoint Size) -> bool
{
FReadSurfaceDataFlags Flags;
TArray<FColor> Pixels;
Pixels.SetNum(Size.X * Size.Y);
if (!DecodePixelsByFormat(Mapping, RowPitch, Size, Size, Format, Flags, Pixels))
return false;
return Callback(Pixels, Size);
});
}
bool ReadSensorImageDataAsyncFColor(
AShaderBasedSensor& Sensor,
ReadImageDataAsyncCallbackFColor&& Callback)
{
auto RenderTarget = Sensor.GetCaptureRenderTarget();
if (RenderTarget == nullptr)
return false;
return ReadImageDataAsyncFColor(*RenderTarget, std::move(Callback));
}
bool ReadImageDataAsyncFLinearColor(
UTextureRenderTarget2D& RenderTarget,
ReadImageDataAsyncCallbackFLinearColor&& Callback)
{
return ReadImageDataAsync(RenderTarget, [Callback = std::move(Callback)](
const void* Mapping,
size_t RowPitch,
size_t BufferHeight,
EPixelFormat Format,
FIntPoint Size) -> bool
{
FReadSurfaceDataFlags Flags;
TArray<FLinearColor> Pixels;
Pixels.SetNum(Size.X * Size.Y);
if (!DecodePixelsByFormat(Mapping, RowPitch, Size, Size, Format, Flags, Pixels))
return false;
return Callback(Pixels, Size);
});
}
bool ReadSensorImageDataAsyncFLinearColor(
AShaderBasedSensor& Sensor,
ReadImageDataAsyncCallbackFLinearColor&& Callback)
{
auto RenderTarget = Sensor.GetCaptureRenderTarget();
if (RenderTarget == nullptr)
return false;
return ReadImageDataAsyncFLinearColor(*RenderTarget, std::move(Callback));
}
}

View File

@ -5,29 +5,101 @@
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "CoreMinimal.h"
#include <CoreMinimal.h>
#include <CoreGlobals.h>
#include <Engine/TextureRenderTarget2D.h>
#include <Runtime/ImageWriteQueue/Public/ImagePixelData.h>
#include <functional>
class FRHIGPUTextureReadback;
class AShaderBasedSensor;
class UTextureRenderTarget2D;
namespace ImageUtil
{
void DecodePixelsByFormat(
void* PixelData,
int32 SourcePitch,
FIntPoint SourceExtent,
FIntPoint DestinationExtent,
EPixelFormat Format,
FReadSurfaceDataFlags Flags,
TArrayView<FColor> Out);
bool DecodePixelsByFormat(
const void* PixelData,
int32 SourcePitch,
FIntPoint SourceExtent,
FIntPoint DestinationExtent,
EPixelFormat Format,
FReadSurfaceDataFlags Flags,
TArrayView<FColor> Out);
void DecodePixelsByFormat(
void* PixelData,
int32 SourcePitch,
FIntPoint SourceExtent,
FIntPoint DestinationExtent,
EPixelFormat Format,
FReadSurfaceDataFlags Flags,
TArrayView<FLinearColor> Out);
bool DecodePixelsByFormat(
const void* PixelData,
int32 SourcePitch,
FIntPoint SourceExtent,
FIntPoint DestinationExtent,
EPixelFormat Format,
FReadSurfaceDataFlags Flags,
TArrayView<FLinearColor> Out);
bool ReadImageData(
UTextureRenderTarget2D& RenderTarget,
TArray<FColor>& Out);
bool ReadImageData(
UTextureRenderTarget2D& RenderTarget,
TArray64<FColor>& Out);
TUniquePtr<TImagePixelData<FColor>> ReadImageData(
UTextureRenderTarget2D& RenderTarget);
TFuture<bool> SaveImageData(
UTextureRenderTarget2D& RenderTarget,
const FStringView& Path);
TFuture<bool> SaveImageData(
TUniquePtr<TImagePixelData<FColor>> Data,
const FStringView& Path);
using ReadImageDataAsyncCallback = std::function<
bool(
const void*, // MappedData
size_t, // RowPitch
size_t, // BufferHeight
EPixelFormat, // Format
FIntPoint // Extent
)
>;
using ReadImageDataAsyncCallbackFColor = std::function<
bool(
TArrayView<const FColor>, // Data
FIntPoint // Extent
)
>;
using ReadImageDataAsyncCallbackFLinearColor = std::function<
bool(
TArrayView<const FLinearColor>, // Data
FIntPoint // Extent
)
>;
bool ReadImageDataAsync(
UTextureRenderTarget2D& RenderTarget,
ReadImageDataAsyncCallback&& Callback);
bool ReadSensorImageDataAsync(
AShaderBasedSensor& Sensor,
ReadImageDataAsyncCallback&& Callback);
bool ReadImageDataAsyncFColor(
UTextureRenderTarget2D& RenderTarget,
ReadImageDataAsyncCallbackFColor&& Callback);
bool ReadSensorImageDataAsyncFColor(
AShaderBasedSensor& Sensor,
ReadImageDataAsyncCallbackFColor&& Callback);
bool ReadImageDataAsyncFLinearColor(
UTextureRenderTarget2D& RenderTarget,
ReadImageDataAsyncCallbackFLinearColor&& Callback);
bool ReadSensorImageDataAsyncFLinearColor(
AShaderBasedSensor& Sensor,
ReadImageDataAsyncCallbackFLinearColor&& Callback);
}

View File

@ -62,7 +62,13 @@ void AInstanceSegmentationCamera::PostPhysTick(UWorld *World, ELevelTick TickTyp
UPrimitiveComponent *Component = Cast<UPrimitiveComponent>(Object);
SceneCapture->ShowOnlyComponents.Emplace(Component);
}
FPixelReader::SendPixelsInRenderThread<AInstanceSegmentationCamera, FColor>(*this);
ImageUtil::ReadSensorImageDataAsyncFColor(*this, [this](
TArrayView<const FColor> Pixels,
FIntPoint Size) -> bool
{
SendImageDataToClient(*this, Pixels);
return true;
});
}

View File

@ -27,5 +27,13 @@ ANormalsCamera::ANormalsCamera(const FObjectInitializer &ObjectInitializer)
void ANormalsCamera::PostPhysTick(UWorld *World, ELevelTick TickType, float DeltaSeconds)
{
TRACE_CPUPROFILER_EVENT_SCOPE(ANormalsCamera::PostPhysTick);
FPixelReader::SendPixelsInRenderThread<ANormalsCamera, FColor>(*this);
ImageUtil::ReadSensorImageDataAsyncFColor(*this, [this](
TArrayView<const FColor> Pixels,
FIntPoint Size) -> bool
{
SendImageDataToClient(*this, Pixels);
return true;
});
}

View File

@ -108,9 +108,6 @@ void FPixelReader::SendPixelsInRenderThread(TSensor& Sensor, bool use16BitFormat
/// Blocks until the render thread has finished all it's tasks.
Sensor.EnqueueRenderSceneImmediate();
FlushRenderingCommands();
SavePixelsToDisk(
*Sensor.CaptureRenderTarget,
TEXT("F:\\Carla\\PythonAPI\\examples\\_out\\image.png"));
// Enqueue a command in the render-thread that will write the image buffer to
// the data stream. The stream is created in the capture thus executed in the

View File

@ -62,7 +62,14 @@ void ASceneCaptureCamera::PostPhysTick(UWorld *World, ELevelTick TickType, float
TRACE_CPUPROFILER_EVENT_SCOPE_TEXT(*ProfilerText);
}
);
FPixelReader::SendPixelsInRenderThread<ASceneCaptureCamera, FColor>(*this);
ImageUtil::ReadSensorImageDataAsyncFColor(*this, [this](
TArrayView<const FColor> Pixels,
FIntPoint Size) -> bool
{
SendImageDataToClient(*this, Pixels);
return true;
});
}
#ifdef CARLA_HAS_GBUFFER_API

View File

@ -27,5 +27,12 @@ ASemanticSegmentationCamera::ASemanticSegmentationCamera(
void ASemanticSegmentationCamera::PostPhysTick(UWorld *World, ELevelTick TickType, float DeltaSeconds)
{
TRACE_CPUPROFILER_EVENT_SCOPE(ASemanticSegmentationCamera::PostPhysTick);
FPixelReader::SendPixelsInRenderThread<ASemanticSegmentationCamera, FColor>(*this);
ImageUtil::ReadSensorImageDataAsyncFColor(*this, [this](
TArrayView<const FColor> Pixels,
FIntPoint Size) -> bool
{
SendImageDataToClient(*this, Pixels);
return true;
});
}

View File

@ -4,15 +4,15 @@
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#include "Carla/Sensor/Sensor.h"
#include "Carla.h"
#include "Carla/Sensor/SensorManager.h"
#include "Carla/Actor/ActorDescription.h"
#include "Carla/Actor/ActorBlueprintFunctionLibrary.h"
#include "Carla/Game/CarlaStatics.h"
#include "Engine/CollisionProfile.h"
#include <Carla/Sensor/Sensor.h>
#include <Carla.h>
#include <Carla/Sensor/SensorManager.h>
#include <Carla/Actor/ActorDescription.h>
#include <Carla/Actor/ActorBlueprintFunctionLibrary.h>
#include <Carla/Game/CarlaStatics.h>
#include <Engine/CollisionProfile.h>
#include <Carla/Sensor/ImageUtil.h>
#include <Carla/Sensor/ShaderBasedSensor.h>
ASensor::ASensor(const FObjectInitializer &ObjectInitializer)
: Super(ObjectInitializer)

View File

@ -14,6 +14,13 @@
#include "GameFramework/Actor.h"
#include <compiler/disable-ue4-macros.h>
#include <carla/Logging.h>
#include <carla/Buffer.h>
#include <carla/BufferView.h>
#include <carla/sensor/SensorRegistry.h>
#include <compiler/enable-ue4-macros.h>
#include "Sensor.generated.h"
struct FActorDescription;
@ -106,10 +113,70 @@ protected:
///
/// You need to provide a reference to self, this is necessary for template
/// deduction.
template <typename SensorT>
FAsyncDataStream GetDataStream(const SensorT &Self)
template <typename SensorType>
FAsyncDataStream GetDataStream(SensorType&& Self)
{
return Stream.MakeAsyncDataStream(Self, GetEpisode().GetElapsedGameTime());
return Stream.MakeAsyncDataStream<std::remove_cvref_t<SensorType>>(
std::forward<SensorType>(Self),
GetEpisode().GetElapsedGameTime());
}
template <
typename SensorType,
typename PixelType>
static void SendImageDataToClient(
SensorType&& Sensor,
TArrayView<PixelType> Pixels)
{
auto Stream = Sensor.GetDataStream(Sensor);
Stream.SetFrameNumber(FCarlaEngine::GetFrameCounter());
auto Buffer = Stream.PopBufferFromPool();
Buffer.copy_from(0, boost::asio::buffer(Pixels.GetData(), Pixels.Num() * sizeof(FColor)));
if (!Buffer.data())
return;
carla::Buffer BufferReady(
std::move(
carla::sensor::SensorRegistry::Serialize(
Sensor,
std::move(Buffer))));
auto BufferView =
carla::BufferView::CreateFrom(
std::move(BufferReady));
#if defined(WITH_ROS2)
auto ROS2 = carla::ros2::ROS2::GetInstance();
if (ROS2->IsEnabled())
{
TRACE_CPUPROFILER_EVENT_SCOPE_STR("ROS2 Send PixelReader");
auto StreamId = carla::streaming::detail::token_type(Sensor.GetToken()).get_stream_id();
auto Res = std::async(std::launch::async, [&Sensor, ROS2, &Stream, StreamId, BufView]()
{
// get resolution of camera
int W = -1, H = -1;
float Fov = -1.0f;
auto WidthOpt = Sensor.GetAttribute("image_size_x");
if (WidthOpt.has_value())
W = FCString::Atoi(*WidthOpt->Value);
auto HeightOpt = Sensor.GetAttribute("image_size_y");
if (HeightOpt.has_value())
H = FCString::Atoi(*HeightOpt->Value);
auto FovOpt = Sensor.GetAttribute("fov");
if (FovOpt.has_value())
Fov = FCString::Atof(*FovOpt->Value);
// send data to ROS2
AActor* ParentActor = Sensor.GetAttachParentActor();
if (ParentActor)
{
FTransform LocalTransformRelativeToParent = Sensor.GetActorTransform().GetRelativeTransform(ParentActor->GetActorTransform());
ROS2->ProcessDataFromCamera(Stream.GetSensorType(), StreamId, LocalTransformRelativeToParent, W, H, Fov, BufView, &Sensor);
}
else
{
ROS2->ProcessDataFromCamera(Stream.GetSensorType(), StreamId, Stream.GetSensorTransform(), W, H, Fov, BufView, &Sensor);
}
});
}
#endif
Stream.Send(Sensor, BufferView);
}
/// Seed of the pseudo-random engine.