Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion client/src/components/ColorSchemeDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ watch(colorScheme, () => {
<template #activator="{ props: tooltipProps }">
<v-icon
v-bind="{ ...modalProps, ...tooltipProps }"
size="25"
size="30"
>
mdi-palette
</v-icon>
Expand Down
66 changes: 66 additions & 0 deletions client/src/components/TransparencyFilterControl.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<script lang="ts">
import { defineComponent } from "vue";
import useState from "@use/useState";

export default defineComponent({
name: "TransparencyFilterControl",
setup() {
const { transparencyThreshold } = useState();

return {
transparencyThreshold,
};
},
});
</script>

<template>
<v-menu
location="bottom"
:close-on-content-click="false"
open-on-hover
>
<template #activator="{ props: menuProps }">
<v-badge

Check warning on line 24 in client/src/components/TransparencyFilterControl.vue

View workflow job for this annotation

GitHub Actions / Lint [eslint]

Expected indentation of 6 spaces but found 8 spaces
:content="`${transparencyThreshold}%`"
:model-value="transparencyThreshold > 0"
location="bottom right"
color="primary"
:offset-x="10"
>

Check warning on line 30 in client/src/components/TransparencyFilterControl.vue

View workflow job for this annotation

GitHub Actions / Lint [eslint]

Expected indentation of 6 spaces but found 8 spaces
<v-icon
v-bind="menuProps"

Check warning on line 32 in client/src/components/TransparencyFilterControl.vue

View workflow job for this annotation

GitHub Actions / Lint [eslint]

Expected indentation of 10 spaces but found 12 spaces
size="30"

Check warning on line 33 in client/src/components/TransparencyFilterControl.vue

View workflow job for this annotation

GitHub Actions / Lint [eslint]

Expected indentation of 10 spaces but found 12 spaces
class="mx-3"

Check warning on line 34 in client/src/components/TransparencyFilterControl.vue

View workflow job for this annotation

GitHub Actions / Lint [eslint]

Expected indentation of 10 spaces but found 12 spaces
:color="transparencyThreshold > 0 ? 'blue' : ''"

Check warning on line 35 in client/src/components/TransparencyFilterControl.vue

View workflow job for this annotation

GitHub Actions / Lint [eslint]

Expected indentation of 10 spaces but found 12 spaces
>
mdi-opacity

Check warning on line 37 in client/src/components/TransparencyFilterControl.vue

View workflow job for this annotation

GitHub Actions / Lint [eslint]

Expected indentation of 10 spaces but found 12 spaces
</v-icon>
</v-badge>

Check warning on line 39 in client/src/components/TransparencyFilterControl.vue

View workflow job for this annotation

GitHub Actions / Lint [eslint]

Expected indentation of 6 spaces but found 8 spaces
</template>
<v-card
min-width="200"
>
<v-card-title class="text-subtitle-1">
Noise Filter {{ transparencyThreshold }}%
</v-card-title>
<v-card-text>
<p>Removes amplitudes below the percentage</p>
<v-slider
v-model="transparencyThreshold"
min="0"
max="100"
step="1"
track-color="grey"
color="primary"
hide-details
>
<template #thumb-label="{ modelValue }">
{{ modelValue }}%
</template>
</v-slider>
</v-card-text>
</v-card>
</v-menu>
</template>

69 changes: 63 additions & 6 deletions client/src/components/geoJS/LayerManager.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { defineComponent, nextTick, onMounted, onUnmounted, PropType, Ref, ref, watch } from "vue";
import { defineComponent, nextTick, onMounted, onUnmounted, PropType, Ref, ref, watch, computed } from "vue";

Check warning on line 2 in client/src/components/geoJS/LayerManager.vue

View workflow job for this annotation

GitHub Actions / Lint [eslint]

'computed' is defined but never used
import * as d3 from "d3";
import { SpectrogramAnnotation, SpectrogramSequenceAnnotation } from "../../api/api";
import {
Expand Down Expand Up @@ -72,6 +72,7 @@
drawingBoundingBox,
boundingBoxError,
fixedAxes,
transparencyThreshold,
} = useState();
const selectedAnnotationId: Ref<null | number> = ref(null);
const hoveredAnnotationId: Ref<null | number> = ref(null);
Expand Down Expand Up @@ -726,6 +727,33 @@
const gValues = ref('');
const bValues = ref('');


function getTransparencyTableValues() {
// transparencyThreshold is expected to be 0-100 (percentage)
// Convert to normalized 0-1
const t = (transparencyThreshold.value ?? 0) / 100;

// quick edge cases
if (t <= 0) {
// threshold 0% → everything visible (alpha=1)
return "1";
}
if (t >= 1) {
// threshold 100% → everything transparent (alpha=0)
return "0";
}
// number of discrete steps (higher = sharper but costlier)
const numSteps = 100;
// which index is the last 'transparent' bucket
const thresholdStep = Math.floor(t * (numSteps - 1));

const values: string[] = [];
for (let i = 0; i < numSteps; i++) {
// for i <= thresholdStep set transparent (0), else opaque (1)
values.push(i <= thresholdStep ? "0" : "1");
}
return values.join(" ");
}
function updateColorFilter() {
if (!backgroundColor.value.includes(',')) {
// convert rgb(0 0 0) to rgb(0, 0, 0)
Expand Down Expand Up @@ -775,7 +803,6 @@

watch([backgroundColor, colorScheme], updateColorFilter);


watch(fixedAxes, setAxes);

return {
Expand All @@ -787,6 +814,7 @@
rValues,
gValues,
bValues,
getTransparencyTableValues,
};
},
});
Expand Down Expand Up @@ -819,16 +847,45 @@
height="0"
style="position: absolute; top: -1px; left: -1px;"
>
<filter id="apply-color-scheme">
<!-- convert to grayscale -->
<filter id="svg-filters">
<feColorMatrix
type="saturate"
values="0"
result="grayscale"
/>

<!-- apply color scheme -->
<feComponentTransfer>
<feColorMatrix
in="grayscale"
type="matrix"
values="0.2126 0.7152 0.0722 0 0
0.2126 0.7152 0.0722 0 0
0.2126 0.7152 0.0722 0 0
0 0 0 1 0"
result="sourceGraphic"
/>

<feColorMatrix
in="sourceGraphic"
type="luminanceToAlpha"
result="luminance"
/>
<feComponentTransfer
in="luminance"
result="transparency-mask">

Check warning on line 874 in client/src/components/geoJS/LayerManager.vue

View workflow job for this annotation

GitHub Actions / Lint [eslint]

Expected 1 line break before closing bracket, but no line breaks found
<feFuncA
type="table"
:tableValues="getTransparencyTableValues()"
/>
</feComponentTransfer>

<feComposite
in="grayscale"
in2="transparency-mask"
operator="in"
result="grayscale-with-transparency"
/>

<feComponentTransfer in="grayscale-with-transparency">
<feFuncR
type="table"
:tableValues="rValues"
Expand Down
6 changes: 5 additions & 1 deletion client/src/components/geoJS/geoJSUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,11 @@ const useGeoJS = () => {
});
}
clearQuadFeatures();
quadFeatureLayer.node().css("filter", "url(#apply-color-scheme)");
// Apply combined filter that includes:
// 1. Grayscale conversion
// 2. Transparency threshold (based on original pixel intensity)
// 3. Color scheme application
quadFeatureLayer.node().css("filter", "url(#svg-filters)");
for (let i = 0; i < imageCount; i += 1) {
quadFeatures.push(quadFeatureLayer.createFeature("quad"));
}
Expand Down
3 changes: 3 additions & 0 deletions client/src/use/useState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ const toggleFixedAxes = () => {
fixedAxes.value = !fixedAxes.value;
};

const transparencyThreshold = ref(0); // 0-100 percentage

type AnnotationState = "" | "editing" | "creating" | "disabled";
export default function useState() {
const setAnnotationState = (state: AnnotationState) => {
Expand Down Expand Up @@ -191,5 +193,6 @@ export default function useState() {
scaledHeight,
fixedAxes,
toggleFixedAxes,
transparencyThreshold,
};
}
5 changes: 5 additions & 0 deletions client/src/views/NABat/NABatSpectrogram.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import ThumbnailViewer from "@components/ThumbnailViewer.vue";
import useState from "@use/useState";
import ColorPickerMenu from "@components/ColorPickerMenu.vue";
import ColorSchemeSelect from "@components/ColorSchemeSelect.vue";
import TransparencyFilterControl from "@/components/TransparencyFilterControl.vue";
import RecordingInfoDialog from "@components/RecordingInfoDialog.vue";
import RecordingAnnotations from "@components/RecordingAnnotations.vue";
import { usePrompt } from '@use/prompt-service';
Expand All @@ -34,6 +35,7 @@ export default defineComponent({
RecordingAnnotations,
ColorPickerMenu,
ColorSchemeSelect,
TransparencyFilterControl,
},
props: {
id: {
Expand Down Expand Up @@ -463,6 +465,9 @@ export default defineComponent({
tooltip-text="Spectrogram background color"
/>
</div>
<div class="mt-4 mr-3">
<transparency-filter-control />
</div>
</v-row>
</v-container>
</v-toolbar>
Expand Down
4 changes: 2 additions & 2 deletions client/src/views/Recordings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,10 @@ export default defineComponent({
return filterTagSet.intersection(itemTagSet).size > 0;
};
const sharedTagFilter: FilterFunction = (value: string, search: string, item?: InternalItem<Recording>) => {
if (filterTags.value.length === 0) {
if (sharedFilterTags.value.length === 0) {
return true;
}
const filterTagSet = new Set(filterTags.value);
const filterTagSet = new Set(sharedFilterTags.value);
const itemTagSet = new Set(item?.raw.tags_text);
return filterTagSet.intersection(itemTagSet).size > 0;
};
Expand Down
5 changes: 5 additions & 0 deletions client/src/views/Spectrogram.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import ThumbnailViewer from "@components/ThumbnailViewer.vue";
import RecordingList from "@components/RecordingList.vue";
import OtherUserAnnotationsDialog from "@/components/OtherUserAnnotationsDialog.vue";
import ColorSchemeDialog from "@/components/ColorSchemeDialog.vue";
import TransparencyFilterControl from "@/components/TransparencyFilterControl.vue";
import useState from "@use/useState";
import RecordingInfoDialog from "@components/RecordingInfoDialog.vue";
export default defineComponent({
Expand All @@ -36,6 +37,7 @@ export default defineComponent({
RecordingList,
OtherUserAnnotationsDialog,
ColorSchemeDialog,
TransparencyFilterControl,
},
props: {
id: {
Expand Down Expand Up @@ -546,6 +548,9 @@ export default defineComponent({
<div class="mt-4">
<color-scheme-dialog />
</div>
<div class="mt-4 mr-3">
<transparency-filter-control />
</div>
</v-row>
</v-container>
</v-toolbar>
Expand Down