Skip to content

Latest commit

 

History

History
83 lines (59 loc) · 2.71 KB

File metadata and controls

83 lines (59 loc) · 2.71 KB

p😭q

音乐的正统在于看 FFT 听音乐!

这道题本质上就是把频谱图转换为声波。

我本来以为这道题还要手撕 FFT 的,结果发现完全不用,首先给了代码,再者[见下文],于是这道题就完全是一道工程题,只要会调库就可以了。

看源码,源码大致可以分成两个部分:正向 FFT,和 gif 生成。

我们就反过来“逆向”每一步就行了。

首先是读 gif,这个没啥好说的,只是细节比较多,直接上代码(趴):

image = Image.open('flag.gif')

db_frames = []

for i, gif_frame in enumerate(ImageSequence.Iterator(image)):
    # 将 gif_frame 转为 [x, y, 3] 的三维数组
    frame = np.asarray(
            gif_frame.convert('RGB').getdata(),
            dtype=np.uint8
        ).reshape(gif_frame.size[1], gif_frame.size[0], 3)

    dbs = []  # 该 frame 中,频率对应的 dB

    for freq in range(0, num_freqs):
        for i, threshold in enumerate(list(range(min_db, max_db + 1, quantize))[::-1], start=1):
            x = threshold - min_db
            y = (freq * 2 + 1) * 2
            color = frame[x][y]
            if (color == white_pixel).all():
                dbs.append(i - 1)
                break

    db_frames.append(dbs)

随后就是要逆向这个 melspectrogram。看源代码:

spectrogram = (
    numpy.around(
        librosa.power_to_db(
            librosa.feature.melspectrogram(
                ...
            )
        ) / quantize
    ) * quantize
)

最外层,/ quantize, numpy.around, * quantize 就是将 dB 取整。

然后再里面一层,就是 librosa.power_to_db,看这个命名……盲猜一下……大概有个函数……可以把它反过来,还真有:librosa.db_to_power

再里面一层,就是 FFT 大头,librosa.feature.melspectrogram,本来呢,我还做好准备要手撕 FFT 的,然后一查文档,发现好家伙,这函数也有逆函数 librosa.feature.inverse.mel_to_audio,这就省事了。

接下来的事就是把上面的过程反过来套一遍就可以了:

db_frames = [ ... ]  # 上面的 db_frames

frames = np.array(db_frames).transpose(1, 0)  # 交换两维,我也不知道为啥要这样,反正接口要对上就对了
frames += min_db
# 如果考虑上面的对 2 取整的话,实际上还要加上期望值 1 才对?

y = librosa.feature.inverse.mel_to_audio(
    librosa.db_to_power(frames),
    22050,  # sample_rate
    n_fft=fft_window_size,
    hop_length=frame_step_size,
    window=window_function_type
)

sf.write('origin.wav', y, 22050)

至此,我们就把 gif 逆向成了 origin.wav

音频是「The flag is, F-L-A-G, 634,971,243,582」。