使用 tensorflow.contrib.signal 重构信号会导致放大或调制(帧、重叠和添加、stft 等)

Posted

技术标签:

【中文标题】使用 tensorflow.contrib.signal 重构信号会导致放大或调制(帧、重叠和添加、stft 等)【英文标题】:reconstructing signal with tensorflow.contrib.signal causes amplification or modulation (frames, overlap_and_add, stft etc) 【发布时间】:2018-07-08 16:16:16 【问题描述】:

更新:我在 librosa 中重新实现了这个进行比较,结果确实与 tensorflow 的结果有很大不同。 Librosa 给出了我期望的结果(但不是 tensorflow)。

我已将其作为issue 发布在 tensorflow 存储库上,但那里很安静,所以我在这里尝试。此外,我不确定这是 tensorflow 中的错误,还是代表我的用户错误。为了完整起见,我也会在此处包含完整的源代码和结果。

A.) 当我使用 hann 窗口(也尝试过汉明)从带有 frame_length=1024frame_step=256 的信号(即 25% 跃点大小,75% 重叠)创建帧时,然后我使用 overlap_and_add 重建,我希望信号能够正确重建(因为 COLA 等)。但相反,它的幅度恰好是原来的两倍。我需要将结果信号除以二才能正确。

B.) 如果我使用 STFT 创建一系列重叠的频谱图,然后使用逆 STFT 重建,再次使用 frame_length=1024frame_step=256,信号再次以双倍幅度重建。

我知道为什么会出现这种情况(hann 在 50% 重叠时的单位增益,所以 75% 重叠会使信号加倍)。但是重构函数考虑到这一点不正常吗?例如。 librosa istft 确实以正确的幅度返回信号,而 tensorflow 返回双倍。

C.) 在任何其他 frame_step 都会进行严重的幅度调制。请参阅下面的图片。这似乎根本不对。

更新:如果我在inverse_stft 中明确设置window_fn=tf.contrib.signal.inverse_stft_window_fn(frame_step),则输出是正确的。所以似乎inverse_stft 中的frame_step 没有被传递到窗口函数中(这也是结果所暗示的)。

原始数据:

帧的张量流输出+overlap_and_add:

stft+istft 的张量流输出:

来自 stft+istft 的 librosa 输出:

张量流代码:

from __future__ import print_function
from __future__ import division

import numpy as np
import scipy.io.wavfile
import math
import random
import matplotlib.pyplot as plt

import tensorflow as tf
out_prefix = 'tensorflow'


def plot(data, title, do_save=True):
    plt.figure(figsize=(20,5))
    plt.plot(data[:3*frame_length])
    plt.ylim([-1, 1])
    plt.title(title)
    plt.grid()
    if do_save: plt.savefig(title + '.png')
    plt.show()


def reconstruct_from_frames(x, frame_length, frame_step):
    name = 'frame'
    frames_T = tf.contrib.signal.frame(x, frame_length=frame_length, frame_step=frame_step)
    windowed_frames_T = frames_T * tf.contrib.signal.hann_window(frame_length, periodic=True)
    output_T = tf.contrib.signal.overlap_and_add(windowed_frames_T, frame_step=frame_step)
    return name, output_T


def reconstruct_from_stft(x, frame_length, frame_step):
    name = 'stft'
    spectrograms_T = tf.contrib.signal.stft(x, frame_length, frame_step)
    output_T = tf.contrib.signal.inverse_stft(spectrograms_T, frame_length, frame_step)
    return name, output_T


def test(fn, input_data):
    print('-'*80)
    tf.reset_default_graph()
    input_T = tf.placeholder(tf.float32, [None]) 
    name, output_T = fn(input_T, frame_length, frame_step)

    title = "...l.s".format(out_prefix, sample_rate, name, frame_length, frame_step)
    print(title)

    with tf.Session():
        output_data =  output_T.eval(input_T:input_data)

#    output_data /= frame_length/frame_step/2 # tensorflow needs this to normalise amp
    plot(output_data, title)
    scipy.io.wavfile.write(title+'.wav', sample_rate, output_data)


def generate_data(duration_secs, sample_rate, num_sin, min_freq=10, max_freq=500, rnd_seed=0, max_val=0):
    '''generate signal from multiple random sin waves'''
    if rnd_seed>0: random.seed(rnd_seed)
    data = np.zeros([duration_secs*sample_rate], np.float32)
    for i in range(num_sin):
        w = np.float32(np.sin(np.linspace(0, math.pi*2*random.randrange(min_freq, max_freq), num=duration_secs*sample_rate)))
        data += random.random() * w
    if max_val>0:
        data *= max_val / np.max(np.abs(data))
    return data


frame_length = 1024
sample_rate = 22050

input_data = generate_data(duration_secs=1, sample_rate=sample_rate, num_sin=1, rnd_seed=2, max_val=0.5)

title = ".orig".format(sample_rate)
plot(input_data, title)
scipy.io.wavfile.write(title+'.wav', sample_rate, input_data)

for frame_step in [256, 512, 768, 1024]:
    test(reconstruct_from_frames, input_data)
    test(reconstruct_from_stft, input_data)

print('done.')

librosa 代码:

from __future__ import print_function
from __future__ import division

import numpy as np
import scipy.io.wavfile
import math
import random
import matplotlib.pyplot as plt

import librosa.core as lc
out_prefix = 'librosa'


def plot(data, title, do_save=True):
    plt.figure(figsize=(20,5))
    plt.plot(data[:3*frame_length])
    plt.ylim([-1, 1])
    plt.title(title)
    plt.grid()
    if do_save: plt.savefig(title + '.png')
    plt.show()


def reconstruct_from_stft(x, frame_length, frame_step):
    name = 'stft'
    stft = lc.stft(x, n_fft=frame_length, hop_length=frame_step)
    istft = lc.istft(stft, frame_step)
    return name, istft


def test(fn, input_data):
    print('-'*80)
    name, output_data = fn(input_data, frame_length, frame_step)

    title = "...l.s".format(out_prefix, sample_rate, name, frame_length, frame_step)
    print(title)

#    output_data /= frame_length/frame_step/2 # tensorflow needs this to normalise amp
    plot(output_data, title)
    scipy.io.wavfile.write(title+'.wav', sample_rate, output_data)


def generate_data(duration_secs, sample_rate, num_sin, min_freq=10, max_freq=500, rnd_seed=0, max_val=0):
    '''generate signal from multiple random sin waves'''
    if rnd_seed>0: random.seed(rnd_seed)
    data = np.zeros([duration_secs*sample_rate], np.float32)
    for i in range(num_sin):
        w = np.float32(np.sin(np.linspace(0, math.pi*2*random.randrange(min_freq, max_freq), num=duration_secs*sample_rate)))
        data += random.random() * w
    if max_val>0:
        data *= max_val / np.max(np.abs(data))
    return data


frame_length = 1024
sample_rate = 22050

input_data = generate_data(duration_secs=1, sample_rate=sample_rate, num_sin=1, rnd_seed=2, max_val=0.5)

title = ".orig".format(sample_rate)
plot(input_data, title)
scipy.io.wavfile.write(title+'.wav', sample_rate, input_data)

for frame_step in [256, 512, 768, 1024]:
    test(reconstruct_from_stft, input_data)

print('done.')
Linux Ubuntu 16.04 从二进制 v1.4.0-19-ga52c8d9、1.4.1 安装的 Tensorflow Python 2.7.14 | Anaconda 自定义(64 位)| (默认,2017 年 10 月 16 日,17:29:19)。 IPython 5.4.1 Cuda 发布 8.0、V8.0.61、cuDNN 6 Geforce GTX 970M,驱动版本:384.111

(刚刚尝试使用 TF1.5、Cuda9.0、cuDNN 7.0.5 以及相同的结果)。

【问题讨论】:

我注意到这个编程的第一部分guide有一个类似的问题。它提到必须使用窗口函数来获得重建信号中的正确幅度。顺便问一下,你提交了错误吗? 【参考方案1】:

你应该使用tf.signal.inverse_stft_window_fn

window_fn=tf.signal.inverse_stft_window_fn(frame_step)
tf_istfts=tf.signal.inverse_stft(tf_stfts, frame_length=frame_length, frame_step=frame_step, fft_length=fft_length, window_fn=window_fn)

在inverse_stft_window_fn查看更多信息

【讨论】:

【参考方案2】:

具有 50% 重叠的 Von Hann 窗的无限序列总和为平坦的单位增益。重叠 25% 时,单位时间内的窗口数量翻倍,因此增益翻倍。

Overlap-add 快速卷积滤波通常在没有重叠和没有(非矩形)窗口的情况下完成,只需对至少滤波器函数的脉冲响应长度进行足够的零填充。任何重叠百分比都不应包括任何添加的零填充长度。

【讨论】:

感谢您的信息。两点:i)我知道单位增益是 50% 重叠,但重构 fn 考虑到这一点不是很正常吗?例如。 librosa 的 istft 可以,这就是我所期望的。也许这不是常态? ii) 在 25% 以外的任何其他跳跃大小下,严重的放大器调制会怎样?我将结果与 librosa 进行了比较,结果非常不同。查看更新后的帖子。

以上是关于使用 tensorflow.contrib.signal 重构信号会导致放大或调制(帧、重叠和添加、stft 等)的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)