comparison cnmf.py @ 0:26838b1f560f

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