1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

写真が音楽になり、音楽が絵になる。Pythonで「世界に一曲」を作る話

1
Posted at

はじめに

ある日、ふと思った。

画像のピクセルって、結局はただの数字だよな。
音声の波形も、結局はただの数字だよな。
じゃあ、画像を音楽にして、音楽を絵にできるんじゃないか?

調べてみると、こういう発想自体は昔からあって、研究やアート作品として存在している。でも「自分の写真をドラッグ&ドロップしたら数十秒で曲になる」みたいな手軽に遊べるツールは、無料のものでは意外と見つからなかった。

ないなら作るしかない。

ということで作ったのが PixelSound Nexus Ultra という Python デスクトップアプリです。

  • 写真を入れると → ピアノ・バイオリン・ドラムが鳴る音楽になる
  • 音楽を入れると → 抽象画みたいな1枚絵になる
  • 動画(MP4)を入れると → フレームと音を合成した絵になる
  • 同じ画像でも「どこから読むか」「どの方向に進むか」で別の曲になる

全部ローカルで動いて、無料のオープンソースライブラリだけで作っています。

今回はこのアプリの発想と仕組み、ところどころに実コード、そして「自分でも作りたい人がAIにどう頼めばいいか」を書いていきます。

⚠️ 先にお断り:今回は完成品(exeやコード)の配布はしません。理由は記事の最後にきちんと書きます。記事中のコードは「こんな考え方で書いている」という抜粋・解説用で、そのままコピーしても動くものではありません。実際に作るときは自分でAIに頼んで、自分の環境で動くものを組み立ててください。


まずは全体像

やっていることは1枚の図で言える。

pixelsound_fig1_overview.png

入力が3種類、出力が2種類、真ん中に変換エンジン。エンジンの中身は全部 numpyPillow、つまり標準的な数値計算と画像処理ライブラリだけで動いています。AIモデルも、有料APIも、特別な楽器音源も使っていません。

正確に言うと「画像と音は同じ数字」というのはちょっと盛った表現で、データ構造としては別物。でも、どちらも数列として扱えば相互に変換できるという意味では同じ。コンピュータの上では、それくらいのことができてしまう時代です。


できること1:写真 → 音楽

スマホで撮った何気ない風景写真を入れると、その写真だけのために作られた曲が出てくる。

明るい写真は明るい曲になる。夕焼けは赤っぽい音色(バイオリンとブラス系)になる。森の写真は緑が支配する音(ストリングスやオルガン)になる。

世界に一枚しかないあなたの写真は、世界に一曲しかないあなたの曲になる

これだけでもう面白い。

どうやって色を音にするか

ここがこのアプリの心臓部。「色を音の高さに変える」だけだと単調になるので、色から「楽器」を選ぶようにしています。

pixelsound_fig2_color_to_instrument.png

色だけじゃなくて「輪郭が強い(境界線がはっきりしている)」ところは打楽器が増える。これがリズムを作る。

実際のコードは関数1個でこれを判定しています(抜粋・概念コードです)。

def choose_pixel_instrument_layers(r, g, b, brightness, saturation, edge):
    """ピクセルの特徴から、鳴らす楽器レイヤーを決める"""
    layers = []

    # 明るいブロックはピアノを足す
    if brightness > 0.50:
        layers.append({"instrument": "piano"})

    # 暗いブロックは低音担当
    if brightness < 0.42:
        layers.append({"instrument": "bass"})
        if brightness < 0.26:
            layers.append({"instrument": "cello"})

    # 赤緑青のうち一番強い成分で楽器を決める
    if r >= g and r >= b:
        layers.append({"instrument": "violin"})
    elif g >= r and g >= b:
        layers.append({"instrument": "strings"})
        layers.append({"instrument": "organ"})
    else:
        layers.append({"instrument": "flute"})
        if b > 0.55:
            layers.append({"instrument": "bell"})

    # 輪郭が強いところはドラムを足す
    if edge > 0.030:
        layers.append({"instrument": "kick"})
    if edge > 0.055:
        layers.append({"instrument": "snare"})

    return layers

1ブロックから複数の楽器レイヤーが返ってくるので、色の成分がそのまま音の厚みになる。これで単純な「色 = 音階」よりずっと音楽らしくなります。

楽器の音はどう作っているか

