Skip to content

Commit 91c91b5

Browse files
committed
add client side percentage noise filter
1 parent 8159e92 commit 91c91b5

File tree

7 files changed

+146
-8
lines changed

7 files changed

+146
-8
lines changed

client/src/components/ColorSchemeDialog.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ watch(colorScheme, () => {
4141
<template #activator="{ props: tooltipProps }">
4242
<v-icon
4343
v-bind="{ ...modalProps, ...tooltipProps }"
44-
size="25"
44+
size="30"
4545
>
4646
mdi-palette
4747
</v-icon>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<script lang="ts">
2+
import { defineComponent } from "vue";
3+
import useState from "@use/useState";
4+
5+
export default defineComponent({
6+
name: "TransparencyFilterControl",
7+
setup() {
8+
const { transparencyThreshold } = useState();
9+
10+
return {
11+
transparencyThreshold,
12+
};
13+
},
14+
});
15+
</script>
16+
17+
<template>
18+
<v-menu
19+
location="bottom"
20+
:close-on-content-click="false"
21+
open-on-hover
22+
>
23+
<template #activator="{ props: menuProps }">
24+
<v-badge
25+
:content="`${transparencyThreshold}%`"
26+
:model-value="transparencyThreshold > 0"
27+
location="bottom right"
28+
color="primary"
29+
:offset-x="10"
30+
>
31+
<v-icon
32+
v-bind="menuProps"
33+
size="30"
34+
class="mx-3"
35+
:color="transparencyThreshold > 0 ? 'blue' : ''"
36+
>
37+
mdi-opacity
38+
</v-icon>
39+
</v-badge>
40+
</template>
41+
<v-card
42+
min-width="200"
43+
>
44+
<v-card-title class="text-subtitle-1">
45+
Noise Filter {{ transparencyThreshold }}%
46+
</v-card-title>
47+
<v-card-text>
48+
<p>Removes amplitudes below the percentage</p>
49+
<v-slider
50+
v-model="transparencyThreshold"
51+
min="0"
52+
max="100"
53+
step="1"
54+
track-color="grey"
55+
color="primary"
56+
hide-details
57+
>
58+
<template #thumb-label="{ modelValue }">
59+
{{ modelValue }}%
60+
</template>
61+
</v-slider>
62+
</v-card-text>
63+
</v-card>
64+
</v-menu>
65+
</template>
66+

client/src/components/geoJS/LayerManager.vue

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import { defineComponent, nextTick, onMounted, onUnmounted, PropType, Ref, ref, watch } from "vue";
2+
import { defineComponent, nextTick, onMounted, onUnmounted, PropType, Ref, ref, watch, computed } from "vue";
33
import * as d3 from "d3";
44
import { SpectrogramAnnotation, SpectrogramSequenceAnnotation } from "../../api/api";
55
import {
@@ -72,6 +72,7 @@ export default defineComponent({
7272
drawingBoundingBox,
7373
boundingBoxError,
7474
fixedAxes,
75+
transparencyThreshold,
7576
} = useState();
7677
const selectedAnnotationId: Ref<null | number> = ref(null);
7778
const hoveredAnnotationId: Ref<null | number> = ref(null);
@@ -726,6 +727,33 @@ export default defineComponent({
726727
const gValues = ref('');
727728
const bValues = ref('');
728729
730+
731+
function getTransparencyTableValues() {
732+
// transparencyThreshold is expected to be 0-100 (percentage)
733+
// Convert to normalized 0-1
734+
const t = (transparencyThreshold.value ?? 0) / 100;
735+
736+
// quick edge cases
737+
if (t <= 0) {
738+
// threshold 0% → everything visible (alpha=1)
739+
return "1";
740+
}
741+
if (t >= 1) {
742+
// threshold 100% → everything transparent (alpha=0)
743+
return "0";
744+
}
745+
// number of discrete steps (higher = sharper but costlier)
746+
const numSteps = 50;
747+
// which index is the last 'transparent' bucket
748+
const thresholdStep = Math.floor(t * (numSteps - 1));
749+
750+
const values: string[] = [];
751+
for (let i = 0; i < numSteps; i++) {
752+
// for i <= thresholdStep set transparent (0), else opaque (1)
753+
values.push(i <= thresholdStep ? "0" : "1");
754+
}
755+
return values.join(" ");
756+
}
729757
function updateColorFilter() {
730758
if (!backgroundColor.value.includes(',')) {
731759
// convert rgb(0 0 0) to rgb(0, 0, 0)
@@ -775,7 +803,6 @@ export default defineComponent({
775803
776804
watch([backgroundColor, colorScheme], updateColorFilter);
777805
778-
779806
watch(fixedAxes, setAxes);
780807
781808
return {
@@ -787,6 +814,7 @@ export default defineComponent({
787814
rValues,
788815
gValues,
789816
bValues,
817+
getTransparencyTableValues,
790818
};
791819
},
792820
});
@@ -819,16 +847,43 @@ export default defineComponent({
819847
height="0"
820848
style="position: absolute; top: -1px; left: -1px;"
821849
>
822-
<filter id="apply-color-scheme">
823-
<!-- convert to grayscale -->
850+
<filter id="svg-filters">
824851
<feColorMatrix
825852
type="saturate"
826853
values="0"
827854
result="grayscale"
828855
/>
829856

830-
<!-- apply color scheme -->
831-
<feComponentTransfer>
857+
<feColorMatrix
858+
in="grayscale"
859+
type="matrix"
860+
values="0.2126 0.7152 0.0722 0 0
861+
0.2126 0.7152 0.0722 0 0
862+
0.2126 0.7152 0.0722 0 0
863+
0 0 0 1 0"
864+
result="sourceGraphic"
865+
/>
866+
867+
<feColorMatrix
868+
in="SourceGraphic"
869+
type="luminanceToAlpha"
870+
result="luminance"
871+
/>
872+
<feComponentTransfer in="luminance" result="transparency-mask">
873+
<feFuncA
874+
type="discrete"
875+
:tableValues="getTransparencyTableValues()"
876+
/>
877+
</feComponentTransfer>
878+
879+
<feComposite
880+
in="grayscale"
881+
in2="transparency-mask"
882+
operator="in"
883+
result="grayscale-with-transparency"
884+
/>
885+
886+
<feComponentTransfer in="grayscale-with-transparency">
832887
<feFuncR
833888
type="table"
834889
:tableValues="rValues"

client/src/components/geoJS/geoJSUtils.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,11 @@ const useGeoJS = () => {
121121
});
122122
}
123123
clearQuadFeatures();
124-
quadFeatureLayer.node().css("filter", "url(#apply-color-scheme)");
124+
// Apply combined filter that includes:
125+
// 1. Grayscale conversion
126+
// 2. Transparency threshold (based on original pixel intensity)
127+
// 3. Color scheme application
128+
quadFeatureLayer.node().css("filter", "url(#svg-filters)");
125129
for (let i = 0; i < imageCount; i += 1) {
126130
quadFeatures.push(quadFeatureLayer.createFeature("quad"));
127131
}

client/src/use/useState.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ const toggleFixedAxes = () => {
7979
fixedAxes.value = !fixedAxes.value;
8080
};
8181

82+
const transparencyThreshold = ref(0); // 0-100 percentage
83+
8284
type AnnotationState = "" | "editing" | "creating" | "disabled";
8385
export default function useState() {
8486
const setAnnotationState = (state: AnnotationState) => {
@@ -191,5 +193,6 @@ export default function useState() {
191193
scaledHeight,
192194
fixedAxes,
193195
toggleFixedAxes,
196+
transparencyThreshold,
194197
};
195198
}

client/src/views/NABat/NABatSpectrogram.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import ThumbnailViewer from "@components/ThumbnailViewer.vue";
2020
import useState from "@use/useState";
2121
import ColorPickerMenu from "@components/ColorPickerMenu.vue";
2222
import ColorSchemeSelect from "@components/ColorSchemeSelect.vue";
23+
import TransparencyFilterControl from "@/components/TransparencyFilterControl.vue";
2324
import RecordingInfoDialog from "@components/RecordingInfoDialog.vue";
2425
import RecordingAnnotations from "@components/RecordingAnnotations.vue";
2526
import { usePrompt } from '@use/prompt-service';
@@ -34,6 +35,7 @@ export default defineComponent({
3435
RecordingAnnotations,
3536
ColorPickerMenu,
3637
ColorSchemeSelect,
38+
TransparencyFilterControl,
3739
},
3840
props: {
3941
id: {
@@ -463,6 +465,9 @@ export default defineComponent({
463465
tooltip-text="Spectrogram background color"
464466
/>
465467
</div>
468+
<div class="mt-4 mr-3">
469+
<transparency-filter-control />
470+
</div>
466471
</v-row>
467472
</v-container>
468473
</v-toolbar>

client/src/views/Spectrogram.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import ThumbnailViewer from "@components/ThumbnailViewer.vue";
2424
import RecordingList from "@components/RecordingList.vue";
2525
import OtherUserAnnotationsDialog from "@/components/OtherUserAnnotationsDialog.vue";
2626
import ColorSchemeDialog from "@/components/ColorSchemeDialog.vue";
27+
import TransparencyFilterControl from "@/components/TransparencyFilterControl.vue";
2728
import useState from "@use/useState";
2829
import RecordingInfoDialog from "@components/RecordingInfoDialog.vue";
2930
export default defineComponent({
@@ -36,6 +37,7 @@ export default defineComponent({
3637
RecordingList,
3738
OtherUserAnnotationsDialog,
3839
ColorSchemeDialog,
40+
TransparencyFilterControl,
3941
},
4042
props: {
4143
id: {
@@ -546,6 +548,9 @@ export default defineComponent({
546548
<div class="mt-4">
547549
<color-scheme-dialog />
548550
</div>
551+
<div class="mt-4 mr-3">
552+
<transparency-filter-control />
553+
</div>
549554
</v-row>
550555
</v-container>
551556
</v-toolbar>

0 commit comments

Comments
 (0)