Skip to content

Commit a1d5563

Browse files
committed
Fixed wordings and typos in README.md
1 parent 7a7ded6 commit a1d5563

File tree

1 file changed

+39
-37
lines changed

1 file changed

+39
-37
lines changed

README.md

+39-37
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,24 @@
55

66

77
# A Small Introduction
8-
UnrealEngine4 is known to be a powerful tool to create virtual worlds as it is a AAA production game engine. Generating temporally consistent data, with automatic pixel-wise annotations from complex scenes, such as traffic scenarios, is a capability worth leveraging for machine learning, or more explicitly deep learning, contexts, and has been explored for a series of projects already. There are plugins available that handle rendering images from UE4 to disk at runtime, such as [UnrealCV](https://unrealcv.org/) and [AirSim](https://github.com/microsoft/AirSim).
8+
UnrealEngine4 is known to be a powerful tool to create virtual worlds as it is a highly valued AAA production game engine. Generating temporally consistent data with automatic pixel-wise annotations from complex scenes, such as traffic scenarios, is a capability worth leveraging, especially for training and validation of machine learning, or more explicitly deep learning, applications, and has been explored in a variety of projects already. Already, there are plugins available that allow rendering images from UE4 to disk at runtime, such as prominently [UnrealCV](https://unrealcv.org/) and [AirSim](https://github.com/microsoft/AirSim). This repository aims to be a tutorial that demonstrates such an 'image capturing' mechanism in detail fo you to understand its inner workings, and in turn enable you to reuse it in a custom fashions that suit the needs of your project.
99

10-
When I was setting up a scene for my research these plugins were just not yet supporting the latest engine version I wanted/needed to use for various feature reasons, and I was missing a place where knowledge of how to setup a capturing component for rendering images to disk myself was explains for non graphics-programmers. There is but of course a lot of source code available from the projects mentioned earlier and there are a lot of postings scattered across multiple platforms explaining parts of the problem and giving code for possible solutions even though they may be meant for a different issue.\
11-
[Image of Scene with Segmentation]\
12-
In this post I want to condense my findings on how to implement a component to capture images to disk from an arbitrary UE4 scene **from scratch** lowering the bar for UE4 beginners. This will include:\
10+
*When I was setting up scenes for my research the formerly mentioned plugins were just not yet supporting the latest engine versions I wanted/needed to use, and also I was missing a place where the knowledge of how to render images to disk myself was explains for non-advanced graphics-programmers. Of course, there are lots of sources for code available online and also there are community blog-entries scattered across multiple platforms explaining parts of the problem as well as possible solutions, even though they typically are targeted for different issues.*
11+
In this repository I want to condense my findings on how to implement a component to capture images to disk from an arbitrary UE4 scene **from scratch** lowering the bar for UE4 beginners. This will include:\
1312
* Rendering images at high FPS without blocking the UE4 rendering thread
1413
* Rendering segmentation (or other graphics buffers) at the same time
1514

16-
**Disclaimer: I do not own any of the code. I merely condensed the sources already available online for easier use!**\
17-
**Also huge thanks to the UE4 AnswerHub community!**
15+
**Disclaimer: I do not own any of the code. I merely condensed the sources already available online for easier use and provide an overview to the general functionality of this particular approach!**\
16+
**Kudos to the UE4 AnswerHub community!**
1817

1918
# Plugin Support
20-
Where the general idea of this repository is to communicate how to setup a custom image capturing in code, providing a baseline for further development to adapt the code to ones individual needs, I understand that Unreal's Blueprint interface is powerful and some people have their reasons not to dive into C++ development with UE4.
19+
The general idea of this repository is to communicate a possible setup for custom image capturing in code. This shall provide a baseline for further development to adapt the code to ones individual needs. I understand that Unreal's Blueprint interface is powerful and some people have their reasons not to dive into C++ development with UE4.
2120

22-
Now there is also a **Plugin** version of the code available. It generally provides the same functionality as the tutorial code and is structured in the same way, with minor tweaks for more straight-forward use in blueprints.
21+
This is why now there is also a **Plugin** version of the code available. It generally provides the same functionality as the tutorial code and is structured in the same way, with minor tweaks for more straight-forward use in blueprints.
2322

2423
It comes with open sources so that everybody may compile it for their platform. (Only Win64 Engine version 4.22 binaries are included, more will follow soon (hopefully...))
2524

26-
To incorporate the Plugin in to your project: Create a **Plugins** directory in your project and copy the ```\UnrealImageCapture\Plugins\CameraCaptureToDisk``` directory. Load the plugin in your project, if not automatically done by the editor, and simply place the `CameraCaptureManager_BP`, which is to be found in the plugin's contents, in the scene and fill in its required slots as depicted below. This will require you to place a ```SceneCapture2D``` in your scene.
25+
To incorporate the Plugin in to your project: Create a **Plugins** directory in your project and copy the ```\UnrealImageCapture\Plugins\CameraCaptureToDisk``` directory. Load the plugin in your project, if not automatically done by the editor, and place the `CameraCaptureManager_BP`, which is to be found in the plugin's contents, in the scene and fill in its required slots as depicted below. This will require you to place a ```SceneCapture2D``` in your scene.
2726
A ```PostProcessMaterial``` for segmentation is also located in the plugin's contents.
2827

2928
![alt text](https://github.com/TimmHess/UnrealImageCapture/blob/master/gfx/ColorCaptureOutline.png)
@@ -37,22 +36,24 @@ An image-capturing-command can be triggered from Blueprint as exemplary depicted
3736

3837

3938
# How to Save Images to Disk In UE4 (without blocking the rendering thread)
40-
I will go through the code step by step so that it is hopefully easier to implement each step as you are following along. The full source code is placed in this git.
39+
I will go through the code step-by-step so that hopefully it will be easier to implement each step as you are following along. The full source code is placed in this repository.
4140

4241
## Prerequisite
43-
You will need a UE4 C++ project.
42+
You will need a UE4 (or UE5) C++ project.
4443

45-
Also, you might have to add a few packages to your `'YourProjectName'.Build.cs` file. These are already included into UnrealEngine, but they are sometimes not loaded automatically which will lead to unpleasant linker errors. Find the `'YourProjectName'.Build.cs` file in the `Source/'YourProjectName/` directory, and add or extend it to inlcude all modules listed in the following line:
44+
Also, you might have to add a few packages to your `'YourProjectName'.Build.cs` file. These are already included into UnrealEngine, however, sometimes they are not added automatically, resulting in unpleasant linker errors. Find the `'YourProjectName'.Build.cs` file in the `Source/'YourProjectName/` directory, and add or extend it to include all modules listed in the following line:
4645

4746
``` cpp
4847
PublicDependencyModuleNames.AddRange(new string[] {"Core", "CoreUObject", "Engine", "InputCore", "ImageWrapper", "RenderCore", "Renderer", "RHI" });
4948
```
5049
5150
#
5251
## Setup A ColorCapture Component
53-
The component I am using for capturing is the ```SceneCaptureComponent2D``` provided as default by the UE4Editor. Placing one of these into your scene will give you a ```ASceneCaptureComponent``` which is its Actor instance. It basically behaves like any other camera component, except for having a viewport that is not restricted by your computer's monitor or main camera viewport, giving the possibility to render images larger than than the actual screen resolution.
52+
I am using a ```SceneCaptureComponent2D``` as the basis for capturing images. Placing one of these into your scene will give you an ```ASceneCaptureComponent``` which is its `Actor` instance. It basically behaves like any other camera component, but its viewport is not restricted by your computer's monitor or main camera viewport. This provides us the possibility to render images of arbitrary resolution independent from the actual screen resolution.
5453
55-
Add a ```CaptureManager``` class of type Actor to your project.
54+
#
55+
56+
> Add a ```CaptureManager``` class of type Actor to your project.
5657
5758
In the ```CaptureManager.h``` we add the following:\
5859
**CaptureManager.h**
@@ -71,11 +72,13 @@ ASceneCapture2D* ColorCaptureComponents;
7172
7273
This enables you to assign a ```CaptureComponent2d``` to your ```CaptureManager``` code.
7374
74-
Compile and place a ```CaptureManager``` in your scene. As it does not have any primitive to render you will only see it in the editor's outline. In the details panel of the placed ```CaptureManager``` you can now see the ```ColorCaptureComponent``` assigned to ```None```. From the drop down menu select the ```CaptureComponent2D``` you already placed in the scene.
75+
> Compile and place a ```CaptureManager``` in your scene.
76+
77+
As it does not have any primitive to render you will only see it in the editor's outline. In the details panel of the placed ```CaptureManager``` you can now see the ```ColorCaptureComponent``` assigned to ```None```. From the drop down menu select the ```CaptureComponent2D``` you already placed in the scene.
7578
76-
Back to code: We will now prepare our yet "naked" CaptureComponent2D for capturing images, creating and assigning a RenderTarget, which is basically a Texture to store our image data to, and setting the camera properties. *Note: You could also do the this in the Editor but if you deal with, for example, multiple capture components etc., you may find it handy to not worry about creating and assigning all the components by hand*.
79+
Back to code: We will now prepare our yet "naked" `CaptureComponent2D` class for capturing images, creating and assigning a `RenderTarget`, which is basically a `Texture` to store our image data to, and setting the camera properties. *Note: You could also do this in the Editor but if you deal with, i.e. multiple capture components, you may find it handy not to worry about creating and assigning all the components by hand!*
7780
78-
Create a setup function to put all your setup code for the CaptureComponents in the CaptureManger:
81+
> Create a setup function to put all your setup code for the CaptureComponents in the CaptureManger:
7982
8083
**CaptureManager.h**
8184
``` cpp
@@ -124,7 +127,7 @@ void ACaptureManager::SetupColorCaptureComponents(ASceneCapture2D* captureCompon
124127
// lookup more showflags in the UE4 documentation..
125128
}
126129
```
127-
And call the code during `BeginPlay` of the `CaptureManager`
130+
> Call the code during `BeginPlay` of the `CaptureManager`
128131
129132
**CaptureManager.cpp**
130133
``` cpp
@@ -138,15 +141,15 @@ void ACaptureManager::BeginPlay()
138141
}
139142
140143
```
141-
Now that we have a RenderTarget applied to our CaptureComponent we can read its data to disk.
144+
Now that because we have a `RenderTarget` applied to our `CaptureComponent` we can read its data to disk.
142145

143146
#
144147
## Organize RenderRequests
145-
We do this by basically re-implementing UE4's code for taking screenshots with the addition of not flushing our rendering pipeline to prevent rendering hiccups dropping the framerate to 3 - 5 FPS.
148+
We do this by basically re-implementing UE4's code for taking screenshots. Importantly, with the addition of not flushing our rendering pipeline. This prevents rendering *hiccups* that drop the framerate to 3 - 5 FPS.
146149

147-
This comes with the price of needing to handle the waiting before an image is done being copied from GPU so that we do not read an old or uninitialized buffer (Render Thread and GameThread are not synchronous). We do this by keeping a queue of ```RenderRequest``` that we can probe for being completed.
150+
This addition will come with the price of needing to handle 'waiting times' before an image is done and copied from GPU. This is important to prevent reading old or uninitialized buffers (remember that `RenderThread` and `GameThread` are asynchronous). We do this by keeping a queue of ```RenderRequest``` that we can probe for being completed.
148151

149-
We add the following ```struct``` to our CaptureManager class:
152+
> We add the following ```struct``` to our `CaptureManager` class:
150153
151154
**CaptureManager.h**
152155
``` cpp
@@ -172,9 +175,9 @@ UCLASS()
172175
class ...
173176
[...]
174177
```
175-
The ```Image``` will be the color buffer our CaptureComponent writes to. ```RenderFence``` is a neat feature of UE4 letting you put a "fence" into the render pipeline that knows when it has passed the full pipeline, giving a way to assess whether our render command must have been passed as well. The ```isPNG``` flag will be important later when we want to also store semantic labels which should not be stored as JPEG as the compression introduces small errors into the color/label data...
178+
The ```Image``` will be the color buffer our `CaptureComponent` writes to. ```RenderFence``` is a neat feature of UE4 letting you put a 'fence' into the render pipeline that can be checked to notify when it has passed the full pipeline. This gives a way to determine whether our render command has passed as well. The ```isPNG``` flag will be important later when we want to also store semantic labels which should not be stored as JPEG as the compression introduces artifacts into the color/label data...
176179

177-
Also we need to add our ```TQueue```, keeping track of our render requests:
180+
> We need to add our ```TQueue```, keeping track of our render requests:
178181

179182
**CaptureManger.h**
180183

@@ -186,7 +189,7 @@ protected:
186189

187190
#
188191
## Implement the image capturing function:
189-
This function will place a render request on the UE4 rendering pipeline asking the data captured from our CaptureComponent to be copied in our Image buffer so that we can further process it in GameThread.
192+
This function will place a render request on the UE4 rendering pipeline asking the data captured from our `CaptureComponent` to be copied in our Image buffer so that we can further process it in `GameThread`.
190193

191194
**CaptureManger.h**
192195

@@ -255,15 +258,14 @@ void ACaptureManager::CaptureColorNonBlocking(ASceneCapture2D* CaptureComponent,
255258
}
256259
```
257260

258-
With this the image data is already stored in our queue, we now need to store it to disk. *Note: UFUNCTION(BlueprintCallable, Category = "ImageCapture") exposes this function to blueprint, so that you could easily test it*
261+
With this, the image data is already stored in our queue, and we now need to store it to disk. *Note: UFUNCTION(BlueprintCallable, Category = "ImageCapture") exposes this function to blueprint, so that you can easily test it*
259262

260263
#
261264
## Save Image Data to Disk
262-
To do so, each tick of the `CaptureManager` we look up the first element of the `RenderQueue`, if its `RenderFence` is completed then we save the image to disk, else we do nothing.
265+
To do so, in each tick of the `CaptureManager` we look up the first element of the `RenderQueue`, if its `RenderFence` is completed then we save the image to disk, else we do nothing.
263266

264-
The last thing we need is a procedure to write to disk, this time without blocking our game thread. For this we implement an asynchronous procedure storing
265-
the data to disk.
266-
[Link to UnrealWiki](https://wiki.unrealengine.com/Using_AsyncTasks)
267+
The last thing we need is a procedure to write to disk, preferably without blocking our `GameThread`.
268+
>We implement an [asynchronous](https://wiki.unrealengine.com/Using_AsyncTasks) procedure storing the data to disk.
267269
268270
**CaptureManager.h**
269271
``` cpp
@@ -314,7 +316,7 @@ void AsyncSaveImageToDiskTask::DoWork(){
314316
UE_LOG(LogTemp, Log, TEXT("Stored Image: %s"), *FileName);
315317
}
316318
```
317-
And a call from the `CaptureManager` to start the async saving process:
319+
> And a call from the `CaptureManager` to start the async saving process:
318320
319321
**CaptureManager.h**
320322
``` cpp
@@ -394,17 +396,17 @@ void ACaptureManager::Tick(float DeltaTime)
394396

395397
#
396398

397-
For test purposes we can call the `CaptureColorNonBlocking()` from the `LevelBlueprint` attaching it to a button press.
399+
For testing purposes we can call the `CaptureColorNonBlocking()` from the `LevelBlueprint` by attaching it to a button pressed event.
398400

399401
[Image of the level blueprint]
400402

401-
The images captured will now be saved into your project's `Saved` directory.
403+
The captured images will now be saved into your project's `Saved` directory.
402404

403405
#
404406
#
405407

406408
# Capturing Segmentation
407-
To get labels for our images we will add a second CaptureComponent equipped with a `PostProcessMaterial` that will render `CustomDepth` that is settable for each actor in the scene, effectively letting us label and visualize categories of actors.
409+
To get labels for our images we will add a second `CaptureComponent` equipped with a `PostProcessMaterial` that visualizes `CustomDepth`. The `CustomDepthStencil` is settable for each actor in the scene, effectively letting us label and visualize categories of, as well as individual, actors.
408410

409411
## Enable Custom Depth Stencils
410412
Find the **ProjectSettings** in your editor and search for *stencil* which will bring up `Custom Depth-Stencil Pass`. Switch this option from `Enabled` to `Enabled with Stencil`.
@@ -424,7 +426,7 @@ Add a `Division` node and connect the `SceneTexture`'s `Color` output to the div
424426
Apply and save the material.
425427

426428
## Setting up Custom-Depth Stencils
427-
You can set the custom-depth in editor or from code, for simplicity I will this time use the editor. Place an arbitrary object(MeshActor) into the scene, and search for `custom depth` in its details panel. Under `Rendering` enable `Render CustomDepth Pass`, and set `CustomDepth Stencil Value` to whatever you like. For illustration purposes set it to 200.
429+
You can set the custom depth in editor or from code. For simplicity I chose the editor. Place an arbitrary object(MeshActor) into the scene, and search for `custom depth` in its details panel. Under `Rendering` enable `Render CustomDepth Pass`, and set `CustomDepth Stencil Value` to whatever you like. For illustration purposes set it to 200.
428430

429431
#
430432

@@ -526,6 +528,6 @@ To save the image information from SegmentationCapture we can simply use the `Ca
526528
#
527529

528530
# Known Issues
529-
The `IImageWrapperModule`'s wrapping of the data is still done in GameThread rather than in a async call, which can actually consume more runtime than the saving to disk. Simply pushing the WrapperModule into the async procedure does suffice since 1) it is a shared pointer, 2) the `ImageWrapperModule.CreateImageWrapper(...)` needs to be called from GameThread. I am grateful for any ideas on that..
531+
The `IImageWrapperModule`'s wrapping of the data is still done in `GameThread` rather than in an async call, which can actually consume more runtime than the saving to disk. Simply pushing the WrapperModule into the async procedure does suffice since 1) it is a shared pointer, 2) the `ImageWrapperModule.CreateImageWrapper(...)` needs to be called from GameThread. I am grateful for any ideas on that..
530532

531-
It is possible that an image is saved every game tick at high fps. If saving to disk is actually slower than the delta time of the game tick another call to the shared IImageWrapper is made while its buffer is read for saving to disk. This results in a game crash. This should be fixable by adding semaphores, I just did not have the time to test this yet.
533+
It is possible that an image is saved every game tick at high fps. If saving to disk is actually slower than the delta time of the game tick another call to the shared `IImageWrapper` is made while its buffer is read for saving to disk. This results in a game crash. This should be fixable by adding semaphores, I just did not have the time to test this yet.

0 commit comments

Comments
 (0)