-
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 bounding boxes plot frontend components #5227
Conversation
} | ||
|
||
const boundingBoxImgCoords: { [rev: string]: ComparisonPlotBoundingBoxes } = { | ||
'exp-83425': [ |
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.
Similar blocks of code found in 3 locations. Consider refactoring.
{ boxes: [{ h: 75, w: 100, x: 100, y: 100 }], label: 'traffic light' }, | ||
{ boxes: [{ h: 30, w: 30, x: 190, y: 310 }], label: 'car' } | ||
], | ||
'exp-e7a67': [ |
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.
Similar blocks of code found in 3 locations. Consider refactoring.
{ boxes: [{ h: 100, w: 100, x: 100, y: 100 }], label: 'traffic light' }, | ||
{ boxes: [{ h: 30, w: 30, x: 190, y: 310 }], label: 'car' } | ||
], | ||
'test-branch': [ |
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.
Similar blocks of code found in 3 locations. Consider refactoring.
export type ComparisonPlots = { | ||
path: string | ||
boundingBoxLabels: ComparisonBoundingBoxLabels | ||
revisions: ComparisonRevisionData |
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've added two values to the PlotsComparisonData
. ComparisonBoundingBoxClasses
inside of ComparisonPlots
(holds the data on all classes in that plot/image row) and ComparisonPlotBoundingBox
(holds the class/coordinates for all boxes in each single image).
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.
Currently, I was thinking that our backend would manage the "selected" state of the classes, but we could also manage that in redux instead.
webview/src/plots/components/comparisonTable/ComparisonTableRow.tsx
Outdated
Show resolved
Hide resolved
] | ||
} | ||
|
||
export const addBoundingBoxes = ( |
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've created a util in the frontend that adds the bounding boxes to a comparison fixture for testing and storybooks. Once we've got the backend part of this working (iterative/dvc#10198), this will all be removed and bounding box data will be part of our original comparison fixture.
PlotsComparisonData | ||
} from 'dvc/src/plots/webview/contract' | ||
|
||
const boxColors = [ |
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.
Looking at other yolo
and other bounding box examples, they seem to rely on a variation of a Tableau color palette. I've grabbed the set of colors that yolo
uses for the boxes for now.
import { ComparisonTableBoundingBoxColorFilter } from './ComparisonTableBoundingBoxColorFilter' | ||
import styles from '../styles.module.scss' | ||
|
||
export const ComparisonTableBoundingBoxImg: React.FC<{ |
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.
Similar blocks of code found in 2 locations. Consider refactoring.
webview/src/plots/components/comparisonTable/ComparisonTableRow.tsx
Outdated
Show resolved
Hide resolved
/> | ||
<label | ||
className={styles.boundingBoxClassesButton} | ||
style={{ background: color }} |
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.
Can we ever get a background color on which to foreground color makes the text not accessible? If so, we can maybe calculate whether it should use a dark or light foreground color.
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.
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.
Yes, apart from the neon green they all look fine. And I don't think there is anything we can do about that neon green anyway.
{plotImg.url && plotImg.boundingBoxes ? ( | ||
<ComparisonTableBoundingBoxImg | ||
src={plotImg.url} | ||
boxCoords={plotImg.boundingBoxes} |
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.
Instead of passing down complex objects (here and further up), can we just pass down an id and go grab the coordinates where they are used instead?
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.
Good point on the fact that we're passing down complex objects! I'll look into moving the bounding box data into an object that ComparisonTableBoundingBoxImg
could grab from.
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 I understood the code correctly, plotImg.boundingBoxes
is not too big of an object and only one level down, you could also pass each property from it separately using the spread operator.
webview/src/plots/components/comparisonTable/styles.module.scss
Outdated
Show resolved
Hide resolved
webview/src/plots/components/comparisonTable/ComparisonTableRow.tsx
Outdated
Show resolved
Hide resolved
WithBoundingBoxes.args = { | ||
data: { | ||
...defaultPlotsData, | ||
comparison: addBoundingBoxes(comparisonPlotsFixture) |
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.
[Q] Why not add these for a single image and have them in the base fixture?
I can see this is the plan
)} | ||
<image href={src} width={naturalWidth} height={naturalHeight} /> | ||
{boxCoords.map(({ label, boxes }) => { | ||
const labelColor = classes[label].color |
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.
[Q] What happens if there is no color found?
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.
[Q] Can you clean up the names? i.e. boxCoords
and classes
. When props are getting drilled down they should have a consistent name the whole way through.
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.
[Q] What happens if there is no color found?
Good catch! I've updated the code to not create any boxes if it can't find a color so nothing breaks. We could also still render the boxes with a default grayish color 🤔
[Q] Can you clean up the names? i.e. boxCoords and classes. When props are getting drilled down they should have a consistent name the whole way through.
Done!
const labelColor = classes[label].color | ||
return boxes.map(({ h, w, x, y }) => ( | ||
<React.Fragment key={label + h + w + x + y}> | ||
<text |
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.
With the current design, I don't think we need this text as we are repeating a lot of information:
I.e. there is a big box that shows traffic light objects are yellow. I would drop this text unless we include the threshold along with the text.
Once there are a lot of boxes this will become even more crowded:
WDYT @dberenbaum?
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.
Let's also consider how we can filter the boxes by a provided threshold (i.e. "Objects detected with prediction threshold >= 0.5")
Doesn't have to be implemented as part of this PR but the data structure/UI will probably need to support it.
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.e. there is a big box that shows traffic light objects are yellow. I would drop this text unless we include the threshold along with the text.
Once there are a lot of boxes this will become even more crowded
Good point! I chose to add labels since all classes toggle buttons may not be showing (if there are too many, we're going to add a "Show More" button that shows the hidden classes).
No strong preference though, I'm happy to remove them!
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.
@AlexandreKempf, what do you think about keeping text labels on each individual box or would it be better to remove them?
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.
Working on object detection, we get used to overlapping text ^^", so we can keep them it is not a problem.
In my opinion, the best behavior would be to have the image with all the labels when not hovered. When hovered, only the bounding box below the mouse gets its text displayed. It is pretty tricky to get which bounding boxes to select based on mouse location, especially with lots of overlapping cases. So generally, what we end up doing is filtering the bounding boxes based on label or score ;)
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.
In my opinion, the best behavior would be to have the image with all the labels when not hovered. When hovered, only the bounding box below the mouse gets its text displayed. It is pretty tricky to get which bounding boxes to select based on mouse location, especially with lots of overlapping cases.
Sounds good! I'll add this feature to our todo list.
Something else that we will need to revisit/implement: How are bounding boxes displayed on/with a zoomed image? In VS Code we currently open the image in an editor and that is it. |
rev: string | ||
classes: ComparisonPlotClass[] | ||
}[] = [ | ||
{ |
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.
Similar blocks of code found in 4 locations. Consider refactoring.
], | ||
rev: 'exp-83425' | ||
}, | ||
{ |
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.
Similar blocks of code found in 4 locations. Consider refactoring.
], | ||
rev: 'exp-e7a67' | ||
}, | ||
{ |
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.
Similar blocks of code found in 4 locations. Consider refactoring.
], | ||
rev: 'main' | ||
}, | ||
{ |
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.
Similar blocks of code found in 4 locations. Consider refactoring.
Images shown by Matt on your work looks super nice ! Congrats 🎉 |
@@ -310,10 +332,26 @@ export class PlotsModel extends ModelWithPersistence { | |||
) | |||
} | |||
|
|||
public toggleComparisonClass(path: string, label: string, selected: boolean) { |
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.
Similar blocks of code found in 2 locations. Consider refactoring.
Code Climate has analyzed commit 511dfea and detected 3 issues on this pull request. Here's the issue category breakdown:
The test coverage on the diff in this pull request is 95.0% (85% is the threshold). This pull request will bring the total coverage in the repository to 95.3% (0.0% change). View more on Code Climate. |
export type ImagePlotOutput = { | ||
revisions: string[] | ||
type: PlotsType | ||
url: string | ||
annotations?: { [label: string]: BoundingBox[] } |
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.
DVC spec has been changed from boxes
to annotations
. I've updated the code accordingly.
Will get back to this later |
main
<- this <- #5241 <- #5250 <- #5305 <- #5313Demo
https://github.com/iterative/vscode-dvc/assets/43496356/68bf681b-d6e3-4069-818f-d3d5d2052633Screen.Recording.2024-02-15.at.11.49.48.AM.mov
To Do
A start to #4917