猫の日記念 AI画像生成 と Grok3のプログラム生成 #ChatGPT #Grok3

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.

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

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

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

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

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

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

import pyaudio
import numpy as np
import pygame
import time
from scipy import signal

# 定数の設定
CHUNK = 8192
RATE = 44100
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):
    if freq < NOTE_FREQ[MIN_NOTE] or freq > NOTE_FREQ[MAX_NOTE]:
        return None, None
    note_num = round(12 * np.log2(freq / 440) + 69)
    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
    octave = note_num // 12 - 1
    note_idx = note_num % 12
    note_name = NOTE_NAMES[note_idx] + str(octave)
    return note_num, note_name

def detect_peaks(fft_data, freqs, threshold):
    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
        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
    tone = np.sin(2 * np.pi * freq * t) * volume
    sound_array = (tone * 32767).astype(np.int16)
    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
    white_height = HEIGHT // 4
    black_height = white_height * 0.6
    volume_area_height = HEIGHT // 4
    stripe_height = 8
    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
                    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]
            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()
    stream = p.open(format=pyaudio.paFloat32,
                   channels=1,
                   rate=RATE,
                   input=True,
                   frames_per_buffer=CHUNK)

    pygame.init()
    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]
        
        freq_amplitudes = detect_peaks(fft_data, freqs[:CHUNK // 2], THRESHOLD)
        current_notes = set()
        note_names = []
        for freq, amp in freq_amplitudes[: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)

    stream.stop_stream()
    stream.close()
    p.terminate()
    pygame.quit()

if __name__ == "__main__":
    main()

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

pip install pyaudio numpy pygame scipy

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

saya: