2023年5月25日 星期四

如何為 Praat 的聲譜圖 (spectrogram) 上色

昨天有網友留言詢問用 Praat 的聲譜圖時可否設定不同能量範圍用不同之顏色繪製? 我查了 Praat 介面並無所獲, 查詢線上使用手冊也沒找到, 看來 Praat 是固定用灰階來表示不同能量, 無法依照不同能量選擇不同顏色. 不過搜尋解決辦法時找到下面這個影片, 作者使用 R 程式將 Praat 匯出的聲譜資料進行後製處理, 利用 R 強大的繪圖功能來繪製彩色的聲譜圖 :





影片中的 R 程式原始碼放在 GitHub :


我用 ChatGPT 將此 R 程式轉換成相應的 Python 程式如下 :

# colored-spectroram.py
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors

# Read Praat spectrogram functions
def convert_spectrogram_to_df():
    # Implementation of convert_spectrogram_to_df function

def pre_emphasize():
    # Implementation of pre_emphasize function

def constrain_dynamic_range(df, column, dynamic_range):
    # Implementation of constrain_dynamic_range function

# Set file paths
spectrogram_file_path = "C:\\Users\\Matt\\Documents\\R\\Read_praat_spectrograms"
my_spect_file = "lake.Spectrogram"

# Load data into DataFrame
df_spectrogram = convert_spectrogram_to_df(my_spect_file)
df_spectrogram = pre_emphasize(df_spectrogram)
df_spectrogram = constrain_dynamic_range(df_spectrogram, "Level_preemp", 120)

# Plot spectrogram
fig, ax = plt.subplots()
pcm = ax.pcolormesh(df_spectrogram["Time"], df_spectrogram["Frequency"], df_spectrogram["Level_preemp_dr"],
                    shading='auto', cmap='gray')
ax.set_xlim(0, 0.56)
ax.set_ylim(0, 5000)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Frequency (Hz)")
plt.colorbar(pcm, ax=ax)
plt.show()

# Plot segments in different colors
onset_l = 0.0324
onset_ei = 0.1254
onset_k = 0.306

df_spectrogram["segment"] = np.where(df_spectrogram["Time"] > onset_k, "k",
                                     np.where(df_spectrogram["Time"] > onset_ei, "eI",
                                              np.where(df_spectrogram["Time"] > onset_l, "l", np.nan)))

df_spectrogram["segment"] = pd.Categorical(df_spectrogram["segment"], categories=["l", "eI", "k"])

segment_colors = ["black", "#8C3B3B", "#1D2F50"]

fig, ax = plt.subplots()
pcm = ax.pcolormesh(df_spectrogram["Time"], df_spectrogram["Frequency"], df_spectrogram["Level_preemp_dr"],
                    shading='auto', cmap=mcolors.ListedColormap(segment_colors))
ax.set_xlim(0, 0.56)
ax.set_ylim(0, 5000)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Frequency (Hz)")
plt.colorbar(pcm, ax=ax)
plt.show()

# Plot spectrogram with log-scaled Y axis
octave_breaks = [125, 250, 500, 1000, 2000, 4000]

fig, ax = plt.subplots()
pcm = ax.pcolormesh(df_spectrogram["Time"], df_spectrogram["Frequency"], df_spectrogram["Level_preemp_dr"],
                    shading='auto', cmap='gray')
ax.set_xlim(0, 0.56)
ax.set_ylim(50, 8000)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Frequency (Hz)")
ax.set_yscale('log')
ax.set_yticks(octave_breaks)
ax.get_yaxis().set_major_formatter(plt.ScalarFormatter())
plt.colorbar(pcm, ax=ax)
plt.show()

# Plot narrowband style spectrogram
my_spect_file_nb = "lake_narrowband.Spectrogram"

df_spectrogram_nb = convert_spectrogram_to_df(my
_spect_file_nb)
df_spectrogram_nb = pre_emphasize(df_spectrogram_nb)
df_spectrogram_nb = constrain_dynamic_range(df_spectrogram_nb, "Level_preemp", None, 95)

fig, ax = plt.subplots()
pcm = ax.pcolormesh(df_spectrogram_nb["Time"], df_spectrogram_nb["Frequency"], df_spectrogram_nb["Level_preemp_dr"],
                    shading='auto', cmap='gray')
ax.set_xlim(0, 0.56)
ax.set_ylim(50, 8000)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Frequency (Hz)")
ax.set_yscale('log')
ax.set_yticks(octave_breaks)
ax.get_yaxis().set_major_formatter(plt.ScalarFormatter())
plt.colorbar(pcm, ax=ax)
plt.show()

# Plot "Mongoose" spectrogram
df_mongoose = convert_spectrogram_to_df("mongoose_12.Spectrogram")
df_mongoose = pre_emphasize(df_mongoose)
df_mongoose = constrain_dynamic_range(df_mongoose, "Level_preemp", 60)

onset_m = 0.0517
onset_a = 0.13
onset_ng = 0.2535
onset_g = 0.3575
onset_u = 0.4016
onset_s = 0.579
endpoint = 0.883

segment_times = [onset_m, onset_a, onset_ng, onset_g, onset_u, onset_s, endpoint]

df_mongoose["segment"] = pd.cut(df_mongoose["Time"], bins=segment_times, labels=False, right=False)
df_mongoose["segment"] = df_mongoose["segment"] + 1

fig, ax = plt.subplots()
pcm = ax.pcolormesh(df_mongoose["Time"], df_mongoose["Frequency"], df_mongoose["Level_preemp_dr"],
                    shading='auto', cmap='gray')
ax.set_xlim(0, 0.89)
ax.set_ylim(0, 6000)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Frequency (Hz)")
ax.set_yscale('log')
ax.set_yticks(octave_breaks)
ax.get_yaxis().set_major_formatter(plt.ScalarFormatter())
plt.colorbar(pcm, ax=ax)
plt.show()

不過裡面有幾個函式未實作, 有空再看看如何補全. 

沒有留言 :