Skip to content
Snippets Groups Projects
Commit fb7a0f6e authored by ferrari's avatar ferrari
Browse files

Fix spectrogram offset

parent 9c90ab7e
No related branches found
No related tags found
No related merge requests found
...@@ -18,6 +18,7 @@ def norm(x, axis=None, eps=1e-18): ...@@ -18,6 +18,7 @@ def norm(x, axis=None, eps=1e-18):
def norm_abs(x, axis=None, eps=1e-18): def norm_abs(x, axis=None, eps=1e-18):
return (x-x.mean(axis, keepdims=axis is not None))/(np.abs(x).max(axis, keepdims=axis is not None) + eps) return (x-x.mean(axis, keepdims=axis is not None))/(np.abs(x).max(axis, keepdims=axis is not None) + eps)
def read(file_path, always_2d=True): def read(file_path, always_2d=True):
try: try:
return sf.read(file_path, always_2d=always_2d) return sf.read(file_path, always_2d=always_2d)
......
...@@ -483,10 +483,11 @@ class Callback(object): ...@@ -483,10 +483,11 @@ class Callback(object):
def _update_spectrogram(self, ax_group): def _update_spectrogram(self, ax_group):
click = ax_group.signal.im.get_ydata() click = ax_group.signal.im.get_ydata()
spec = np.flipud(20*np.log10(plt.mlab.specgram(click, Fs=self.sr, NFFT=self.nfft, noverlap=self.nfft-1, spec, _, t = plt.mlab.specgram(click, Fs=self.sr, NFFT=self.nfft, noverlap=self.nfft-1, pad_to=2*self.nfft)
pad_to=2*self.nfft)[0])) spec = np.flipud(20*np.log10(spec))
ax_group.spectrogram.im.set_data(spec) ax_group.spectrogram.im.set_data(spec)
ax_group.spectrogram.im.set_clim(spec.max() - SPSC, spec.max()) ax_group.spectrogram.im.set_clim(spec.max() - SPSC, spec.max())
ax_group.spectrogram.im.set_extent([t[0] - 1/self.sr/2, t[-1] + 1/self.sr/2, 0., self.sr/2])
def increase_res(self, event): def increase_res(self, event):
if self.nfft > int(10e-3*self.sr): if self.nfft > int(10e-3*self.sr):
......
import argparse
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import rgb_to_hsv, hsv_to_rgb
import imageio as io
from skimage.filters import scharr
from scipy.ndimage import label, binary_opening
import os
import sys
from matplotlib.widgets import Button, Slider
from matplotlib.backend_bases import MouseButton
def wb(channel, perc=0.05):
mi, ma = (np.percentile(channel, perc, axis=(0, 1)), np.percentile(channel, 100.0-perc, axis=(0, 1)))
channel = np.uint8(np.clip((channel-mi)*255.0/(ma-mi+1e-18), 0, 255))
return channel
def wb2(channel, per=0.05):
hsv = rgb_to_hsv(channel/255)
grey = (channel/255).sum(-1)
mi, ma = (np.percentile(grey, per), np.percentile(grey, 100-per))
if mi > 0.01:
hue = (hsv[grey <= mi][..., 0].mean() + hsv[grey >= ma][..., 0].mean())/2
else:
hue = hsv[grey >= ma][..., 0].mean()
hsv[..., 0] += 0.5 - hue
hsv[..., 0] %= 1
return hsv_to_rgb(hsv)
def sat(arr):
m = arr.max(-1)
return (m - arr.min(-1))/m
def fo(m):
a = m.any(1)
b = m.any(0)
return (np.argmax(a), len(a) - np.argmax(a[::-1]), np.argmax(b), len(b) - np.argmax(b[::-1]))
def unmapping(x,y,fov_h,fov_v,width,height):
x = -(x - width/2) * np.tan(fov_h / 4) / (width / 2)
y = (y - height/2) * np.tan(fov_v / 4) / (height / 2)
theta = 2*np.arctan(np.sqrt(x**2 + y**2))
delta = np.arctan2(y,x)
return np.cos(delta)*np.tan(theta), np.sin(delta)*np.tan(theta)
def sat_val(arr):
m = arr.max(-1)
return -3*np.clip((m - arr.min(-1))/m - 0.15, 0, 0.5) + np.clip(m/255-0.8, 0, 0.2)*10
def hori(im):
sv = sat_val(im) > 0.8
sv[:np.median(np.argwhere(sv)[:, 0]).astype(int)] = True
lab = label(scharr(binary_opening(sv)) > 0.5)[0]
v_min = np.inf
l_min = 0
for l, n in enumerate(np.unique(lab, return_counts=True)[1][1:], start=1):
if n < 1024:
continue
v = np.polyfit(*np.argwhere(lab == l).T[::-1], 2, full=True)[1]
if v < v_min:
l_min = l
v_min = v
return np.argwhere(lab == l_min)
def unmap_hori(h_pos, im):
a = unmapping(*h_pos.T[::-1], 94.4 * np.pi / 180, 55 * np.pi / 180, im.shape[1], im.shape[0])
return np.polyfit(*a, 2)
class Callback(object):
def __init__(self, inp):
self.reader = io.get_reader(inp, format='ffmpeg')
self._len = self.reader.count_frames()
self.fig = plt.figure(inp, figsize=[16, 9], constrained_layout=True)
gs = self.fig.add_gridspec(2, 1, height_ratios=[9, 1])
controls = gs[1].subgridspec(2, 5, height_ratios=[1, 3])
self.screen = self.fig.add_subplot(gs[0])
self.screen.set(xticks=[], yticks=[])
self.idx = 0
self.frame = self.reader.get_data(self.idx)
self.img = self.screen.imshow(self.frame)
self.bar = Slider(self.fig.add_subplot(controls[0, :], zorder=10),
'frame #', 0, self._len, valinit=0, valstep=1, valfmt='%5d ')
self.bar.valtext.set_family('monospace')
self.bar.on_changed(self.new_frame)
self.resize_b = Button(self.fig.add_subplot(controls[1, 0], zorder=10), 'Resize plot')
self.resize_b.on_clicked(self.resize)
self.normal_b = Button(self.fig.add_subplot(controls[1, 1], zorder=10), 'Auto\nwhite balance')
self.normal_b.on_clicked(self.normalize)
for v in "fullscreen,home,back,forward,pan,zoom,save,quit,grid,yscale,xscale,all_axes".split(','):
plt.rcParams[f'keymap.{v}'] = []
self.cid = self.fig.canvas.mpl_connect('key_press_event', self.key_pressed)
self.cid3 = self.fig.canvas.mpl_connect('button_release_event', self.mouse_release)
self.sperm_pos = np.zeros((2, 2, 2)) #sperm whale, start/stop, x/y
self.screen.set_xlim(self.screen.get_xlim())
self.screen.set_ylim(self.screen.get_ylim())
self.marker = self.screen.scatter(100, 100, marker='+', c=[4*[0]])
self.t = np.arange(self.frame.shape[1])
h_pos = hori(self.frame)
self.line, = self.screen.plot(self.t, np.polyval(np.polyfit(*h_pos.T[::-1], 2), self.t), c='r')
self.poly = unmap_hori(h_pos, self.frame)
text_ax = self.fig.add_subplot(controls[1, -2:])
self.text = text_ax.text(0.5, 0.5, 'Sperm whale 1 : ( , ) - ( , ) ratio - \n'
'Sperm whale 2 : ( , ) - ( , ) angle ° ',
horizontalalignment='center',
verticalalignment='center', transform=text_ax.transAxes, family='monospace')
text_ax.axis('off')
self.closed = False
plt.draw()
plt.pause(0.2)
self.fig.set_constrained_layout(False)
def new_frame(self, val):
self.idx = int(val)
self.frame = self.reader.get_data(self.idx)
self.img.set_data(self.frame)
self.marker.set_offsets(np.array([[100, 100]]))
self.marker.set_color([4*[0]])
self.sperm_pos[:] = 0
self.text.set_text('Sperm whale 1 : ( , ) - ( , ) ratio - \n'
'Sperm whale 2 : ( , ) - ( , ) angle ° ')
plt.draw()
def resize(self, event):
self.fig.set_constrained_layout(True)
plt.draw()
plt.pause(0.2)
self.fig.set_constrained_layout(False)
def normalize(self, event):
self.img.set_data(wb(wb2(self.frame)))
plt.draw()
def key_pressed(self, event):
key = event.key
move = 0
if key == 'j' or key == 'left':
move = -1
elif key == 'k' or key == 'right':
move = 1
elif key == 'up':
move = -5
elif key == 'pageup':
move = -10
elif key == 'down':
move = 5
elif key == 'pagedown':
move = 10
elif key == 'r':
self.resize(None)
elif key == 'n':
self.normalize(None)
elif key == 'h':
h_pos = hori(self.frame)
self.line.set_data(self.t, np.polyval(np.polyfit(*h_pos.T[::-1], 2), self.t))
self.poly = unmap_hori(h_pos, self.frame)
plt.draw()
elif key == 'enter':
print('hello')
if move:
self.bar.set_val(min(max(0, self.idx + move), self._len - 1))
plt.draw()
def mouse_release(self, event):
if event.inaxes != self.screen or self.fig.canvas.manager.toolbar.mode:
return None
if event.button == MouseButton.LEFT:
self.sperm_pos[0, self.pos_select[0]] = event.xdata, event.ydata
elif event.button == MouseButton.RIGHT:
self.sperm_pos[1, self.pos_select[1]] = event.xdata, event.ydata
if False:
self.text.set_text(f'Sperm whale 1 : ({self.sperm_pos[0,0,0]:5.0f}, {self.sperm_pos[0,0,1]:5.0f}) '
f'- ({self.sperm_pos[0,1,0]:5.0f}, {self.sperm_pos[0,1,1]:5.0f})'
f' ratio {len1/len2:5.2f} - {len2/len1:5.2f}\n'
f'Sperm whale 2 : ({self.sperm_pos[1,0,0]:5.0f}, {self.sperm_pos[1,0,1]:5.0f}) '
f'- ({self.sperm_pos[1,1,0]:5.0f}, {self.sperm_pos[1,1,1]:5.0f})'
f' angle {np.arccos(np.abs(np.dot(vec1,vec2))/(len1*len2))*180/np.pi:5.2f} ° ')
else:
self.text.set_text(f'Sperm whale 1 : ({self.sperm_pos[0,0,0]:5.0f}, {self.sperm_pos[0,0,1]:5.0f}) '
f'- ({self.sperm_pos[0,1,0]:5.0f}, {self.sperm_pos[0,1,1]:5.0f}) ratio - \n'
f'Sperm whale 2 : ( , ) - ( , ) angle ° ')
plt.draw()
def close(self):
self.reader.close()
del self.resize_b
self.closed = True
def __del__(self):
if not self.closed:
self.close()
def main(args):
if args.out == '':
outpath = args.input.rsplit('.', 1)[0] + '.pred.h5'
else:
outpath = args.out
if os.path.isfile(outpath):
if not (args.erase or args.resume):
print(f'Out file {outpath} already exist and erase or resume option isn\'t set.')
return 1
elif args.resume:
print(f'Out file {outpath} does not already exist and resume option is set.')
return 1
callback = Callback(args.input)
plt.show()
callback.close()
return 0
if __name__ == '__main__':
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("input", type=str, help="Input file")
parser.add_argument("--out", type=str, default='', help="Output file. Default to the input_path'.pred.h5'")
group = parser.add_mutually_exclusive_group()
group.add_argument("--erase", action='store_true', help="If out file exist and this option is not given,"
" the computation will be halted")
group.add_argument("--resume", action='store_true', help="If out file exist and this option is given,"
" the previous annotation file will be loaded")
sys.exit(main(parser.parse_args()))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment