annotate cnmf.py @ 7:294f66d285af

some previous changes
author mitian
date Tue, 05 May 2015 08:54:09 +0100
parents 56a2ca9359d0
children 915c849b17ea
rev   line source
mi@0 1 """
mi@0 2 C-NMF method for segmentation, modified from here:
mi@0 3
mi@0 4 Nieto, O., Jehan, T., Convex Non-negative Matrix Factorization For Automatic
mi@0 5 Music Structure Identification. Proc. of the 38th IEEE International Conference
mi@0 6 on Acoustics, Speech, and Signal Processing (ICASSP). Vancouver, Canada, 2013.
mi@0 7 """
mi@0 8
mi@0 9 __author__ = "Oriol Nieto"
mi@0 10 __copyright__ = "Copyright 2014, Music and Audio Research Lab (MARL)"
mi@0 11 __license__ = "GPL"
mi@0 12 __version__ = "1.0"
mi@0 13 __email__ = "oriol@nyu.edu"
mi@0 14
mi@0 15 import numpy as np
mi@0 16 import pymf
mi@0 17
mi@0 18 # Local stuff
mi@0 19 from utils import SegUtil
mi@0 20
mitian@7 21 # Algorithm params
mitian@7 22 h = 8 # Size of median filter for features in C-NMF
mitian@7 23 R = 15 # Size of the median filter for the activation matrix C-NMF
mitian@7 24 rank = 4 # Rank of decomposition for the boundaries
mitian@7 25 rank_labels = 6 # Rank of decomposition for the labels
mitian@7 26 R_labels = 6 # Size of the median filter for the labels
mi@0 27
mi@0 28 def cnmf(S, rank, niter=500):
mi@0 29 """(Convex) Non-Negative Matrix Factorization.
mi@0 30
mi@0 31 Parameters
mi@0 32 ----------
mi@0 33 S: np.array(p, N)
mi@0 34 Features matrix. p row features and N column observations.
mi@0 35 rank: int
mi@0 36 Rank of decomposition
mi@0 37 niter: int
mi@0 38 Number of iterations to be used
mi@0 39
mi@0 40 Returns
mi@0 41 -------
mi@0 42 F: np.array
mi@0 43 Cluster matrix (decomposed matrix)
mi@0 44 G: np.array
mi@0 45 Activation matrix (decomposed matrix)
mi@0 46 (s.t. S ~= F * G)
mi@0 47 """
mi@0 48 nmf_mdl = pymf.CNMF(S, num_bases=rank)
mi@0 49 nmf_mdl.factorize(niter=niter)
mi@0 50 F = np.asarray(nmf_mdl.W)
mi@0 51 G = np.asarray(nmf_mdl.H)
mi@0 52 return F, G
mi@0 53
mi@0 54
mi@0 55 def most_frequent(x):
mi@0 56 """Returns the most frequent value in x."""
mi@0 57 return np.argmax(np.bincount(x))
mi@0 58
mi@0 59
mi@0 60 def compute_labels(X, rank, R, bound_idxs, niter=300):
mi@0 61 """Computes the labels using the bounds."""
mi@0 62
mi@0 63 X = X.T
mi@0 64 try:
mi@0 65 F, G = cnmf(X, rank, niter=niter)
mi@0 66 except:
mi@0 67 return [1]
mi@0 68
mi@0 69 label_frames = filter_activation_matrix(G.T, R)
mi@0 70 label_frames = np.asarray(label_frames, dtype=int)
mi@0 71
mi@0 72 # Get labels from the label frames
mi@0 73 labels = []
mi@0 74 bound_inters = zip(bound_idxs[:-1], bound_idxs[1:])
mi@0 75 for bound_inter in bound_inters:
mi@0 76 if bound_inter[1] - bound_inter[0] <= 0:
mi@0 77 labels.append(np.max(label_frames) + 1)
mi@0 78 else:
mi@0 79 labels.append(most_frequent(
mi@0 80 label_frames[bound_inter[0]:bound_inter[1]]))
mi@0 81
mi@0 82 return labels
mi@0 83
mi@0 84
mi@0 85 def filter_activation_matrix(G, R):
mi@0 86 """Filters the activation matrix G, and returns a flattened copy."""
mi@0 87 idx = np.argmax(G, axis=1)
mi@0 88 max_idx = np.arange(G.shape[0])
mi@0 89 max_idx = (max_idx, idx.flatten())
mi@0 90 G[:, :] = 0
mi@0 91 G[max_idx] = idx + 1
mi@0 92 G = np.sum(G, axis=1)
mitian@4 93 G = SegUtil.median_filter(G[:, np.newaxis], R)
mi@0 94 return G.flatten()
mi@0 95
mi@0 96
mitian@7 97 def segmentation(X, rank=4, R=15, h=8, niter=300):
mi@0 98 """
mi@0 99 Gets the segmentation (boundaries and labels) from the factorization
mi@0 100 matrices.
mi@0 101
mi@0 102 Parameters
mi@0 103 ----------
mi@0 104 X: np.array()
mi@0 105 Features matrix (e.g. chromagram)
mi@0 106 rank: int
mi@0 107 Rank of decomposition
mi@0 108 R: int
mi@0 109 Size of the median filter for activation matrix
mi@0 110 niter: int
mi@0 111 Number of iterations for k-means
mi@0 112 bound_idxs : list
mi@0 113 Use previously found boundaries (None to detect them)
mi@0 114
mi@0 115 Returns
mi@0 116 -------
mi@0 117 bounds_idx: np.array
mi@0 118 Bound indeces found
mi@0 119 labels: np.array
mi@0 120 Indeces of the labels representing the similarity between segments.
mi@0 121 """
mi@0 122
mi@0 123 # Filter
mitian@1 124 X = SegUtil.median_filter(X, M=h)
mi@0 125 X = X.T
mi@0 126
mi@0 127 # Find non filtered boundaries
mi@0 128 bound_idxs = None
mi@0 129 while True:
mi@0 130 if bound_idxs is None:
mi@0 131 try:
mi@0 132 F, G = cnmf(X, rank, niter=niter)
mi@0 133 except:
mi@0 134 return np.empty(0), [1]
mi@0 135
mi@0 136 # Filter G
mi@0 137 G = filter_activation_matrix(G.T, R)
mi@0 138 if bound_idxs is None:
mi@0 139 bound_idxs = np.where(np.diff(G) != 0)[0] + 1
mi@0 140
mi@0 141 if len(np.unique(bound_idxs)) <= 2:
mi@0 142 rank += 1
mi@0 143 bound_idxs = None
mi@0 144 else:
mi@0 145 break
mi@0 146
mitian@7 147 return G, bound_idxs