-
Notifications
You must be signed in to change notification settings - Fork 29
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
Add backend logic for bounding box plots #5250
Conversation
x_max: number | ||
y_min: number | ||
y_max: number | ||
} |
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 based the output off of iterative/dvclive#766 (comment) description, but this will most likely change and we will need to adjust accordingly.
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.
Updated to top
, bottom
, right
, left
based off the top-left corner after a discussion with Alex (see his comment):
@@ -112,6 +116,10 @@ export class PlotsModel extends ModelWithPersistence { | |||
PersistenceKey.PLOTS_COMPARISON_MULTI_PLOT_VALUES, | |||
{} | |||
) | |||
this.comparisonClassesSelected = this.revive( |
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.
this.comparisonClassesSelected
keeps track of what class labels (and by extension, the boxes) are on/off.
extension/src/plots/model/collect.ts
Outdated
const acc: ComparisonPlotClasses = {} | ||
|
||
for (const id of selectedRevisionIds) { | ||
for (const path of paths) { |
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.
What I meant by removing nested loops was not moving one inside of another function (I know realize we have 4 nested loops total), but can we change the code to drop some or at least make them more performant.
I see that in the fourth nested loop, we check for selected state. Maybe for a start, we could loop only through paths
that are also in comparisonClassesSelected
, since we only care about labels in comparisonClassesSelected[path]
.
There might be other optimizations to be made as well.
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.
What I meant by removing nested loops was not moving one inside of another function (I know realize we have 4 nested loops total), but can we change the code to drop some or at least make them more performant.
Thanks for the clarification!
I see that in the fourth nested loop, we check for selected state. Maybe for a start, we could loop only through paths that are also in comparisonClassesSelected, since we only care about labels in comparisonClassesSelected[path].
comparisonClassesSelected
only holds classes that the user has toggled directly, it doesn't hold every visible class. Unless I'm missing something, we can't rely on it to detect which paths need to be looped over to collect classes.
The four loops are:
- looping over each revision (up to 7)
- looping over each path (could be a lot depending on how many plots the user has)
- looping over each path img (will usually be just one, unless it's an image step plot)
- looping over each class, gathering only the selected class boxes
I don't see a way to get rid of the first three loops, since for us to gather a plot's classes, we need to check for classes in each single image and collect the data. Atleast the third loop will likely just be a single item array.
We could get rid of the fourth loop by passing all image box data to the frontend and have ComparisonTableBoundingBoxImg
filter out the boxes, but that means passing larger arrays that the component needs to loop over. Seems like we would want to limit the data passed to the frontend as much as possible but please correct me if I'm wrong.
Another alternative is having collectSelectedComparisonPlots
gather class information along with the selected comparison plot data. I chose not to do this since that would make the function more complex, but if we think it's worth it for performance, I'm alright with doing that.
TL;DR
I'm not seeing a good way to get rid of any of our four loops. I'll review the loops and see what we can do to optimize them for now.
@sroy3, what do you think?
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.
Reviewed optimizing the loops! Instead of looping over every revision/path in comparisonData
, we could start by looping over the already formed plots
array. The plots
already have a collected classDetails
that tells you if a path has selected classes or not, lettings us skip early. Aka:
- looping over each
plots
path (skip over ifclassDetails
is empty) - looping over each revision
- looping over each path img (will usually be just one, unless it's an image step plot)
- looping over each class, gathering only the selected class boxes
Not seeing any other optimizations besides the two ideas I mentioned in my previous comment (have collectSelectedComparisonPlots
collect box data, have ComparisonTableBoundingBoxImg
do the box filtering).
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.
One thing I'm seeing is this:
if (selectedState === false) {
continue
}
if (imgClasses.length === 0) {
return
}
Why do we have to go through four loops to return nothing when we know it before hand? I would make this the first loop (the selected classes).
If there are no other possible optimizations, then I guess it's fine to keep as is.
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.
if (selectedState === false) {
continue
}
Why do we have to go through four loops to return nothing when we know it before hand? I would make this the first loop (the selected classes).
Aren't we already getting the selected classes in the first loop? In fact the line if (selectedState === false) {...
no longer exists in the code. Did Github direct you the latest changes or is there something I'm missing here?
One thing I'm seeing is this:
if (imgClasses.length === 0) {
return
}
Good catch on if (imgClasses.length === 0) {...
! I didn't spot the fact that we might be needlessy looping over selected classes if an img has no attached boxes to begin with. I'll update the code to check for img.boxes
before running getSelectedImgComparisonPlotClasses
.
extension/src/plots/model/index.ts
Outdated
} | ||
|
||
return collectSelectedComparisonPlotClasses({ | ||
comparisonClassesSelected: this.getComparisonClassesSelected(), |
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.
If this is empty, should this return {}
as well? This would avoid having to loop through selectedRevisionIds
for nothing.
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.
On further inspection, comparisonClassesSelected
is only going to hold classes that the user has deselected/selected, it doesn't hold every visible class so we are unable to use it as a check for if there are selected classes or not.
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.
This variable probably needs to be renamed to avoid confusion. Maybe something like comparisonClassesSelectedState
🤔
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.
But then it's calling collectSelectedComparisonPlotClasses
, which should return and empty object anyway, no? If we avoid calling it and the looping when we can, it's a good thing.
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.
No, collectSelectedComparisonPlotClasses
could still return selected class boxes. Currently, we select all classes in a plot by default and comparisonClassesSelected
only gets updated with a class (setting it as true or false) when a user clicks directly on a class button. Hope that makes sense :)
extension/src/plots/model/index.ts
Outdated
} | ||
|
||
return collectSelectedComparisonPlotClasses({ | ||
comparisonClassesSelected: this.getComparisonClassesSelected(), |
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.
But then it's calling collectSelectedComparisonPlotClasses
, which should return and empty object anyway, no? If we avoid calling it and the looping when we can, it's a good thing.
extension/src/plots/model/collect.ts
Outdated
const acc: ComparisonPlotClasses = {} | ||
|
||
for (const id of selectedRevisionIds) { | ||
for (const path of paths) { |
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.
One thing I'm seeing is this:
if (selectedState === false) {
continue
}
if (imgClasses.length === 0) {
return
}
Why do we have to go through four loops to return nothing when we know it before hand? I would make this the first loop (the selected classes).
If there are no other possible optimizations, then I guess it's fine to keep as is.
Going to go ahead and merge this and #5305 into our first PR since they've been sitting for a while and are approved. |
main
<- #5227 <- #5241 <- this <- #5305This PR updates our comparison dvc output fixture with bounding box info and updates the backend accordingly. It also adds logic for a user toggling boxes on/off.