DALL·E-2025-02-22-21.17.55---A-highly-realistic-and-adorable-fluffy-kitten-celebrating-Cat-Day-on-February-22nd.-The-kitten-has-big,-sparkling-eyes-and-soft-fur,-wearing-a-delicat

2/22は猫の日ですね。ということで、画像生成AIに猫の日の画像を作ってもらいました。
まずはChatGPT (DALL-E)。ChatGPTでプロンプトを考えてもらってから、画像を生成してもらっています。ChatGPT作成のプロンプトは下記。(英語の方が良い感じに鳴るので英語で)

A fluffy and adorable kitten celebrating Cat Day on February 22nd. The kitten is wearing a cute ribbon or a tiny festive hat, playing on a soft cushion. The background features a banner with 'Cat Day' written on it, fish-shaped balloons, and paw-print decorations. Warm sunlight fills the scene, creating a cozy and joyful atmosphere. The color palette is mainly pastel, with soft pink, mint green, and cream blending harmoniously.
ComfyUI_00197_

次はローカル動作のFlux.1 dev。同じプロンプトを使っています。

image

XのGrok3でも同じくプロンプトを考えてもらってから生成した画像。
かなり写真ぽい仕上がりになりますね。プロンプトは下記。

日本の猫の日(2月22日)を祝うテーマで、ふわふわの毛並みを持つ可愛い子猫が主役のコンセプト写真。子猫は桜の花びらが散る春らしい背景の中で、リボンや小さな和風のおもちゃ(たとえば、てまりや鈴)と一緒に遊んでいる。柔らかい日差しが差し込み、温かくて優しい雰囲気を演出。色合いはパステル調で、ピンク、白、淡い緑を基調に、子猫の愛らしさを引き立てる。
image (1)

ChatGPT生成のプロンプトと同じプロンプトで、Grok3で生成したらこんな感じになりました。

次に、Grok3で猫の手も借りたいプログラム作成を試しましたが、結構な速度と精度で生成しますね。ローカルのDeepSeek-R1では結局うまく動かなかったプログラムを指示だけで生成できました。(何度かエラーを修正してもらったり、要求・機能の拡張を指示しました)

スクリーンショット 2025-02-22 22.02.22

マイクで拾った音を分析してピアノ鍵盤でリアルタイムに表示するPythonプログラムです。(学生時代こんなの作ったけど、FFTなど色々自前で書かないとだめだったなとか思い出しながら)
(追記)コメントも追加してもらいました

import pyaudio  # 音声入力を扱うためのライブラリ
import numpy as np  # 数値計算、特にFFTに使用
import pygame  # グラフィック描画と音声再生に使用
import time  # ループの待機時間制御に使用
from scipy import signal  # ピーク検出に使用

# 定数の設定
CHUNK = 8192  # FFTのウィンドウサイズ(分解能 = RATE / CHUNK ≈ 5.4Hz)
RATE = 44100  # サンプリングレート(Hz)
WIDTH = 1280  # ウィンドウの幅(ピクセル)
HEIGHT = 720  # ウィンドウの高さ(ピクセル)
THRESHOLD = 0.15  # ピーク検出の振幅閾値(ノイズ除去用)
SMOOTH_WINDOW = 5  # 音階の平滑化に使うフレーム数
MIN_NOTE = 43  # ボーカル音域の下限(G2)
MAX_NOTE = 84  # ボーカル音域の上限(C6)

# MIDIノート番号と周波数の対応を辞書で定義(A4 = 440Hz基準)
NOTE_FREQ = {i: 440 * 2 ** ((i - 69) / 12) for i in range(MIN_NOTE, MAX_NOTE + 1)}
NOTE_NAMES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']  # 音階名リスト

def freq_to_note(freq):
    # 周波数をMIDIノート番号と音階名に変換する関数
    if freq < NOTE_FREQ[MIN_NOTE] or freq > NOTE_FREQ[MAX_NOTE]:
        return None, None  # ボーカル音域外なら無効
    note_num = round(12 * np.log2(freq / 440) + 69)  # 周波数からMIDI番号を計算
    if note_num < MIN_NOTE or note_num > MAX_NOTE:
        return None, None  # 範囲外なら無効
    expected_freq = NOTE_FREQ[note_num]  # 期待される周波数
    if abs(freq - expected_freq) / expected_freq > 0.05:
        return None, None  # ±5%以上の誤差があれば無効
    octave = note_num // 12 - 1  # オクターブを計算
    note_idx = note_num % 12  # 12音階内のインデックス
    note_name = NOTE_NAMES[note_idx] + str(octave)  # 音階名(例: C4)
    return note_num, note_name

def detect_peaks(fft_data, freqs, threshold):
    # FFTデータからピークを検出し、周波数と振幅のリストを返す関数
    max_amplitude = np.max(fft_data)  # 最大振幅を取得
    if max_amplitude == 0:
        return []  # データがゼロなら空リスト
    # ピーク検出(高さ、距離、顕著性を条件に)
    peaks, properties = signal.find_peaks(fft_data, height=max_amplitude * threshold, distance=10, prominence=max_amplitude * 0.1)
    detected = []
    for p in peaks:
        freq = abs(freqs[p])  # ピークの周波数
        if freq < NOTE_FREQ[MIN_NOTE] or freq > NOTE_FREQ[MAX_NOTE]:
            continue  # ボーカル音域外を除外
        # 周囲5サンプルの重み付き平均で周波数を補正
        window = 5
        start = max(0, p - window)
        end = min(len(fft_data), p + window + 1)
        weighted_freq = np.average(freqs[start:end], weights=fft_data[start:end])
        if weighted_freq < NOTE_FREQ[MIN_NOTE] or weighted_freq > NOTE_FREQ[MAX_NOTE]:
            continue  # 補正後も音域外なら除外
        detected.append((weighted_freq, fft_data[p]))  # (周波数, 振幅)を追加
    return detected

def smooth_notes(history):
    # 過去数フレームのノートを平滑化して安定性を向上させる関数
    if not history:
        return set()  # 履歴が空なら空セット
    note_counts = {}
    for frame in history:
        for note in frame:
            note_counts[note] = note_counts.get(note, 0) + 1  # 出現回数をカウント
    # 半分以上出現したノートのみを採用
    smoothed = {note for note, count in note_counts.items() if count >= len(history) / 2}
    return smoothed

def generate_tone(freq, amplitude, duration=0.1, max_amplitude=1.0):
    # 指定周波数と振幅で正弦波サウンドを生成する関数
    sample_rate = 44100
    t = np.linspace(0, duration, int(sample_rate * duration), False)  # 時間軸
    volume = min(amplitude / max_amplitude, 1.0) * 0.5  # 振幅をボリュームに変換(最大0.5)
    tone = np.sin(2 * np.pi * freq * t) * volume  # 正弦波生成
    sound_array = (tone * 32767).astype(np.int16)  # 16ビット整数に変換
    stereo_array = np.column_stack((sound_array, sound_array))  # ステレオ化
    return pygame.sndarray.make_sound(stereo_array)  # サウンドオブジェクトを返す

def draw_piano(screen, active_notes, freq_amplitudes):
    # ピアノ鍵盤とボリューム表示を描画する関数
    white_count = sum(1 for n in range(MIN_NOTE, MAX_NOTE + 1) if n % 12 not in [1, 3, 6, 8, 10])  # 白鍵数: 25
    white_width = WIDTH // white_count  # 白鍵の幅
    black_width = white_width * 0.65  # 黒鍵の幅(白鍵の65%)
    white_height = HEIGHT // 4  # 白鍵の高さ
    black_height = white_height * 0.6  # 黒鍵の高さ(白鍵の60%)
    volume_area_height = HEIGHT // 4  # ボリューム表示エリアの高さ
    stripe_height = 8  # 縞模様の1本の厚さ(線+隙間)
    max_stripes = volume_area_height // stripe_height  # 最大縞数

    screen.fill((255, 255, 255))  # 画面を白で塗りつぶし

    # 周波数と振幅をノートにマッピング
    amplitude_dict = {}
    for freq, amp in freq_amplitudes:
        note_num, _ = freq_to_note(freq)
        if note_num:
            amplitude_dict[note_num] = max(amplitude_dict.get(note_num, 0), amp)  # 最大振幅を記録

    # 白鍵の描画
    white_notes = []
    white_idx = 0
    for note in range(MIN_NOTE, MAX_NOTE + 1):
        if note % 12 not in [1, 3, 6, 8, 10]:  # 白鍵のみ
            white_notes.append(note)  # 白鍵リストに追加
            rect = pygame.Rect(white_idx * white_width, HEIGHT - white_height, white_width - 1, white_height)
            color = (255, 0, 0) if note in active_notes else (200, 200, 200)  # アクティブなら赤、そうでなければ灰色
            pygame.draw.rect(screen, color, rect)  # 白鍵を描画
            pygame.draw.rect(screen, (0, 0, 0), rect, 1)  # 枠線を描画

            if note in amplitude_dict:
                amp = amplitude_dict[note]
                max_amp = max(amplitude_dict.values()) if amplitude_dict else 1  # 最大振幅で正規化
                num_stripes = int((amp / max_amp) * max_stripes)  # 表示する縞数
                for j in range(min(num_stripes, max_stripes)):
                    line_y = HEIGHT - white_height - (j + 1) * stripe_height  # 縞のY位置
                    pygame.draw.rect(screen, (0, 100, 0),  # 深緑の縞
                                    (white_idx * white_width, line_y, white_width - 1, stripe_height // 2))
            white_idx += 1

    # 黒鍵の描画
    black_offsets = {1: 0.65, 3: 0.75, 6: 0.65, 8: 0.70, 10: 0.75}  # 黒鍵のオフセット(白鍵幅に対する位置)
    white_note_positions = {n: i for i, n in enumerate(white_notes)}  # 白鍵のMIDI番号からインデックスへのマッピング
    for note in range(MIN_NOTE, MAX_NOTE + 1):
        if note % 12 in [1, 3, 6, 8, 10]:  # 黒鍵のみ
            # 黒鍵の直前の白鍵を基準に位置を決定
            prev_white = max([n for n in white_notes if n < note], default=MIN_NOTE)
            white_idx = white_note_positions[prev_white]
            note_in_octave = note % 12
            x_pos = white_idx * white_width + white_width * black_offsets[note_in_octave]  # 黒鍵のX位置
            rect = pygame.Rect(x_pos, HEIGHT - white_height, black_width, black_height)
            color = (255, 0, 0) if note in active_notes else (0, 0, 0)  # アクティブなら赤、そうでなければ黒
            pygame.draw.rect(screen, color, rect)  # 黒鍵を描画

            if note in amplitude_dict:
                amp = amplitude_dict[note]
                max_amp = max(amplitude_dict.values()) if amplitude_dict else 1
                num_stripes = int((amp / max_amp) * max_stripes)
                for j in range(min(num_stripes, max_stripes)):
                    line_y = HEIGHT - white_height - (j + 1) * stripe_height
                    pygame.draw.rect(screen, (0, 100, 0),  # 深緑の縞
                                    (x_pos, line_y, black_width, stripe_height // 2))

def main():
    # メイン関数:音声入力、分析、描画、再生を処理
    p = pyaudio.PyAudio()  # PyAudioのインスタンスを作成
    stream = p.open(format=pyaudio.paFloat32,  # 音声ストリームを開く(32ビット浮動小数点形式)
                   channels=1,  # モノラル
                   rate=RATE,
                   input=True,
                   frames_per_buffer=CHUNK)

    pygame.init()  # Pygameを初期化
    pygame.mixer.init()  # ミキサー(音声再生)を初期化
    screen = pygame.display.set_mode((WIDTH, HEIGHT))  # 表示ウィンドウを作成
    pygame.display.set_caption("Real-time Vocal Range Analyzer")  # ウィンドウタイトル
    font = pygame.font.Font(None, 36)  # テキスト表示用のフォント

    note_history = []  # ノートの履歴を保持
    last_played_notes = set()  # 最後に再生したノート
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False  # ウィンドウを閉じたら終了

        # 音声データを取得
        data = np.frombuffer(stream.read(CHUNK, exception_on_overflow=False), dtype=np.float32)
        window = signal.windows.hann(CHUNK)  # ハニング窓を適用
        windowed_data = data * window  # 窓関数をかける
        freqs = np.fft.fftfreq(len(windowed_data), 1.0 / RATE)  # 周波数軸を計算
        fft_data = np.abs(np.fft.fft(windowed_data))[:CHUNK // 2]  # FFTを実行(正の周波数のみ)
        
        freq_amplitudes = detect_peaks(fft_data, freqs[:CHUNK // 2], THRESHOLD)  # ピークを検出
        current_notes = set()  # 現在のフレームのノート
        note_names = []  # 表示用の音階名リスト
        for freq, amp in freq_amplitudes[:3]:  # 上位3つのピークを処理
            note_num, note_name = freq_to_note(freq)
            if note_num:
                current_notes.add(note_num)
                note_names.append(f"{note_name} ({freq:.1f}Hz)")  # 音階名と周波数を記録

        note_history.append(current_notes)  # 履歴に追加
        if len(note_history) > SMOOTH_WINDOW:
            note_history.pop(0)  # 古いフレームを削除
        active_notes = smooth_notes(note_history)  # 平滑化されたノートを取得

        # 音の再生
        max_amp = max([amp for _, amp in freq_amplitudes] or [1])  # 現在の最大振幅
        new_notes = active_notes - last_played_notes  # 新しく検出されたノート
        for note in new_notes:
            freq = NOTE_FREQ[note]
            amp = next((a for f, a in freq_amplitudes if abs(f - freq) < 5), 0)  # 近い周波数の振幅
            sound = generate_tone(freq, amp, max_amplitude=max_amp)  # サウンド生成
            sound.play()  # 再生
        last_played_notes = active_notes.copy()  # 最後のノートを更新

        # ピアノ鍵盤とボリューム表示を描画
        draw_piano(screen, active_notes, freq_amplitudes)
        for i, note_info in enumerate(note_names):
            text = font.render(note_info, True, (0, 0, 0))  # テキストをレンダリング
            screen.blit(text, (10, 10 + i * 40))  # 画面に表示
        
        pygame.display.flip()  # 画面を更新
        time.sleep(0.01)  # CPU使用率を抑えるため少し待機

    # 終了処理
    stream.stop_stream()
    stream.close()
    p.terminate()
    pygame.quit()

if __name__ == "__main__":
    main()  # プログラム実行

動作させるため、最初に下記ライブラリのインストールが必要です。

pip install pyaudio numpy pygame scipy

ちなみに、上記画像生成、プログラミング、すべて無料の範囲内で実施できています。