Source code for py2shpss.HPSS

import sys
import numpy as np
import scipy.signal

from nptyping import NDArray, Shape, Floating
from typing import Any

from py2shpss import metric

[docs] class HPSS(object): def __init__(self, mode : str = 'hm21', iter : int = 30, h_size : int = 1, p_size : int = 1, eval_obj : bool =False, *args, **kwargs): assert(iter >= 1) assert(h_size >= 1) assert(p_size >= 1) self.h_filter, self.p_filter = self.__create_filter(h_size, p_size) self.iter = iter self.eval_obj = eval_obj if mode == 'hm21': self.call = self._call_hm21 elif mode == 'idiv': self.call = self._call_idiv self.qH = kwargs["qH"] if "qH" in kwargs.keys() else 0.1 self.qP = kwargs["qP"] if "qP" in kwargs.keys() else 0.1 else: self.call = self._call_hm21 print("Caution: mode should be either hm21 or idiv. (hm21 is set.)", file=sys.stderr)
[docs] def __create_filter(self, h_size, p_size): h_filter = np.ones(1 + 2 * h_size) p_filter = np.ones(1 + 2 * p_size) h_filter[h_size] = 0 p_filter[p_size] = 0 h_filter = h_filter / np.sum(h_filter) p_filter = p_filter / np.sum(p_filter) h_filter = np.expand_dims(h_filter, 0) p_filter = np.expand_dims(p_filter, 1) return h_filter, p_filter
[docs] def __call__(self, spec : NDArray[Shape['*,*'], Floating], *args, **kwargs): """Run the HPSS algorithm on the given spectrogram. Args: spec (numpy 2D array): input spectrogram Returns: H (numpy 2D array): Harmonic spectrogram. P (numpy 2D array): Percussive spectrogram. obj (numpy 2D array, or None): Objective function log, if eval_obj is True """ return self.call(spec, *args, **kwargs)
[docs] def _call_hm21(self, Y): H = Y / np.sqrt(2) P = Y / np.sqrt(2) if self.eval_obj: obj = [] for i in range(self.iter): H_tmp = scipy.signal.convolve2d(H, self.h_filter, boundary='fill', mode='same', fillvalue=0) P_tmp = scipy.signal.convolve2d(P, self.p_filter, boundary='fill', mode='same', fillvalue=0) d = np.sqrt(H_tmp ** 2 + P_tmp ** 2) H = Y * H_tmp / d P = Y * P_tmp / d if self.eval_obj: h_smoothness = metric.spectral_smoothness(H)[0] p_smoothness = metric.spectral_smoothness(P)[1] obj.append([h_smoothness, p_smoothness]) return H, P, (obj if self.eval_obj else None)
[docs] def _call_idiv(self, Y): H = Y / np.sqrt(2) P = Y / np.sqrt(2) M = H * 0 + 0.5 if self.eval_obj: obj = [] for i in range(self.iter): ah = 2*(1 + self.qH) ap = 2*(1 + self.qP) bh = scipy.signal.convolve2d(H, self.h_filter, boundary='fill', mode='same', fillvalue=0) bp = scipy.signal.convolve2d(P, self.p_filter, boundary='fill', mode='same', fillvalue=0) ch = 2 * self.qH * M * Y**2 cp = 2 * self.qP * (1 - M) * Y**2 H = (bh + np.sqrt(bh ** 2 + ah * ch)) /ah P = (bp + np.sqrt(bp ** 2 + ap * cp)) /ap M = H**2/(H**2 + P**2 + 1e-10) if self.eval_obj: h_smoothness = metric.spectral_smoothness(H)[0] p_smoothness = metric.spectral_smoothness(P)[1] idiv = metric.i_divergence(Y**2, H**2 + P**2) obj.append([h_smoothness, p_smoothness, idiv]) H = M * Y P = (1 - M) * Y return H, P, (obj if self.eval_obj else None)