牛骨文教育服务平台(让学习变的简单)
博文笔记

Python将图片写到音频频谱

创建时间:2017-12-21 投稿人: 浏览次数:903

用到的库:pillow、numpy、matplotlib

源码GitHub

# -*- coding: utf-8 -*-

import wave

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
from matplotlib.mlab import window_none


def img2wav(img_path, wav_path, fft_size=1024):
    """
    把图片写到音频频域
    :param img_path: 输入图片路径
    :param wav_path: 输出音频路径
    :param fft_size: 图片每列代表的音频长度,也是频域长度的两倍
    """

    # 读取图片,转为灰度图
    img = Image.open(img_path).convert("L")
    # 缩放到高度 = fft_size / 2 (令负频率全为0)
    img = img.resize((img.width * fft_size // 2 // img.height, fft_size // 2),
                     Image.BICUBIC)

    # 转为numpy数组
    img = np.array(img, "float")
    # 变换到-100~0分贝
    img = img * (100 / 255) - 100
    # 单位从分贝转成1,此时取值为0~1
    # amp_dB = 20 * ln(amp) / ln(10)
    # amp = exp(amp_dB / 20 * ln(10))
    img = np.exp(img * (np.log(10) / 20))
    # 翻转(索引小的频率小)然后转置(要迭代列)
    img = img[::-1].T

    # 防溢出,每列总振幅不能超过1
    max_sum = max(col.sum() for col in img)
    if max_sum > 1:
        img /= max_sum

    with wave.open(wav_path, "wb") as f:
        # (nchannels, sampwidth, framerate, nframes, comptype, compname)
        f.setparams((1, 2, 44100, len(img) * fft_size, "NONE", ""))
        for col in img:
            # 傅里叶反变换
            data = np.fft.ifft(col * fft_size, fft_size).real * 32767

            # 最后一次防溢出,限制范围在-32768~32767
            for index in np.where(data < -32768):
                data[index] = -32768
            for index in np.where(data > 32767):
                data[index] = 32767

            # 写到wav文件
            data = data.astype("short")
            f.writeframesraw(data)


def draw_spectrum(wav_path, fft_size=1024):
    """
    画音频频谱图
    :param wav_path: 输入音频路径
    :param fft_size: 傅里叶变换用的长度
    """

    # 读wav
    with wave.open(wav_path, "rb") as f:
        n_samples = f.getnframes()
        data = f.readframes(n_samples)
        n_channels = f.getnchannels()
        sample_rate = f.getframerate()

    # 转为numpy数组
    data = np.fromstring(data, "short")
    # 取第一个声道
    data.shape = (n_samples, n_channels)
    data = data.T[0]

    # 画频谱,无加窗和重叠
    plt.specgram(data / 32767, fft_size, sample_rate, window=window_none,
                 noverlap=0, scale="dB")
    plt.show()


if __name__ == "__main__":
    img2wav("test.jpg", "test.wav")
    draw_spectrum("test.wav")

测试图片:
lena
9
python

自己画的频谱效果
lena
9
python

AU的频谱效果,因为AU有加窗和重叠所以和自己画的有点不一样
lena
9
python

声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。