「ピアノっぽい音」「バイオリンっぽい音」は全部 numpy で正弦波を組み合わせて作っています。本物の楽器音源(サンプル音源)は使っていません。

ピアノ風の音はこんな感じ。

def synth_piano_like(freq, duration, sample_rate):
    n = int(duration * sample_rate)
    t = np.linspace(0, duration, n, dtype=np.float32)
    
    # 基音 + 倍音を重ねる(ピアノは倍音構成が複雑)
    sig = (1.00 * np.sin(2 * np.pi * freq * t)
         + 0.48 * np.sin(2 * np.pi * freq * 2.01 * t)
         + 0.23 * np.sin(2 * np.pi * freq * 3.02 * t)
         + 0.10 * np.sin(2 * np.pi * freq * 4.03 * t))
    
    # ピアノは打鍵後すぐ減衰する
    env = np.exp(-3.1 * t / duration)
    return sig * env

np.sin を4つ重ねて、np.exp で急減衰させているだけ。それでもちゃんとピアノっぽく聞こえるから面白い。

「これはピアノとは呼べない」というプロの方の指摘は正しいです。あくまでピアノ風の合成音で、本物のグランドピアノと比べたら別物。でも個人が無料で、ローカルで、numpy だけで鳴らせる音としては十分楽しめる。

バイオリンならビブラートを追加、ベルなら非整数倍音、ドラムなら周波数を急降下させる。楽器の特徴をそのまま数式にしている。プログラミングというより数学の音楽化に近い。


できること2:音楽 → 絵

逆に音楽ファイルを入れると、その曲を視覚化した1枚絵が出てくる。

正直に言うと、ちゃんとした絵にはならない。人物画とか風景画みたいなものは出てこない。出てくるのは抽象画というか、データアートというか、そういう類のもの。

でもこれが意外と「この曲ってこんな雰囲気だったのか」という発見になる。激しい曲は派手な絵に、静かな曲は落ち着いた絵になる。音の見た目が見える。

音から絵を作る流れ

  1. 音声ファイルを波形にする
  2. FFT で周波数解析(低音・中音・高音に分ける)
  3. 音量・変化量・周波数の重心も計算する
  4. これらを HSV 色空間にマッピングして RGB に変換
  5. 走査順で画像に配置
  6. 軽くブラーやコントラストをかけて絵っぽくする

色を作るところはこんな感じ。

# 低音 → 色相の基準, 中音 → 色相シフト, 高音 → さらにシフト
h = np.mod(base + low * 0.16 + mid * 0.34 + high * 0.78
           + change * 0.42 + centroid * 0.28, 1.0)

# 中音と高音が大きいほど鮮やか
s = 0.68 + 0.32 * np.maximum(mid, high)

# 音量と変化量で明るさが決まる
v = 0.12 + 0.88 * np.maximum.reduce([energy, change, high])

# HSV から RGB に変換
rgb = rgb_from_hsv(h, s, v)

これは厳密な信号処理ではなく、芸術表現としてのマッピングです。「低音はオレンジっぽい」とか「高音は青っぽい」みたいな感覚は人によって違うので、自分で式を変えていくのが楽しいところでもあります。

バイナリそのものも混ぜる

これがちょっとしたこだわりポイント。スペクトログラム(周波数解析)だけだと、似た曲は似た絵になりがち。なので、ファイルのバイナリそのものも画像化に混ぜています。

# ファイルバイナリを直接読む
file_bytes = read_file_bytes_sampled(media_path, target_len)
# デコードした音声PCMも読む
pcm_bytes = audio_pcm_bytes(audio, target_len)
# 設定文字列も混ぜる(同じ素材でも起点や方向で違う絵にするため)
setting_bytes = f"{corner}|{direction}|{scan_mode}".encode("utf-8")

mixed = file_bytes + pcm_bytes + digest + setting_bytes

こうすると、同じ曲のWAVとMP3で違う絵になる。フォーマットの差まで絵に出る。これは楽しい。


できること3:同じ画像なのに違う曲になる

ここが個人的に一番気に入っているところ。

同じ写真でも、

  • 起点:左上 / 右上 / 左下 / 右下 / 中央
  • 方向:右 / 左 / 上 / 下
  • 走査方式:通常 / 折り返し / 渦巻き / 放射状 / 斜め / ブロックシャッフル

これらを変えると、聞こえ方が大きく変わる別の曲になる。

