Module thunderfish.fishfinder
Expand source code
import sys
import os
import warnings
import argparse
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as ml
from audioio import PlayAudio, fade, write_audio
from thunderlab.configfile import ConfigFile
from thunderlab.dataloader import DataLoader
from thunderlab.powerspectrum import nfft, decibel, psd, spectrogram
from thunderlab.powerspectrum import add_multi_psd_config, multi_psd_args
from .version import __version__, __year__
from .harmonics import harmonic_groups, harmonic_groups_args, psd_peak_detection_args
from .harmonics import add_psd_peak_detection_config, add_harmonic_groups_config, colors_markers
from .bestwindow import clip_amplitudes, clip_args, best_window_indices
from .bestwindow import best_window_args
from .thunderfish import configuration, save_configuration
# check: import logging https://docs.python.org/2/howto/logging.html#logging-basic-tutorial
class SignalPlot:
def __init__(self, data, samplingrate, unit, filename, channel, verbose, cfg):
self.filename = filename
self.channel = channel
self.samplerate = samplingrate
self.data = data
self.unit = unit
self.cfg = cfg
self.verbose = verbose
self.tmax = (len(self.data)-1)/self.samplerate
self.toffset = 0.0
self.twindow = 8.0
if self.twindow > self.tmax:
self.twindow = np.round(2 ** (np.floor(np.log(self.tmax) / np.log(2.0)) + 1.0))
self.ymin = -1.0
self.ymax = +1.0
self.trace_artist = None
self.spectrogram_artist = None
self.fmin = 0.0
self.fmax = 0.0
self.decibel = True
self.freq_resolution = self.cfg.value('frequencyResolution')
self.deltaf = 1.0
self.mains_freq = self.cfg.value('mainsFreq')
self.power_label = None
self.all_peaks_artis = None
self.good_peaks_artist = None
self.power_artist = None
self.power_frequency_label = None
self.peak_artists = []
self.legend = True
self.legendhandle = None
self.help = False
self.helptext = []
self.allpeaks = []
self.fishlist = []
self.mains = []
self.peak_specmarker = []
self.peak_annotation = []
self.min_clip = self.cfg.value('minClipAmplitude')
self.max_clip = self.cfg.value('maxClipAmplitude')
self.colorrange, self.markerrange = colors_markers()
# audio output:
self.audio = PlayAudio()
# set key bindings:
plt.rcParams['keymap.fullscreen'] = 'ctrl+f'
plt.rcParams['keymap.pan'] = 'ctrl+m'
plt.rcParams['keymap.quit'] = 'ctrl+w, alt+q, q'
plt.rcParams['keymap.yscale'] = ''
plt.rcParams['keymap.xscale'] = ''
plt.rcParams['keymap.grid'] = ''
#plt.rcParams['keymap.all_axes'] = ''
# the figure:
plt.ioff()
self.fig = plt.figure(figsize=(15, 9))
self.fig.canvas.manager.set_window_title(self.filename + ' channel {0:d}'.format(self.channel))
self.fig.canvas.mpl_connect('key_press_event', self.keypress)
self.fig.canvas.mpl_connect('button_press_event', self.buttonpress)
self.fig.canvas.mpl_connect('pick_event', self.onpick)
self.fig.canvas.mpl_connect('resize_event', self.resize)
# trace plot:
self.axt = self.fig.add_axes([0.1, 0.7, 0.87, 0.25])
self.axt.set_ylabel('Amplitude [{:s}]'.format(self.unit))
ht = self.axt.text(0.98, 0.05, '(ctrl+) page and arrow up, down, home, end: scroll', ha='right',
transform=self.axt.transAxes)
self.helptext.append(ht)
ht = self.axt.text(0.98, 0.15, '+, -, X, x: zoom in/out', ha='right', transform=self.axt.transAxes)
self.helptext.append(ht)
ht = self.axt.text(0.98, 0.25, 'y,Y,v,V: zoom amplitudes', ha='right', transform=self.axt.transAxes)
self.helptext.append(ht)
ht = self.axt.text(0.98, 0.35, 'p,P: play audio (display,all)', ha='right', transform=self.axt.transAxes)
self.helptext.append(ht)
ht = self.axt.text(0.98, 0.45, 'ctrl-f: full screen', ha='right', transform=self.axt.transAxes)
self.helptext.append(ht)
ht = self.axt.text(0.98, 0.55, 'w: plot waveform into png file', ha='right', transform=self.axt.transAxes)
self.helptext.append(ht)
ht = self.axt.text(0.98, 0.65, 's: save figure', ha='right', transform=self.axt.transAxes)
self.helptext.append(ht)
ht = self.axt.text(0.98, 0.75, 'S: save audiosegment', ha='right', transform=self.axt.transAxes)
self.helptext.append(ht)
ht = self.axt.text(0.98, 0.85, 'q: quit', ha='right', transform=self.axt.transAxes)
self.helptext.append(ht)
ht = self.axt.text(0.98, 0.95, 'h: toggle this help', ha='right', transform=self.axt.transAxes)
self.helptext.append(ht)
self.axt.set_xticklabels([])
# spectrogram:
self.axs = self.fig.add_axes([0.1, 0.45, 0.87, 0.25])
self.axs.set_xlabel('Time [seconds]')
self.axs.set_ylabel('Frequency [Hz]')
# power spectrum:
self.axp = self.fig.add_axes([0.1, 0.1, 0.87, 0.25])
ht = self.axp.text(0.98, 0.9, 'r, R: frequency resolution', ha='right', transform=self.axp.transAxes)
self.helptext.append(ht)
ht = self.axp.text(0.98, 0.8, 'f, F: zoom', ha='right', transform=self.axp.transAxes)
self.helptext.append(ht)
ht = self.axp.text(0.98, 0.7, '(ctrl+) left, right: move', ha='right', transform=self.axp.transAxes)
self.helptext.append(ht)
ht = self.axp.text(0.98, 0.6, 'l: toggle legend', ha='right', transform=self.axp.transAxes)
self.helptext.append(ht)
ht = self.axp.text(0.98, 0.5, 'd: toggle decibel', ha='right', transform=self.axp.transAxes)
self.helptext.append(ht)
ht = self.axp.text(0.98, 0.4, 'm: toggle mains filter', ha='right', transform=self.axp.transAxes)
self.helptext.append(ht)
ht = self.axp.text(0.98, 0.3, 'left mouse: show peak properties', ha='right', transform=self.axp.transAxes)
self.helptext.append(ht)
ht = self.axp.text(0.98, 0.2, 'shift/ctrl + left/right mouse: goto previous/next harmonic', ha='right',
transform=self.axp.transAxes)
self.helptext.append(ht)
# plot:
for ht in self.helptext:
ht.set_visible(self.help)
self.update_plots(False)
plt.show()
def __del__(self):
self.audio.close()
def remove_peak_annotation(self):
for fm in self.peak_specmarker:
fm.remove()
self.peak_specmarker = []
for fa in self.peak_annotation:
fa.remove()
self.peak_annotation = []
def annotate_peak(self, peak, harmonics=-1, inx=-1):
# marker:
if inx >= 0:
m, = self.axs.plot([self.toffset + 0.01 * self.twindow], [peak[0]], linestyle='None',
color=self.colorrange[inx % len(self.colorrange)],
marker=self.markerrange[inx], ms=10.0, mec=None, mew=0.0, zorder=2)
else:
m, = self.axs.plot([self.toffset + 0.01 * self.twindow], [peak[0]], linestyle='None',
color='k', marker='o', ms=10.0, mec=None, mew=0.0, zorder=2)
self.peak_specmarker.append(m)
# annotation:
fwidth = self.fmax - self.fmin
pl = []
pl.append(r'$f=$%.1f Hz' % peak[0])
pl.append(r'$h=$%d' % harmonics)
pl.append(r'$p=$%g' % peak[1])
pl.append(r'$c=$%.0f' % peak[2])
self.peak_annotation.append(self.axp.annotate('\n'.join(pl), xy=(peak[0], peak[1]),
xytext=(peak[0] + 0.03 * fwidth, peak[1]),
bbox=dict(boxstyle='round', facecolor='white'),
arrowprops=dict(arrowstyle='-')))
def annotate_fish(self, fish, inx=-1):
self.remove_peak_annotation()
for harmonic, freq in enumerate(fish[:, 0]):
peak = self.allpeaks[np.abs(self.allpeaks[:, 0] - freq) < 0.8 * self.deltaf, :]
if len(peak) > 0:
self.annotate_peak(peak[0, :], harmonic, inx)
self.fig.canvas.draw()
def update_plots(self, draw=True):
self.remove_peak_annotation()
# trace:
self.axt.set_xlim(self.toffset, self.toffset + self.twindow)
t0 = int(np.round(self.toffset * self.samplerate))
t1 = int(np.round((self.toffset + self.twindow) * self.samplerate))
if t1>len(self.data):
t1 = len(self.data)
time = np.arange(t0, t1) / self.samplerate
if self.trace_artist == None:
self.trace_artist, = self.axt.plot(time, self.data[t0:t1,self.channel])
else:
self.trace_artist.set_data(time, self.data[t0:t1,self.channel])
self.axt.set_ylim(self.ymin, self.ymax)
# compute power spectrum:
n_fft = nfft(self.samplerate, self.freq_resolution)
t00 = t0
t11 = t1
w = t11 - t00
minw = n_fft * (self.cfg.value('minPSDAverages') + 1) // 2
if t11 - t00 < minw:
w = minw
t11 = t00 + w
if t11 >= len(self.data):
t11 = len(self.data)
t00 = t11 - w
if t00 < 0:
t00 = 0
t11 = w
freqs, power = psd(self.data[t00:t11,self.channel], self.samplerate,
self.freq_resolution, detrend=ml.detrend_mean)
self.deltaf = freqs[1] - freqs[0]
# detect fish:
h_kwargs = psd_peak_detection_args(self.cfg)
h_kwargs.update(harmonic_groups_args(self.cfg))
self.fishlist, fzero_harmonics, self.mains, self.allpeaks, peaks, lowth, highth, center = harmonic_groups(freqs, power, verbose=self.verbose, **h_kwargs)
highth = center + highth - 0.5 * lowth
lowth = center + 0.5 * lowth
# spectrogram:
t2 = t1 + n_fft
freqs, bins, specpower = spectrogram(self.data[t0:t2,self.channel],
self.samplerate,
self.freq_resolution,
detrend=ml.detrend_mean)
z = decibel(specpower)
z = np.flipud(z)
extent = self.toffset, self.toffset + np.amax(bins), freqs[0], freqs[-1]
self.axs.set_xlim(self.toffset, self.toffset + self.twindow)
if self.spectrogram_artist == None:
self.fmax = np.round((freqs[-1] / 4.0) / 100.0) * 100.0
min = highth
min = np.percentile(z, 70.0)
max = np.percentile(z, 99.9) + 30.0
# cm = plt.get_cmap( 'hot_r' )
cm = plt.get_cmap('jet')
self.spectrogram_artist = self.axs.imshow(z, aspect='auto',
extent=extent, vmin=min, vmax=max,
cmap=cm, zorder=1)
else:
self.spectrogram_artist.set_data(z)
self.spectrogram_artist.set_extent(extent)
self.axs.set_ylim(self.fmin, self.fmax)
# power spectrum:
self.axp.set_xlim(self.fmin, self.fmax)
if self.deltaf >= 1000.0:
dfs = '%.3gkHz' % 0.001 * self.deltaf
else:
dfs = '%.3gHz' % self.deltaf
tw = float(w) / self.samplerate
if tw < 1.0:
tws = '%.3gms' % (1000.0 * tw)
else:
tws = '%.3gs' % (tw)
a = 2 * w // n_fft - 1 # number of ffts
m = ''
if self.cfg.value('mainsFreq') > 0.0:
m = ', mains=%.0fHz' % self.cfg.value('mainsFreq')
if self.power_frequency_label == None:
self.power_frequency_label = self.axp.set_xlabel(
r'Frequency [Hz] (nfft={:d}, $\Delta f$={:s}: T={:s}/{:d}{:s})'.format(n_fft, dfs, tws, a, m))
else:
self.power_frequency_label.set_text(
r'Frequency [Hz] (nfft={:d}, $\Delta f$={:s}: T={:s}/{:d}{:s})'.format(n_fft, dfs, tws, a, m))
self.axp.set_xlim(self.fmin, self.fmax)
if self.power_label == None:
self.power_label = self.axp.set_ylabel('Power')
if self.decibel:
if len(self.allpeaks) > 0:
self.allpeaks[:, 1] = decibel(self.allpeaks[:, 1])
power = decibel(power)
pmin = np.min(power[freqs < self.fmax])
pmin = np.floor(pmin / 10.0) * 10.0
pmax = np.max(power[freqs < self.fmax])
pmax = np.ceil(pmax / 10.0) * 10.0
doty = pmax - 5.0
self.power_label.set_text('Power [dB]')
self.axp.set_ylim(pmin, pmax)
else:
pmax = np.max(power[freqs < self.fmax])
doty = pmax
pmax *= 1.1
self.power_label.set_text('Power')
self.axp.set_ylim(0.0, pmax)
if self.all_peaks_artis == None:
self.all_peaks_artis, = self.axp.plot(self.allpeaks[:, 0],
np.zeros(len(self.allpeaks[:, 0])) + doty,
'o', color='#ffffff')
self.good_peaks_artist, = self.axp.plot(peaks, np.zeros(len(peaks)) + doty,
'o', color='#888888')
else:
self.all_peaks_artis.set_data(self.allpeaks[:, 0],
np.zeros(len(self.allpeaks[:, 0])) + doty)
self.good_peaks_artist.set_data(peaks, np.zeros(len(peaks)) + doty)
labels = []
fsizes = [np.sqrt(np.sum(self.fishlist[k][:, 1])) for k in range(len(self.fishlist))]
fmaxsize = np.max(fsizes) if len(fsizes) > 0 else 1.0
for k in range(len(self.peak_artists)):
self.peak_artists[k].remove()
self.peak_artists = []
for k in range(len(self.fishlist)):
if k >= len(self.markerrange):
break
fpeaks = self.fishlist[k][:, 0]
fpeakinx = [int(np.round(fp / self.deltaf)) for fp in fpeaks if fp < freqs[-1]]
fsize = 7.0 + 10.0 * (fsizes[k] / fmaxsize) ** 0.5
fishpoints, = self.axp.plot(fpeaks[:len(fpeakinx)], power[fpeakinx], linestyle='None',
color=self.colorrange[k % len(self.colorrange)],
marker=self.markerrange[k], ms=fsize,
mec=None, mew=0.0, zorder=1)
self.peak_artists.append(fishpoints)
if self.deltaf < 0.1:
labels.append('%4.2f Hz' % fpeaks[0])
elif self.deltaf < 1.0:
labels.append('%4.1f Hz' % fpeaks[0])
else:
labels.append('%4.0f Hz' % fpeaks[0])
if len(self.mains) > 0:
fpeaks = self.mains[:, 0]
fpeakinx = np.array([np.round(fp / self.deltaf) for fp in fpeaks if fp < freqs[-1]], dtype=int)
fishpoints, = self.axp.plot(fpeaks[:len(fpeakinx)],
power[fpeakinx], linestyle='None',
marker='.', color='k', ms=10, mec=None, mew=0.0, zorder=2)
self.peak_artists.append(fishpoints)
labels.append('%3.0f Hz mains' % self.cfg.value('mainsFreq'))
ncol = (len(labels)-1) // 8 + 1
self.legendhandle = self.axs.legend(self.peak_artists[:len(labels)], labels, loc='upper right', ncol=ncol)
self.legenddict = dict()
for legpoints, (finx, fish) in zip(self.legendhandle.get_lines(), enumerate(self.fishlist)):
legpoints.set_picker(8)
self.legenddict[legpoints] = [finx, fish]
self.legendhandle.set_visible(self.legend)
if self.power_artist == None:
self.power_artist, = self.axp.plot(freqs, power, 'b', zorder=3)
else:
self.power_artist.set_data(freqs, power)
if draw:
self.fig.canvas.draw()
def keypress(self, event):
# print('pressed', event.key)
if event.key in '+=X':
if self.twindow * self.samplerate > 20:
self.twindow *= 0.5
self.update_plots()
elif event.key in '-x':
if self.twindow < len(self.data) / self.samplerate:
self.twindow *= 2.0
self.update_plots()
elif event.key == 'pagedown':
if self.toffset + 0.5 * self.twindow < len(self.data) / self.samplerate:
self.toffset += 0.5 * self.twindow
self.update_plots()
elif event.key == 'pageup':
if self.toffset > 0:
self.toffset -= 0.5 * self.twindow
if self.toffset < 0.0:
self.toffset = 0.0
self.update_plots()
elif event.key == 'a':
if self.min_clip == 0.0 or self.max_clip == 0.0:
self.min_clip, self.max_clip = clip_amplitudes(
self.data[:,self.channel],
**clip_args(self.cfg, self.samplerate))
try:
if self.cfg.value('windowSize') <= 0.0:
self.cfg.set('windowSize', (len(self.data)-1)/self.samplerate)
idx0, idx1, clipped = best_window_indices(
self.data[:,self.channel], self.samplerate,
min_clip=self.min_clip, max_clip=self.max_clip,
**best_window_args(self.cfg))
if idx1 > 0:
self.toffset = idx0 / self.samplerate
self.twindow = (idx1 - idx0) / self.samplerate
self.twindow *= 2.0/(self.cfg.value('numberPSDWindows')+1.0)
self.update_plots()
except UserWarning as e:
if self.verbose > 0:
print(str(e))
elif event.key == 'ctrl+pagedown':
if self.toffset + 5.0 * self.twindow < len(self.data) / self.samplerate:
self.toffset += 5.0 * self.twindow
self.update_plots()
elif event.key == 'ctrl+pageup':
if self.toffset > 0:
self.toffset -= 5.0 * self.twindow
if self.toffset < 0.0:
self.toffset = 0.0
self.update_plots()
elif event.key == 'down':
if self.toffset + self.twindow < len(self.data) / self.samplerate:
self.toffset += 0.05 * self.twindow
self.update_plots()
elif event.key == 'up':
if self.toffset > 0.0:
self.toffset -= 0.05 * self.twindow
if self.toffset < 0.0:
self.toffset = 0.0
self.update_plots()
elif event.key == 'home':
if self.toffset > 0.0:
self.toffset = 0.0
self.update_plots()
elif event.key == 'end':
toffs = np.floor(len(self.data) / self.samplerate / self.twindow) * self.twindow
if self.toffset < toffs:
self.toffset = toffs
self.update_plots()
elif event.key == 'y':
h = self.ymax - self.ymin
c = 0.5 * (self.ymax + self.ymin)
self.ymin = c - h
self.ymax = c + h
self.axt.set_ylim(self.ymin, self.ymax)
self.fig.canvas.draw()
elif event.key == 'Y':
h = 0.25 * (self.ymax - self.ymin)
c = 0.5 * (self.ymax + self.ymin)
self.ymin = c - h
self.ymax = c + h
self.axt.set_ylim(self.ymin, self.ymax)
self.fig.canvas.draw()
elif event.key == 'v':
t0 = int(np.round(self.toffset * self.samplerate))
t1 = int(np.round((self.toffset + self.twindow) * self.samplerate))
min = np.min(self.data[t0:t1,self.channel])
max = np.max(self.data[t0:t1,self.channel])
h = 0.5 * (max - min)
c = 0.5 * (max + min)
self.ymin = c - h
self.ymax = c + h
self.axt.set_ylim(self.ymin, self.ymax)
self.fig.canvas.draw()
elif event.key == 'V':
self.ymin = -1.0
self.ymax = +1.0
self.axt.set_ylim(self.ymin, self.ymax)
self.fig.canvas.draw()
elif event.key == 'left':
if self.fmin > 0.0:
fwidth = self.fmax - self.fmin
self.fmin -= 0.5 * fwidth
self.fmax -= 0.5 * fwidth
if self.fmin < 0.0:
self.fmin = 0.0
self.fmax = fwidth
self.axs.set_ylim(self.fmin, self.fmax)
self.axp.set_xlim(self.fmin, self.fmax)
self.fig.canvas.draw()
elif event.key == 'right':
if self.fmax < 0.5 * self.samplerate:
fwidth = self.fmax - self.fmin
self.fmin += 0.5 * fwidth
self.fmax += 0.5 * fwidth
self.axs.set_ylim(self.fmin, self.fmax)
self.axp.set_xlim(self.fmin, self.fmax)
self.fig.canvas.draw()
elif event.key == 'ctrl+left':
if self.fmin > 0.0:
fwidth = self.fmax - self.fmin
self.fmin = 0.0
self.fmax = fwidth
self.axs.set_ylim(self.fmin, self.fmax)
self.axp.set_xlim(self.fmin, self.fmax)
self.fig.canvas.draw()
elif event.key == 'ctrl+right':
if self.fmax < 0.5 * self.samplerate:
fwidth = self.fmax - self.fmin
fm = 0.5 * self.samplerate
self.fmax = np.ceil(fm / fwidth) * fwidth
self.fmin = self.fmax - fwidth
if self.fmin < 0.0:
self.fmin = 0.0
self.fmax = fwidth
self.axs.set_ylim(self.fmin, self.fmax)
self.axp.set_xlim(self.fmin, self.fmax)
self.fig.canvas.draw()
elif event.key in 'f':
if self.fmax < 0.5 * self.samplerate or self.fmin > 0.0:
fwidth = self.fmax - self.fmin
if self.fmax < 0.5 * self.samplerate:
self.fmax = self.fmin + 2.0 * fwidth
elif self.fmin > 0.0:
self.fmin = self.fmax - 2.0 * fwidth
if self.fmin < 0.0:
self.fmin = 0.0
self.fmax = 2.0 * fwidth
self.axs.set_ylim(self.fmin, self.fmax)
self.axp.set_xlim(self.fmin, self.fmax)
self.fig.canvas.draw()
elif event.key in 'F':
if self.fmax - self.fmin > 1.0:
fwidth = self.fmax - self.fmin
self.fmax = self.fmin + 0.5 * fwidth
self.axs.set_ylim(self.fmin, self.fmax)
self.axp.set_xlim(self.fmin, self.fmax)
self.fig.canvas.draw()
elif event.key in 'r':
if self.freq_resolution < 1000.0:
self.freq_resolution *= 2.0
self.update_plots()
elif event.key in 'R':
if 1.0 / self.freq_resolution < self.tmax:
self.freq_resolution *= 0.5
self.update_plots()
elif event.key in 'd':
self.decibel = not self.decibel
self.update_plots()
elif event.key in 'm':
if self.cfg.value('mainsFreq') == 0.0:
self.cfg.set('mainsFreq', self.mains_freq)
else:
self.cfg.set('mainsFreq', 0.0)
self.update_plots()
elif event.key in 't':
t_diff = self.cfg.value('highThresholdFactor') - self.cfg.value('lowThresholdFactor')
self.cfg.set('lowThresholdFactor', self.cfg.value('lowThresholdFactor') - 0.1)
if self.cfg.value('lowThresholdFactor') < 0.1:
self.cfg.set('lowThresholdFactor', 0.1)
self.cfg.set('highThresholdFactor', self.cfg.value('lowThresholdFactor') + t_diff)
print('lowThresholdFactor =', self.cfg.value('lowThresholdFactor'))
self.update_plots()
elif event.key in 'T':
t_diff = self.cfg.value('highThresholdFactor') - self.cfg.value('lowThresholdFactor')
self.cfg.set('lowThresholdFactor', self.cfg.value('lowThresholdFactor') + 0.1)
if self.cfg.value('lowThresholdFactor') > 20.0:
self.cfg.set('lowThresholdFactor', 20.0)
self.cfg.set('highThresholdFactor', self.cfg.value('lowThresholdFactor') + t_diff)
print('lowThresholdFactor =', self.cfg.value('lowThresholdFactor'))
self.update_plots()
elif event.key == 'escape':
self.remove_peak_annotation()
self.fig.canvas.draw()
elif event.key in 'h':
self.help = not self.help
for ht in self.helptext:
ht.set_visible(self.help)
self.fig.canvas.draw()
elif event.key in 'l':
self.legend = not self.legend
self.legendhandle.set_visible(self.legend)
self.fig.canvas.draw()
elif event.key in 'w':
self.plot_waveform()
elif event.key in 'p':
self.play_segment()
elif event.key in 'P':
self.play_all()
elif event.key in '1' :
self.play_tone('c3')
elif event.key in '2' :
self.play_tone('a3')
elif event.key in '3' :
self.play_tone('e4')
elif event.key in '4' :
self.play_tone('a4')
elif event.key in '5' :
self.play_tone('c5')
elif event.key in '6' :
self.play_tone('e5')
elif event.key in '7' :
self.play_tone('g5')
elif event.key in '8' :
self.play_tone('a5')
elif event.key in '9' :
self.play_tone('c6')
elif event.key in 'S':
self.save_segment()
def buttonpress( self, event ) :
# print('mouse pressed', event.button, event.key, event.step)
if event.inaxes == self.axp:
if event.key == 'shift' or event.key == 'control':
# show next or previous harmonic:
if event.key == 'shift':
if event.button == 1:
ftarget = event.xdata / 2.0
elif event.button == 3:
ftarget = event.xdata * 2.0
else:
if event.button == 1:
ftarget = event.xdata / 1.5
elif event.button == 3:
ftarget = event.xdata * 1.5
foffs = event.xdata - self.fmin
fwidth = self.fmax - self.fmin
self.fmin = ftarget - foffs
self.fmax = self.fmin + fwidth
self.axs.set_ylim(self.fmin, self.fmax)
self.axp.set_xlim(self.fmin, self.fmax)
self.fig.canvas.draw()
else:
# put label on peak
self.remove_peak_annotation()
# find closest peak:
fwidth = self.fmax - self.fmin
peakdist = np.abs(self.allpeaks[:, 0] - event.xdata)
inx = np.argmin(peakdist)
if peakdist[inx] < 0.005 * fwidth:
peak = self.allpeaks[inx, :]
# find fish:
foundfish = False
for finx, fish in enumerate(self.fishlist):
if np.min(np.abs(fish[:, 0] - peak[0])) < 0.8 * self.deltaf:
self.annotate_fish(fish, finx)
foundfish = True
break
if not foundfish:
self.annotate_peak(peak)
self.fig.canvas.draw()
else:
self.fig.canvas.draw()
def onpick(self, event):
# print('pick')
legendpoint = event.artist
finx, fish = self.legenddict[legendpoint]
self.annotate_fish(fish, finx)
def resize(self, event):
# print('resized', event.width, event.height)
leftpixel = 80.0
rightpixel = 20.0
xaxispixel = 50.0
toppixel = 20.0
timeaxis = 0.42
left = leftpixel / event.width
width = 1.0 - left - rightpixel / event.width
xaxis = xaxispixel / event.height
top = toppixel / event.height
height = (1.0 - timeaxis - top) / 2.0
if left < 0.5 and width < 1.0 and xaxis < 0.3 and top < 0.2:
self.axt.set_position([left, timeaxis + height, width, height])
self.axs.set_position([left, timeaxis, width, height])
self.axp.set_position([left, xaxis, width, timeaxis - 2.0 * xaxis])
def plot_waveform(self):
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
name = self.filename.split('.')[0]
if self.channel > 0:
ax.set_title('{filename} channel={channel:d}'.format(
filename=self.filename, channel=self.channel))
figfile = '{name}-{channel:d}-{time:.4g}s-waveform.png'.format(
name=name, channel=self.channel, time=self.toffset)
else:
ax.set_title(self.filename)
figfile = '{name}-{time:.4g}s-waveform.png'.format(
name=name, time=self.toffset)
t0 = int(np.round(self.toffset * self.samplerate))
t1 = int(np.round((self.toffset + self.twindow) * self.samplerate))
if t1>len(self.data):
t1 = len(self.data)
time = np.arange(t0, t1) / self.samplerate
if self.twindow < 1.0:
ax.set_xlabel('Time [ms]')
ax.set_xlim(1000.0 * self.toffset,
1000.0 * (self.toffset + self.twindow))
ax.plot(1000.0 * time, self.data[t0:t1,self.channel])
else:
ax.set_xlabel('Time [s]')
ax.set_xlim(self.toffset, self.toffset + self.twindow)
ax.plot(time, self.data[t0:t1,self.channel])
ax.set_ylabel('Amplitude [{:s}]'.format(self.unit))
fig.tight_layout()
fig.savefig(figfile)
fig.clear()
plt.close(fig)
print('saved waveform figure to', figfile)
def play_segment(self):
t0 = int(np.round(self.toffset * self.samplerate))
t1 = int(np.round((self.toffset + self.twindow) * self.samplerate))
playdata = 1.0 * self.data[t0:t1,self.channel]
fade(playdata, self.samplerate, 0.1)
self.audio.play(playdata, self.samplerate, blocking=False)
def save_segment(self):
t0s = int(np.round(self.toffset))
t1s = int(np.round(self.toffset + self.twindow))
t0 = int(np.round(self.toffset * self.samplerate))
t1 = int(np.round((self.toffset + self.twindow) * self.samplerate))
savedata = 1.0 * self.data[t0:t1,self.channel]
filename = self.filename.split('.')[0]
segmentfilename = '{name}-{time0:.4g}s-{time1:.4g}s.wav'.format(
name=filename, time0=t0s, time1 = t1s)
write_audio(segmentfilename, savedata, self.data.samplerate)
print('saved segment to: ' , segmentfilename)
def play_all(self):
self.audio.play(self.data[:,self.channel], self.samplerate,
blocking=False)
def play_tone( self, frequency ) :
self.audio.beep(1.0, frequency)
def short_user_warning(message, category, filename, lineno, file=None, line=''):
if file is None:
file = sys.stderr
if category == UserWarning:
file.write('%s line %d: %s\n' % ('/'.join(filename.split('/')[-2:]), lineno, message))
else:
s = warnings.formatwarning(message, category, filename, lineno, line)
file.write(s)
def main(cargs=None):
warnings.showwarning = short_user_warning
# config file name:
cfgfile = __package__ + '.cfg'
# command line arguments:
if cargs is None:
cargs = sys.argv[1:]
parser = argparse.ArgumentParser(
description='Display waveform, and power spectrum with detected fundamental frequencies of EOD recordings.',
epilog='version %s by Jan Benda (2015-%s)' % (__version__, __year__))
parser.add_argument('--version', action='version', version=__version__)
parser.add_argument('-v', action='count', dest='verbose')
parser.add_argument('-c', '--save-config', nargs='?', default='', const=cfgfile,
type=str, metavar='cfgfile',
help='save configuration to file cfgfile (defaults to {0})'.format(cfgfile))
parser.add_argument('file', nargs='?', default='', type=str,
help='name of the file with the time series data')
parser.add_argument('channel', nargs='?', default=0, type=int,
help='channel to be displayed')
args = parser.parse_args(cargs)
filepath = args.file
# set verbosity level from command line:
verbose = 0
if args.verbose != None:
verbose = args.verbose
if len(args.save_config):
# save configuration:
cfg = configuration()
cfg.load_files(cfgfile, filepath, 4, verbose)
save_configuration(cfg, cfgfile)
return
elif len(filepath) == 0:
parser.error('you need to specify a file containing some data')
# load configuration:
cfg = configuration()
cfg.load_files(cfgfile, filepath, 4, verbose-1)
# load data:
filename = os.path.basename(filepath)
channel = args.channel
# TODO: add blocksize and backsize as configuration parameter!
with DataLoader(filepath, 60.0, 10.0, verbose) as data:
SignalPlot(data, data.samplerate, data.unit, filename, channel, verbose, cfg)
if __name__ == '__main__':
main()
# 50301L02.WAV t=9 bis 9.15 sec
## 1 fish:
# simple aptero (clipped):
# python fishfinder.py ~/data/fishgrid/Panama2014/MP3_1/20140517_RioCanita/40517L14.WAV
# nice sterno:
# python fishfinder.py ~/data/fishgrid/Panama2014/MP3_1/20140517_RioCanita/40517L31.WAV
# sterno (clipped) with a little bit of background:
# python fishfinder.py ~/data/fishgrid/Panama2014/MP3_1/20140517_RioCanita/40517L26.WAV
# simple brachy (clipped, with a very small one in the background): still difficult, but great with T=4s
# python fishfinder.py ~/data/fishgrid/Panama2014/MP3_1/20140517_RioCanita/40517L30.WAV
# eigenmannia (very nice): EN086.MP3
# single, very nice brachy, with difficult psd:
# python fishfinder.py ~/data/fishgrid/Panama2014/MP3_1/20140517_RioCanita/40517L19.WAV
# python fishfinder.py ~/data/fishgrid/Panama2014/MP3_1/20140517_RioCanita/40517L2[789].WAV
## 2 fish:
# 2 aptero:
# python fishfinder.py ~/data/fishgrid/Panama2014/MP3_1/20140517_RioCanita/40517L10.WAV
# EN098.MP3 and in particular EN099.MP3 nice 2Hz beat!
# 2 brachy beat:
# python fishfinder.py ~/data/fishgrid/Panama2014/MP3_1/20140517_RioCanita/40517L08.WAV
# >= 2 brachys:
# python fishfinder.py ~/data/fishgrid/Panama2014/MP3_1/20140517_RioCanita/40517L2[12789].WAV
## one sterno with weak aptero:
# python fishfinder.py ~/data/fishgrid/Panama2014/MP3_1/20140517_RioCanita/40517L11.WAV
# EN144.MP3
## 2 and 2 fish:
# python fishfinder.py ~/data/fishgrid/Panama2014/MP3_1/20140517_RioCanita/40517L12.WAV
## one aptero with brachy:
# EN148
## lots of fish:
# python fishfinder.py ~/data/fishgrid/Panama2014/MP3_1/20140517_RioCanita/40517L07.WAV
# EN065.MP3 EN066.MP3 EN067.MP3 EN103.MP3 EN104.MP3
# EN109: 1Hz beat!!!!
# EN013: doppel detection of 585 Hz
# EN015,30,31: noise estimate problem
# EN083.MP3 aptero glitch
# EN146 sek 4 sterno frequency glitch
# EN056.MP3 EN080.MP3 difficult low frequencies
# EN072.MP3 unstable low and high freq
# EN122.MP3 background fish detection difficulties at low res
# problems: EN088, EN089, 20140524_RioCanita/EN055 sterno not catched, EN056, EN059
Functions
def short_user_warning(message, category, filename, lineno, file=None, line='')
-
Expand source code
def short_user_warning(message, category, filename, lineno, file=None, line=''): if file is None: file = sys.stderr if category == UserWarning: file.write('%s line %d: %s\n' % ('/'.join(filename.split('/')[-2:]), lineno, message)) else: s = warnings.formatwarning(message, category, filename, lineno, line) file.write(s)
def main(cargs=None)
-
Expand source code
def main(cargs=None): warnings.showwarning = short_user_warning # config file name: cfgfile = __package__ + '.cfg' # command line arguments: if cargs is None: cargs = sys.argv[1:] parser = argparse.ArgumentParser( description='Display waveform, and power spectrum with detected fundamental frequencies of EOD recordings.', epilog='version %s by Jan Benda (2015-%s)' % (__version__, __year__)) parser.add_argument('--version', action='version', version=__version__) parser.add_argument('-v', action='count', dest='verbose') parser.add_argument('-c', '--save-config', nargs='?', default='', const=cfgfile, type=str, metavar='cfgfile', help='save configuration to file cfgfile (defaults to {0})'.format(cfgfile)) parser.add_argument('file', nargs='?', default='', type=str, help='name of the file with the time series data') parser.add_argument('channel', nargs='?', default=0, type=int, help='channel to be displayed') args = parser.parse_args(cargs) filepath = args.file # set verbosity level from command line: verbose = 0 if args.verbose != None: verbose = args.verbose if len(args.save_config): # save configuration: cfg = configuration() cfg.load_files(cfgfile, filepath, 4, verbose) save_configuration(cfg, cfgfile) return elif len(filepath) == 0: parser.error('you need to specify a file containing some data') # load configuration: cfg = configuration() cfg.load_files(cfgfile, filepath, 4, verbose-1) # load data: filename = os.path.basename(filepath) channel = args.channel # TODO: add blocksize and backsize as configuration parameter! with DataLoader(filepath, 60.0, 10.0, verbose) as data: SignalPlot(data, data.samplerate, data.unit, filename, channel, verbose, cfg)
Classes
class SignalPlot (data, samplingrate, unit, filename, channel, verbose, cfg)
-
Expand source code
class SignalPlot: def __init__(self, data, samplingrate, unit, filename, channel, verbose, cfg): self.filename = filename self.channel = channel self.samplerate = samplingrate self.data = data self.unit = unit self.cfg = cfg self.verbose = verbose self.tmax = (len(self.data)-1)/self.samplerate self.toffset = 0.0 self.twindow = 8.0 if self.twindow > self.tmax: self.twindow = np.round(2 ** (np.floor(np.log(self.tmax) / np.log(2.0)) + 1.0)) self.ymin = -1.0 self.ymax = +1.0 self.trace_artist = None self.spectrogram_artist = None self.fmin = 0.0 self.fmax = 0.0 self.decibel = True self.freq_resolution = self.cfg.value('frequencyResolution') self.deltaf = 1.0 self.mains_freq = self.cfg.value('mainsFreq') self.power_label = None self.all_peaks_artis = None self.good_peaks_artist = None self.power_artist = None self.power_frequency_label = None self.peak_artists = [] self.legend = True self.legendhandle = None self.help = False self.helptext = [] self.allpeaks = [] self.fishlist = [] self.mains = [] self.peak_specmarker = [] self.peak_annotation = [] self.min_clip = self.cfg.value('minClipAmplitude') self.max_clip = self.cfg.value('maxClipAmplitude') self.colorrange, self.markerrange = colors_markers() # audio output: self.audio = PlayAudio() # set key bindings: plt.rcParams['keymap.fullscreen'] = 'ctrl+f' plt.rcParams['keymap.pan'] = 'ctrl+m' plt.rcParams['keymap.quit'] = 'ctrl+w, alt+q, q' plt.rcParams['keymap.yscale'] = '' plt.rcParams['keymap.xscale'] = '' plt.rcParams['keymap.grid'] = '' #plt.rcParams['keymap.all_axes'] = '' # the figure: plt.ioff() self.fig = plt.figure(figsize=(15, 9)) self.fig.canvas.manager.set_window_title(self.filename + ' channel {0:d}'.format(self.channel)) self.fig.canvas.mpl_connect('key_press_event', self.keypress) self.fig.canvas.mpl_connect('button_press_event', self.buttonpress) self.fig.canvas.mpl_connect('pick_event', self.onpick) self.fig.canvas.mpl_connect('resize_event', self.resize) # trace plot: self.axt = self.fig.add_axes([0.1, 0.7, 0.87, 0.25]) self.axt.set_ylabel('Amplitude [{:s}]'.format(self.unit)) ht = self.axt.text(0.98, 0.05, '(ctrl+) page and arrow up, down, home, end: scroll', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.15, '+, -, X, x: zoom in/out', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.25, 'y,Y,v,V: zoom amplitudes', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.35, 'p,P: play audio (display,all)', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.45, 'ctrl-f: full screen', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.55, 'w: plot waveform into png file', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.65, 's: save figure', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.75, 'S: save audiosegment', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.85, 'q: quit', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.95, 'h: toggle this help', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) self.axt.set_xticklabels([]) # spectrogram: self.axs = self.fig.add_axes([0.1, 0.45, 0.87, 0.25]) self.axs.set_xlabel('Time [seconds]') self.axs.set_ylabel('Frequency [Hz]') # power spectrum: self.axp = self.fig.add_axes([0.1, 0.1, 0.87, 0.25]) ht = self.axp.text(0.98, 0.9, 'r, R: frequency resolution', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.8, 'f, F: zoom', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.7, '(ctrl+) left, right: move', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.6, 'l: toggle legend', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.5, 'd: toggle decibel', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.4, 'm: toggle mains filter', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.3, 'left mouse: show peak properties', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.2, 'shift/ctrl + left/right mouse: goto previous/next harmonic', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) # plot: for ht in self.helptext: ht.set_visible(self.help) self.update_plots(False) plt.show() def __del__(self): self.audio.close() def remove_peak_annotation(self): for fm in self.peak_specmarker: fm.remove() self.peak_specmarker = [] for fa in self.peak_annotation: fa.remove() self.peak_annotation = [] def annotate_peak(self, peak, harmonics=-1, inx=-1): # marker: if inx >= 0: m, = self.axs.plot([self.toffset + 0.01 * self.twindow], [peak[0]], linestyle='None', color=self.colorrange[inx % len(self.colorrange)], marker=self.markerrange[inx], ms=10.0, mec=None, mew=0.0, zorder=2) else: m, = self.axs.plot([self.toffset + 0.01 * self.twindow], [peak[0]], linestyle='None', color='k', marker='o', ms=10.0, mec=None, mew=0.0, zorder=2) self.peak_specmarker.append(m) # annotation: fwidth = self.fmax - self.fmin pl = [] pl.append(r'$f=$%.1f Hz' % peak[0]) pl.append(r'$h=$%d' % harmonics) pl.append(r'$p=$%g' % peak[1]) pl.append(r'$c=$%.0f' % peak[2]) self.peak_annotation.append(self.axp.annotate('\n'.join(pl), xy=(peak[0], peak[1]), xytext=(peak[0] + 0.03 * fwidth, peak[1]), bbox=dict(boxstyle='round', facecolor='white'), arrowprops=dict(arrowstyle='-'))) def annotate_fish(self, fish, inx=-1): self.remove_peak_annotation() for harmonic, freq in enumerate(fish[:, 0]): peak = self.allpeaks[np.abs(self.allpeaks[:, 0] - freq) < 0.8 * self.deltaf, :] if len(peak) > 0: self.annotate_peak(peak[0, :], harmonic, inx) self.fig.canvas.draw() def update_plots(self, draw=True): self.remove_peak_annotation() # trace: self.axt.set_xlim(self.toffset, self.toffset + self.twindow) t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) if t1>len(self.data): t1 = len(self.data) time = np.arange(t0, t1) / self.samplerate if self.trace_artist == None: self.trace_artist, = self.axt.plot(time, self.data[t0:t1,self.channel]) else: self.trace_artist.set_data(time, self.data[t0:t1,self.channel]) self.axt.set_ylim(self.ymin, self.ymax) # compute power spectrum: n_fft = nfft(self.samplerate, self.freq_resolution) t00 = t0 t11 = t1 w = t11 - t00 minw = n_fft * (self.cfg.value('minPSDAverages') + 1) // 2 if t11 - t00 < minw: w = minw t11 = t00 + w if t11 >= len(self.data): t11 = len(self.data) t00 = t11 - w if t00 < 0: t00 = 0 t11 = w freqs, power = psd(self.data[t00:t11,self.channel], self.samplerate, self.freq_resolution, detrend=ml.detrend_mean) self.deltaf = freqs[1] - freqs[0] # detect fish: h_kwargs = psd_peak_detection_args(self.cfg) h_kwargs.update(harmonic_groups_args(self.cfg)) self.fishlist, fzero_harmonics, self.mains, self.allpeaks, peaks, lowth, highth, center = harmonic_groups(freqs, power, verbose=self.verbose, **h_kwargs) highth = center + highth - 0.5 * lowth lowth = center + 0.5 * lowth # spectrogram: t2 = t1 + n_fft freqs, bins, specpower = spectrogram(self.data[t0:t2,self.channel], self.samplerate, self.freq_resolution, detrend=ml.detrend_mean) z = decibel(specpower) z = np.flipud(z) extent = self.toffset, self.toffset + np.amax(bins), freqs[0], freqs[-1] self.axs.set_xlim(self.toffset, self.toffset + self.twindow) if self.spectrogram_artist == None: self.fmax = np.round((freqs[-1] / 4.0) / 100.0) * 100.0 min = highth min = np.percentile(z, 70.0) max = np.percentile(z, 99.9) + 30.0 # cm = plt.get_cmap( 'hot_r' ) cm = plt.get_cmap('jet') self.spectrogram_artist = self.axs.imshow(z, aspect='auto', extent=extent, vmin=min, vmax=max, cmap=cm, zorder=1) else: self.spectrogram_artist.set_data(z) self.spectrogram_artist.set_extent(extent) self.axs.set_ylim(self.fmin, self.fmax) # power spectrum: self.axp.set_xlim(self.fmin, self.fmax) if self.deltaf >= 1000.0: dfs = '%.3gkHz' % 0.001 * self.deltaf else: dfs = '%.3gHz' % self.deltaf tw = float(w) / self.samplerate if tw < 1.0: tws = '%.3gms' % (1000.0 * tw) else: tws = '%.3gs' % (tw) a = 2 * w // n_fft - 1 # number of ffts m = '' if self.cfg.value('mainsFreq') > 0.0: m = ', mains=%.0fHz' % self.cfg.value('mainsFreq') if self.power_frequency_label == None: self.power_frequency_label = self.axp.set_xlabel( r'Frequency [Hz] (nfft={:d}, $\Delta f$={:s}: T={:s}/{:d}{:s})'.format(n_fft, dfs, tws, a, m)) else: self.power_frequency_label.set_text( r'Frequency [Hz] (nfft={:d}, $\Delta f$={:s}: T={:s}/{:d}{:s})'.format(n_fft, dfs, tws, a, m)) self.axp.set_xlim(self.fmin, self.fmax) if self.power_label == None: self.power_label = self.axp.set_ylabel('Power') if self.decibel: if len(self.allpeaks) > 0: self.allpeaks[:, 1] = decibel(self.allpeaks[:, 1]) power = decibel(power) pmin = np.min(power[freqs < self.fmax]) pmin = np.floor(pmin / 10.0) * 10.0 pmax = np.max(power[freqs < self.fmax]) pmax = np.ceil(pmax / 10.0) * 10.0 doty = pmax - 5.0 self.power_label.set_text('Power [dB]') self.axp.set_ylim(pmin, pmax) else: pmax = np.max(power[freqs < self.fmax]) doty = pmax pmax *= 1.1 self.power_label.set_text('Power') self.axp.set_ylim(0.0, pmax) if self.all_peaks_artis == None: self.all_peaks_artis, = self.axp.plot(self.allpeaks[:, 0], np.zeros(len(self.allpeaks[:, 0])) + doty, 'o', color='#ffffff') self.good_peaks_artist, = self.axp.plot(peaks, np.zeros(len(peaks)) + doty, 'o', color='#888888') else: self.all_peaks_artis.set_data(self.allpeaks[:, 0], np.zeros(len(self.allpeaks[:, 0])) + doty) self.good_peaks_artist.set_data(peaks, np.zeros(len(peaks)) + doty) labels = [] fsizes = [np.sqrt(np.sum(self.fishlist[k][:, 1])) for k in range(len(self.fishlist))] fmaxsize = np.max(fsizes) if len(fsizes) > 0 else 1.0 for k in range(len(self.peak_artists)): self.peak_artists[k].remove() self.peak_artists = [] for k in range(len(self.fishlist)): if k >= len(self.markerrange): break fpeaks = self.fishlist[k][:, 0] fpeakinx = [int(np.round(fp / self.deltaf)) for fp in fpeaks if fp < freqs[-1]] fsize = 7.0 + 10.0 * (fsizes[k] / fmaxsize) ** 0.5 fishpoints, = self.axp.plot(fpeaks[:len(fpeakinx)], power[fpeakinx], linestyle='None', color=self.colorrange[k % len(self.colorrange)], marker=self.markerrange[k], ms=fsize, mec=None, mew=0.0, zorder=1) self.peak_artists.append(fishpoints) if self.deltaf < 0.1: labels.append('%4.2f Hz' % fpeaks[0]) elif self.deltaf < 1.0: labels.append('%4.1f Hz' % fpeaks[0]) else: labels.append('%4.0f Hz' % fpeaks[0]) if len(self.mains) > 0: fpeaks = self.mains[:, 0] fpeakinx = np.array([np.round(fp / self.deltaf) for fp in fpeaks if fp < freqs[-1]], dtype=int) fishpoints, = self.axp.plot(fpeaks[:len(fpeakinx)], power[fpeakinx], linestyle='None', marker='.', color='k', ms=10, mec=None, mew=0.0, zorder=2) self.peak_artists.append(fishpoints) labels.append('%3.0f Hz mains' % self.cfg.value('mainsFreq')) ncol = (len(labels)-1) // 8 + 1 self.legendhandle = self.axs.legend(self.peak_artists[:len(labels)], labels, loc='upper right', ncol=ncol) self.legenddict = dict() for legpoints, (finx, fish) in zip(self.legendhandle.get_lines(), enumerate(self.fishlist)): legpoints.set_picker(8) self.legenddict[legpoints] = [finx, fish] self.legendhandle.set_visible(self.legend) if self.power_artist == None: self.power_artist, = self.axp.plot(freqs, power, 'b', zorder=3) else: self.power_artist.set_data(freqs, power) if draw: self.fig.canvas.draw() def keypress(self, event): # print('pressed', event.key) if event.key in '+=X': if self.twindow * self.samplerate > 20: self.twindow *= 0.5 self.update_plots() elif event.key in '-x': if self.twindow < len(self.data) / self.samplerate: self.twindow *= 2.0 self.update_plots() elif event.key == 'pagedown': if self.toffset + 0.5 * self.twindow < len(self.data) / self.samplerate: self.toffset += 0.5 * self.twindow self.update_plots() elif event.key == 'pageup': if self.toffset > 0: self.toffset -= 0.5 * self.twindow if self.toffset < 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'a': if self.min_clip == 0.0 or self.max_clip == 0.0: self.min_clip, self.max_clip = clip_amplitudes( self.data[:,self.channel], **clip_args(self.cfg, self.samplerate)) try: if self.cfg.value('windowSize') <= 0.0: self.cfg.set('windowSize', (len(self.data)-1)/self.samplerate) idx0, idx1, clipped = best_window_indices( self.data[:,self.channel], self.samplerate, min_clip=self.min_clip, max_clip=self.max_clip, **best_window_args(self.cfg)) if idx1 > 0: self.toffset = idx0 / self.samplerate self.twindow = (idx1 - idx0) / self.samplerate self.twindow *= 2.0/(self.cfg.value('numberPSDWindows')+1.0) self.update_plots() except UserWarning as e: if self.verbose > 0: print(str(e)) elif event.key == 'ctrl+pagedown': if self.toffset + 5.0 * self.twindow < len(self.data) / self.samplerate: self.toffset += 5.0 * self.twindow self.update_plots() elif event.key == 'ctrl+pageup': if self.toffset > 0: self.toffset -= 5.0 * self.twindow if self.toffset < 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'down': if self.toffset + self.twindow < len(self.data) / self.samplerate: self.toffset += 0.05 * self.twindow self.update_plots() elif event.key == 'up': if self.toffset > 0.0: self.toffset -= 0.05 * self.twindow if self.toffset < 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'home': if self.toffset > 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'end': toffs = np.floor(len(self.data) / self.samplerate / self.twindow) * self.twindow if self.toffset < toffs: self.toffset = toffs self.update_plots() elif event.key == 'y': h = self.ymax - self.ymin c = 0.5 * (self.ymax + self.ymin) self.ymin = c - h self.ymax = c + h self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'Y': h = 0.25 * (self.ymax - self.ymin) c = 0.5 * (self.ymax + self.ymin) self.ymin = c - h self.ymax = c + h self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'v': t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) min = np.min(self.data[t0:t1,self.channel]) max = np.max(self.data[t0:t1,self.channel]) h = 0.5 * (max - min) c = 0.5 * (max + min) self.ymin = c - h self.ymax = c + h self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'V': self.ymin = -1.0 self.ymax = +1.0 self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'left': if self.fmin > 0.0: fwidth = self.fmax - self.fmin self.fmin -= 0.5 * fwidth self.fmax -= 0.5 * fwidth if self.fmin < 0.0: self.fmin = 0.0 self.fmax = fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key == 'right': if self.fmax < 0.5 * self.samplerate: fwidth = self.fmax - self.fmin self.fmin += 0.5 * fwidth self.fmax += 0.5 * fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key == 'ctrl+left': if self.fmin > 0.0: fwidth = self.fmax - self.fmin self.fmin = 0.0 self.fmax = fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key == 'ctrl+right': if self.fmax < 0.5 * self.samplerate: fwidth = self.fmax - self.fmin fm = 0.5 * self.samplerate self.fmax = np.ceil(fm / fwidth) * fwidth self.fmin = self.fmax - fwidth if self.fmin < 0.0: self.fmin = 0.0 self.fmax = fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key in 'f': if self.fmax < 0.5 * self.samplerate or self.fmin > 0.0: fwidth = self.fmax - self.fmin if self.fmax < 0.5 * self.samplerate: self.fmax = self.fmin + 2.0 * fwidth elif self.fmin > 0.0: self.fmin = self.fmax - 2.0 * fwidth if self.fmin < 0.0: self.fmin = 0.0 self.fmax = 2.0 * fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key in 'F': if self.fmax - self.fmin > 1.0: fwidth = self.fmax - self.fmin self.fmax = self.fmin + 0.5 * fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key in 'r': if self.freq_resolution < 1000.0: self.freq_resolution *= 2.0 self.update_plots() elif event.key in 'R': if 1.0 / self.freq_resolution < self.tmax: self.freq_resolution *= 0.5 self.update_plots() elif event.key in 'd': self.decibel = not self.decibel self.update_plots() elif event.key in 'm': if self.cfg.value('mainsFreq') == 0.0: self.cfg.set('mainsFreq', self.mains_freq) else: self.cfg.set('mainsFreq', 0.0) self.update_plots() elif event.key in 't': t_diff = self.cfg.value('highThresholdFactor') - self.cfg.value('lowThresholdFactor') self.cfg.set('lowThresholdFactor', self.cfg.value('lowThresholdFactor') - 0.1) if self.cfg.value('lowThresholdFactor') < 0.1: self.cfg.set('lowThresholdFactor', 0.1) self.cfg.set('highThresholdFactor', self.cfg.value('lowThresholdFactor') + t_diff) print('lowThresholdFactor =', self.cfg.value('lowThresholdFactor')) self.update_plots() elif event.key in 'T': t_diff = self.cfg.value('highThresholdFactor') - self.cfg.value('lowThresholdFactor') self.cfg.set('lowThresholdFactor', self.cfg.value('lowThresholdFactor') + 0.1) if self.cfg.value('lowThresholdFactor') > 20.0: self.cfg.set('lowThresholdFactor', 20.0) self.cfg.set('highThresholdFactor', self.cfg.value('lowThresholdFactor') + t_diff) print('lowThresholdFactor =', self.cfg.value('lowThresholdFactor')) self.update_plots() elif event.key == 'escape': self.remove_peak_annotation() self.fig.canvas.draw() elif event.key in 'h': self.help = not self.help for ht in self.helptext: ht.set_visible(self.help) self.fig.canvas.draw() elif event.key in 'l': self.legend = not self.legend self.legendhandle.set_visible(self.legend) self.fig.canvas.draw() elif event.key in 'w': self.plot_waveform() elif event.key in 'p': self.play_segment() elif event.key in 'P': self.play_all() elif event.key in '1' : self.play_tone('c3') elif event.key in '2' : self.play_tone('a3') elif event.key in '3' : self.play_tone('e4') elif event.key in '4' : self.play_tone('a4') elif event.key in '5' : self.play_tone('c5') elif event.key in '6' : self.play_tone('e5') elif event.key in '7' : self.play_tone('g5') elif event.key in '8' : self.play_tone('a5') elif event.key in '9' : self.play_tone('c6') elif event.key in 'S': self.save_segment() def buttonpress( self, event ) : # print('mouse pressed', event.button, event.key, event.step) if event.inaxes == self.axp: if event.key == 'shift' or event.key == 'control': # show next or previous harmonic: if event.key == 'shift': if event.button == 1: ftarget = event.xdata / 2.0 elif event.button == 3: ftarget = event.xdata * 2.0 else: if event.button == 1: ftarget = event.xdata / 1.5 elif event.button == 3: ftarget = event.xdata * 1.5 foffs = event.xdata - self.fmin fwidth = self.fmax - self.fmin self.fmin = ftarget - foffs self.fmax = self.fmin + fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() else: # put label on peak self.remove_peak_annotation() # find closest peak: fwidth = self.fmax - self.fmin peakdist = np.abs(self.allpeaks[:, 0] - event.xdata) inx = np.argmin(peakdist) if peakdist[inx] < 0.005 * fwidth: peak = self.allpeaks[inx, :] # find fish: foundfish = False for finx, fish in enumerate(self.fishlist): if np.min(np.abs(fish[:, 0] - peak[0])) < 0.8 * self.deltaf: self.annotate_fish(fish, finx) foundfish = True break if not foundfish: self.annotate_peak(peak) self.fig.canvas.draw() else: self.fig.canvas.draw() def onpick(self, event): # print('pick') legendpoint = event.artist finx, fish = self.legenddict[legendpoint] self.annotate_fish(fish, finx) def resize(self, event): # print('resized', event.width, event.height) leftpixel = 80.0 rightpixel = 20.0 xaxispixel = 50.0 toppixel = 20.0 timeaxis = 0.42 left = leftpixel / event.width width = 1.0 - left - rightpixel / event.width xaxis = xaxispixel / event.height top = toppixel / event.height height = (1.0 - timeaxis - top) / 2.0 if left < 0.5 and width < 1.0 and xaxis < 0.3 and top < 0.2: self.axt.set_position([left, timeaxis + height, width, height]) self.axs.set_position([left, timeaxis, width, height]) self.axp.set_position([left, xaxis, width, timeaxis - 2.0 * xaxis]) def plot_waveform(self): fig = plt.figure() ax = fig.add_subplot(1, 1, 1) name = self.filename.split('.')[0] if self.channel > 0: ax.set_title('{filename} channel={channel:d}'.format( filename=self.filename, channel=self.channel)) figfile = '{name}-{channel:d}-{time:.4g}s-waveform.png'.format( name=name, channel=self.channel, time=self.toffset) else: ax.set_title(self.filename) figfile = '{name}-{time:.4g}s-waveform.png'.format( name=name, time=self.toffset) t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) if t1>len(self.data): t1 = len(self.data) time = np.arange(t0, t1) / self.samplerate if self.twindow < 1.0: ax.set_xlabel('Time [ms]') ax.set_xlim(1000.0 * self.toffset, 1000.0 * (self.toffset + self.twindow)) ax.plot(1000.0 * time, self.data[t0:t1,self.channel]) else: ax.set_xlabel('Time [s]') ax.set_xlim(self.toffset, self.toffset + self.twindow) ax.plot(time, self.data[t0:t1,self.channel]) ax.set_ylabel('Amplitude [{:s}]'.format(self.unit)) fig.tight_layout() fig.savefig(figfile) fig.clear() plt.close(fig) print('saved waveform figure to', figfile) def play_segment(self): t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) playdata = 1.0 * self.data[t0:t1,self.channel] fade(playdata, self.samplerate, 0.1) self.audio.play(playdata, self.samplerate, blocking=False) def save_segment(self): t0s = int(np.round(self.toffset)) t1s = int(np.round(self.toffset + self.twindow)) t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) savedata = 1.0 * self.data[t0:t1,self.channel] filename = self.filename.split('.')[0] segmentfilename = '{name}-{time0:.4g}s-{time1:.4g}s.wav'.format( name=filename, time0=t0s, time1 = t1s) write_audio(segmentfilename, savedata, self.data.samplerate) print('saved segment to: ' , segmentfilename) def play_all(self): self.audio.play(self.data[:,self.channel], self.samplerate, blocking=False) def play_tone( self, frequency ) : self.audio.beep(1.0, frequency)
Methods
def remove_peak_annotation(self)
-
Expand source code
def remove_peak_annotation(self): for fm in self.peak_specmarker: fm.remove() self.peak_specmarker = [] for fa in self.peak_annotation: fa.remove() self.peak_annotation = []
def annotate_peak(self, peak, harmonics=-1, inx=-1)
-
Expand source code
def annotate_peak(self, peak, harmonics=-1, inx=-1): # marker: if inx >= 0: m, = self.axs.plot([self.toffset + 0.01 * self.twindow], [peak[0]], linestyle='None', color=self.colorrange[inx % len(self.colorrange)], marker=self.markerrange[inx], ms=10.0, mec=None, mew=0.0, zorder=2) else: m, = self.axs.plot([self.toffset + 0.01 * self.twindow], [peak[0]], linestyle='None', color='k', marker='o', ms=10.0, mec=None, mew=0.0, zorder=2) self.peak_specmarker.append(m) # annotation: fwidth = self.fmax - self.fmin pl = [] pl.append(r'$f=$%.1f Hz' % peak[0]) pl.append(r'$h=$%d' % harmonics) pl.append(r'$p=$%g' % peak[1]) pl.append(r'$c=$%.0f' % peak[2]) self.peak_annotation.append(self.axp.annotate('\n'.join(pl), xy=(peak[0], peak[1]), xytext=(peak[0] + 0.03 * fwidth, peak[1]), bbox=dict(boxstyle='round', facecolor='white'), arrowprops=dict(arrowstyle='-')))
def annotate_fish(self, fish, inx=-1)
-
Expand source code
def annotate_fish(self, fish, inx=-1): self.remove_peak_annotation() for harmonic, freq in enumerate(fish[:, 0]): peak = self.allpeaks[np.abs(self.allpeaks[:, 0] - freq) < 0.8 * self.deltaf, :] if len(peak) > 0: self.annotate_peak(peak[0, :], harmonic, inx) self.fig.canvas.draw()
def update_plots(self, draw=True)
-
Expand source code
def update_plots(self, draw=True): self.remove_peak_annotation() # trace: self.axt.set_xlim(self.toffset, self.toffset + self.twindow) t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) if t1>len(self.data): t1 = len(self.data) time = np.arange(t0, t1) / self.samplerate if self.trace_artist == None: self.trace_artist, = self.axt.plot(time, self.data[t0:t1,self.channel]) else: self.trace_artist.set_data(time, self.data[t0:t1,self.channel]) self.axt.set_ylim(self.ymin, self.ymax) # compute power spectrum: n_fft = nfft(self.samplerate, self.freq_resolution) t00 = t0 t11 = t1 w = t11 - t00 minw = n_fft * (self.cfg.value('minPSDAverages') + 1) // 2 if t11 - t00 < minw: w = minw t11 = t00 + w if t11 >= len(self.data): t11 = len(self.data) t00 = t11 - w if t00 < 0: t00 = 0 t11 = w freqs, power = psd(self.data[t00:t11,self.channel], self.samplerate, self.freq_resolution, detrend=ml.detrend_mean) self.deltaf = freqs[1] - freqs[0] # detect fish: h_kwargs = psd_peak_detection_args(self.cfg) h_kwargs.update(harmonic_groups_args(self.cfg)) self.fishlist, fzero_harmonics, self.mains, self.allpeaks, peaks, lowth, highth, center = harmonic_groups(freqs, power, verbose=self.verbose, **h_kwargs) highth = center + highth - 0.5 * lowth lowth = center + 0.5 * lowth # spectrogram: t2 = t1 + n_fft freqs, bins, specpower = spectrogram(self.data[t0:t2,self.channel], self.samplerate, self.freq_resolution, detrend=ml.detrend_mean) z = decibel(specpower) z = np.flipud(z) extent = self.toffset, self.toffset + np.amax(bins), freqs[0], freqs[-1] self.axs.set_xlim(self.toffset, self.toffset + self.twindow) if self.spectrogram_artist == None: self.fmax = np.round((freqs[-1] / 4.0) / 100.0) * 100.0 min = highth min = np.percentile(z, 70.0) max = np.percentile(z, 99.9) + 30.0 # cm = plt.get_cmap( 'hot_r' ) cm = plt.get_cmap('jet') self.spectrogram_artist = self.axs.imshow(z, aspect='auto', extent=extent, vmin=min, vmax=max, cmap=cm, zorder=1) else: self.spectrogram_artist.set_data(z) self.spectrogram_artist.set_extent(extent) self.axs.set_ylim(self.fmin, self.fmax) # power spectrum: self.axp.set_xlim(self.fmin, self.fmax) if self.deltaf >= 1000.0: dfs = '%.3gkHz' % 0.001 * self.deltaf else: dfs = '%.3gHz' % self.deltaf tw = float(w) / self.samplerate if tw < 1.0: tws = '%.3gms' % (1000.0 * tw) else: tws = '%.3gs' % (tw) a = 2 * w // n_fft - 1 # number of ffts m = '' if self.cfg.value('mainsFreq') > 0.0: m = ', mains=%.0fHz' % self.cfg.value('mainsFreq') if self.power_frequency_label == None: self.power_frequency_label = self.axp.set_xlabel( r'Frequency [Hz] (nfft={:d}, $\Delta f$={:s}: T={:s}/{:d}{:s})'.format(n_fft, dfs, tws, a, m)) else: self.power_frequency_label.set_text( r'Frequency [Hz] (nfft={:d}, $\Delta f$={:s}: T={:s}/{:d}{:s})'.format(n_fft, dfs, tws, a, m)) self.axp.set_xlim(self.fmin, self.fmax) if self.power_label == None: self.power_label = self.axp.set_ylabel('Power') if self.decibel: if len(self.allpeaks) > 0: self.allpeaks[:, 1] = decibel(self.allpeaks[:, 1]) power = decibel(power) pmin = np.min(power[freqs < self.fmax]) pmin = np.floor(pmin / 10.0) * 10.0 pmax = np.max(power[freqs < self.fmax]) pmax = np.ceil(pmax / 10.0) * 10.0 doty = pmax - 5.0 self.power_label.set_text('Power [dB]') self.axp.set_ylim(pmin, pmax) else: pmax = np.max(power[freqs < self.fmax]) doty = pmax pmax *= 1.1 self.power_label.set_text('Power') self.axp.set_ylim(0.0, pmax) if self.all_peaks_artis == None: self.all_peaks_artis, = self.axp.plot(self.allpeaks[:, 0], np.zeros(len(self.allpeaks[:, 0])) + doty, 'o', color='#ffffff') self.good_peaks_artist, = self.axp.plot(peaks, np.zeros(len(peaks)) + doty, 'o', color='#888888') else: self.all_peaks_artis.set_data(self.allpeaks[:, 0], np.zeros(len(self.allpeaks[:, 0])) + doty) self.good_peaks_artist.set_data(peaks, np.zeros(len(peaks)) + doty) labels = [] fsizes = [np.sqrt(np.sum(self.fishlist[k][:, 1])) for k in range(len(self.fishlist))] fmaxsize = np.max(fsizes) if len(fsizes) > 0 else 1.0 for k in range(len(self.peak_artists)): self.peak_artists[k].remove() self.peak_artists = [] for k in range(len(self.fishlist)): if k >= len(self.markerrange): break fpeaks = self.fishlist[k][:, 0] fpeakinx = [int(np.round(fp / self.deltaf)) for fp in fpeaks if fp < freqs[-1]] fsize = 7.0 + 10.0 * (fsizes[k] / fmaxsize) ** 0.5 fishpoints, = self.axp.plot(fpeaks[:len(fpeakinx)], power[fpeakinx], linestyle='None', color=self.colorrange[k % len(self.colorrange)], marker=self.markerrange[k], ms=fsize, mec=None, mew=0.0, zorder=1) self.peak_artists.append(fishpoints) if self.deltaf < 0.1: labels.append('%4.2f Hz' % fpeaks[0]) elif self.deltaf < 1.0: labels.append('%4.1f Hz' % fpeaks[0]) else: labels.append('%4.0f Hz' % fpeaks[0]) if len(self.mains) > 0: fpeaks = self.mains[:, 0] fpeakinx = np.array([np.round(fp / self.deltaf) for fp in fpeaks if fp < freqs[-1]], dtype=int) fishpoints, = self.axp.plot(fpeaks[:len(fpeakinx)], power[fpeakinx], linestyle='None', marker='.', color='k', ms=10, mec=None, mew=0.0, zorder=2) self.peak_artists.append(fishpoints) labels.append('%3.0f Hz mains' % self.cfg.value('mainsFreq')) ncol = (len(labels)-1) // 8 + 1 self.legendhandle = self.axs.legend(self.peak_artists[:len(labels)], labels, loc='upper right', ncol=ncol) self.legenddict = dict() for legpoints, (finx, fish) in zip(self.legendhandle.get_lines(), enumerate(self.fishlist)): legpoints.set_picker(8) self.legenddict[legpoints] = [finx, fish] self.legendhandle.set_visible(self.legend) if self.power_artist == None: self.power_artist, = self.axp.plot(freqs, power, 'b', zorder=3) else: self.power_artist.set_data(freqs, power) if draw: self.fig.canvas.draw()
def keypress(self, event)
-
Expand source code
def keypress(self, event): # print('pressed', event.key) if event.key in '+=X': if self.twindow * self.samplerate > 20: self.twindow *= 0.5 self.update_plots() elif event.key in '-x': if self.twindow < len(self.data) / self.samplerate: self.twindow *= 2.0 self.update_plots() elif event.key == 'pagedown': if self.toffset + 0.5 * self.twindow < len(self.data) / self.samplerate: self.toffset += 0.5 * self.twindow self.update_plots() elif event.key == 'pageup': if self.toffset > 0: self.toffset -= 0.5 * self.twindow if self.toffset < 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'a': if self.min_clip == 0.0 or self.max_clip == 0.0: self.min_clip, self.max_clip = clip_amplitudes( self.data[:,self.channel], **clip_args(self.cfg, self.samplerate)) try: if self.cfg.value('windowSize') <= 0.0: self.cfg.set('windowSize', (len(self.data)-1)/self.samplerate) idx0, idx1, clipped = best_window_indices( self.data[:,self.channel], self.samplerate, min_clip=self.min_clip, max_clip=self.max_clip, **best_window_args(self.cfg)) if idx1 > 0: self.toffset = idx0 / self.samplerate self.twindow = (idx1 - idx0) / self.samplerate self.twindow *= 2.0/(self.cfg.value('numberPSDWindows')+1.0) self.update_plots() except UserWarning as e: if self.verbose > 0: print(str(e)) elif event.key == 'ctrl+pagedown': if self.toffset + 5.0 * self.twindow < len(self.data) / self.samplerate: self.toffset += 5.0 * self.twindow self.update_plots() elif event.key == 'ctrl+pageup': if self.toffset > 0: self.toffset -= 5.0 * self.twindow if self.toffset < 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'down': if self.toffset + self.twindow < len(self.data) / self.samplerate: self.toffset += 0.05 * self.twindow self.update_plots() elif event.key == 'up': if self.toffset > 0.0: self.toffset -= 0.05 * self.twindow if self.toffset < 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'home': if self.toffset > 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'end': toffs = np.floor(len(self.data) / self.samplerate / self.twindow) * self.twindow if self.toffset < toffs: self.toffset = toffs self.update_plots() elif event.key == 'y': h = self.ymax - self.ymin c = 0.5 * (self.ymax + self.ymin) self.ymin = c - h self.ymax = c + h self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'Y': h = 0.25 * (self.ymax - self.ymin) c = 0.5 * (self.ymax + self.ymin) self.ymin = c - h self.ymax = c + h self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'v': t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) min = np.min(self.data[t0:t1,self.channel]) max = np.max(self.data[t0:t1,self.channel]) h = 0.5 * (max - min) c = 0.5 * (max + min) self.ymin = c - h self.ymax = c + h self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'V': self.ymin = -1.0 self.ymax = +1.0 self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'left': if self.fmin > 0.0: fwidth = self.fmax - self.fmin self.fmin -= 0.5 * fwidth self.fmax -= 0.5 * fwidth if self.fmin < 0.0: self.fmin = 0.0 self.fmax = fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key == 'right': if self.fmax < 0.5 * self.samplerate: fwidth = self.fmax - self.fmin self.fmin += 0.5 * fwidth self.fmax += 0.5 * fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key == 'ctrl+left': if self.fmin > 0.0: fwidth = self.fmax - self.fmin self.fmin = 0.0 self.fmax = fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key == 'ctrl+right': if self.fmax < 0.5 * self.samplerate: fwidth = self.fmax - self.fmin fm = 0.5 * self.samplerate self.fmax = np.ceil(fm / fwidth) * fwidth self.fmin = self.fmax - fwidth if self.fmin < 0.0: self.fmin = 0.0 self.fmax = fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key in 'f': if self.fmax < 0.5 * self.samplerate or self.fmin > 0.0: fwidth = self.fmax - self.fmin if self.fmax < 0.5 * self.samplerate: self.fmax = self.fmin + 2.0 * fwidth elif self.fmin > 0.0: self.fmin = self.fmax - 2.0 * fwidth if self.fmin < 0.0: self.fmin = 0.0 self.fmax = 2.0 * fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key in 'F': if self.fmax - self.fmin > 1.0: fwidth = self.fmax - self.fmin self.fmax = self.fmin + 0.5 * fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key in 'r': if self.freq_resolution < 1000.0: self.freq_resolution *= 2.0 self.update_plots() elif event.key in 'R': if 1.0 / self.freq_resolution < self.tmax: self.freq_resolution *= 0.5 self.update_plots() elif event.key in 'd': self.decibel = not self.decibel self.update_plots() elif event.key in 'm': if self.cfg.value('mainsFreq') == 0.0: self.cfg.set('mainsFreq', self.mains_freq) else: self.cfg.set('mainsFreq', 0.0) self.update_plots() elif event.key in 't': t_diff = self.cfg.value('highThresholdFactor') - self.cfg.value('lowThresholdFactor') self.cfg.set('lowThresholdFactor', self.cfg.value('lowThresholdFactor') - 0.1) if self.cfg.value('lowThresholdFactor') < 0.1: self.cfg.set('lowThresholdFactor', 0.1) self.cfg.set('highThresholdFactor', self.cfg.value('lowThresholdFactor') + t_diff) print('lowThresholdFactor =', self.cfg.value('lowThresholdFactor')) self.update_plots() elif event.key in 'T': t_diff = self.cfg.value('highThresholdFactor') - self.cfg.value('lowThresholdFactor') self.cfg.set('lowThresholdFactor', self.cfg.value('lowThresholdFactor') + 0.1) if self.cfg.value('lowThresholdFactor') > 20.0: self.cfg.set('lowThresholdFactor', 20.0) self.cfg.set('highThresholdFactor', self.cfg.value('lowThresholdFactor') + t_diff) print('lowThresholdFactor =', self.cfg.value('lowThresholdFactor')) self.update_plots() elif event.key == 'escape': self.remove_peak_annotation() self.fig.canvas.draw() elif event.key in 'h': self.help = not self.help for ht in self.helptext: ht.set_visible(self.help) self.fig.canvas.draw() elif event.key in 'l': self.legend = not self.legend self.legendhandle.set_visible(self.legend) self.fig.canvas.draw() elif event.key in 'w': self.plot_waveform() elif event.key in 'p': self.play_segment() elif event.key in 'P': self.play_all() elif event.key in '1' : self.play_tone('c3') elif event.key in '2' : self.play_tone('a3') elif event.key in '3' : self.play_tone('e4') elif event.key in '4' : self.play_tone('a4') elif event.key in '5' : self.play_tone('c5') elif event.key in '6' : self.play_tone('e5') elif event.key in '7' : self.play_tone('g5') elif event.key in '8' : self.play_tone('a5') elif event.key in '9' : self.play_tone('c6') elif event.key in 'S': self.save_segment()
-
Expand source code
def buttonpress( self, event ) : # print('mouse pressed', event.button, event.key, event.step) if event.inaxes == self.axp: if event.key == 'shift' or event.key == 'control': # show next or previous harmonic: if event.key == 'shift': if event.button == 1: ftarget = event.xdata / 2.0 elif event.button == 3: ftarget = event.xdata * 2.0 else: if event.button == 1: ftarget = event.xdata / 1.5 elif event.button == 3: ftarget = event.xdata * 1.5 foffs = event.xdata - self.fmin fwidth = self.fmax - self.fmin self.fmin = ftarget - foffs self.fmax = self.fmin + fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() else: # put label on peak self.remove_peak_annotation() # find closest peak: fwidth = self.fmax - self.fmin peakdist = np.abs(self.allpeaks[:, 0] - event.xdata) inx = np.argmin(peakdist) if peakdist[inx] < 0.005 * fwidth: peak = self.allpeaks[inx, :] # find fish: foundfish = False for finx, fish in enumerate(self.fishlist): if np.min(np.abs(fish[:, 0] - peak[0])) < 0.8 * self.deltaf: self.annotate_fish(fish, finx) foundfish = True break if not foundfish: self.annotate_peak(peak) self.fig.canvas.draw() else: self.fig.canvas.draw()
def onpick(self, event)
-
Expand source code
def onpick(self, event): # print('pick') legendpoint = event.artist finx, fish = self.legenddict[legendpoint] self.annotate_fish(fish, finx)
def resize(self, event)
-
Expand source code
def resize(self, event): # print('resized', event.width, event.height) leftpixel = 80.0 rightpixel = 20.0 xaxispixel = 50.0 toppixel = 20.0 timeaxis = 0.42 left = leftpixel / event.width width = 1.0 - left - rightpixel / event.width xaxis = xaxispixel / event.height top = toppixel / event.height height = (1.0 - timeaxis - top) / 2.0 if left < 0.5 and width < 1.0 and xaxis < 0.3 and top < 0.2: self.axt.set_position([left, timeaxis + height, width, height]) self.axs.set_position([left, timeaxis, width, height]) self.axp.set_position([left, xaxis, width, timeaxis - 2.0 * xaxis])
def plot_waveform(self)
-
Expand source code
def plot_waveform(self): fig = plt.figure() ax = fig.add_subplot(1, 1, 1) name = self.filename.split('.')[0] if self.channel > 0: ax.set_title('{filename} channel={channel:d}'.format( filename=self.filename, channel=self.channel)) figfile = '{name}-{channel:d}-{time:.4g}s-waveform.png'.format( name=name, channel=self.channel, time=self.toffset) else: ax.set_title(self.filename) figfile = '{name}-{time:.4g}s-waveform.png'.format( name=name, time=self.toffset) t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) if t1>len(self.data): t1 = len(self.data) time = np.arange(t0, t1) / self.samplerate if self.twindow < 1.0: ax.set_xlabel('Time [ms]') ax.set_xlim(1000.0 * self.toffset, 1000.0 * (self.toffset + self.twindow)) ax.plot(1000.0 * time, self.data[t0:t1,self.channel]) else: ax.set_xlabel('Time [s]') ax.set_xlim(self.toffset, self.toffset + self.twindow) ax.plot(time, self.data[t0:t1,self.channel]) ax.set_ylabel('Amplitude [{:s}]'.format(self.unit)) fig.tight_layout() fig.savefig(figfile) fig.clear() plt.close(fig) print('saved waveform figure to', figfile)
def play_segment(self)
-
Expand source code
def play_segment(self): t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) playdata = 1.0 * self.data[t0:t1,self.channel] fade(playdata, self.samplerate, 0.1) self.audio.play(playdata, self.samplerate, blocking=False)
def save_segment(self)
-
Expand source code
def save_segment(self): t0s = int(np.round(self.toffset)) t1s = int(np.round(self.toffset + self.twindow)) t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) savedata = 1.0 * self.data[t0:t1,self.channel] filename = self.filename.split('.')[0] segmentfilename = '{name}-{time0:.4g}s-{time1:.4g}s.wav'.format( name=filename, time0=t0s, time1 = t1s) write_audio(segmentfilename, savedata, self.data.samplerate) print('saved segment to: ' , segmentfilename)
def play_all(self)
-
Expand source code
def play_all(self): self.audio.play(self.data[:,self.channel], self.samplerate, blocking=False)
def play_tone(self, frequency)
-
Expand source code
def play_tone( self, frequency ) : self.audio.beep(1.0, frequency)