Agrarsense
Camera.cpp
Go to the documentation of this file.
1// Copyright (c) 2023 FrostBit Software Lab at the Lapland University of Applied Sciences
2//
3// This work is licensed under the terms of the MIT license.
4// For a copy, see <https://opensource.org/licenses/MIT>.
5
6#include "Camera.h"
7
14
15#include "NiagaraComponent.h"
16#include "Components/PrimitiveComponent.h"
17#include "Materials/Material.h"
18#include "ImageUtils.h"
19#include "Async/Async.h"
20#include "HighResScreenshot.h"
21#include "ImageWriteTask.h"
22#include "ImageWriteQueue.h"
23#include "Misc/DateTime.h"
24#include "Math/Color.h"
25#include "Blueprint/UserWidget.h"
26#include "GameFramework/PlayerController.h"
27#include "GeoReferencingSystem.h"
28
29ACamera::ACamera(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
30{
31 PrimaryActorTick.bCanEverTick = true;
32 PrimaryActorTick.TickGroup = TG_PostUpdateWork;
33
34 CaptureRenderTarget = CreateDefaultSubobject<UTextureRenderTarget2D>(FName(*FString::Printf(TEXT("CaptureRenderTarget"))));
35 CaptureRenderTarget->CompressionSettings = TextureCompressionSettings::TC_Default;
36 CaptureRenderTarget->SRGB = false;
37 CaptureRenderTarget->bAutoGenerateMips = false;
38 CaptureRenderTarget->bGPUSharedFlag = true;
39 CaptureRenderTarget->AddressX = TextureAddress::TA_Clamp;
40 CaptureRenderTarget->AddressY = TextureAddress::TA_Clamp;
41
42 CaptureComponent2D = CreateDefaultSubobject<USceneCaptureComponent2D>(FName(*FString::Printf(TEXT("SceneCaptureComponent2D"))));
43 CaptureComponent2D->SetupAttachment(RootComponent);
44 CaptureComponent2D->PrimitiveRenderMode = ESceneCapturePrimitiveRenderMode::PRM_RenderScenePrimitives;
45 CaptureComponent2D->bCaptureOnMovement = false;
46 CaptureComponent2D->bCaptureEveryFrame = false;
47 CaptureComponent2D->bAlwaysPersistRenderingState = true;
48}
49
51{
52 SetupCamera(newParameters);
53}
54
55void ACamera::AddPostProcessingMaterial(const FString& Path, float Weight)
56{
57 UMaterial* PostProcessingMat = Cast<UMaterial>(StaticLoadObject(UMaterial::StaticClass(), nullptr, *Path));
58 if (PostProcessingMat && CaptureComponent2D)
59 {
60 FPostProcessSettings& PostProcessSettings = CaptureComponent2D->PostProcessSettings;
61 PostProcessSettings.AddBlendable(PostProcessingMat, Weight);
62 }
63 else
64 {
65 FString Sensor = GetSensorName();
66 FString Msg = "Failed to add Post processing material to " + Sensor;
68 }
69}
70
72{
74 {
75 FPostProcessSettings& PostProcessSettings = CaptureComponent2D->PostProcessSettings;
76 PostProcessSettings.RemoveBlendable(Material);
77 }
78}
79
80void ACamera::Init(FCameraBaseParameters parameters, bool SimulateSensor)
81{
83 SetSimulateSensor(SimulateSensor);
84 SetupCamera(parameters);
86}
87
89{
90 if (IsValid(LogFile))
91 {
92 // File has already been created, return
93 return;
94 }
95
96 FLogFileSettings Settings;
99 Settings.QueueLength = MAX_int32; // Only write the log after destroying the sensor
100 Settings.KeepFileOpen = false;
101 Settings.Timestamp = false;
102 Settings.OverrideFilePath = true;
103 Settings.FilePath = FileSavePath;
104
105 LogFile = NewObject<ULogFile>(ULogFile::StaticClass());
106 if (IsValid(LogFile))
107 {
108 FString FileName = "camera_metadata";
109 LogFile->Create(FileName, Settings);
110
111 // Write camera metadata first line to explain the values.
112 GeoReferencingSystem = AGeoReferencingSystem::GetGeoReferencingSystem(GetWorld());
114 {
115 WriteToLogFile("image_name, X location, Y location, Z location, yaw rotation, pitch rotation, roll rotation, GPS latitude, GPS longitude, GPS altitude");
116 }
117 else
118 {
119 WriteToLogFile("image_name, X location, Y location, Z location, yaw rotation, pitch rotation, roll rotation");
120 }
121 }
122}
123
125{
127 {
128#if WITH_EDITOR
129 UE_LOG(LogTemp, Warning, TEXT("Camera.cpp: CaptureComponent2D is nullptr!"));
130#endif
131 return;
132 }
133
134 CameraParameters = parameters;
135
137
139 {
141 }
142
143 const bool UsePostProcessingEffects = CameraParameters.PostProcessingEffects;
144 auto& PostProcessSettings = CaptureComponent2D->PostProcessSettings;
145
146 if (parameters.UsePhysicLensDistortionEffect)
147 {
148 if (!PhysicLensDistortion.IsValid())
149 {
150 const FString Path = "/Game/Agrarsense/Materials/PostProcessingMaterials/PhysicLensDistortion.PhysicLensDistortion";
151 PhysicLensDistortion = Cast<UMaterial>(StaticLoadObject(UMaterial::StaticClass(), nullptr, *Path));
152
153 if (PhysicLensDistortion.IsValid())
154 {
155 PostProcessSettings.AddBlendable(PhysicLensDistortion.Get(), 1.0f);
156#if WITH_EDITOR
157 UE_LOG(LogTemp, Warning, TEXT("Camera.cpp: Added physics lens distortion effect."));
158#endif
159 }
160 }
161 }
162 else if (PhysicLensDistortion.IsValid())
163 {
164 PostProcessSettings.RemoveBlendable(PhysicLensDistortion.Get());
165 PhysicLensDistortion.Reset();
166#if WITH_EDITOR
167 UE_LOG(LogTemp, Warning, TEXT("Camera.cpp: Removed physics lens distortion effect."));
168#endif
169 }
170
171 if (parameters.UseIceLensEffect)
172 {
173 if (!IceMaterialInstance.IsValid())
174 {
175 const FString Path = "/Game/Agrarsense/Materials/PostProcessingMaterials/CameraPostProcessEffects/m_ice_lens_effect";
176 UMaterial* LoadedIceMaterial = Cast<UMaterial>(StaticLoadObject(UMaterial::StaticClass(), nullptr, *Path));
177
178 // Create UMaterialInstanceDynamic from LoadedIceMaterial
179 IceMaterialInstance = UMaterialInstanceDynamic::Create(LoadedIceMaterial, nullptr);
180 if (IceMaterialInstance.IsValid())
181 {
182 // Add material to CaptureComponent2D PostProcessSettings
183 PostProcessSettings.AddBlendable(IceMaterialInstance.Get(), 1.0f);
184#if WITH_EDITOR
185 UE_LOG(LogTemp, Warning, TEXT("Camera.cpp: Added ice lens effect."));
186#endif
187 }
188 }
189
190 UMaterialInstanceDynamic* IceMatInstance = IceMaterialInstance.Get();
191 if (IceMatInstance)
192 {
193 // Set IceMaterialInstance scalar parameters values
194 IceMatInstance->SetScalarParameterValue(FName("Strength"), parameters.IceLensEffectStrength);
195 IceMatInstance->SetScalarParameterValue(FName("Angle"), parameters.IceLensEffectAngle);
196#if WITH_EDITOR
197 UE_LOG(LogTemp, Warning, TEXT("Camera.cpp: Changed ice lens effect parameters."));
198#endif
199 }
200 }
201 else if (IceMaterialInstance.IsValid())
202 {
203 UMaterialInstanceDynamic* IceMatInstance = IceMaterialInstance.Get();
204 if (IceMatInstance)
205 {
206 // Remove material from CaptureComponent2D PostProcessSettings
207 PostProcessSettings.RemoveBlendable(IceMatInstance);
208 IceMaterialInstance.Reset();
209
210#if WITH_EDITOR
211 UE_LOG(LogTemp, Warning, TEXT("Camera.cpp: Removed ice lens effect."));
212#endif
213 }
214 }
215
218 const float TargetGamma = CameraParameters.TargetGamma;
219 const bool Enable16BitFormat = CameraParameters.Enable16BitFormat;
220
221 CaptureRenderTarget->InitCustomFormat(ImageWidth, ImageHeight, Enable16BitFormat ? PF_FloatRGB : PF_B8G8R8A8, !UsePostProcessingEffects);
222
223 CaptureRenderTarget->TargetGamma = TargetGamma;
224
225 // Set PostProcessSettings override (on or off)
226 PostProcessSettings.bOverride_AutoExposureMethod = UsePostProcessingEffects;
227 PostProcessSettings.bOverride_AutoExposureBias = UsePostProcessingEffects;
228 PostProcessSettings.bOverride_AutoExposureMinBrightness = UsePostProcessingEffects;
229 PostProcessSettings.bOverride_AutoExposureMaxBrightness = UsePostProcessingEffects;
230 PostProcessSettings.bOverride_AutoExposureSpeedUp = UsePostProcessingEffects;
231 PostProcessSettings.bOverride_AutoExposureSpeedDown = UsePostProcessingEffects;
232 PostProcessSettings.bOverride_HistogramLogMin = UsePostProcessingEffects;
233 PostProcessSettings.bOverride_HistogramLogMax = UsePostProcessingEffects;
234 PostProcessSettings.bOverride_CameraShutterSpeed = UsePostProcessingEffects;
235 PostProcessSettings.bOverride_CameraISO = UsePostProcessingEffects;
236 PostProcessSettings.bOverride_DepthOfFieldFstop = UsePostProcessingEffects;
237 PostProcessSettings.bOverride_DepthOfFieldMinFstop = UsePostProcessingEffects;
238 PostProcessSettings.bOverride_DepthOfFieldBladeCount = UsePostProcessingEffects;
239 PostProcessSettings.bOverride_FilmSlope = UsePostProcessingEffects;
240 PostProcessSettings.bOverride_FilmToe = UsePostProcessingEffects;
241 PostProcessSettings.bOverride_FilmShoulder = UsePostProcessingEffects;
242 PostProcessSettings.bOverride_FilmWhiteClip = UsePostProcessingEffects;
243 PostProcessSettings.bOverride_FilmBlackClip = UsePostProcessingEffects;
244 PostProcessSettings.bOverride_MotionBlurAmount = UsePostProcessingEffects;
245 PostProcessSettings.bOverride_MotionBlurMax = UsePostProcessingEffects;
246 PostProcessSettings.bOverride_MotionBlurPerObjectSize = UsePostProcessingEffects;
247 PostProcessSettings.bOverride_WhiteTemp = UsePostProcessingEffects;
248 PostProcessSettings.bOverride_WhiteTint = UsePostProcessingEffects;
249 PostProcessSettings.bOverride_ColorContrast = UsePostProcessingEffects;
250 PostProcessSettings.bOverride_SceneFringeIntensity = UsePostProcessingEffects;
251 PostProcessSettings.bOverride_ChromaticAberrationStartOffset = UsePostProcessingEffects;
252 PostProcessSettings.bOverride_AmbientOcclusionIntensity = UsePostProcessingEffects;
253 PostProcessSettings.bOverride_AmbientOcclusionRadius = UsePostProcessingEffects;
254 PostProcessSettings.bOverride_AmbientOcclusionStaticFraction = UsePostProcessingEffects;
255 PostProcessSettings.bOverride_AmbientOcclusionFadeDistance = UsePostProcessingEffects;
256 PostProcessSettings.bOverride_AmbientOcclusionPower = UsePostProcessingEffects;
257 PostProcessSettings.bOverride_AmbientOcclusionBias = UsePostProcessingEffects;
258 PostProcessSettings.bOverride_AmbientOcclusionQuality = UsePostProcessingEffects;
259 PostProcessSettings.bOverride_BloomMethod = UsePostProcessingEffects;
260 PostProcessSettings.bOverride_BloomIntensity = UsePostProcessingEffects;
261 PostProcessSettings.bOverride_BloomThreshold = UsePostProcessingEffects;
262 PostProcessSettings.bOverride_LensFlareIntensity = UsePostProcessingEffects;
263 PostProcessSettings.bOverride_DepthOfFieldFocalDistance = UsePostProcessingEffects;
264 PostProcessSettings.bOverride_DepthOfFieldDepthBlurAmount = UsePostProcessingEffects;
265 PostProcessSettings.bOverride_DepthOfFieldDepthBlurRadius = UsePostProcessingEffects;
266
267 // Set PostProcessSettings values
268 PostProcessSettings.CameraShutterSpeed = CameraParameters.ShutterSpeed;
269 PostProcessSettings.CameraISO = CameraParameters.ISO;
270 PostProcessSettings.DepthOfFieldFstop = CameraParameters.Aperture;
271 PostProcessSettings.DepthOfFieldFocalDistance = CameraParameters.FocalDistance;
272 PostProcessSettings.DepthOfFieldDepthBlurAmount = CameraParameters.DepthBlurAmount;
273 PostProcessSettings.DepthOfFieldDepthBlurRadius = CameraParameters.DepthBlurRadius;
274 PostProcessSettings.DepthOfFieldMinFstop = CameraParameters.DofMinFStop;
275 PostProcessSettings.DepthOfFieldBladeCount = CameraParameters.DofBladeCount;
276 PostProcessSettings.FilmSlope = CameraParameters.FilmSlope;
277 PostProcessSettings.FilmToe = CameraParameters.FilmToe;
278 PostProcessSettings.FilmShoulder = CameraParameters.FilmShoulder;
279 PostProcessSettings.FilmBlackClip = CameraParameters.FilmBlackClip;
280 PostProcessSettings.FilmWhiteClip = CameraParameters.FilmWhiteClip;
281 PostProcessSettings.AutoExposureMinBrightness = CameraParameters.ExposureMinBrightness;
282 PostProcessSettings.AutoExposureMaxBrightness = CameraParameters.ExposureMaxBrightness;
283 PostProcessSettings.AutoExposureSpeedUp = CameraParameters.ExposureSpeedUp;
284 PostProcessSettings.AutoExposureSpeedDown = CameraParameters.ExposureSpeedDown;
285 PostProcessSettings.MotionBlurAmount = CameraParameters.MotionBlurIntensity;
286 PostProcessSettings.MotionBlurMax = CameraParameters.MotionBlurMax;
287 PostProcessSettings.MotionBlurPerObjectSize = CameraParameters.MotionBlurMinObjSize;
288 PostProcessSettings.LensFlareIntensity = CameraParameters.LensFlareIntensity;
289 PostProcessSettings.BloomIntensity = CameraParameters.BloomIntensity;
290 PostProcessSettings.WhiteTemp = CameraParameters.WhiteTemp;
291 PostProcessSettings.WhiteTint = CameraParameters.WhiteTint;
292 PostProcessSettings.SceneFringeIntensity = CameraParameters.ChromAberrIntensity;
293 PostProcessSettings.ChromaticAberrationStartOffset = CameraParameters.ChromAberrOffset;
294
297
299 {
300 // Enable HDR on CaptureSource, only allowed for RGB Camera for now.
301 CaptureComponent2D->CaptureSource = ESceneCaptureSource::SCS_FinalColorHDR;
302 }
303 else
304 {
305 CaptureComponent2D->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
306 }
307
308 CaptureComponent2D->bUseRayTracingIfEnabled = true;
309 CaptureComponent2D->UpdateContent();
310 CaptureComponent2D->Activate();
311
313
314 FString Encoding = "bgr8";
315
316 // Create ROS Image message
317 ImageMsg.Reset();
318 ImageMsg = MakeShared<ROSMessages::sensor_msgs::Image>();
319 ImageMsg->header.frame_id = "world";
320 ImageMsg->height = ImageWidth;
321 ImageMsg->width = ImageHeight;
322 ImageMsg->encoding = Encoding;
323 ImageMsg->is_bigendian = 0;
324 ImageMsg->step = ImageWidth * 4;
325
327
328 if (!UnrealWindow.IsValid())
329 {
330 // Create and setup UnrealWindow
331 FString windowName = GetSensorIdentifier() + " Window";
332 UnrealWindow = MakeShareable(new FUnrealWindow(ImageWidth, ImageHeight, windowName));
333
334 // Set on Window closed event
336 UnrealWindow->GetSWindow()->SetOnWindowClosed(OnWindowClosedDelegate);
337
338 // try to create Camera parameter Widget from path
339 FSoftClassPath WidgetClassPath(TEXT("/Game/Agrarsense/Widgets/Camera/WBP_CameraControlsMenu.WBP_CameraControlsMenu_C"));
340 UClass* WidgetClass = WidgetClassPath.TryLoadClass<UUserWidget>();
341
342 APlayerController* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0);
343
344 if (WidgetClass && PlayerController)
345 {
346 UCameraWidgetBase* WidgetInstance = CreateWidget<UCameraWidgetBase>(PlayerController, WidgetClass);
347 if (WidgetInstance)
348 {
349 bool ShowGuide = false;
350 WidgetInstance->Setup(this, ShowGuide);
351 this->AddWidgetToWindow(WidgetInstance);
352 }
353 }
354 }
355
356 bool Resized = false;
357 if (UnrealWindow.IsValid())
358 {
359 UnrealWindow->SetupComponent(CaptureRenderTarget);
360 if (UnrealWindow->GetWindowWidth() != ImageWidth || UnrealWindow->GetWindowHeight() != ImageHeight)
361 {
362 UnrealWindow->ResizeWindow(ImageWidth, ImageHeight);
363 Resized = true;
364 }
365 }
366
368
369 if (Resized)
370 {
372 }
373}
374
376{
377 Super::BeginPlay();
378
379 // Register to PreActorTick where USceneCaptureComponent2D will get rendered.
380 OnPreTickDelegate = FWorldDelegates::OnWorldPreActorTick.AddUObject(this, &ACamera::PreActorTick);
381
382 // Check if we should use ATickManager which ticks in parallel, or use Post actor tick
383 // In there we capture the screen pixels with FrameGrabber API,
384 // might do some extra processing, save image to disk and send data to ROS.
386 {
387 SetActorTickEnabled(false);
388 TickEntry = ATickManager::AddTick(this, BindTick(this, &ACamera::EndOfFrameParellel), ETickType::LateTickParallel);
389 }
390 else
391 {
392 SetActorTickEnabled(true);
393 OnPostTickDelegate = FWorldDelegates::OnWorldPostActorTick.AddUObject(this, &ACamera::EndOfFrame);
394 }
395
396 // Get all existing components set to be hidden for all Camera sensors (like Lidar point cloud visualization).
397 auto Components = ASensor::GetComponentsToHide();
398 for (int32 i = 0; i < Components.Num(); i++)
399 {
400 UPrimitiveComponent* Primitive = Components[i].Get();
401 if (Primitive)
402 {
403 HidePrimitiveComponent(Primitive);
404 }
405 }
406
407 OnPrimitiveAdded.AddUniqueDynamic(this, &ACamera::HidePrimitiveComponent);
408}
409
410void ACamera::EndPlay(const EEndPlayReason::Type EndPlayReason)
411{
412 Super::EndPlay(EndPlayReason);
413
414 FWorldDelegates::OnWorldPreActorTick.Remove(OnPreTickDelegate);
415
417 {
419 }
420 else
421 {
422 FWorldDelegates::OnWorldPostActorTick.Remove(OnPostTickDelegate);
423 }
424
425 if (UnrealWindow.IsValid())
426 {
427 OnWindowClosedDelegate.Unbind();
428 UnrealWindow->DestroyWindow();
429 UnrealWindow.Reset();
430 }
431
433
435 {
436 CaptureRenderTarget->ConditionalBeginDestroy();
437 CaptureRenderTarget = nullptr;
438 }
439
441 {
442 CaptureComponent2D->UnregisterComponent();
443 CaptureComponent2D->ConditionalBeginDestroy();
444 CaptureComponent2D = nullptr;
445 }
446
447 ImageMsg.Reset();
448 IceMaterialInstance.Reset();
449
450 if (BGR8Buffer)
451 {
452 delete[] BGR8Buffer;
453 BGR8Buffer = nullptr;
454 }
456}
457
458void ACamera::PreActorTick(UWorld* World, ELevelTick TickType, float DeltaSeconds)
459{
460 TRACE_CPUPROFILER_EVENT_SCOPE(ACamera::PreActorTick);
461
462 if (ShouldSimulate(DeltaSeconds) && CaptureComponent2D)
463 {
464 // Capture scene now.
465 // We could use CaptureSceneDeferred but it would not work if World (Spectator) Rendering is disabled.
466 CaptureComponent2D->CaptureScene();
467
468 if (FrameGrabber.IsValid())
469 {
470 FrameGrabber->CaptureThisFrame(FFramePayloadPtr());
471 }
472 }
473}
474
475void ACamera::EndOfFrame(UWorld* World, ELevelTick TickType, float DeltaSeconds)
476{
478}
479
480void ACamera::EndOfFrameParellel(float DeltaTime)
481{
483}
484
485bool ACamera::ShouldSimulate(const float DeltaSeconds)
486{
487 if (!CanSimulateSensor())
488 {
489 ShouldSimulateCamera = false;
491 }
492
494 {
497 }
498
499 FrameRateTimer += DeltaSeconds;
501 {
502 FrameRateTimer = 0.0f;
505 }
506
507 ShouldSimulateCamera = false;
508
510}
511
513{
514 TRACE_CPUPROFILER_EVENT_SCOPE(ACamera::FrameGrabberCapture);
515
516 if (!ShouldSimulateCamera || !FrameGrabber.IsValid())
517 {
518 return;
519 }
520
522 {
523 return;
524 }
525
526 TArray<FCapturedFrameData> Frames = FrameGrabber->GetCapturedFrames();
527 if (Frames.Num())
528 {
529 FCapturedFrameData& LastFrame = Frames.Last();
530 TArray<FColor>& ImageBuffer = LastFrame.ColorBuffer;
531
532 AddProcessingToFrameBuffer(ImageBuffer);
533
535 {
537 AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this, ImageBuffer]()
538 {
540 });
541 }
542
544 }
545}
546
547void ACamera::AddProcessingToFrameBuffer(TArray<FColor>& buffer)
548{
549 // Only DVSCamera adds processing to the frame buffer
550 return;
551}
552
553void ACamera::HidePrimitiveComponent(UPrimitiveComponent* PrimitiveComponent)
554{
555 if (PrimitiveComponent && CaptureComponent2D)
556 {
557 CaptureComponent2D->HideComponent(PrimitiveComponent);
558 }
559}
560
562{
563 if (!UnrealWindow.IsValid())
564 {
565#if WITH_EDITOR
566 UE_LOG(LogTemp, Warning, TEXT("Camera.cpp: UnrealWindow is nullptr!"));
567#endif
568 return;
569 }
570
571 // Destroy old FrameGrabber
573
574 // Get SceneViewport TSharedPtr
575 auto SceneViewport = UnrealWindow->GetSceneViewport();
576
577 if (SceneViewport.IsValid() && CaptureRenderTarget)
578 {
579 EPixelFormat PixelFormat = GetPixelFormatFromRenderTargetFormat(TextureFormat);
580 FIntPoint Size = SceneViewport->GetSize();
581
583 {
584 // Currently only DVS camera needs this texture to render DVS output into separate FUnrealWindow.
585 CaptureFrameTexture = UTexture2D::CreateTransient(Size.X, Size.Y, PixelFormat);
586 CaptureFrameTexture->UpdateResource();
587 }
588
589 FrameGrabber = MakeShareable(new FFrameGrabber(SceneViewport.ToSharedRef(), Size, PixelFormat));
590 FrameGrabber->StartCapturingFrames();
591 }
592}
593
595{
596 if (FrameGrabber.IsValid())
597 {
598 FrameGrabber->StopCapturingFrames();
599 FrameGrabber->Shutdown();
600 FrameGrabber.Reset();
601 }
602
604 {
605 CaptureFrameTexture->RemoveFromRoot();
606 CaptureFrameTexture->ConditionalBeginDestroy();
607 CaptureFrameTexture = nullptr;
608 }
609}
610
611void ACamera::SendImageDataToROS(const TArray<FColor>& FrameBuffer, int32 Width, int32 Height)
612{
613 TRACE_CPUPROFILER_EVENT_SCOPE(ACamera::SendImageDataToROS);
614
615 UTopic* Topic = GetROSTopic();
616 if (!SendDataToROS
617 || !Topic
618 || !IsROSConnected()
619 || FrameBuffer.IsEmpty()
620 || !ImageMsg.IsValid())
621 {
622 return;
623 }
624
625 ImageMsg->height = Height;
626 ImageMsg->width = Width;
627
628 // Cast TArray<FColor> into uint8[] data with all channels (RBGA)
629 // Currently not in use but left here incase this is ever needed again.
630 //ImageMsg->encoding = "bgra8";
631 //ImageMsg->step = Width * 4;
632 //ImageMsg->data = const_cast<uint8*>(reinterpret_cast<const uint8*>(FrameBuffer.GetData()));
633
634 // Convert TArray<FColor> to uint8[] data without alpha channel
635 // By removing alpha channel, we reduce ~25% of ROS bandwidth,
636 // but costs a bit more in processing time here
637 const int32 BufferSize = Width * Height * 3;
638 if (!BGR8Buffer || CurrentBufferSize != BufferSize)
639 {
640 // Resize buffer if needed
641 delete[] BGR8Buffer;
642 BGR8Buffer = new uint8[BufferSize];
643 CurrentBufferSize = BufferSize;
644 }
645
646 uint8* Dest = BGR8Buffer;
647
648 const FColor* Source = FrameBuffer.GetData();
649 const int32 PixelCount = FrameBuffer.Num();
650
651 for (int32 i = 0; i < PixelCount; ++i)
652 {
653 const FColor& Pixel = Source[i];
654 *Dest++ = Pixel.B;
655 *Dest++ = Pixel.G;
656 *Dest++ = Pixel.R;
657 }
658
659 ImageMsg->step = Width * 3;
660 ImageMsg->data = BGR8Buffer;
661
662 // Send Image to ROS
663 Topic->Publish(ImageMsg);
664}
665
666void ACamera::SaveImageToDisk(const TArray<FColor> FrameBuffer, int32 Width, int32 Height)
667{
668 TRACE_CPUPROFILER_EVENT_SCOPE(ACamera::SaveImageToDisk);
669
670 if (FrameBuffer.IsEmpty() || Width == 0 || Height == 0)
671 {
672 return;
673 }
674
675 int32 Size = Width * Height;
676 if (FrameBuffer.Num() != Size)
677 {
678 FString Msg = "Camera sensor: Unable to save Image to disk. FrameBuffer size and resolution don't match!";
680 return;
681 }
682
683 FString ImageName = FString::Printf(TEXT("image_%d.png"), ImageNumber);
684 FString fullFileName = FString::Printf(TEXT("%s%s"), *FileSavePath, *ImageName);
685 ++ImageNumber;
686
687 SaveCameraMetaDataToDisk(ImageName);
688
689 FIntPoint DestSize(Width, Height);
690 TImagePixelData<FColor> PixelData(DestSize);
691 PixelData.Pixels = FrameBuffer;
692
693 // Create ImageTask
694 TUniquePtr<FImageWriteTask> ImageTask = MakeUnique<FImageWriteTask>();
695 ImageTask->PixelData = MakeUnique<TImagePixelData<FColor>>(PixelData);
696 ImageTask->Filename = fullFileName;
697 ImageTask->Format = EImageFormat::PNG;
698 ImageTask->CompressionQuality = (int32)EImageCompressionQuality::Default;
699 ImageTask->bOverwriteFile = true;
700 ImageTask->PixelPreProcessors.Add(TAsyncAlphaWrite<FColor>(255));
701
702 // Get Screenshot config and enqueue image save task
703 FHighResScreenshotConfig& HighResScreenshotConfig = GetHighResScreenshotConfig();
704 TFuture<bool> CompletionFuture = HighResScreenshotConfig.ImageWriteQueue->Enqueue(MoveTemp(ImageTask));
705}
706
707void ACamera::SaveCameraMetaDataToDisk(const FString& ImageName)
708{
709 if (!IsValid(LogFile))
710 {
711 // If the log file located in base class is not valid, return here.
712 return;
713 }
714
715 const FVector ActorPosition = GetActorLocation();
716 const FRotator ActorRotation = GetActorRotation();
717
718 FString MetaData;
719
721 {
722 FGeographicCoordinates GeoCoordinates = UCoordinateConversionUtilities::UnrealToGeographicCoordinates(GeoReferencingSystem, ActorPosition);
723 MetaData = FString::Printf(TEXT("%s, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.6f, %.6f, %.2f"), *ImageName, ActorPosition.X, ActorPosition.Y, ActorPosition.Z,
724 ActorRotation.Pitch, ActorRotation.Yaw, ActorRotation.Roll,
725 GeoCoordinates.Latitude, GeoCoordinates.Longitude, GeoCoordinates.Altitude);
726 }
727 else
728 {
729 MetaData = FString::Printf(TEXT("%s, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f"), *ImageName, ActorPosition.X, ActorPosition.Y, ActorPosition.Z,
730 ActorRotation.Pitch, ActorRotation.Yaw, ActorRotation.Roll);
731 }
732
733 // Write to the log file (this is written after sensor is destroyed)
734 WriteToLogFile(MetaData);
735}
736
737void ACamera::OnWindowClosed(const TSharedRef<SWindow>& Window)
738{
739#if WITH_EDITOR
740 UE_LOG(LogTemp, Warning, TEXT("Camera.cpp: Camera window closed. Destroying Camera sensor.."));
741#endif
742
743 // Broadcast event that this Camera sensor is about be destroyed when user has clicked Window Close button.
744 OnCameraWindowClosed.Broadcast(this);
745
746 // Destroy this camera sensor
747 this->Destroy();
748}
749
750void ACamera::AddWidgetToWindow(UWidget* WidgetToAdd)
751{
752 if (UnrealWindow.IsValid() && WidgetToAdd)
753 {
754 UnrealWindow->AddUWidgetToWindow(WidgetToAdd);
755 }
756}
757
758void ACamera::RemoveWidgetFromWindow(UWidget* WidgetToRemove)
759{
760 if (UnrealWindow.IsValid() && WidgetToRemove)
761 {
762 UnrealWindow->RemoveUWidgetFromWindow(WidgetToRemove);
763 }
764}
765
766void ACamera::SetShadowRendering(bool RenderShadows)
767{
769 {
770 auto& CameraShowFlags = CaptureComponent2D->ShowFlags;
771 CameraShowFlags.SetDynamicShadows(RenderShadows);
772 CameraShowFlags.SetContactShadows(RenderShadows);
773 CameraShowFlags.SetCapsuleShadows(RenderShadows);
774 }
775}
776
777void ACamera::SetTemporalAA(bool SetTemporal)
778{
780 {
781 auto& CameraShowFlags = CaptureComponent2D->ShowFlags;
782 CameraShowFlags.SetTemporalAA(SetTemporal);
783 }
784}
785
786void ACamera::ResizeCamera(int Width, int Height)
787{
788 if (Width == 0 || Height == 0)
789 {
790 return;
791 }
792
793 CameraParameters.Width = Width;
794 CameraParameters.Height = Height;
796}
static auto BindTick(ObjectType *Object, FunctionType Function)
Definition: TickManager.h:182
bool UseParallelLateTick
Definition: Camera.h:220
void AddPostProcessingMaterial(const FString &Path, float Weight=1.0f)
Definition: Camera.cpp:55
FTickEntry TickEntry
Definition: Camera.h:281
TSharedPtr< FFrameGrabber > FrameGrabber
Definition: Camera.h:313
void ChangeCameraParameters(FCameraBaseParameters newParameters)
Definition: Camera.cpp:50
virtual ESensorTypes GetSensorType() const override
Definition: Camera.h:73
FCameraBaseParameters CameraParameters
Definition: Camera.h:329
FOnWindowClosed OnWindowClosedDelegate
Definition: Camera.h:342
virtual void Init(FCameraBaseParameters parameters, bool SimulateSensor=true)
Definition: Camera.cpp:80
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override
Definition: Camera.cpp:410
int ImageWidth
Definition: Camera.h:346
USceneCaptureComponent2D * CaptureComponent2D
Definition: Camera.h:270
void RemovePostProcessingMaterial(UMaterial *Material)
Definition: Camera.cpp:71
void SaveCameraMetaDataToDisk(const FString &ImageName)
Definition: Camera.cpp:707
AGeoReferencingSystem * GeoReferencingSystem
Definition: Camera.h:338
float FrameRateTimer
Definition: Camera.h:354
void ResizeCamera(int Width=1280, int Height=720)
Definition: Camera.cpp:786
void SaveImageToDisk(const TArray< FColor > FrameBuffer, int32 Width, int32 Height)
Definition: Camera.cpp:666
FDelegateHandle OnPostTickDelegate
Definition: Camera.h:225
bool ShouldSimulateCamera
Definition: Camera.h:351
void SetShadowRendering(bool RenderShadows)
Definition: Camera.cpp:766
void OnWindowClosed(const TSharedRef< SWindow > &Window)
Definition: Camera.cpp:737
void StartFrameGrabber()
Definition: Camera.cpp:561
void FrameGrabberCapture()
Definition: Camera.cpp:512
virtual void SendImageDataToROS(const TArray< FColor > &FrameBuffer, int32 Width, int32 Height)
Definition: Camera.cpp:611
FDelegateHandle OnPreTickDelegate
Definition: Camera.h:222
int ImageHeight
Definition: Camera.h:347
UTextureRenderTarget2D * CaptureRenderTarget
Definition: Camera.h:267
void AddWidgetToWindow(UWidget *WidgetToAdd)
Definition: Camera.cpp:750
TSharedPtr< ROSMessages::sensor_msgs::Image > ImageMsg
Definition: Camera.h:344
int32 CurrentBufferSize
Definition: Camera.h:360
bool SaveCurrentFrameToDiskRequested
Definition: Camera.h:349
void SetTemporalAA(bool SetTemporal)
Definition: Camera.cpp:777
void CreateLogFile() override
Definition: Camera.cpp:88
UTexture2D * CaptureFrameTexture
Definition: Camera.h:277
void SetupCamera(FCameraBaseParameters parameters)
Definition: Camera.cpp:124
TWeakObjectPtr< UMaterial > PhysicLensDistortion
Definition: Camera.h:285
void RemoveWidgetFromWindow(UWidget *WidgetToRemove)
Definition: Camera.cpp:758
FCameraDelegate_OnWindowResized OnCameraWindowResized
Definition: Camera.h:147
void HidePrimitiveComponent(UPrimitiveComponent *PrimitiveComponent)
Definition: Camera.cpp:553
TEnumAsByte< ETextureRenderTargetFormat > TextureFormat
Definition: Camera.h:133
void ReleaseFrameGrabber()
Definition: Camera.cpp:594
virtual void EndOfFrameParellel(float DeltaTime)
Definition: Camera.cpp:480
virtual void AddProcessingToFrameBuffer(TArray< FColor > &buffer)
Definition: Camera.cpp:547
uint8 * BGR8Buffer
Definition: Camera.h:358
TSharedPtr< FUnrealWindow > UnrealWindow
Definition: Camera.h:279
virtual void EndOfFrame(UWorld *World, ELevelTick TickType, float DeltaSeconds)
Definition: Camera.cpp:475
float CameraFrameRate
Definition: Camera.h:353
ACamera(const FObjectInitializer &ObjectInitializer)
Definition: Camera.cpp:29
virtual void PreActorTick(UWorld *World, ELevelTick TickType, float DeltaSeconds)
Definition: Camera.cpp:458
FCameraDelegate_OnWindowClosed OnCameraWindowClosed
Definition: Camera.h:140
virtual void BeginPlay() override
Definition: Camera.cpp:375
TWeakObjectPtr< UMaterialInstanceDynamic > IceMaterialInstance
Definition: Camera.h:283
bool ShouldSimulate(const float DeltaSeconds)
Definition: Camera.cpp:485
int ImageNumber
Definition: Camera.h:356
bool CanSimulateSensor() const
Definition: Sensor.h:161
static FPrimitiveAdded OnPrimitiveAdded
Definition: Sensor.h:354
FString GetSensorIdentifier() const
Definition: Sensor.h:74
virtual void CreateDataSavePath()
Definition: Sensor.cpp:225
FString GetSensorName() const
Definition: Sensor.h:95
void SetSimulateSensor(bool SimulateSensor)
Definition: Sensor.h:151
UTopic * GetROSTopic() const
Definition: Sensor.h:141
ULogFile * LogFile
Definition: Sensor.h:347
bool SendDataToROS
Definition: Sensor.h:344
static TArray< TWeakObjectPtr< UPrimitiveComponent > > GetComponentsToHide()
Definition: Sensor.h:256
FString FileSavePath
Definition: Sensor.h:349
void WriteToLogFile(const FString &Message)
Definition: Sensor.cpp:265
FORCEINLINE bool IsROSConnected() const
Definition: Sensor.h:192
virtual void CreateROSTopic()
Definition: Sensor.cpp:169
static void RemoveTick(FTickEntry TickEntry)
Definition: TickManager.cpp:90
static FTickEntry AddTick(UObject *Object, std::function< void(float)> Function, ETickType Type)
Definition: TickManager.cpp:55
static void Log(const FString &Message, bool LogToTextFile=true, bool LogToROS=true)
virtual void Setup(ACamera *CameraRef, bool ShowOnStartup=true)
static FGeographicCoordinates UnrealToGeographicCoordinates(AGeoReferencingSystem *GeoReferencingSystem, const FVector &Position)
void Create(const FString &FileNameWithoutExtension, FLogFileSettings Settings)
Definition: LogFile.cpp:40
bool KeepFileOpen
Definition: LogFile.h:42
bool Timestamp
Definition: LogFile.h:39
FString FilePath
Definition: LogFile.h:54
FFileWriteOptions FileWriteOptions
Definition: LogFile.h:45
int32 QueueLength
Definition: LogFile.h:48
bool OverrideFilePath
Definition: LogFile.h:51
FFileCreationOptions FileCreationOptions
Definition: LogFile.h:36