Slide 1

Slide 1 text

/33 PyCon JP 2026 Python × 信号処理入門! 音で絵を描く スペクトログラムアートのつくりかた Drawing Pictures with Sound: Spectrogram Art with Python and Signal Processing 2026.8.22(Day2) 10:30-11:00 wat @watlablog

Slide 2

Slide 2 text

自己紹介 お前誰よ? ⚫ 名前:wat (Kenta WATANABE) ⚫ 職業:電機メーカーのメカエンジニア 音振動、 熱流体関連のシミュレーションや実験 ⚫ 趣味: • Python (2019年〜) • ピアノ (独学) • 囲碁 (アマ五段くらい) ⚫ 活動: • Python系技術ブログ「WATLAB」運営 https://watlab-blog.com/ • 技術ツール制作 https://watlab-tools.com/ • 著書「いきなりプログラミングPython(翔泳社)」 https://www.shoeisha.co.jp/book/detail/9784798184869 1 /33 @watlablog

Slide 3

Slide 3 text

このトークについて ⚫ このトークで話すこと ・スペクトログラムの読み方と歴史 ・画像を音に変換する仕組み ⚫ このトークで話さないこと ・Pythonの環境構築 ・詳しい数式の導出 ・完成コードの逐次解説 ・スペクトログラムそのものの描き方 2 /33 スペクトログラムを作成するPythonコードは PyConJP2025の資料を参照してください https://github.com/watlablog/pyconjp2025-wat 今回のスライドで紹介しているデモコードは こちらのGitHubリンクから参照してください https://github.com/watlablog/pyconjp2026-wat ⚫ このトークで持ち帰れるもの ・音と画像を繋ぐPythonの使い方 →信号処理と画像処理の面白い部分をちょっとだけ知ることができる

Slide 4

Slide 4 text

目次 1. スペクトログラムを読んでみよう 2. スペクトログラムアートとは 3. 人が音と絵を行き来してきた歴史 4. 画像をスペクトログラムに変換してみよう 5. Pythonで絵を鳴らしてみよう 6. 画像や音声を改善しよう 7. まとめ 3 /33

Slide 5

Slide 5 text

目次 1. スペクトログラムを読んでみよう 1.1.スペクトログラムとは? 2. スペクトログラムアートとは 3. 人が音と絵を行き来してきた歴史 4. 画像をスペクトログラムに変換してみよう 5. Pythonで絵を鳴らしてみよう 6. 画像や音声を改善しよう 7. まとめ 4 /33

Slide 6

Slide 6 text

1. 1.スペクトログラムとは? 5 /33 ⚫ スペクトログラムとは? 視ればどんな音なのかがわかる音声を可視化した図 音の周波数と振幅(大きさ)の時間変化を一望できるため、あらゆる音声分析現場で使われている。 音声ファイルがあれば、PythonでNumPyやSciPy、Matplotlibを使って簡単に描画できる。 周波数 振幅 時間 ※口笛の音声

Slide 7

Slide 7 text

目次 1. スペクトログラムを読んでみよう 2. スペクトログラムアートとは 2.1.スペクトログラムアートを聴いてみよう 3. 人が音と絵を行き来してきた歴史 4. 画像をスペクトログラムに変換してみよう 5. Pythonで絵を鳴らしてみよう 6. 画像や音声を改善しよう 7. まとめ 6 /33

Slide 8

Slide 8 text

2.1.スペクトログラムアートを聴いてみよう ⚫ 次の音声を聴いてみてください。 何の絵が描かれているでしょうか? 7 /33

Slide 9

Slide 9 text

2.1.スペクトログラムアートを聴いてみよう ⚫ スペクトログラムアート 音で絵を描いたもの 再生した音をスペクトログラムで分析すると絵が浮かび上がってくる。 ※正解:ネコのあくび 8 /33

Slide 10

Slide 10 text

目次 1. スペクトログラムを読んでみよう 2. スペクトログラムアートとは 3. 人が音と絵を行き来してきた歴史 4. 画像をスペクトログラムに変換してみよう 5. Pythonで絵を鳴らしてみよう 6. 画像や音声を改善しよう 7. まとめ 9 /33

Slide 11

Slide 11 text

3.人が音と絵を行き来してきた歴史 1930前後 1940 1941 1946 1957 1958 10 /33 図形を光学音声へ変換するグラフィカル・サウンドの実験が広く実施された 〜 例)Norman McLaren, Drawn sound[1] 録音ではなく、最初から図形や縞模様をフィルムに描く ↓ 模様の濃淡・幅の変化が通る光の量を変える ↓ Film 光を電気信号へ変換 ↓ スピーカーから出力 1999 2016 Sound pattern Pen Drawn soundのイメージ図 音づくりが「録音するもの」から「目で見て描いて設計するもの」に変わった [1]An introduction to the hand-drawn sounds of NORMAN McLAREN, https://www.nfb.ca/film/pen_point_percussion/

