Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
185aacc
move ipython import into function
satra Apr 20, 2025
5c5d1be
return figure from plots
satra Apr 20, 2025
54b2396
fix returns and annotations
satra Apr 20, 2025
ad5624c
revisions to make getting started notebook work
satra Apr 20, 2025
f7d82b0
updated from precommit
satra Apr 21, 2025
9e57025
Merge remote-tracking branch 'origin/main' into fix/plotting
satra Apr 21, 2025
0f50d2a
Merge branch 'main' into fix/plotting
satra Apr 29, 2025
3a1a017
fix ruff issue
satra Apr 29, 2025
944ab5d
Merge branch 'main' into fix/plotting
ibevers Jun 10, 2025
04837da
Merge branch 'main' into fix/plotting
ibevers Jul 23, 2025
2e2c9cb
Add better error handling and tests for plotting functions
ibevers Jul 23, 2025
32ec537
Update pydantic to 2.11.0
ibevers Jul 23, 2025
bef8d63
Update pydantic to >=2.11.0 <3.0.0
ibevers Jul 28, 2025
346ebaf
Merge branch 'main' into fix/plotting
ibevers Jul 28, 2025
fe4998e
Merge branch 'main' into fix/plotting
ibevers Aug 11, 2025
945c395
Import torchaudio in plotting_test and skip if not available
ibevers Aug 11, 2025
05728ff
Remove stray torchaudio import
ibevers Aug 12, 2025
9369b2f
Change to opencv-python-headless
ibevers Aug 13, 2025
da5c7d2
Merge branch 'main' into fix/plotting
ibevers Aug 13, 2025
4cbcd28
Merge branch 'main' into fix/plotting
fabiocat93 Aug 14, 2025
35eacd7
fixed matplotlib inline command location in the getting_started notebook
fabiocat93 Aug 14, 2025
555f56e
fixed matplotlib inline command location in the tutorial notebooks
fabiocat93 Aug 14, 2025
dabcd86
fixed style
fabiocat93 Aug 14, 2025
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
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ nltk = {version = "~=3.9", optional = true}
sentence-transformers = {version = "~=3.1", optional = true}
pylangacq = {version = "~=0.19", optional = true}
mediapipe = {version = "~=0.10", optional = true}
opencv-python = {version = "~=4.10", optional = true}
opencv-python-headless = {version = "~=4.10", optional = true}
ultralytics = {version = "~=8.3", optional = true}
av = {version = "~=14.2", optional = true}

