OwlWhisper: 初心者向けのキャラクターエージェント

AI実況

はじめに

OwlWhisperは、高速な音声認識ライブラリ「Faster Whisper」と、高品質な音声合成ライブラリ「Style-Bert-VITS2」を組み合わせたプロジェクトです。初心者でも簡単に音声認識と音声合成を体験できるように設計されています。チャットボットの応答には、Google社のGemini Proモデルを使用しています。

特徴

  • WSL2、Docker、Streamlit、FastAPI、PyTorch、CUDA、Style-Bert-VITS2、Faster Whisperを使用した最新の技術スタック
  • Webカメラを使った音声認識APIの提供
  • 音声対話型のチャットボット機能(Gemini Proモデルを使用)

必要な環境

  • Windows 10以降のOS
  • WSL2 (Windows Subsystem for Linux 2)
  • Docker
  • NVIDIA GPUとCUDAドライバー

セットアップ

  1. このリポジトリをクローンします。
  2. WSL2とDockerをインストールし、設定します。
  3. NVIDIA GPUとCUDAドライバーを正しくインストールします。
  4. .envファイルの設定(.env.exampleを参考にしてください)

GOOGLE_AI_STUDIO_API_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
CAMERA_URL=http://host.docker.internal:8100/camera
FAST_WISPER_API_URL=http://fast-wisper-api:8181/transcribe
STYLE_BERT_VITS2_API_URL=http://style-bert-vits2-api:5000/voice

Webカメラ API の起動方法

  1. コマンドプロンプトを開きます。
  2. プロジェクトのルートディレクトリに移動します。
  3. 以下のコマンドを実行します:

C:\Prj\OwlWhisper>camera-api.bat
  1. Anacondaの仮想環境 "yva" がアクティベートされ、Webカメラ APIが起動します。
FastAPIを使ってWebカメラ映像をストリーミング配信するAPI
はじめに近年、IoTデバイスの普及に伴い、リアルタイムでのビデオストリーミングが様々な用途で利用されるようになりました。本記事では、Python製のWebフレームワークであるFastAPIを使って、Webカメラからの映像を複数の端末にブロー...
【claude3】YlvaVisionAPI(Webカメラからリアルタイムの画像を取得+ブロードキャストするためのAPI)
こちらの記事もおすすめYlvaVisionAPIYlvaVisionAPIは、Webカメラからリアルタイムの画像を取得し、ブロードキャストするためのAPIです。このREADMEでは、APIの機能、セットアップ方法、使用方法について説明します...

チャットボットの起動方法

  1. WSL2のターミナルを開きます。
  2. プロジェクトのルートディレクトリに移動します。
  3. 以下のコマンドを実行します:

maki@TurtleTower:/mnt/c/Prj/OwlWhisper$ docker-compose up
  1. Docker Composeが各サービス(Style-Bert-VITS2 API、Faster Whisper API、Streamlitアプリケーション)を起動します。

使い方

Chatbotの起動

  • Webカメラ APIは、http://localhost:8100 でアクセスできます。
  • チャットボットは、Streamlitアプリケーションとして http://localhost:8502 で利用できます。

OwlWhisper Chatbot

キャラクターの描画

Vtude Studioでキャラクターを描画してOBS Studio等で重ねてください。

OwlWhisper Character

Docker Compose の設定

docker-compose.ymlファイルでは、以下の3つのサービスを定義しています:

  1. style-bert-vits2-api: Style-Bert-VITS2 APIサービス
  2. owl-wisper: Streamlitアプリケーションサービス
  3. fast-wisper-api: Faster Whisper APIサービス

各サービスの設定には、ビルドコンテキスト、Dockerfileの場所、ボリュームマウント、ポートマッピング、環境変数、GPUリソースの割り当てなどが含まれています。


version: '3.8'

services:
  style-bert-vits2-api:
    build: 
      context: .
      dockerfile: Style-Bert-VITS2/Dockerfile.external
    volumes:
      - ./Style-Bert-VITS2:/app
      - ./Style-Bert-VITS2/model_assets:/model_assets
      - ./Style-Bert-VITS2/Data:/Data
    ports:
      - "8000:8000"
      - "5000:5000"
    tty: true
    working_dir: /app
    command: >
      sh -c "python initialize.py && 
             python server_fastapi.py"

  owl-wisper:
    build: .
    volumes:
      - ./streamlit:/app
      - ./demo:/demo
      - ./.cache:/root/.cache
      - /tmp/.X11-unix:/tmp/.X11-unix
      - /mnt/wslg:/mnt/wslg
      - ./.streamlit:/root/.streamlit
    environment:
      - PULSE_SERVER=/mnt/wslg/PulseServer
      - DISPLAY=$DISPLAY
      - WAYLAND_DISPLAY=$WAYLAND_DISPLAY
      - XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR
    ports:
      - 8502:8502
      - 8503:8503
    env_file:
      - .env
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [ gpu ]
    working_dir: /app
    tty: true
    command: streamlit run main.py --server.port 8502

  fast-wisper-api:
    build: 
      context: .
      dockerfile: faster-whisper-docker/Dockerfile
    volumes:
      - ./faster-whisper-docker:/app
      - ./faster-whisper-docker/.cache:/root/.cache
      - /tmp/.X11-unix:/tmp/.X11-unix
      - /mnt/wslg:/mnt/wslg
    environment:
      - PULSE_SERVER=/mnt/wslg/PulseServer
      - DISPLAY=$DISPLAY
      - WAYLAND_DISPLAY=$WAYLAND_DISPLAY
      - XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR
    ports:
      - 8181:8181
    env_file:
      - .env
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [ gpu ]
    working_dir: /app
    tty: true
    command: uvicorn faster_whisper_api_server:app --reload  --host=0.0.0.0 --port=8181

Streamlitアプリケーションのソースコード解説

main.pyファイルには、Streamlitアプリケーションのメインロジックが含まれています。以下、コードの解説です:


import streamlit as st
from audio_recorder_streamlit import audio_recorder
import requests
import base64
import time
from PIL import Image
import cv2
import io
import os

# モジュールのインポート(音声生成、GENAIユーティリティ等)
from modules.voice_generator import generate_voice
from modules.genai_utils import genai, MODEL_GENAI, SYSTEM_PROMPT, MODEL_GENAI_V
  • 必要なモジュールをインポートします。streamlitはStreamlitアプリケーションのフレームワーク、audio_recorder_streamlitは音声録音ウィジェット、requestsはHTTPリクエストの送信、base64はBase64エンコーディング、PILcv2は画像処理、ioはファイルI/O、osはシステム操作に使用します。
  • modules.voice_generatormodules.genai_utilsから、音声生成とGemini Proモデルの機能をインポートします。

# カメラURLとビデオキャプチャの設定
CAMERA_URL = os.getenv("CAMERA_URL")
cap = cv2.VideoCapture(CAMERA_URL)

FAST_WISPER_API_URL = os.getenv("FAST_WISPER_API_URL")
  • カメラURLとビデオキャプチャの設定を行います。CAMERA_URLは環境変数から取得し、cv2.VideoCaptureを使用してビデオキャプチャを初期化します。
  • FAST_WISPER_API_URLも環境変数から取得します。

# Streamlitアプリケーションのタイトル設定
st.title("Owl Wisper DEMO")
  • Streamlitアプリケーションのタイトルを設定します。

# サイドバーの設定
with st.sidebar:
    st.markdown("""
        <img src="https://raw.githubusercontent.com/Sunwood-ai-labs/OwlWhisper/main/docs/OwlWhisper.png" height=200px align="left"/>
        """, unsafe_allow_html=True)

    # 音声録音ウィジェット
    audio_bytes = audio_recorder(pause_threshold=30)
    ret, _ = cap.read()
    if(ret):
        st.success("CAM OK")
    else:
        st.warning("CAM NG")
    input_vision = st.checkbox('画像を入力', value=True)
    img_scale = st.slider('画像のスケール', min_value=1, max_value=10, value=3, step=1)
  • サイドバーを設定します。
  • st.markdownを使用して、OwlWhisperのロゴ画像を表示します。
  • audio_recorderウィジェットを使用して音声録音機能を提供します。
  • cap.read()を使用してカメラの状態を確認し、正常に動作している場合は"CAM OK"、そうでない場合は"CAM NG"を表示します。
  • st.checkboxを使用して、画像入力の有効/無効を切り替えるチェックボックスを表示します。
  • st.sliderを使用して、画像のスケールを調整するスライダーを表示します。

# メッセージの保存用
if "messages" not in st.session_state:
    st.session_state.messages = []
  • st.session_stateを使用して、メッセージの保存用リストを初期化します。

# 音声データが存在する場合、Wisper APIに送信してテキストに変換
if audio_bytes is not None:
    files = {"audio_file": ("audio.wav", audio_bytes, "audio/wav")}
    response = requests.post(FAST_WISPER_API_URL, files=files)

    if response.status_code == 200:
        data = response.json()
        transcribed_text = "".join(segment['text'] for segment in data["transcribed_text"])
        prompt = transcribed_text
    else:
        st.error("音声をテキストに変換する際にエラーが発生しました。")
else:
    prompt = st.text_input("What is up?")
  • 音声データが存在する場合、Faster Whisper APIに送信してテキストに変換します。
  • requests.postを使用して、音声データをファイル形式でFaster Whisper APIに送信します。
  • レスポンスのステータスコードが200(成功)の場合、transcribed_textからプロンプトを抽出します。
  • レスポンスのステータスコードが200以外の場合、エラーメッセージを表示します。
  • 音声データが存在しない場合、st.text_inputを使用してテキスト入力欄を表示します。

# プロンプト(テキスト入力または音声入力からのテキスト)が存在する場合
if prompt:
    st.session_state.messages.append({"role": "user", "content": prompt})
    # 画像データの取得と処理
    ret, image_data = cap.read()
    if ret and input_vision:
        height, width = image_data.shape[:2]
        new_height, new_width = height // img_scale, width // img_scale
        image_data = cv2.resize(image_data, (new_width, new_height))
        image_data_rgb = cv2.cvtColor(image_data, cv2.COLOR_BGR2RGB)
        img = Image.fromarray(image_data_rgb)
        buffer = io.BytesIO()
        img.save(buffer, format="JPEG")
        buffer.seek(0)
        img = Image.open(buffer)
        # GENAIモデルを使用して画像付きでレスポンス生成
        assistant_message = MODEL_GENAI_V.generate_content([prompt, img])
    else:
        # GENAIモデルを使用してテキストのみでレスポンス生成
        assistant_message = MODEL_GENAI.generate_content(prompt)

    # アシスタントメッセージの表示
    if ret and input_vision:
        st.session_state.messages.append({"role": "assistant", "content": assistant_message.text, "image": image_data})
    else:
        st.session_state.messages.append({"role": "assistant", "content": assistant_message.text})

    for message in st.session_state.messages:
        with st.chat_message(message["role"]):
            st.markdown(message["content"])
            if "image" in message:
                st.image(message["image"], use_column_width=True)

    # 音声応答の生成と再生
    voice_data = generate_voice(assistant_message.text)
    if voice_data:
        audio_str = "data:audio/ogg;base64,%s" % (base64.b64encode(voice_data).decode())
        audio_html = f"""
            <audio autoplay=True>
                <source src="{audio_str}" type="audio/ogg" autoplay=True>
                Your browser does not support the audio element.
            </audio>
        """
        st.markdown(audio_html, unsafe_allow_html=True)
  • プロンプト(テキスト入力または音声入力からのテキスト)が存在する場合、以下の処理を行います:
    • ユーザーメッセージをst.session_state.messagesに追加します。
    • 画像データを取得し、リサイズと色空間の変換を行います。
      -- 画像入力が有効で、画像データが正常に取得できた場合、MODEL_GENAI_V.generate_contentを使用して画像付きでレスポンスを生成します。
    • 画像入力が無効または画像データが取得できなかった場合、MODEL_GENAI.generate_contentを使用してテキストのみでレスポンスを生成します。
    • アシスタントメッセージをst.session_state.messagesに追加します。画像データがある場合は、メッセージに画像も含めます。
    • st.session_state.messages内の各メッセージを表示します。st.chat_messageを使用してメッセージの役割(ユーザーまたはアシスタント)を表示し、st.markdownを使用してメッセージの内容を表示します。画像データがある場合は、st.imageを使用して画像を表示します。
    • generate_voice関数を使用して、アシスタントメッセージのテキストから音声応答を生成します。
    • 生成された音声データがある場合、Base64エンコードを行い、HTMLの<audio>タグを使用して音声を自動再生します。

まとめ

OwlWhisperは、初心者でも簡単に音声認識と音声合成を体験できるプロジェクトです。WSL2、Docker、Streamlit、FastAPI、PyTorch、CUDA、Style-Bert-VITS2、Faster Whisperを使用した最新の技術スタックを採用しており、Webカメラを使った音声認識APIと音声対話型のチャットボット機能を提供しています。

チャットボットの応答には、Google社のGemini Proモデルを使用しています。Gemini Proは、高品質な自然言語処理モデルであり、ユーザーの入力に対して適切で自然な応答を生成することができます。

このプロジェクトを通して、音声認識・合成技術の基礎を学ぶことができます。また、提供されているソースコードを参考に、自分なりのアプリケーションを開発することもできます。

main.pyファイルでは、Streamlitアプリケーションのメインロジックを実装しています。音声録音、画像入力、テキスト入力、音声認識、画像処理、Gemini Proモデルを使用したレスポンス生成、音声合成など、様々な機能を組み合わせて、インタラクティブなチャットボットを実現しています。

ぜひOwlWhisperを試してみて、音声認識・合成の世界を体験してみてください!プロジェクトのソースコードを参考に、自分なりのアイデアを実現することもできます。音声技術の可能性を探求し、創造的なアプリケーションを開発してみましょう!

リポジトリ

GitHub - Sunwood-ai-labs/OwlWhisper
Contribute to Sunwood-ai-labs/OwlWhisper development by creating an account on GitHub.

コメント

タイトルとURLをコピーしました