pixelsound_fig3_scan_patterns.png

「画像をどの順番で読むか」を変えるとピクセルの並びが変わって、結果として音の並びも変わる。中央起点で渦巻き、左上起点で通常、右下起点で折り返し...と変えていくと、同じ写真から何通りも違う表情の曲が作れる

実装は意外とシンプルで、走査順を生成する関数が選択肢の数だけある。

def generate_scan_path(width, height, corner, direction, scan_mode):
    """指定された設定で、画像を読む順番を生成する"""
    if scan_mode == "渦巻き走査":
        return generate_spiral_path(width, height, corner, direction)
    if scan_mode == "放射状走査":
        return generate_radial_path(width, height, corner, direction)
    if scan_mode == "斜め走査":
        return generate_diagonal_path(width, height, corner, direction)
    if scan_mode == "ブロックシャッフル":
        return generate_block_shuffle_path(width, height, corner, direction)
    # ... 他のパターン

この関数が [(x1, y1), (x2, y2), ...] というリストを返してきて、その順番で画像を読み、その順番で音を鳴らす。たったそれだけ。でも結果は劇的に変わる。


アプリの中で実際に動いているコード

エンジンの全体像をもう少し具体的に。画像 → 音 のメイン関数はこんな構造です(概念コード)。

def image_to_audio(image_path, seconds, corner, direction, scan_mode):
    # 秒数からブロック数を決める(60秒なら約100ブロック)
    grid_w, grid_h = auto_grid_size(image_path, seconds)
    
    # 指定走査順でブロックを読む
    blocks = read_blocks_by_scan(image_path, grid_w, grid_h, 
                                  corner, direction, scan_mode)
    
    # 出力バッファ(ステレオ)
    total_samples = int(seconds * SAMPLE_RATE)
    audio = np.zeros((total_samples, 2), dtype=np.float32)
    
    # 各ブロックを音に変換
    for i, block in enumerate(blocks):
        r, g, b = block["mean_color"]
        brightness = block["brightness"]
        edge = block["edge"]
        
        # 色から楽器を選ぶ
        layers = choose_pixel_instrument_layers(r, g, b, brightness, ...)
        
        # 各楽器を合成して加算
        for layer in layers:
            mono = synth_instrument(layer["instrument"], freq, duration, ...)
            audio[start:end, :] += pan_stereo(mono, pan)
    
    return normalize_audio(audio)

ループしてるだけ。やっていることは「ブロックを読む → 楽器を選ぶ → 音を作る → 足す」これだけ。

魔法は使っていない。ただの足し算が、曲になる


自分でも作ってみたい人へ:AIにどう頼むか

ここまで読んで「自分も作りたい」と思った人に向けて、AIへの頼み方を書いておきます。配布できないなら、自分で作ってもらえばいい。

pixelsound_fig4_ai_steps.png

ポイントはいきなり全部頼まないこと。動くものを作って、動いたら次を足す。これがバイブコーディングの基本リズム。

必要なもの

  • Python 3.10以降(自分は 3.14 で動かしている)
  • numpyPillowtkinter(標準)、tkinterdnd2pydub
  • 任意:ffmpeg(MP3やMP4を扱うなら必要)

すべて個人の学習・趣味の範囲では無料で使えます。

⚠️ AI を使うときの注意:ChatGPT や Claude など、無料枠で十分作れます。有料プランや API を契約する必要はありません。「APIキーを使ってください」と書いてあるサンプルをそのまま使うと従量課金が発生することがあるので、まずは普通のチャット画面で会話するところから始めてください。

Step 1:最小構成を頼む

最初の1発目はこのくらいシンプルに。

Pythonで、tkinterのGUIアプリを作ってください。
- 画像ファイルをドラッグ&ドロップで受け取る
- 画像のピクセルを左上から右へ順番に読む
- 各ピクセルの明るさを音の高さに変換する
- numpyでサイン波を生成してWAVファイルに保存する
- ファイル選択ダイアログで保存先を選べるようにする

これでまず動くものを出してもらう。動かないところはエラーメッセージをそのまま貼って直してもらう。

Step 2:機能を1つずつ足す

今のコードに、以下の機能を追加してください。
- 走査の起点を選べるようにする(左上・右上・左下・右下・中央)
- 走査の方向を選べるようにする(右・左・上・下)
- 走査方式を選べるようにする(通常・折り返し・渦巻き)

今のコードに」と書くのが大事。前のコードを貼り付けて、その上で追加する形にすると、AIが既存の構造を保ったまま機能を足してくれる。

Step 3:音色を増やす

今は単純なサイン波だけですが、ピクセルの色によって楽器を変えたいです。
- 赤が強いピクセル → バイオリン風(鋸波+ビブラート)
- 緑が強いピクセル → ストリングス風(複数倍音)
- 青が強いピクセル → フルート風(純音+息ノイズ)
- 明るいピクセル → ピアノ風(倍音+急減衰)
- 暗いピクセル → ベース風(低周波+歪み)
- 輪郭が強い場所 → ドラム(キック・スネア・ハイハット)
ADSRエンベロープを使って合成してください。

ここでようやく「曲」っぽくなる。

Step 4:逆方向(音 → 画像)

今度は逆に、音声ファイルから画像を生成する機能を追加してください。
- WAVを読み込み、FFTで周波数解析する
- 低音・中音・高音の3バンドに分ける
- それぞれをHSV色空間の色相・彩度・明度にマッピングする
- 走査順に画像を生成する
- PNGで保存する

Step 5:自分の感性を反映

ここからが本番。「もっと派手にしたい」「ジブリっぽい色にしたい」「サイバーパンクな感じで」みたいな自分の感性をAIに伝える。

これはバイブコーディングの真骨頂で、完成形の言語化が9割

過去の記事でも書いたんだけど、プログラミングをずっとやってきて気づいたのは「プログラミングそのものより、何を作りたいかを言葉にできるかの方が大事」ということ。AIが書いてくれる時代だからこそ、ここの差が大きく出る。

つまずきポイント

実際に作ると必ず詰まる場所があります。先に書いておきます。

  • tkinterdnd2 が入らないpip install tkinterdnd2 で失敗するときは py -m pip install tkinterdnd2 を試す
  • MP3対応しない:ffmpeg の PATH 通しが必要。ffmpeg -version がコマンドプロンプトで通ればOK
  • 音が割れるnumpy の値を -1.0 〜 1.0 に正規化してから int16 に変換。np.tanh を使ったソフトリミッターを入れると安定する
    def soft_limiter(audio, drive=1.7):
        return np.tanh(audio * drive) / np.tanh(drive)
    
  • 画像が真っ黒:HSVからRGBへの変換で値が 0〜1 の範囲に収まっているか確認

このあたりはAIに「このエラーが出ます。原因と修正方法を教えてください」と聞けば大体解決する。


⚠️ 入れる素材と、出てきたものの扱いについて

ここがすごく大事。自分で作って、自分で楽しむ分には何の問題もないアプリです。でも、他人の素材を使ったり、出てきたものを公開したりするときは要注意

入れていいもの・避けたほうがいいもの

入れる素材 自分で楽しむ SNS等で公開
自分で撮った写真
自分で描いた絵
自作の音声・楽曲
フリー素材(規約確認済み) △(規約次第)
市販CD・配信音源 △(私的利用) ×
他人のSNS写真 × ×
著作権のある楽曲 △(私的利用) ×

特に注意したいのは音楽。市販の曲やストリーミング配信の楽曲を入力に使って、出てきた画像をSNSにアップすると、状況によっては著作権の問題になります(派生物として扱われる可能性)。

逆に自分で撮った写真を入力にして、出てきた曲をSNSに上げるのは、基本的に問題ありません。出力された曲はあなたが作った素材から生まれたものです。

商用利用したい場合

これは個別に専門家へ相談してください。

  • 使ったライブラリのライセンス確認(numpy / Pillow / pydub などはオープンソースですが、それぞれ条件あり)
  • ffmpeg を組み込む場合はライセンスの種類が複雑(商用配布は要注意)
  • 入力素材の権利処理
  • 出力物の利用範囲

「商用OK」と一概には言えないので、ビジネスで使うなら弁護士や法務担当に確認が必要です。

「あなたが作った」と言われても困ること

これも先に書いておきます。

  • この記事を読んで、誰かが似たアプリを作って配布したり販売したりした結果、トラブルが起きても、私は一切の責任を負えません
  • この記事のコードはあくまで概念の説明用で、そのまま動くものではありません。動かなかった、思った通りに動かなかった、でも対応できません
  • この記事を見てやったらこうなった」系のクレームは、AIや該当ライブラリの提供元、あるいはご自身でご対応ください

冷たく聞こえるかもしれませんが、個人がブログで発想を共有するというのは、こういう線引きをしないと書けなくなってしまうんです。「面白そう」と思った人が、自分の責任で楽しんでくれることを願っています。


ここから広がる未来

リスクの話ばかりだとつまらないので、最後はわくわくする話で終わります。

このアプリ、正直まだまだ序の口。妄想を書いていく。

  • 動画 → 音楽の連続変換:MP4の各フレームを連続して曲にすれば、映像の音楽化ができる。リアルタイムでBGMを生成する映像作品とか
  • 絵から曲、曲から絵、絵から曲... の無限ループで進化する作品
  • 同じ写真でみんなで遊ぶコミュニティ。「私の解釈」と「あなたの解釈」が違うのが面白い
  • VR空間で絵の中を歩くと曲が変わるインスタレーション
  • 植物の成長を毎日撮影して、365日分の曲を作る長期プロジェクト
  • 日記の代わりに、その日撮った写真を曲にして残す。10年後に再生したら、その日の感情が音で蘇る

考え始めるとキリがない。

画像と音は、コンピュータの上では数字の世界で繋がっている。これに気づくと、世界の見方がちょっと変わる。

僕自身は今、Excelを楽器に変えるツールも作っている。身の回りのあらゆるものを音と絵の素材にできる時代になっている。これは本当にわくわくする。


なぜ完成品を配布しないか(大事な話)

「面白そう、コードください」「exeください」と言われそうな気もするんですが、今回は配布しません。理由を正直に書きます。

exeを配布する怖さ

PythonアプリをPyInstallerなどでexe化すると、知らない人でもダブルクリックで動かせるようになる。便利な反面、これが怖い。

  • ウイルス判定される:自作のexeは、ウイルス対策ソフトに「不審なファイル」と判定されることがよくある
  • セキュリティ警告:Windowsの「不明な発行元」警告が出て、受け取った側が不安になる
  • 改変リスク:再配布された場合、悪意あるコードを混ぜられて「あなたが作ったものです」と広まる可能性
  • コード署名証明書は年間数万円。個人で払うのは厳しい

コードを配布する怖さ

ソースコード公開も気軽にやると痛い目を見る。

  • ライブラリのライセンス確認を全部正しく明記する必要がある(地味に大変)
  • 動作環境による事故:「動きません」「エラーが出ます」の対応で時間がいくらあっても足りない
  • 改変版の責任問題:誰かが改変して配布したものでトラブルが起きると、元の作者が問い合わせを受けることがある
  • 悪用の踏み台になる可能性:今回のアプリ自体は無害ですが、技術的なテンプレートとして流用される可能性はゼロじゃない

じゃあどうするか

僕は「作り方を共有する」というスタンスを取っています。

  • 仕組みを記事にする
  • AIへの頼み方を共有する
  • つまずきポイントを書いておく

こうすれば、興味を持った人は自分の手で作れる。自分で作ったものは自分のもの。配布の怖さもない。何より、作る過程が一番楽しい

完成品をもらうより、自分で作った方が10倍面白いと、個人的には思っています。


おわりに

写真が音楽になって、音楽が絵になる。

書いていてあらためて思うけど、これってちょっと前なら考えられなかった話。numpyPillowtkinter があれば、個人がローカルで、無料で、こんなことができる時代になっている。

しかもAIに相談しながら作れば、プログラミング経験が浅くても形にできる。作りたいものを言葉にできれば、それはもう半分完成している

この記事を読んで「自分も作ってみたい」と思った人がいたら、ぜひAIに話しかけてみてください。最初は思った通りに動かないかもしれない。でも、

  • 3日触れば「動くもの」になる
  • 1週間で「人に見せたいもの」になる
  • 1ヶ月で「自分だけのツール」になる

そして、できたものを記事にしてください。誰かの「作ってみよう」のきっかけになります。

未来は、待つものじゃなくて、自分で鳴らすものです。

それでは、よい創作を。


この記事の内容は個人の感想・経験に基づくもので、特定のツール・サービス・技術について保証するものではありません。実際に作って公開・配布する際は、各ライブラリのライセンスや素材の権利関係をご自身で確認してください。

コメントやLGTM、励みになります。読んでくれてありがとうございました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?