Expand Down Expand Up @@ -91,7 +91,7 @@ text = [
video = [
"av",
"mediapipe",
"opencv-python",
"opencv-python-headless",
"ultralytics"
]

Expand Down
44 changes: 35 additions & 9 deletions src/senselab/audio/tasks/plotting/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
import matplotlib.pyplot as plt
import numpy as np
import torch
from matplotlib.pyplot import Figure

from senselab.audio.data_structures import Audio
from senselab.utils.data_structures import logger


def plot_waveform(audio: Audio, title: str = "Waveform", fast: bool = False) -> None:
def plot_waveform(audio: Audio, title: str = "Waveform", fast: bool = False) -> Figure:
"""Plots the waveform of an Audio object.

Args:
Expand Down Expand Up @@ -43,9 +44,10 @@ def plot_waveform(audio: Audio, title: str = "Waveform", fast: bool = False) ->
figure.suptitle(title)
plt.xlabel("Time [s]")
plt.show(block=False)
return figure


def plot_specgram(audio: Audio, mel_scale: bool = False, title: str = "Spectrogram", **spect_kwargs: Any) -> None: # noqa : ANN401
def plot_specgram(audio: Audio, mel_scale: bool = False, title: str = "Spectrogram", **spect_kwargs: Any) -> Figure: # noqa : ANN401
"""Plots the spectrogram of an Audio object.

Args:
Expand Down Expand Up @@ -107,18 +109,41 @@ def _power_to_db(

# Extract the spectrogram
if mel_scale:
from senselab.audio.tasks.features_extraction.torchaudio import extract_mel_spectrogram_from_audios
from senselab.audio.tasks.features_extraction.torchaudio import (
extract_mel_spectrogram_from_audios,
)

spectrogram = extract_mel_spectrogram_from_audios([audio], **spect_kwargs)[0]["mel_spectrogram"]
result = extract_mel_spectrogram_from_audios([audio], **spect_kwargs)[0]
spectrogram = result["mel_spectrogram"]
y_axis_label = "Mel Frequency"
else:
from senselab.audio.tasks.features_extraction.torchaudio import extract_spectrogram_from_audios
from senselab.audio.tasks.features_extraction.torchaudio import (
extract_spectrogram_from_audios,
)

spectrogram = extract_spectrogram_from_audios([audio], **spect_kwargs)[0]["spectrogram"]
result = extract_spectrogram_from_audios([audio], **spect_kwargs)[0]
spectrogram = result["spectrogram"]
y_axis_label = "Frequency [Hz]"

if spectrogram.dim() != 2:
raise ValueError("Spectrogram must be a 2D tensor.")
# Handle edge cases for spectrogram tensor
if not isinstance(spectrogram, torch.Tensor):
raise ValueError("Spectrogram extraction failed - returned non-tensor result.")

if torch.isnan(spectrogram).all():
error_msg = "Spectrogram extraction failed - all values are NaN. " "Audio may be too short."
raise ValueError(error_msg)

# Handle multi-channel spectrograms (3D tensor: channels x frequency x time)
if spectrogram.dim() == 3:
# Use the first channel for plotting
spectrogram = spectrogram[0]
elif spectrogram.dim() == 1:
# Handle case where spectrogram is 1D (very short audio)
error_msg = "Audio is too short to generate a meaningful spectrogram " "for plotting."
raise ValueError(error_msg)
elif spectrogram.dim() != 2:
error_msg = f"Spectrogram must be a 2D tensor, got {spectrogram.dim()}D tensor."
raise ValueError(error_msg)

# Determine time and frequency scale
num_frames = spectrogram.size(1)
Expand All @@ -133,7 +158,7 @@ def _power_to_db(
else:
freq_axis = torch.linspace(0, audio.sampling_rate / 2, num_freq_bins)

plt.figure(figsize=(10, 4))
figure = plt.figure(figsize=(12, 4))
plt.imshow(
_power_to_db(spectrogram.numpy()),
aspect="auto",
Expand All @@ -146,6 +171,7 @@ def _power_to_db(
plt.ylabel(y_axis_label)
plt.xlabel("Time [Sec]")
plt.show(block=False)
return figure


def play_audio(audio: Audio) -> None:
Expand Down
13 changes: 8 additions & 5 deletions src/senselab/utils/tasks/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

import matplotlib.cm as cm
import matplotlib.pyplot as plt
from matplotlib.pyplot import Figure

from senselab.utils.data_structures import ScriptLine


def plot_transcript(transcript: ScriptLine) -> None:
def plot_transcript(transcript: ScriptLine) -> Figure:
"""Plots the transcript visualization over time.

Args:
Expand Down Expand Up @@ -39,7 +40,7 @@ def plot_transcript(transcript: ScriptLine) -> None:
end_times.append(chunk.end)

# Create a figure and axis
_, ax = plt.subplots(figsize=(12, 6))
figure, ax = plt.subplots(figsize=(12, 6))

# Plot each text segment and add text label
for i, text in enumerate(texts):
Expand All @@ -55,7 +56,8 @@ def plot_transcript(transcript: ScriptLine) -> None:
ax.set_title("Transcript Visualization Over Time")

# Show the plot
plt.show()
plt.show(block=False)
return figure


def plot_segment(segments: List[ScriptLine]) -> None:
Expand Down Expand Up @@ -86,7 +88,7 @@ def plot_segment(segments: List[ScriptLine]) -> None:
labels.append(segment.speaker)

# Create a figure and axis
_, ax = plt.subplots(figsize=(12, 6))
figure, ax = plt.subplots(figsize=(12, 6))

# Create a color map based on unique labels
unique_labels = list(set(labels))
Expand All @@ -108,4 +110,5 @@ def plot_segment(segments: List[ScriptLine]) -> None:
ax.set_title("Segment Visualization Over Time")

# Show the plot
plt.show()
plt.show(block=False)
return figure
Loading
Loading