From 4be705f67bd4642b2a85e92368fa56baee49ce3b Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 6 May 2025 11:47:45 +0200 Subject: [PATCH 1/2] move check for equality to the very top --- scripts/pyaudio3dtools/audioarray.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/scripts/pyaudio3dtools/audioarray.py b/scripts/pyaudio3dtools/audioarray.py index f548622242..36b5f47560 100644 --- a/scripts/pyaudio3dtools/audioarray.py +++ b/scripts/pyaudio3dtools/audioarray.py @@ -350,9 +350,11 @@ def compare( raise NotImplementedError( f"wav-diff tool not available for {curr_platform}" ) - + search_path = toolsdir.joinpath(curr_platform.replace("Windows", "Win32")) - wdiff = search_path.joinpath("wav-diff").with_suffix(".exe" if curr_platform == "Windows" else "") + wdiff = search_path.joinpath("wav-diff").with_suffix( + ".exe" if curr_platform == "Windows" else "" + ) if not wdiff.exists(): wdiff = shutil.which("wav-diff") @@ -405,7 +407,6 @@ def compare( result["MLD"] = mld_max - # Run remanining tests after checking if the lenght differs lengths_differ = ref.shape[0] != test.shape[0] @@ -678,16 +679,21 @@ def ssnr( """ Calculate Segmental SNR for test_sig to ref_sig as defined in ISO/IEC 14496-4 """ + # check if diff of signal is zero already, then SNR is infinite, since no noise + signals_equal = (ref_sig == test_sig).all() + if signals_equal: + return np.asarray([np.inf] * ref_sig.shape[1]) + ss = list() + # allocation here ref_sig_norm = ref_sig / -np.iinfo(np.int16).min + # allocation here test_sig_norm = test_sig / -np.iinfo(np.int16).min - - # check if diff of signal is zero already, then SNR is infinite, since no noise + # allocation here diff_sig_norm = ref_sig_norm - test_sig_norm - if np.all(diff_sig_norm == 0): - return np.asarray([np.inf] * ref_sig_norm.shape[1]) + # allocation here channels_identical_idx = np.sum(np.abs(diff_sig_norm), axis=0) == 0 denom_add = 10**-13 * len_seg -- GitLab From 2ac91783b689fd2cb2669d1bc9ace4d9678d1bbf Mon Sep 17 00:00:00 2001 From: Jan Kiene Date: Tue, 6 May 2025 13:22:45 +0200 Subject: [PATCH 2/2] reduce memory usage of ssnr function --- scripts/pyaudio3dtools/audioarray.py | 40 +++++++++++++--------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/scripts/pyaudio3dtools/audioarray.py b/scripts/pyaudio3dtools/audioarray.py index 36b5f47560..1ed7caedec 100644 --- a/scripts/pyaudio3dtools/audioarray.py +++ b/scripts/pyaudio3dtools/audioarray.py @@ -623,7 +623,9 @@ def limiter(x: np.ndarray, fs: int): fr_sig[idx_min] = -32768 -def get_framewise(x: np.ndarray, chunk_size: int, zero_pad=False) -> np.ndarray: +def get_framewise( + x: np.ndarray, chunk_size: int, zero_pad=False, scale_fac=1.0 +) -> np.ndarray: """Generator to yield a signal frame by frame If array size is not a multiple of chunk_size, last frame contains the remainder @@ -635,6 +637,8 @@ def get_framewise(x: np.ndarray, chunk_size: int, zero_pad=False) -> np.ndarray: Size of frames to yield zero_pad: bool Whether to zero pad the last chunk if there are not enough samples + scale_fac: float + scale returned chunks with this factor Yields ------- @@ -643,9 +647,9 @@ def get_framewise(x: np.ndarray, chunk_size: int, zero_pad=False) -> np.ndarray: """ n_frames = x.shape[0] // chunk_size for i in range(n_frames): - yield x[i * chunk_size : (i + 1) * chunk_size, :] + yield x[i * chunk_size : (i + 1) * chunk_size, :] * scale_fac if x.shape[0] % chunk_size: - last_chunk = x[n_frames * chunk_size :, :] + last_chunk = x[n_frames * chunk_size :, :] * scale_fac if zero_pad: yield np.pad( last_chunk, [[0, chunk_size - (x.shape[0] % chunk_size)], [0, 0]] @@ -684,29 +688,23 @@ def ssnr( if signals_equal: return np.asarray([np.inf] * ref_sig.shape[1]) - ss = list() - - # allocation here - ref_sig_norm = ref_sig / -np.iinfo(np.int16).min - # allocation here - test_sig_norm = test_sig / -np.iinfo(np.int16).min - # allocation here - diff_sig_norm = ref_sig_norm - test_sig_norm - - # allocation here - channels_identical_idx = np.sum(np.abs(diff_sig_norm), axis=0) == 0 + n_channels = ref_sig.shape[1] + channels_identical_idx = np.asarray( + [(ref_sig[:, c] == test_sig[:, c]).all() for c in range(n_channels)] + ) + # iterate over test signal too to allow power comparison to threshold + ss = list() denom_add = 10**-13 * len_seg segment_counter = np.zeros(ref_sig.shape[1]) - - # iterate over test signal too to allow power comparison to threshold - for ref_seg, diff_seg, test_seg in zip( - get_framewise(ref_sig_norm, len_seg, zero_pad=True), - get_framewise(diff_sig_norm, len_seg, zero_pad=True), - get_framewise(test_sig_norm, len_seg, zero_pad=True), + # apply normalization factor on the chunks to avoid big reallocation of the whole signal + norm_fac = 1 / -np.iinfo(np.int16).min + for ref_seg, test_seg in zip( + get_framewise(ref_sig, len_seg, zero_pad=True, scale_fac=norm_fac), + get_framewise(test_sig, len_seg, zero_pad=True, scale_fac=norm_fac), ): nrg_ref = np.sum(ref_seg**2, axis=0) - nrg_diff = np.sum(diff_seg**2, axis=0) + nrg_diff = np.sum((test_seg - ref_seg) ** 2, axis=0) ss_seg = np.log10(1 + nrg_ref / (denom_add + nrg_diff)) -- GitLab