Skip to content

[sensor] Visualize observation from depth sensor#1066

Merged
bigbike merged 29 commits intomasterfrom
depth-viz
Apr 13, 2021
Merged

[sensor] Visualize observation from depth sensor#1066
bigbike merged 29 commits intomasterfrom
depth-viz

Conversation

@bigbike
Copy link
Copy Markdown
Contributor

@bigbike bigbike commented Feb 1, 2021

Motivation and Context

As titled. In current GUI mode (viewer), we do not have a way to visualize the depth info which is not displayable. This diff is to address this problem.

  • It introduces a new class called SensorInfoVisualizer as a helper class, which provides and manages the framebuffer, renderbuffer, shaders that are necessary in the visualization.
  • It introduces a new shader class called DepthVisualizerShader that leverages the existing depth.vert and depth.frag to visualize the depth info.
  • It would work for regular depth sensor, and also the fisheye depth sensor (TODO).
  • Scalable: This framework can be easily extended to visualize the other sensor info other than the depth map.
  • In viewer, press key '7' to switch between rgb sensor and the depth sensor.

Lastest results:
rgb depth:
rgb-depth-1

Fisheye depth:
fisheye-depth-1

==== OBSOLETE ========
Regular RGB sensor:
rgb-viz
Depth sensor in the same camera angle:
depth-viz

How Has This Been Tested

Types of changes

  • Docs change / refactoring / dependency upgrade
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have completed my CLA (see CONTRIBUTING)
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@facebook-github-bot facebook-github-bot added the CLA Signed Do not delete this pull request or issue due to inactivity. label Feb 1, 2021
@bigbike bigbike requested a review from eundersander February 1, 2021 04:21
@mosra
Copy link
Copy Markdown
Contributor

mosra commented Feb 1, 2021

Unrelated: it's interesting how @bigbike and @eundersander have almost the same avatar, what's the probability of that happening? 😄

Comment thread src/utils/viewer/viewer.cpp Outdated
sensorRenderTarget->blitRgbaToDefault();
// Immediately bind the main buffer back so that the "imgui" below can work
// properly
Mn::GL::defaultFramebuffer.bind();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this removed? What's the reason?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I am supposed to move it out of the bracket. Definitely a bug introduced. Will fix.

Comment thread src/utils/viewer/viewer.cpp
Comment thread src/esp/sensor/VisualSensor.cpp Outdated
Comment on lines +49 to +52
default:
// Have to had this default, otherwise the clang-tidy will be pissed off,
// and will not let me pass
break;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😆

what was the error it gave you? this seems silly

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's really that bad, you can always use a magic NoLint comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CI test failed directly in the clang-tidy section.
It says missing code for the other enum terms.
And look at this long list:
None = 0,
Color = 1,
Depth = 2,
Normal = 3,
Semantic = 4,
Path = 5,
...

Copy link
Copy Markdown
Contributor Author

@bigbike bigbike Feb 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Skylion007 : the "default" is not a bad concept.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right, I didn't realize there's a lot more values. In that case either enumerating them all or adding a default. In this case the default probably makes sense.

Comment thread src/esp/gfx/SensorInfoVisualizer.cpp Outdated
Comment on lines +33 to +37
// depth buffer is 24-bit integer pixel
depthBuffer_.setStorage(Mn::GL::RenderbufferFormat::DepthComponent24,
framebufferSize);
frameBuffer_.attachRenderbuffer(
Mn::GL::Framebuffer::BufferAttachment::Depth, depthBuffer_);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you actually need the depth buffer? Isn't it only rendering a single textured triangle, no matter what kind of data gets visualized?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need a depth buffer later if I would like to "interact“. For example, if I would like to select and highlight some objects. ( I need to draw these objects on top of this framebuffer.)

