Source code for nussl.separation.factorization.ica

import copy

import numpy as np
import sklearn

from .. import SeparationBase
from ... import AudioSignal
from ...core import utils

[docs]class ICA(SeparationBase): """ Separate sources using the Independent Component Analysis, given observations of the audio scene. nussl's ICA is a wrapper for sci-kit learn's implementation of FastICA, and provides a way to interop between nussl's :ref:`AudioSignal` objects and FastICA. References: `sci-kit learn FastICA <>`_ Args: audio_signals: list of AudioSignal objects containing the observations of the mixture. Will be converted into a single multichannel AudioSignal. max_iterations (int): Max number of iterations to run ICA for. Defaults to 200. **kwargs: Additional keyword arguments that will be passed to `sklearn.decomposition.FastICA` """ def __init__(self, audio_signals, max_iterations=200, **kwargs): super().__init__(input_audio_signal=audio_signals) # FastICA setup attributes self.num_components = self.audio_signal.num_channels self.kwargs = kwargs self.max_iterations = max_iterations # Results attributes self.estimated_sources = None self.estimated_mixing_params = None self.mean = None self.ica_output = None @property def audio_signal(self): """ Copy of AudioSignal that is made on initialization. """ return self._audio_signal @audio_signal.setter def audio_signal(self, audio_signals): """ Takes a list of audio signals and constructs a single multichannel audio signal object. Args: audio_signal (list or AudioSignal): Either a multichannel audio signal, or a list of AudioSignals containing the observations. """ if isinstance(audio_signals, list): audio_signals = utils.verify_audio_signal_list_strict(audio_signals) audio_data = np.vstack([s.audio_data for s in audio_signals]) audio_signal = audio_signals[0].make_copy_with_audio_data(audio_data) self._audio_signal = audio_signal elif isinstance(audio_signals, AudioSignal): self._audio_signal = copy.deepcopy(audio_signals) def run(self): ica = sklearn.decomposition.FastICA( n_components=self.num_components, max_iter=self.max_iterations, **self.kwargs) # save for normalizing the estimated signals max_input_amplitude = np.max(np.abs(self.audio_signal.audio_data)) # run ICA ica_output = ica.fit_transform(self.audio_signal.audio_data.T).T # now normalize the estimated signals max_output_amplitude = np.max(np.abs(ica_output)) ica_output /= max_output_amplitude ica_output *= max_input_amplitude # store the resultant computations self.estimated_mixing_params = ica.mixing_ self.mean = ica.mean_ self.ica_output = ica_output return self.ica_output def make_audio_signals(self): estimated_sources = [ AudioSignal( audio_data_array=self.ica_output[i, :], sample_rate=self.audio_signal.sample_rate) for i in range(self.ica_output.shape[0]) ] return estimated_sources