Slide 12

Slide 12 text

3.人が音と絵を行き来してきた歴史 1930前後 1940 1941 1946 1957 1958 11 /33 音のエネルギー分布を時間×周波数の画像として記録 〜 例)Bell研究所のKoenig, Dunn, LacyがSound Spectrograph[2]を発表 約2.4[s]の音を磁気テープへ録音 ↓ 同じ音を繰り返し再生 ↓ 狭い帯域のアナログフィルタで 周波数を順番に走査 ↓ 出力の強さを紙の濃淡として記録 1999 2016 Sound Spectrographのイメージ図 音を「聴いて消えるもの」から「見て読み解けるもの」へ ※FFTやデジタル信号処理は使っていないが、考え方は現代のスペクトログラム [2] W. Koenig, H. K. Dunn, and L. Y. Lacy, “The Sound Spectrograph,” JASA, Vol. 18, No. 1, pp. 19–49, 1946.

Slide 13

Slide 13 text

3.人が音と絵を行き来してきた歴史 1930前後 1940 1941 1946 1957 1958 〜 1999 12 /33 目で見えた音をそのまま機械へ渡す技術 例)ANS Synthesizer[3]の誕生 黒く塗られたガラスをスクラッチして 音を描く ※横方向時間、縦方向音の高さ ↓ 黒いガラスの裏から光を当てる ↓ 受光素子が光を電気信号へ変換 ↓ 予め用意された720個の純音を合成して鳴らす 2016 ANSのイメージ図 音を「読み解く」から、時間と周波数に「配置する」へ変えた [3] Stanislav Kreichi, “The ANS Synthesizer: Composing on a Photoelectronic Instrument” Leonardo, Vol. 28, No. 1, 1995, pp. 59–62

Slide 14

Slide 14 text

3.人が音と絵を行き来してきた歴史 1930前後 1940 1941 1946 13 /33 完成した音楽の中へ絵(意味)を隠すスペクトログラムアートの技術 例)1999:Aphex Twin 2001:Venetian Snares 2007:Nine Inch Nails 2016:DOOM 音を分析した人しか見ることができない、 隠し要素(イースターエッグ)、アートとして の表現をする人が増えてきた 1957 1958 〜 1999 2016 作曲者の顔 ネコ デジタル音楽制作環境や信号処理技術の普及によって スペクトログラムはアートとして楽しめるようになった 手

Slide 15

Slide 15 text

14 /33 それ、Pythonでできるよ (N番煎じ)

Slide 16

Slide 16 text

/33 スペクトログラムアートをPythonでつくれるようになると… 信号処理と画像処理を同時に学ぶことができる! 「アートを綺麗につくる」という明確な目的でそれぞれの分野に入門できるため、 信号処理や画像処理の難しさを体感しつつ、学んだ結果を即実践できる。

Slide 17

Slide 17 text

目次 1. スペクトログラムを読んでみよう 2. スペクトログラムアートとは 3. 人が音と絵を行き来してきた歴史 4. 画像をスペクトログラムに変換してみよう 4.1.画像を読み込む 4.2.画像の輝度を振幅に変換する 4.3.位相を設定する 5. Pythonで絵を鳴らしてみよう 6. 画像や音声を改善しよう 7. まとめ 16 /33

Slide 18

Slide 18 text

4.1.画像を読み込む 17 /33 ⚫ png画像はx, y方向の各ピクセルにRGBAの輝度値(0-255)を持つ 画像処理プログラミングはまず画像を数値に変換するところからはじまる x Red pip install pillow from PIL import Image import numpy as np y Green with Image.open("cat.png") as image: input_image = np.array(image.convert("RGBA")) NumPy行列に変換 RGBAに変換 Blue Alpha cat.png https://github.com/watlablog/pyconjp2026-wat/blob/main/src/00_img2spectrogram.py

Slide 19

Slide 19 text

4.2.画像の輝度を振幅に変換する 18 /33 ⚫ RGBAの数値を一つの行列にまとめるために、グレースケールに変換 今回は白背景+透明度が存在するpng画像を対象とするため、 黒に近いほど高輝度にし、さらに透明度の影響も反映されるようにした。 def image_to_magnitude(image): """暗い画素ほど振幅が大きい0〜1の行列を作る。""" →グレースケールへ変換 rgba = image.convert("RGBA") grayscale = np.array(rgba.convert("L"), dtype=np.float64) / 255.0 alpha = np.array(rgba.getchannel("A"), dtype=np.float64) / 255.0 →アルファ値の取得 # 黒を1、白を0にして、透明度を振幅へ反映する。 magnitude = (1.0 - grayscale) * alpha →反転グレースケールへ変換し、透明度をかける magnitude /= magnitude.max() return magnitude グレースケール変換 https://github.com/watlablog/pyconjp2026-wat/blob/main/src/00_img2spectrogram.py