Comment thread src/esp/gfx/SensorInfoVisualizer.cpp Outdated
Comment on lines +58 to +69
void SensorInfoVisualizer::blitRgbaToDefault() {
frameBuffer_.mapForRead(Mn::GL::Framebuffer::ColorAttachment{0});
CORRADE_ASSERT(
frameBuffer_.viewport() == Mn::GL::defaultFramebuffer.viewport(),
"SensorInfoVisualizer::blitRgbaToDefault(): a mismatch on viewport"
"between current framebuffer and the default framebuffer.", );

Mn::GL::AbstractFramebuffer::blit(
frameBuffer_, Mn::GL::defaultFramebuffer, frameBuffer_.viewport(),
Mn::GL::defaultFramebuffer.viewport(), Mn::GL::FramebufferBlit::Color,
Mn::GL::FramebufferBlitFilter::Nearest);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, is the extra framebuffer needed at all? Since the only use of this visualizer is in the viewer, where you do want to have it in the default framebuffer anyway.

I'd personally just have draw() take the target framebuffer it renders to and that's it. I don't see a reason why there would need to be yet another render target for visualization operations.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comments below.

Comment thread src/esp/gfx/DepthVisualizerShader.cpp Outdated
setUniform(uniformLocation("depthTexture"), DepthTextureUnit);

depthUnprojectionUniform_ = uniformLocation("depthUnprojection");
CORRADE_INTERNAL_ASSERT(depthUnprojectionUniform_ != ID_UNDEFINED);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember we discussed this once already, but I feel like it needs repeating -- ID_UNDEFINED is Habitat-specific, it has no relation to Magnum, and if it ever changes for some reason, the assert will no longer work.

If you want to avoid using the -1 because "coding guidelines forbid magic constants" etc, I'd suggest doing this instead:

CORRADE_INTERNAL_ASSERT(depthUnprojectionUniform_ >= 0);

Same elsewhere.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.
I did not remember we discussed this before.

Comment thread src/esp/gfx/DepthVisualizerShader.h Outdated

#ifndef ESP_GFX_DEPTHVISUALIZERSHADER_H_
#define ESP_GFX_DEPTHVISUALIZERSHADER_H_
#include <Corrade/Containers/EnumSet.h>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include not used here :)

Comment thread src/esp/gfx/SensorInfoVisualizer.h Outdated
@brief helper class to visualize undisplayble info such as depth, semantic ids
from the sensor on a framebuffer
*/
class SensorInfoVisualizer {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just SensorVisualizer?

Comment thread src/esp/sensor/VisualSensor.cpp Outdated
Comment on lines +38 to +46
// setup the shader
Mn::Resource<Mn::GL::AbstractShaderProgram, gfx::DepthVisualizerShader>
shader = visualizer.getShader<gfx::DepthVisualizerShader>(
gfx::SensorInfoType::Depth);
shader->bindDepthTexture(renderTarget().getDepthTexture())
.setDepthUnprojection(*depthUnprojection())
.setDepthScaling(depthScaling);
// draw to the framebuffer
visualizer.draw(shader.key());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the API design here. Why do you first retrieve a shader resource to set its uniforms but then pass just a resource key to draw() so draw() has to fetch the same shader resource again (and then do additional error handling)? Why not have just a draw(GL::AbstractShaderProgram&)? Or maybe even just expose the mesh and call a draw() on it directly from here.

Honestly the more I look at it, the less useful the overly generic SensorVisualizer seems to be. I'd make it actually aware of what it's doing instead of splitting the responsibilities partly between this class and the SensorVisualizer. That way:

  • it can create the shader on-demand and set the uniforms on its own, exposing a simpler API to be used from VisualSensor
  • thus you don't need to do the direct shader setup here (this is not a good place for such low-level stuff I'd say)
  • and so you don't need the complex templated getShader() getter and neither the resource manager nor any of that stuff, you can just have the shader(s) one by one directly in the class as the class is fully aware of what it can and cannot do

Copy link
Copy Markdown
Contributor

@Skylion007 Skylion007 Feb 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, this should be more tightly integrated with Sensor. What's the benefit of this class? It really seems like we need a way to simplify shader setup so that it can be easily reused from VisualSensor.

Furthermore, doing it like that would make it easier to expose to Python for example. This feels like it would be a pain to expose to the Python viewer.

Copy link
Copy Markdown
Contributor Author

@bigbike bigbike Feb 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To both of you: @mosra @Skylion007
Why I need this class?
I need a container to hold the framebuffer, renderbuffer, shaders, the big triangle (GL mesh) as a canvas etc. These resources which are necessary to do the visualization. I tried to put them in the VisualSensor. Then VisualSensor becomes too much and too ugly. Also I hope this infra can be used in the NonVisualSensor in the future.

Why I need the framebuffer and renderbuffer?
If it is only for "visualization", yes, the default framebuffer is good enough. But if I need "interaction", it is not enough. For example, I am thinking it would be awesome if we can "select and highlight" some objects even in the depth or semantic visualization.
(I know I need to further change the shader in this case.)

Now answer the questions separately:

@mosra:

it can create the shader on-demand

Constructing a shader is expensive. I prefer to keep it once it is constructed. So I need the ResourceManager either in this class or in the VisualSensor. I prefer to do it in this SensorVisualizer class so that later after we have developed the NonVisualSensor I can use it as well.

I don't understand the API design here.
maybe even just expose the mesh

Yes, when I coded it up, I wished to call shader->draw() directly. But I found I needed that mesh which was NOT exposed (and I do not want to). I was thinking retrieve this shader via key was cheap to do.

draw(GL::AbstractShaderProgram&)

OK, I can do it.

you can just have the shader(s) one by one directly in the class as the class is fully aware of what it can and cannot do

Putting all the shaders (there might be a lot in the future) one by one in which class, the VisualSensor class? It would become very ugly. I guess I do not understand you completely, do I? Hope you can explain a bit further.

Also I do not want to hold a copy of these shaders in every sensor. I need a container class to manage these shaders for me. So I have this SensorVisualizer class.

the complex templated getShader()

Not just for depth, semantic info, I am paving the way for the future, e.g., force, lidar, etc. Any info that is not directly displayable, but has certain ways to be visualized.

@Skylion007 :

What's the benefit of this class?

See my above comments on why I need this class.

simplify shader setup so that it can be easily reused from VisualSensor.

I believe this is a clean way in order NOT to mess up with the VisualSensor.

doing it like that would make it easier to expose to Python for example

You will have to expose the entire SensorVisualizer class simply because it appears as 1 input parameter in the argument list of this function visualizeObservation in VisualSensor?

Comment thread src/shaders/depth.frag Outdated
Comment on lines +36 to +38
highp float r = originalDepth / depthScaling;
highp float b = 0.5 - 0.5 * r;
fragmentColor = vec4(r, r, b, 1.0);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest to use colormaps for better visualization output -- DebugTools::ColorMap. Especially the Turbo color map is tuned to make minor differences nicely visible and the output uniformly bright.

Further, to make it more generic, I'd name it a "color map transform" instead of "depth scaling", as that allows you to later use the same API for semantic ID visualization as well. See how MeshVisualizer does it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool stuff. I will study it.

Copy link
Copy Markdown
Contributor Author

@bigbike bigbike Feb 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mosra: By the way, I understand you can apply such ColorMap in magnum built-in MeshVisusalizer shader.
Question: how can I apply it to my customized shader??

Comment thread src/shaders/depth.frag Outdated
Comment on lines +30 to +32
#ifdef DEPTH_VISUALIZATER
highp float
#endif
Copy link
Copy Markdown
Contributor

@mosra mosra Feb 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel these #ifdefs are a bit excessive. Declare the variable once above for both cases so you don't need to do this.

Thinking more about this, I feel like two completely unrelated shaders are mashed up together. They're not even exposed through the same C++ class. Since this is for an interactive visualization and not ML, one extra pass won't matter, so what about

  1. unprojecting depth as usual using the original shader
  2. then have a shader that's dedicated just to visualization, which then processes the unprojected depth

That way the VisualizerShader doesn't need the depth-related inputs and can be thus easily adapted to visualizing any other rendered data, such as semantic IDs.

Copy link
Copy Markdown
Contributor Author

@bigbike bigbike Feb 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a shader that's dedicated just to visualization

I used to have a dedicated shader. But it is different from what you suggested here -- dedicated to process the unprojected depth. My shader will still do both depth unprojection + processing like it does here. And then I thought you might say, well why not leverage the existing depth.frag to avoid code duplication?? Clearly my guess took the wrong direction this time. :)

Well, I guess the focus is that do I need the depth-related input?
Answer: it depends whether I need "visualization ONLY" or "visualization + interaction".
Your comments is based on the former, while I was thinking the latter.
I am actually considering adding depth in later (directly read from the depthRenderTexture and output to the gl_FragDepth) since it gives me the ability for "interaction".

@mosra
Copy link
Copy Markdown
Contributor

mosra commented Feb 2, 2021

I feel like I already spent more time on the review here than it would take to implement a TextureVisualizer shader directly in magnum, so just in short -- this is how I think it should work:

  1. Render everything the usual way into a framebuffer X, with depth unprojected to a texture from a depth buffer. Or render just the depth, because the color will get discarded anyway.
  2. Draw a full-screen quad into the same framebuffer X, visualizing the depth using a color map, only color, with depth test disabled.

That way you:

  • end up with a framebuffer that has the depth visualized but also the depth correct, which satisfies your point about "interaction",
  • don't need to do anything special with gl_FragDepth,
  • don't need a whole new Visualizer class with yet another color and depth framebuffer and all the scaffolding and documentation and testing around, as this is reusing what's already there,
  • don't need to conflate the depth unprojection shader with visualization (seriously, from a design perspective, it just doesn't make sense to conflate the two, it will only cause problems down the road),
  • and everything is much more modular and easier to extend -- if, let's say, a semantic ID visualization needs to be done (and it can't be done with MeshVisualizer for some strange reason), it means rendering just another fullscreen triangle with another custom shader to the same color buffer, without the shader again having to take care of depth unprojection or writing to gl_FragDepth

