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