Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
b195817
initial test
BryonLewis May 27, 2026
86ff528
Auto-populate a new box or line with a segmentation polygon, head/tai…
mattdawkins Sep 18, 2026
3bd65a0
Auto-populate the stereo-mapped copy of a new box or line too
mattdawkins Sep 18, 2026
5d17494
Populate the mapped line even when its head point already created the…
mattdawkins Sep 18, 2026
e7e0742
Send the drawn line with its auto-populate prompt
mattdawkins Sep 18, 2026
bda2c97
Preserve manual geometry edits during auto-population
mattdawkins Sep 20, 2026
f666a0c
Surface auto-populate failures and keep masks when keypoint extractio…
mattdawkins Sep 20, 2026
45d0bbd
Use the frontend segmentation response contract in auto-populate
mattdawkins Sep 20, 2026
b18ed31
Preserve all auto-populated mask polygons and extract points from the…
mattdawkins Sep 21, 2026
7999774
Refit a stereo-mapped box to its mask when the two disagree
mattdawkins Sep 21, 2026
79c63dc
Keep every mask component in point-click segmentation
mattdawkins Sep 21, 2026
3a152bd
Derive head/tail for a point-segmented mask when auto-populate points…
mattdawkins Sep 21, 2026
69cebf5
Map a new box to the other stereo camera through its mask
mattdawkins Sep 21, 2026
cb8be07
Re-derive point-segmentation head/tail on every click until the user …
mattdawkins Sep 21, 2026
7c82c2d
Merge dev/point-seg-multipolygon into dev/auto-populate-mask-points
mattdawkins Sep 22, 2026
c98013b
Populate the mapped stereo copy through stereo_segment
mattdawkins Sep 22, 2026
d84a4d5
Merge remote-tracking branch 'ssh/main' into dev/point-seg-multipolygon
mattdawkins Sep 22, 2026
ed94a41
Merge remote-tracking branch 'ssh/dev/auto-populate-mask-points' into…
mattdawkins Sep 22, 2026
e280040
Refresh stereo length after segmentation refinements and simplify seg…
mattdawkins Sep 22, 2026
c9c9ad0
Merge main before integrating browser segmentation
mattdawkins Sep 22, 2026
2e0e7aa
Merge remote-tracking branch 'ssh/dev/point-seg-multipolygon' into sa…
mattdawkins Sep 22, 2026
cc76e79
Add browser SAM2/SAM3 segmentation and stereo geometry generation
mattdawkins Sep 22, 2026
8230ccb
Align browser stereo transfer with desktop sampling and refinement
mattdawkins Sep 22, 2026
d8c4276
Move SAM mask selection and post-processing into reusable ONNX graph
mattdawkins Sep 22, 2026
336d1f9
Move sparse stereo disparity sampling into a reusable ONNX graph
mattdawkins Sep 22, 2026
9a37cbb
Move helper ONNX exporter sources to VIAME plugins
mattdawkins Sep 22, 2026
5010875
Merge remote-tracking branch 'ssh/main' into pr1672-sam3-onnx-web
mattdawkins Sep 25, 2026
2f7fbd1
Keep the pixel data when capturing a frame from an image element
mattdawkins Sep 25, 2026
4083f3a
Fall back to the faster stereo method when the model cannot run
mattdawkins Sep 25, 2026
0ee04e0
Always clear the companion box layer once it has nothing to show
mattdawkins Sep 25, 2026
bb21e95
Report why the stereo model failed to load, and retry it next time
mattdawkins Sep 25, 2026
ce2ca16
only use SAM2, SAM3 OOM errors
BryonLewis Sep 25, 2026
e326385
make reset actually reset the points
BryonLewis Sep 25, 2026
d70a8c2
better progress for installing/loading them SAM model
BryonLewis Sep 25, 2026
93f5ed4
CPU/GPU options, cancel CPU option, run CPU async
BryonLewis Sep 25, 2026
4e12b13
refactor new track settings into a dialog
BryonLewis Sep 25, 2026
c3a79c4
make sure autopopulate works for subsequent frames in a track
BryonLewis Sep 25, 2026
62a6ee8
allow easier switching between GPU/CPU
BryonLewis Sep 25, 2026
3da2a7a
allow cancellation of auto-populating in GPU mode
BryonLewis Sep 25, 2026
11d77c2
warning for cpu version
BryonLewis Sep 25, 2026
975ae35
linting
BryonLewis Sep 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 144 additions & 13 deletions client/dive-common/components/EditorMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import {
computed,
defineComponent,
onBeforeUnmount,
PropType,
ref,
watch,
Expand Down Expand Up @@ -103,6 +104,16 @@ export default defineComponent({
type: Function as PropType<() => Promise<boolean>>,
default: undefined,
},
/** True while browser auto-populate (mask/points) is embedding or predicting. */
autoPopulateBusy: {
type: Boolean,
default: false,
},
/** Live status from the SAM session during auto-populate (encode/predict). */
autoPopulateStatus: {
type: String as PropType<string | null>,
default: null,
},
},
emits: [
'set-annotation-state',
Expand All @@ -114,6 +125,7 @@ export default defineComponent({
'text-query',
'text-query-all-frames',
'open-external-link',
'cancel-auto-populate',
],
setup(props, { emit }) {
const toolTimeTimeout = ref<number | null>(null);
Expand Down Expand Up @@ -248,8 +260,8 @@ export default defineComponent({
id: r.name,
icon: r.icon.value || 'mdi-pencil',
active: props.editingTrack && r.active.value,
loading: (r.loading?.value ?? false)
|| (r instanceof SegmentationPointClick && r.predicting.value),
// Model download/init only — keep the tool usable while a mask runs.
loading: r.loading?.value ?? false,
description: r.name,
click: () => r.activate(),
mousetrap: [
Expand Down Expand Up @@ -279,6 +291,10 @@ export default defineComponent({

const mousetrap = computed((): Mousetrap[] => [
...flatten(editButtons.value.map((b) => b.mousetrap || [])),
...(autoPopulateBusyVisible.value ? [{
bind: 'esc',
handler: () => { cancelAutoPopulate(); },
}] : []),
]);

const activeEditButton = computed(() => editButtons.value.find((b) => b.active) || editButtons.value[0]);
Expand All @@ -302,6 +318,13 @@ export default defineComponent({
if (props.multiSelectActive) {
return { text: 'Multi-select Mode', icon: 'mdi-call-merge', color: 'error' };
}
if (props.autoPopulateBusy && !activeSegmentationRecipe.value) {
return {
text: 'Auto-populating',
icon: 'mdi-loading mdi-spin',
color: 'warning',
};
}
if (activeSegmentationRecipe.value) {
return {
text: `${props.editingDetails === 'Editing' ? 'Editing' : 'Creating'} Segment`,
Expand Down Expand Up @@ -337,8 +360,82 @@ export default defineComponent({
return segRecipe?.loading.value ?? false;
});

const segmentationTooltip = 'Left click: positive point. Esc to cancel. Middle click or shift+click for negative.';
/** After a long predict, promote Reset to an explicit Cancel control. */
const SEGMENTATION_CANCEL_WARNING_MS = 3000;
const segmentationCancelWarning = ref(false);
let segmentationCancelWarningTimer: ReturnType<typeof setTimeout> | null = null;
watch(segmentationPredicting, (predicting) => {
if (segmentationCancelWarningTimer !== null) {
clearTimeout(segmentationCancelWarningTimer);
segmentationCancelWarningTimer = null;
}
if (predicting) {
segmentationCancelWarning.value = false;
segmentationCancelWarningTimer = setTimeout(() => {
segmentationCancelWarningTimer = null;
if (segmentationPredicting.value) {
segmentationCancelWarning.value = true;
}
}, SEGMENTATION_CANCEL_WARNING_MS);
} else {
segmentationCancelWarning.value = false;
}
});

/** Same 3s promotion for auto-populate encode/predict (especially CPU). */
const autoPopulateCancelWarning = ref(false);
let autoPopulateCancelWarningTimer: ReturnType<typeof setTimeout> | null = null;
const autoPopulateBusyVisible = computed(
() => props.autoPopulateBusy && !activeSegmentationRecipe.value,
);
watch(autoPopulateBusyVisible, (busy) => {
if (autoPopulateCancelWarningTimer !== null) {
clearTimeout(autoPopulateCancelWarningTimer);
autoPopulateCancelWarningTimer = null;
}
if (busy) {
autoPopulateCancelWarning.value = false;
autoPopulateCancelWarningTimer = setTimeout(() => {
autoPopulateCancelWarningTimer = null;
if (autoPopulateBusyVisible.value) {
autoPopulateCancelWarning.value = true;
}
}, SEGMENTATION_CANCEL_WARNING_MS);
} else {
autoPopulateCancelWarning.value = false;
}
});
onBeforeUnmount(() => {
if (segmentationCancelWarningTimer !== null) {
clearTimeout(segmentationCancelWarningTimer);
}
if (autoPopulateCancelWarningTimer !== null) {
clearTimeout(autoPopulateCancelWarningTimer);
}
});

const segmentationTooltip = 'Left click for positive, middle or shift+click for negative points. Right click to confirm or Esc to cancel.';
const segmentationStatusHint = computed(() => {
if (segmentationLoading.value) return 'Loading segmentation model…';
if (segmentationCancelWarning.value) {
return 'Still computing — click Cancel or press Esc to abort.';
}
if (segmentationPredicting.value) {
return 'Computing segmentation… Press Esc to cancel.';
}
if (autoPopulateBusyVisible.value) {
if (autoPopulateCancelWarning.value) {
return 'Still auto-populating — click Cancel or press Esc to abort.';
}
return props.autoPopulateStatus
|| 'Auto-populating mask/points… Press Esc to cancel.';
}
return null;
});

function cancelAutoPopulate() {
emit('cancel-auto-populate');
}
const editingTooltip = computed(() => {
if (props.editingDetails === 'disabled' || !props.editingMode || typeof props.editingMode !== 'string') {
return '';
Expand Down Expand Up @@ -375,7 +472,12 @@ export default defineComponent({
activeSegmentationRecipe,
segmentationPredicting,
segmentationLoading,
segmentationCancelWarning,
segmentationTooltip,
segmentationStatusHint,
autoPopulateBusyVisible,
autoPopulateCancelWarning,
cancelAutoPopulate,
// Text query
textQueryDialogOpen,
textQueryInput,
Expand Down Expand Up @@ -432,11 +534,8 @@ export default defineComponent({
Multi-select in progress. Editing is disabled.
Select additional tracks to merge or group.
</span>
<span v-else-if="segmentationLoading">
Loading segmentation model...
</span>
<span v-else-if="segmentationPredicting">
Computing segmentation...
<span v-else-if="segmentationStatusHint">
{{ segmentationStatusHint }}
</span>
<span v-else-if="activeSegmentationRecipe">
{{ segmentationTooltip }}
Expand Down Expand Up @@ -573,19 +672,51 @@ export default defineComponent({
</v-tooltip>
</outlined-labeled-group>
</span>
<!-- Segmentation Reset button -->
<!-- Segmentation Reset / Cancel button -->
<template v-if="activeSegmentationRecipe && editingMode === 'Point'">
<v-btn
color="error"
:color="segmentationCancelWarning ? 'warning' : 'error'"
class="mx-1"
small
:disabled="!activeSegmentationRecipe.hasPoints() || segmentationPredicting"
:disabled="!segmentationPredicting
&& !activeSegmentationRecipe.hasPoints()
&& !activeSegmentationRecipe.hasPendingPrediction()"
:title="segmentationPredicting
? (segmentationCancelWarning
? 'Cancel the in-progress segmentation'
: 'Cancel (Esc)')
: 'Clear points (Esc)'"
@click="activeSegmentationRecipe.resetPoints()"
>
<v-icon left>
mdi-close
{{ segmentationCancelWarning ? 'mdi-cancel' : 'mdi-close' }}
</v-icon>
{{ segmentationCancelWarning ? 'Cancel' : 'Reset' }}
<span
v-if="segmentationPredicting && !segmentationCancelWarning"
class="text-caption ml-1"
>(Esc)</span>
</v-btn>
</template>
<!-- Auto-populate Cancel (promotes after 3s like magic wand) -->
<template v-else-if="autoPopulateBusyVisible">
<v-btn
:color="autoPopulateCancelWarning ? 'warning' : 'error'"
class="mx-1"
small
:title="autoPopulateCancelWarning
? 'Cancel the in-progress auto-populate'
: 'Cancel (Esc)'"
@click="cancelAutoPopulate"
>
<v-icon left>
{{ autoPopulateCancelWarning ? 'mdi-cancel' : 'mdi-close' }}
</v-icon>
Reset
Cancel
<span
v-if="!autoPopulateCancelWarning"
class="text-caption ml-1"
>(Esc)</span>
</v-btn>
</template>
<!-- Hide delete controls when in segmentation mode -->
Expand Down
Loading
Loading