@bigbike bigbike mentioned this pull request Mar 12, 2021
11 tasks
@bigbike
Copy link
Copy Markdown
Contributor Author

bigbike commented Apr 3, 2021

Dear reviewers:
The diff is ready to be reviewed again. Code has been significantly refactored based on the reviewers' comments.
In this version the Magnum::ColorMap is adopted and the parameter is fine tuned.
See the results (depth sensor) as follows:
rgb-depth-1
fisheye-depth-1

@bigbike bigbike mentioned this pull request Apr 3, 2021
11 tasks
@bigbike
Copy link
Copy Markdown
Contributor Author

bigbike commented Apr 7, 2021

@mosra: This is diff is ready to be reviewed again.

Copy link
Copy Markdown
Contributor

@mosra mosra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found just two very minor things.

I was wondering if it would be useful to allow the user to change the colormap used, but maybe do that only when someone really needs that. Turbo is a good default.

Comment thread src/esp/gfx/TextureVisualizerShader.cpp Outdated
Comment on lines +22 to +24
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = OUTPUT_ATTRIBUTE_LOCATION_COLOR)
#endif
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could do without the #ifdef EXPLICIT_ATTRIB_LOCATION around -- in Magnum that's only for compatibility with GL 2.1 / ES2 / WebGL 1, which you don't care about here.

(Also remove the related addSource() in the shader cpp, of course.)

bigbike and others added 2 commits April 12, 2021 22:26
@bigbike bigbike merged commit 6b6594e into master Apr 13, 2021
@bigbike bigbike deleted the depth-viz branch April 13, 2021 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Do not delete this pull request or issue due to inactivity.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants