Skip to content

Track multi-confidence filtering/displaying#460

Merged
BryonLewis merged 23 commits into
masterfrom
client/confidence-sort
Feb 25, 2021
Merged

Track multi-confidence filtering/displaying#460
BryonLewis merged 23 commits into
masterfrom
client/confidence-sort

Conversation

@BryonLewis
Copy link
Copy Markdown
Collaborator

@BryonLewis BryonLewis commented Nov 25, 2020

Fixes #423

Old style didn't properly support multiple types on a single track and swapping between them based on the selected types and the confidence filter adjustments.

  • Added a local variable currentConfidencePair to indicate the index of the sorted confidence pairs that should be displayed.
  • Within useTrackFilters I modified the filtering function to set the currentConfidencePair when it is visible and above the confidenceThreshold. Let me know how you feel about having a side effect inside of .some.
  • Returned the confidencePairs[currentConfidencePair] when using track.getType() so that it will render the proper type when displayed in AnnotationLayers, and trackList.
  • Updated useEventChart and useLineChart to utilize the track.getType() function instead of the confidencePairs[0][0] so they would reflect the proper type as well.
  • Added some tests to the track.spec.ts to test setting the currentConfidencePair, as well as changing the current type.

Old Version:
oldConfidenceSort
New Version:
newConfidenceSort

Questions :
(made a determination that these questions will be placed in a new PR or as new issues, I just want to fix the fundamental problem first in a smaller PR. Feel free to add comments)

  • Determine what we want to do when deleting tracks by type? Do we delete the type from the track, or do we delete the whole track?
  • Should we display all types as text in the upper right corner? In the older version of VIAME-Web all types were displayed as a list in the upper right corner of the annotation. This can be done with some minor modifications.

@BryonLewis BryonLewis marked this pull request as ready for review November 25, 2020 18:54
@subdavis
Copy link
Copy Markdown
Contributor

Determine what we want to do when deleting tracks by type? Do we delete the type from the track, or do we delete the whole track?

Delete type until only 1 remains, then delete track (IMO).

Should we display all types as text in the upper right corner? In the older version of VIAME-Web all types were displayed as a list in the upper right corner of the annotation. This can be done with some minor modifications.

Yeah I think we should. It would be nice to stack them instead of side-by-side, I think.

Copy link
Copy Markdown
Contributor

@subdavis subdavis left a comment

Choose a reason for hiding this comment

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

I had to go pause and have a plate of cold thanksgiving while I thought about this.

the problem

The problem is communicating a new bit of state to consumers of the filteredTracks computed prop. Places that use this prop need to know something new: "What is the confidence pair in this track that caused it be included in this list". Before, that was implicit. There was only 1 possibility. Now there are several.

You've implemented this by sort of stashing this information inside the track object.

I object to that for a couple reasons:

  • It improperly decouples filteredTracks from the currentConfidencePair. You don't care about currentConfidnecePair as context unless you already have a dependency on filteredTracks.
  • It adds new state/methods to the track API that aren't really related to a track itself.
  • Side effects.

Alternative.

It seems to me like the best outcome would be to change the shape of filteredTracks to include this new bit of information.

const filteredTracks: Ref<{
  track: Track,
  context: {
    confidencePairIndex: number;
  };
}[]>

This involves either changing the interface in a bunch of places or having another computed property that strips out just the track part for interfaces where the context isn't needed. IMO it would be best to just change the interface everywhere.

Naturally the enabledTracks interface will change also. I think that's fine. This new context is nice because any process that needs to explain why it filtered something can just tack on new state, and modules that accept a filtered list can specify which explanations they need.

In the short term, if you want to just declare this interface FilterItem {} at the top of useTrackFilters and import it from everywhere, that's perfectly fine with me.

If you would like me to do the tedious chore of changing the interface everywhere, I am happy to do so.


You could also "re-derive" this information in-place whereever you need it. If you have a track, and you have the list of enabledTypes, you can easily figure out which of a track's types caused its inclusion. However, this does involve subtly duplicating some logic which could lead to bugs. I don't recommend this option.


Another thought: we could actually formalize a "context" object for tracks that our various filtering processes could use to stash information like this. The trouble with that is I don't know where the scope of that context ends. Should we actually be keeping a list of IDs for the checkboxes, or should there just be a "checked" context var? Should we have a "selectedTrackId" state, or a selected context var?

We could make sure that the context object was side-effect safe. I don't know if that would be better than the above proposal.

