Skip to content

Commit b41689b

Browse files
alexarjeclaude
andcommitted
ssm(): add combine=True for a single horizontal+vertical motion SSM
ssm(features='motiongrams', combine=True) concatenates the horizontal and vertical motiongram feature sequences per frame and computes one self-similarity matrix, returning a single MgImage that captures both axes of motion in one display (instead of the default MgList of two separate SSMs). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f25b585 commit b41689b

1 file changed

Lines changed: 32 additions & 0 deletions

File tree

musicalgestures/_ssm.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def mg_ssm(
7575
use_median=False,
7676
kernel_size=5,
7777
invert_yaxis=True,
78+
combine=False,
7879
title=None,
7980
target_name=None,
8081
overwrite=False):
@@ -89,6 +90,9 @@ def mg_ssm(
8990
blur (str, optional): 'Average' to apply a 10px * 10px blurring filter, 'None' otherwise. Defaults to 'None'.
9091
norm (int, optional): Normalize the columns of the feature sequence. Possible to compute Manhattan norm (1), Euclidean norm (2), Minimum norm (-np.inf), Maximum norm (np.inf), etc. Defaults to np.inf.
9192
norm_threshold (float, optional): Only the columns with norm at least `norm_threshold` are normalized. Defaults to 0.001.
93+
combine (bool, optional): For 'motiongrams', compute a single SSM from the concatenated
94+
horizontal + vertical motiongram features (both axes of motion in one display) and
95+
return a single MgImage instead of an MgList of two. Defaults to False.
9296
cmap (str, optional): A Colormap instance or registered colormap name. The colormap maps the C values to colors. Defaults to 'gray_r'.
9397
use_median (bool, optional): If True the algorithm applies a median filter on the thresholded frame-difference stream. Defaults to False.
9498
kernel_size (int, optional): Size of the median filter (if `use_median=True`) or the erosion filter (if `filtertype='blob'`). Defaults to 5.
@@ -148,6 +152,34 @@ def mg_ssm(
148152
# Normalize feature sequence
149153
X = librosa.util.normalize(self.ssm_fig.data[0].astype('float32'), norm=norm, threshold=norm_threshold)
150154
Y = librosa.util.normalize(self.ssm_fig.data[1].astype('float32'), norm=norm, threshold=norm_threshold)
155+
156+
# Combined SSM: stack horizontal + vertical motiongram features per frame so a
157+
# single self-similarity matrix reflects both axes of motion at once.
158+
if combine:
159+
n = min(X.shape[-1], Y.shape[-1])
160+
XY = np.concatenate([X[..., :n], Y[..., :n]], axis=0)
161+
XY_ssm = slow_dot(np.transpose(XY), XY, self.length)
162+
163+
fig, ax = plt.subplots(figsize=(8, 8))
164+
if title == 'filename':
165+
ax.set_title('Combined motion SSM: ' + os.path.basename(self.of + self.fex))
166+
elif title:
167+
ax.set_title(title)
168+
else:
169+
ax.set_title('Combined (horizontal + vertical) motion SSM')
170+
img = ax.imshow(XY_ssm, aspect='auto', cmap=cmap)
171+
if invert_yaxis:
172+
ax.invert_yaxis()
173+
ax.set_xlabel('Time [frames]')
174+
ax.set_ylabel('Time [frames]')
175+
cb_norm = mpl.colors.Normalize(vmin=0, vmax=1.0)
176+
fig.colorbar(mpl.cm.ScalarMappable(norm=cb_norm, cmap=cmap), ax=ax, aspect=50)
177+
fig.tight_layout()
178+
plt.savefig(target_name, format='png', facecolor='white', transparent=False)
179+
plt.close()
180+
self.ssm_combined = MgImage(target_name)
181+
return MgImage(target_name)
182+
151183
# Compute SSM using dot product
152184
X_ssm = slow_dot(np.transpose(X), X, self.length)
153185
Y_ssm = slow_dot(np.transpose(Y), Y, self.length)

0 commit comments

Comments
 (0)