From ec1834a7fbe2d0d22642accca03c239fdbc75800 Mon Sep 17 00:00:00 2001 From: Daniel Brooks Date: Sun, 22 Sep 2019 18:28:56 -0700 Subject: [PATCH] fix the --illustrate option so that it scales the hashes correctly The hashes were not scaled to correctly overlay the spectrogram; they were squached and unreadable. I also changed it to display the hashes from the reference where the match was found in addition to the hashes from the query and those hashes that matched. There are also now checkboxes for turning each of these three on or off. Fixes #65. --- audfprint_match.py | 111 +++++++++++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 35 deletions(-) diff --git a/audfprint_match.py b/audfprint_match.py index d97026e..2847a32 100644 --- a/audfprint_match.py +++ b/audfprint_match.py @@ -16,6 +16,7 @@ # Don't sweat failure to import graphics support. try: + import matplotlib import matplotlib.pyplot as plt import librosa.display except: @@ -416,58 +417,98 @@ def file_match_to_msgs(self, analyzer, ht, qry, number=None): else: msgrslt.append(qrymsg + "\t" + ht.names[tophitid]) if self.illustrate: - self.illustrate_match(analyzer, ht, qry) + self.illustrate_match(analyzer, ht, qry, rslts) return msgrslt - def illustrate_match(self, analyzer, ht, filename): + def illustrate_match(self, analyzer, ht, filename, results): """ Show the query fingerprints and the matching ones plotted over a spectrogram """ - # Make the spectrogram - # d, sr = librosa.load(filename, sr=analyzer.target_sr) - d, sr = audio_read.audio_read(filename, sr=analyzer.target_sr, channels=1) - sgram = np.abs(stft.stft(d, n_fft=analyzer.n_fft, - hop_length=analyzer.n_hop, - window=np.hanning(analyzer.n_fft + 2)[1:-1])) - sgram = 20.0 * np.log10(np.maximum(sgram, np.max(sgram) / 1e6)) - sgram = sgram - np.mean(sgram) - # High-pass filter onset emphasis - # [:-1,] discards top bin (nyquist) of sgram so bins fit in 8 bits - # spectrogram enhancement - if self.illustrate_hpf: - HPF_POLE = 0.98 - sgram = np.array([scipy.signal.lfilter([1, -1], - [1, -HPF_POLE], s_row) - for s_row in sgram])[:-1, ] - sgram = sgram - np.max(sgram) - librosa.display.specshow(sgram, sr=sr, hop_length=analyzer.n_hop, - y_axis='linear', x_axis='time', - cmap='gray_r', vmin=-80.0, vmax=0) # Do the match? q_hashes = analyzer.wavfile2hashes(filename) - # Run query, get back the hashes for match zero - results, matchhashes = self.match_hashes(ht, q_hashes, hashesfor=0) + # Run query + #results = self.match_hashes(ht, q_hashes) if self.sort_by_time: - results = sorted(results, key=lambda x: -x[2]) + results = sorted(results, key=lambda x: x[2]) + # Make the spectrograms + fig, ax = plt.subplots() + def spectrogram(audio): + (d, sr) = audio + sgram = np.abs(stft.stft(d, n_fft=analyzer.n_fft, + hop_length=analyzer.n_hop, + window=np.hanning(analyzer.n_fft + 2)[1:-1])) + sgram = 20.0 * np.log10(np.maximum(sgram, np.max(sgram) / 1e6)) + sgram = sgram - np.mean(sgram) + # High-pass filter onset emphasis + # [:-1,] discards top bin (nyquist) of sgram so bins fit in 8 bits + # spectrogram enhancement + if self.illustrate_hpf: + HPF_POLE = 0.98 + sgram = np.array([scipy.signal.lfilter([1, -1], + [1, -HPF_POLE], s_row) + for s_row in sgram])[:-1, ] + sgram = sgram - np.max(sgram) + librosa.display.specshow(sgram, sr=sr, hop_length=analyzer.n_hop, + y_axis='linear', x_axis='time', + cmap='gray_r', vmin=-80.0, vmax=0, ax=None) + spectrogram(audio_read.audio_read(filename, + sr=analyzer.target_sr, + channels=1)), # Convert the hashes to landmarks + (tophitid, nhashaligned, aligntime, nhashraw, rank, min_time, max_time) = results[0] lms = audfprint_analyze.hashes2landmarks(q_hashes) + matchhashes = None + otherhashes = [] + hits = ht.get_hits(q_hashes) + t_hop = analyzer.n_hop / analyzer.target_sr + min_time_of_query = aligntime - q_hashes[0][0] - (1 / t_hop) + max_time_of_query = aligntime + q_hashes[-1][0] + (1 / t_hop) + reference_landmarks = [(t - aligntime, bin1, bin2, dt) + for (t, bin1, bin2, dt) + in audfprint_analyze.hashes2landmarks(ht.retrieve(results[0][0]))] + for idx in range(0, len(results)): + (id, _, aligntime, _, _, min_time, max_time) = results[idx] + hashes = self._unique_match_hashes(id, hits, aligntime) + if idx == 0: + matchhashes = hashes + elif id == tophitid and min_time_of_query <= aligntime + min_time and aligntime + max_time <= max_time_of_query: + otherhashes.append(hashes) mlms = audfprint_analyze.hashes2landmarks(matchhashes) # Overplot on the spectrogram - plt.plot(np.array([[x[0], x[0] + x[3]] for x in lms]).T, - np.array([[x[1], x[2]] for x in lms]).T, - '.-g') - plt.plot(np.array([[x[0], x[0] + x[3]] for x in mlms]).T, - np.array([[x[1], x[2]] for x in mlms]).T, - '.-r') + hscale = analyzer.target_sr/analyzer.n_fft + vscale = analyzer.n_hop/analyzer.target_sr + def p(landmarks, *args, **kwargs): + return ax.plot(np.array([[x[0] * vscale, (x[0] + x[3]) * vscale] for x in landmarks]).T, + np.array([[x[1] * hscale, x[2] * hscale] for x in landmarks]).T, + *args, + **kwargs) + plots = [p(lms, '.-g', color='green', label="Query"), + p(reference_landmarks, '.-b', color='blue', label="Reference"), + p(mlms, '.-r', color='red', label="Matching Hashes")] + if len(otherhashes) > 0: + plots.append(p(audfprint_analyze.hashes2landmarks(np.concatenate(otherhashes)), '.-y', color='orange', label="Other Matches")) + # Add title - plt.title(filename + " : Matched as " + ht.names[results[0][0]] - + (" with %d of %d hashes" % (len(matchhashes), - len(q_hashes)))) + ax.set_title(filename + " : Matched as " + str(ht.hashesperid[results[0][0]]) + + (" with %d of %d hashes" % (len(matchhashes), + len(q_hashes)))) + # Hash Selector + rax = plt.axes([0.01, 0.01, 0.3, 0.15]) + labels = [p[0].get_label() for p in plots] + check = matplotlib.widgets.CheckButtons(rax, + labels, + [p[0].get_visible() for p in plots]) + def click_checkbox(label): + index = labels.index(label) + plot = plots[index] + for l in plot: + l.set_visible(not l.get_visible()) + plt.draw() + check.on_clicked(click_checkbox) # Display plt.show() # Return return results - def localtest(): """Function to provide quick test""" pat = '/Users/dpwe/projects/shazam/Nine_Lives/*mp3'