Comment thread client/src/track.ts Outdated
setType(trackType: string, confidenceVal = 1) {
const old = this.confidencePairs;
this.confidencePairs = [[trackType, confidenceVal]];
this.currentConfidencePair = 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 believe this is out-of-scope for the concerns of track.ts. This is display/visualization state. It would be like adding an isSelected or isChecked property. This causes the UI's display specification to leak into the track API.

Comment thread client/src/use/useTrackFilters.ts Outdated
));
.some(([confkey, confval], index) => {
if (confval >= confidenceThresh && checkedSet.has(confkey)) {
track.setCurrentConfidencePair(index);
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.

Side effects inside computed functions should really be avoided.

This is especially dicey because parts of the track state are reactive, parts aren't and this could easily cause an infinite loop, so the implementation details of this function are critical to whether or not this code works.

@subdavis
Copy link
Copy Markdown
Contributor

Thought about it some more today.

If you want to do something easier like a dedicated track context, what about this?

class Track {
  /* Disclaimer about usage and reactivity */
  context: Map<string, string | number>;
}

I don't think methods to get/set the map are necessary. just track.context.get('key') and track.context.set('key', 'value')

The map sort of guarantees it's not reactive, and the lack of methods guarantees we aren't bumping the notifier.

@BryonLewis
Copy link
Copy Markdown
Collaborator Author

BryonLewis commented Dec 17, 2020

Getting back into this I want to attempt to accurately summarize this and the possible implications.

Summarize the highlights:

  • useTracks has been changed to useFilteredTracks and has a structure indicating the confidencePairIndex which is the current confidence pair that should be visible based on the confidence filter and the visibility of the types.
  • To accommodate this the track.getType() function now takes in an argument being the index. This allows the current ConfidencePair index to be shown based on the useFilteredTracks.
  • In LayerManager the dataTrack item has been modified to provide the trackType which is the current type of the track provided by useFilteredTracks, and confidencePairs has been changed to represent the list of confidencePairs, I think it did that originally but at some point it was swapped to just the first confidencePair in the list. I think these variable names make more sense. You have the full list of confidencePairs and the currentPair provided by trackType.
  • The change to FrameDataTrack required updating the rendering code in each of the layers. This is where it could impact subscribers of vue-media-annotator depending on how they are using this
  • The TextLayer has been modified with the capability to draw multiple confidence pairs with the current type being closes to the bounding box and having an indicator. Right now I've disabled drawing additional confidencePairs with the text layer by setting additionalNum=0. I believe we may want the user to be able to specify this because some tracks could have 100+ pairs assoicated with them.
  • There are now helper functions when deleting visible types which will remove confidence pairs from the track. This will also be used when we implement the confidence pair stuff in the TrackDetailsEditor (The old Attributes view for a track).

@BryonLewis BryonLewis linked an issue Feb 11, 2021 that may be closed by this pull request
@subdavis subdavis self-requested a review February 18, 2021 19:59
Copy link
Copy Markdown
Contributor

@subdavis subdavis left a comment

Choose a reason for hiding this comment

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

I can't figure out how to get the viewer to show more than 1 confidence pair. Is there a setting somewher? Enabling / disabling types works as expected (the next highest type appears), but I only ever see one even though the code for multiple is clearly here.

Comment thread client/src/use/useTrackFilters.ts Outdated
Comment thread client/src/components/LayerManager.vue Outdated
const intervalTree = useIntervalTree();
const trackMap = useTrackMap();
const tracksRef = useEnabledTracks();
const filteredTracksRef = useEnabledTracks();
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.

Please call this tracksRef or more specifically enabledTracksRef. filteredTracks means something else already.

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 know this will need to change in a lot of places, but having filteredTracks mean several different things is probably not a good idea.

Comment thread client/src/components/LayerManager.vue Outdated
editingTrack: false | EditAnnotationTypes,
selectedTrackId: TrackId | null,
tracks: readonly Track[],
filteredTracks: readonly FilteredTrack[],
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.

Rename to tracks or enabledTracks.

Comment thread client/src/layers/TextLayer.ts Outdated
y: number;
offsetY?: number;
offsetX?: number;
currentPair?: boolean;
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.

Please add a comment describing what this is.

Also, maybe make this a required property? You're already providing it everywhere, so the code makes no use of the condition where currentPair: undefined, and it's nice to not have to worry about the undefined check if that's not a valid, meaningful state.

Comment thread client/src/layers/TextLayer.ts Outdated
* @returns value or null. null indicates that the text should not be displayed.
*/
function defaultFormatter(track: FrameDataTrack): TextData[] | null {
function defaultFormatter(track: FrameDataTrack, additionalNum = 0): TextData[] | null {
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.

additionalNum is sort of an awkward way of specifying a max value. Does it mean 1 will always be shown, plus some additionalNum?

Could we just make this maxLines or maxPairs and default it to 1? I think that's clearer, then you wouldn't have to do (maxPairs + 1) below (which is odd anyway because it means that maxPairs = 0 will show 1 line)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think restructuring this will make it clearer by moving and renaming maxPairs to maxAdditionalPairs down closer to where it is drawn instead of with the default type:confidence pair.

Comment thread client/src/use/useLineChart.ts Outdated
Comment thread client/src/use/useTrackFilters.ts Outdated
Comment thread client/src/layers/TextLayer.ts Outdated
let pairCount = 0;
if (track.confidencePairs.length) {
for (let i = 0; i < track.confidencePairs.length; i += 1) {
if (pairCount >= maxPairs) {
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 you change maxPairs, this line has to change, but I think it should because this isn't what you'd expect if maxPairs wasn't already off-by-one.

Comment thread client/src/track.ts Outdated
Comment on lines +346 to +348
if (this.confidencePairs[index]) {
return this.confidencePairs[index];
}
Copy link
Copy Markdown
Contributor

@subdavis subdavis Feb 22, 2021

Choose a reason for hiding this comment

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

This doesn't seem right.

if confidencePairs is [['foo', 0.5], ['bar', 0.6]] and I call getType(20000), I'll get ['foo', 0.5]. I think I should get null.

Edit, from above, perhaps this should just raise an out of bounds exception?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I was maintaining parity with the existing getType() which returned the 0 indexed confidencePair if it existed or else it would return null. So I modified it to return the 0 index confidencePair by default if it existed and index was out of scope. Or else it would return null if the confidencePairs were empty.

We can change this but I'll need to review and understand why we had it like this in the first place. I imagine it has something to do with creation or CSV files which don't always contain types.

Comment thread client/src/use/useEventChart.ts Outdated
if (confidencePairs.length) {
const trackType = confidencePairs[0][0];
const type = track.getType(filtered.context.confidencePairIndex);
const trackType = type ? type[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.

This is causing me to question the signature of track.getType()

It should probably always return [string, number] instead of the possibility of a null type, right? Like, if you ask for an out-of-bounds pair, that seems like an invalid execution path that should also throw an error from within the getter.

Why would getType ever be null? Why would you ever have a reference to a confidencePair index that doesn't exist?

@BryonLewis
Copy link
Copy Markdown
Collaborator Author

I can't figure out how to get the viewer to show more than 1 confidence pair. Is there a setting somewher? Enabling / disabling types works as expected (the next highest type appears), but I only ever see one even though the code for multiple is clearly here.

image
Here I set additionalNum to 2. The current value is closest to the box and more opaque than the additional values added. It also has the ** next to it which can be removed or changed.

It should be the default value of additionalNum in the defaultFormatter function. By default we always want to display the highest confidence pair for the text value. I don't know the exact format we want for additional values to be displayed. Do we want the top X values? If it has 200 values and we are filtered down to item number 50, do the top 5 values show any relevance? Should we take the closes X values in distance to the current value. Meaning do we grab x/2 above and below the current value and only below if this is the top value already? I left the display code in there as a beginning for supporting more visually. I y believe this needs to be decided before we activate it as well as allowing users to specify themselves how many additional pairs they see.

BryonLewis and others added 4 commits February 22, 2021 11:29
Co-authored-by: Brandon Davis <brandon.davis@kitware.com>
Co-authored-by: Brandon Davis <brandon.davis@kitware.com>
Co-authored-by: Brandon Davis <brandon.davis@kitware.com>
@BryonLewis
Copy link
Copy Markdown
Collaborator Author

@subdavis hopefully I didn't botched this up too horribly. I believe that I addressed all of your comments and tried to make things clearer. After looking at what calls getType there seems to be no reason for it to not through an error if the confidencePairs[index] doesn't exist. We will just need to ensure in the future on import that all tracks have a default pair (unknown, 1.0) if nothing exists.

@BryonLewis BryonLewis requested a review from subdavis February 23, 2021 18:53
@subdavis
Copy link
Copy Markdown
Contributor

Pending your thoughts on #603, this is good to go.

* defaultFormatter

* simpler syntax

* comments

* Change line height
Copy link
Copy Markdown
Contributor

@subdavis subdavis left a comment

Choose a reason for hiding this comment

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

Thanks for making these changes. I think just changing the names for enabledTracks and TrackWithContext are the biggest improvements.

@BryonLewis BryonLewis merged commit 0035e19 into master Feb 25, 2021
@BryonLewis BryonLewis deleted the client/confidence-sort branch February 25, 2021 16:49
@subdavis subdavis restored the client/confidence-sort branch February 26, 2021 01:25
@subdavis subdavis mentioned this pull request Feb 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] [Client] Type visibility controls seem broken [BUG] Multiple Confidence Pairs not sorting and disabling properly

3 participants