Slide 20

Slide 20 text

4.2.画像の輝度を振幅に変換する 19 /33 ⚫ スペクトログラムの描画ルールから画像をマッピングするサイズを決める スペクトログラムはSTFT(Short-Time Fourier Transform)で描画する。 予め決めた分析条件を想定して、時間軸範囲と周波数軸範囲を決定する。 Nf 個 ・サンプリング周波数 ・フレームサイズ ・ホップサイズ(オーバーラップ率) ・全時間長 ・時間軸サンプル数 ・周波数軸サンプル数 Nt 個 STFT処理のイメージ図 STFTの資料やコードはこちら https://github.com/watlablog/pyconjp2025-wat

Slide 21

Slide 21 text

4.2.画像の輝度を振幅に変換する 20 /33 ⚫ スペクトログラムの描画ルールから画像をマッピングするサイズを決める 複雑なSTFT計算はSciPyの.signal.ShortTimeFFTにお任せ。 Nf 個=len(frequencies) stft = signal.ShortTimeFFT.from_window( "hann", 窓関数=ハニング窓 SAMPLE_RATE, サンプリング周波数 nperseg=FRAME_SIZE, フレームサイズ noverlap=FRAME_SIZE - HOP_SIZE, オーバーラップ symmetric_win=False, 周期窓 fft_mode="onesided2X", 正の周波数だけを残し補正 mfft=FRAME_SIZE, FFT点数 scale_to="magnitude", 振幅スペクトルとして補正 ) times = stft.t(SAMPLE_COUNT) frequencies = stft.f Nt 個=len(times) 画像をマッピングするサイズはこの変数で決まる 窓関数や負の周波数領域って? https://watlab-blog.com/2019/04/21/python-fft/

Slide 22

Slide 22 text

4.2.画像の輝度を振幅に変換する 21 /33 ⚫ グレースケール画像をリサイズして時間×周波数の2次元配列に変換する 用意したキャンバスに座標系を合わせ、画像をリサイズでマッピングする。 Nf 個 上下反転(座標合わせ) frequency_oriented_image = np.flipud(image_magnitude) target_magnitude = np.array( Image.fromarray(frequency_oriented_image.astype (np.float32)).resize( (times.size, frequencies.size), Image.Resampling.BILINEAR, ), dtype=np.float64, ) .resize() リサイズ np.flipud() .resize() Nt 個 ターゲットの振幅スペクトログラム https://github.com/watlablog/pyconjp2026-wat/blob/main/src/00_img2spectrogram.py

Slide 23

Slide 23 text

4.3.位相を設定する 22 /33 ⚫ ランダム位相をマッピングする 振幅スペクトログラムと同じサイズで位相を設定する ランダム(-π〜π) random_phase = np.random.default_rng(PHASE_SEED).uniform( -np.pi, np.pi, size=target_magnitude.shape, ) 振幅(Amplitude) 振幅が同じでも位相(Phase) が異なる波の例 振幅スペクトログラム 位相スペクトログラム [s] 画像から求めた振幅だけでは音はつくれない 位相情報を設定することが必要 https://github.com/watlablog/pyconjp2026-wat/blob/main/src/00_img2spectrogram.py

Slide 24

Slide 24 text

目次 1. スペクトログラムを読んでみよう 2. スペクトログラムアートとは 3. 人が音と絵を行き来してきた歴史 4. 画像をスペクトログラムに変換してみよう 5. Pythonで絵を鳴らしてみよう 6. 画像や音声を改善しよう 7. まとめ 23 /33

Slide 25

Slide 25 text

5.Pythonで絵を鳴らしてみよう 24 /33 ⚫ スペクトログラムを実際の音にするには、逆STFTが必要 複雑な逆STFTはSciPyの.istftにお任せ 振幅A 復元された時間波形 位相φ (scipy.ioのwavfileで保存) 逆STFTのイメージ図 複素スペクトログラム target_spectrogram = target_magnitude * np.exp(1j * random_phase) output_samples = stft.istft(target_spectrogram, k1=SAMPLE_COUNT) 複素スペクトログラム 逆STFT https://github.com/watlablog/pyconjp2026-wat/blob/main/src/00_img2spectrogram.py

Slide 26

Slide 26 text

5.Pythonで絵を鳴らしてみよう 25 /33 ⚫ 復元された音を再度スペクトログラムにする ……なんかイメージ(画像)と違う ⚫ なぜぼやけるのか? 位相をランダムに割り当てている所が最もあやしい。 振幅を定義しても各信号の位相関係によっては 合成波は打ち消しあって消えてしまうことがある。 合成前 合成後 https://github.com/watlablog/pyconjp2026-wat/blob/main/src/00_img2spectrogram.py

Slide 27

Slide 27 text

目次 1. スペクトログラムを読んでみよう 2. スペクトログラムアートとは 3. 人が音と絵を行き来してきた歴史 4. 画像をスペクトログラムに変換してみよう 5. Pythonで絵を鳴らしてみよう 6. 画像や音声を改善しよう 6.1.位相を最適化する 6.2.耳の特性を考慮する 6.3.画像処理でアート表現を高める 7. まとめ 26 /33

Slide 28

Slide 28 text

6.1.位相を最適化する 27 /33 ⚫ Griffin-Lim法[4] 振幅だけが決まっている状態で、時間波形として整合する位相を反復的に推定する方法 初期位相=ランダム 反復 位相を更新 元の振幅を使用 元振幅=ターゲット [4] Griffin, D. W. & Lim, J. S. (1984), “Signal Estimation from Modified Short-Time Fourier Transform,” IEEE TASSP, 32(2), 236–243.

Slide 29

Slide 29 text

6.1.位相を最適化する 28 /33 ⚫ Griffin-Lim法[4] 位相を改善することで元画像の再現度が向上する。 for iteration in range(1, GRIFFIN_LIM_ITERATIONS + 1): spectrogram = target_magnitude * np.exp(1j * phase) output_samples = stft.istft(spectrogram, k1=SAMPLE_COUNT) estimated_spectrogram = stft.stft(output_samples) phase = np.angle(estimated_spectrogram) ランダム位相の場合 Griffin-Lim法による推定位相の場合 (反復回数=10) 複素スペクトログラムを構成 逆STFT STFT 更新するための位相スペクトログラム 画像の再現度は向上したが、 音は聞くに耐えない電子音 →少し改善を試みる https://github.com/watlablog/pyconjp2026-wat/blob/main/src/01_img2spectrogram_griffinlim.py

Slide 30

Slide 30 text

6.2.耳の特性を考慮する 29 /33 ⚫ 等ラウドネス曲線やA特性が示すように、人は低周波数域と高周波数域が聴き難い 振幅スペクトルを高周波数側※に配置することで、違和感のある音を軽減することができる。 ※高周波数側の方が広く使える 等ラウドネス曲線[5] [5] 出典:Wikimedia Commons, “Equal loudness no caption.svg” (Public Domain) A特性[6] (聴感補正) [6] 出典:https://watlab-blog.com/2019/05/15/a-weightings/

Slide 31

Slide 31 text

6.2.耳の特性を考慮する 30 /33 15[kHz]-24[kHz] ⚫ 絵を高周波数域に逃すことで、聴感への影響を減らすことができる 高周波帯域に画像を配置することで絵の内容を はっきりと見せつつ、聴感への影響も最小にできる https://github.com/watlablog/pyconjp2026-wat/blob/main/src/01_img2spectrogram_griffinlim.py

Slide 32

Slide 32 text

6.3.画像処理でアート表現を高める 31 /33 ⚫ 様々な画像処理を組み合わせることで、よりスペクトログラム向きの絵に変換する グレースケール画像 x A:ポスタリゼーション B:Blur + Sobelフィルタ import cv2 cv2.GaussianBlur() cv2.Sobel() cv2.hypot() 色の階調を落とすことで ベタ塗り感を出す技術 A+B(輝度値調整) pip install opencv-python ぼかし処理で細かい毛をまとめ、 Sobelフィルタでエッジを際立たせる https://github.com/watlablog/pyconjp2026-wat/blob/main/src/02_img2spectrogram_imgprocessing.py

Slide 33

Slide 33 text

6.3.画像処理でアート表現を高める ⚫ 様々な画像処理を組み合わせることで、より視認性の良い解析結果になる 画像処理をしない場合 ポスタリゼーションとBlur+Sobelフィルタ処理を適用 https://github.com/watlablog/pyconjp2026-wat/blob/main/src/02_img2spectrogram_imgprocessing.py 32 /33

Slide 34

Slide 34 text

7.まとめ 33 /33 スペクトログラムアートを題材にすることで、Pythonで音と絵を繋いでみた ⚫ 画像も音もPythonには多くのライブラリがあり、低い学習コストで両方学べる 画像からスペクトログラムアートを自由につくれるWebアプリ SpectraDraw:https://spectra-draw.watlab-tools.com/ 是非遊んでみてください!

Slide 35

Slide 35 text

34 /33