Mercurial > hg > segmentation
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 |