-
-
Notifications
You must be signed in to change notification settings - Fork 596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modifying the landmark mapper loop to support hybrid mappings #360
Modifying the landmark mapper loop to support hybrid mappings #360
Conversation
Hi @omar-h-omar, Well spotted with the function I was just thinking how to best do this, as it would indeed be nice to "revive" this function so we don't have this loop duplicated in several places. I don't think it's trivial: Sometimes we want the function to just return Let me know what you think - I think I might be leaning towards my last idea. On another note, in your loop, there's |
Thinking about this some more, maybe putting it all in It also quite clearly expresses what we actually want - get the vertex index, either directly or indirectly, through whatever means needed (the caller doesn't care about that - they just want the vertex index). |
Hi @patrikhuber, Actually my initial approach was to modify the I think creating About the landmark conversion loop, I was unsure if we want to raise an exception if the user didn't map the landmarks directly and no landmark definitions were found? Currently |
…get_vertex_index in the fitting functions
I've added |
include/eos/fitting/fitting.hpp
Outdated
template <class T> | ||
inline auto get_corresponding_pointset(const T& landmarks, const core::LandmarkMapper& landmark_mapper, | ||
const morphablemodel::MorphableModel& morphable_model) | ||
inline std::optional<int> get_vertex_index(const std::string landmark_name, const core::LandmarkMapper& landmark_mapper, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cpp17::optional
const auto converted_name = landmark_mapper.convert(landmark_name); | ||
if (!converted_name) | ||
{ // no mapping defined for the current landmark | ||
return std::nullopt; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now we want to stick with eos's cpp17::nullopt
for these. I think for one of the next releases, I'd like to move to C++17 as a minimum, then we can drop all the std/cpp17 "magic" for optional
, but for now, let's keep it compatible with C++14.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see I'll make the change now
include/eos/fitting/fitting.hpp
Outdated
inline auto get_corresponding_pointset(const T& landmarks, const core::LandmarkMapper& landmark_mapper, | ||
const morphablemodel::MorphableModel& morphable_model) | ||
inline std::optional<int> get_vertex_index(const std::string landmark_name, const core::LandmarkMapper& landmark_mapper, | ||
cpp17::optional<std::unordered_map<std::string, int>> landmark_definitions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we probably want to pass the landmark_definitions
as a reference too, so just add const ... &
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity what is the benefit behind having it as a reference here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good question and I was actually trying to google it before making my earlier comment. If std::optional
is a "pointer type" (i.e. it holds a pointer to the underlying data, and when you pass it around, even by value, only the address/pointer is passed around), then we might as well pass it by value (without the const ... &
), as it will then make no difference whether we pass around a pointer (32/64 bit) or a reference (32/64 bit). However if all the data is stored in the optional
object directly, then we might potentially copy around a big/heavy object if we pass by value (without the const &
).
I think optional
would be a pointer type... but I couldn't really find out. So to be safe I'd just pass it by const&
, even if perhaps not needed, I don't think it has a drawback either. If you know more than me, I'm happy to leave it! :-D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation, I can see where the issue is. Using const&
makes sure we avoid passing the whole object if optional
isn't a pointer. It's really interesting how C++ handles these situations. I haven't encountered anything like it in other languages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, that's exactly it. I think it's quite cool how C++ allows you to make these choice, as it's useful if performance is relevant. And I think this is a function where we definitely don't want to pass any heavy objects (though, as I said, assuming optional is a pointer type, which I think it is, passing by value would be equally fine here).
I think in general, passing by value is a good default too, and if I remember correctly, it often allows better optimisations by the compiler. It can get quite messy too though - see e.g. https://twitter.com/herbsutter/status/1422644603883192320/photo/1. I think there's a couple of talks from Herb Sutter where he explains this, but I don't remember which one exactly.
…ions as a reference
I think this looks great, thank you Omar!
Good question. So if I understand correctly, this would happen if the user uses a landmarking scheme where say a landmark_name would be "nose.outer_corner" or something, but then a particular model's landmark_definitions wouldn't contain that specific landmark. Is that correct? I think I'd lean towards also just returning a |
Yes that's exactly the scenario I was thinking of. My main worry was just that some users may not be aware of the skipping. I can see how the skipping can be useful as well. How would you feel about raising a runtime warning before returning a |
How could a user not be aware of the skipping though, since we return an optional in If I understand you correctly, you'd propose a |
I was actually thinking of users using eos from python. They may not be aware of the skipping since For the warning, I was actually thinking of raising it similar to how it's done in python. However after some googling, it looks like that isn't an option in C++. So in this case, you're right. Let's just return a |
Good point with calling it from Python. Indeed it could get unnoticed in that scenario, and probably cause a lengthy debugging session for someone. But I don't have a good idea how we could make it better, except for just making sure the documentation is good. Indeed C++ doesn't have any mechanism for warnings. |
include/eos/fitting/fitting.hpp
Outdated
continue; | ||
} | ||
} else | ||
std::optional<int> vertex_idx = get_vertex_index(landmarks[i].name, landmark_mapper, morphable_model.get_landmark_definitions()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@omar-h-omar One more std::
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd also add a const
here.
include/eos/fitting/fitting.hpp
Outdated
// Todo: This might be worth mentioning in the function documentation of fit_shape_and_pose. | ||
int vertex_idx; | ||
if (morphable_model.get_landmark_definitions()) | ||
std::optional<int> vertex_idx = get_vertex_index(landmarks[i].name, landmark_mapper, morphable_model.get_landmark_definitions()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another std here, plus can add const.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops sorry missed those
Looks great, thank you! Merging. |
Yeah It's a shame. I don't know how to make it better as well though. Hopefully, the documentation should be enough :) |
@patrikhuber I've modified one of the fitting loops to allow for "hybrid" mappers where some points are mapped from landmark identifier to landmark definitions and others are mapped directly. If you're happy with the change below, I will apply those changes to the other fitting functions. I was actually thinking of modifying the function
get_corresponding_pointset
which seems to contain a simple landmark conversion loop and is currently not being used. What do you think?