Spaces:
Runtime error
Runtime error
app.pyに新しい機能を追加し、サンプルファイルの読み込み処理を改善しました。また、`requirements.txt`に新たに依存関係を追加しました。
Browse files- README_jp.md +124 -0
- test_performance.py +175 -0
README_jp.md
ADDED
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# DittoTalkingHead プロジェクト
|
2 |
+
|
3 |
+
## プロジェクト概要
|
4 |
+
このリポジトリは、音声から自然なトーキングヘッド動画を生成するDittoモデルの実装と、そのAPI化を行うプロジェクトです。単一の画像と音声ファイルから、音声に同期した自然な表情や口の動きを持つ動画を生成します。
|
5 |
+
|
6 |
+
## 作業フロー
|
7 |
+
本プロジェクトは、Statement of Work (SOW) ドキュメントを作成してから実装を行う段階的なアプローチを採用しています。
|
8 |
+
|
9 |
+
### フェーズ構成
|
10 |
+
1. **Phase 1**: 基本実装 - Hugging Face Spacesへのデプロイ
|
11 |
+
2. **Phase 2**: API実装 - REST API化とクライアント実装
|
12 |
+
3. **Phase 3**: パフォーマンス最適化 - 処理速度の大幅改善
|
13 |
+
|
14 |
+
## ディレクトリ構造
|
15 |
+
|
16 |
+
### `/` ルートディレクトリ
|
17 |
+
- `README.md` - Hugging Face Spaces用のメタデータ付きREADME
|
18 |
+
- `README_jp.md` - 日本語プロジェクト説明(このファイル)
|
19 |
+
- `README_ditto-talkinghead.md` - Dittoモデルの技術詳細
|
20 |
+
- `README_hf_space.md` - Hugging Face Spacesデプロイメント説明
|
21 |
+
- `app.py` - Gradio Webアプリケーションのメインファイル
|
22 |
+
- `inference.py` - モデル推論の実装
|
23 |
+
- `model_manager.py` - モデルファイルの管理・ダウンロード
|
24 |
+
- `test_api.py` - API機能のテストスクリプト
|
25 |
+
- `test_api_client.py` - APIクライアントの実装
|
26 |
+
- `test_performance.py` - パフォーマンステストツール
|
27 |
+
- `requirements.txt` - Python依存関係
|
28 |
+
- `environment.yaml` - Conda環境設定
|
29 |
+
- `packages.txt` - システムパッケージ依存関係
|
30 |
+
|
31 |
+
### `/ToDo` 計画ドキュメント
|
32 |
+
- `plan-v0.md` - 初期調査報告と実装計画
|
33 |
+
- `0717-1_Phase1_SOW_revised.md` - Phase 1 実装仕様書(基本実装)
|
34 |
+
- `0717-2_Phase2_API_SOW.md` - Phase 2 実装仕様書(API化)
|
35 |
+
- `0717-3_Phase3_Performance_SOW.md` - Phase 3 実装仕様書(高速化)
|
36 |
+
|
37 |
+
### `/core` コアモデル実装
|
38 |
+
- `/atomic_components` - モデルの基本コンポーネント
|
39 |
+
- `audio2motion.py` - 音声から動きへの変換
|
40 |
+
- `avatar_registrar.py` - アバター登録処理
|
41 |
+
- `wav2feat.py` - 音声特徴抽出
|
42 |
+
- `warp_f3d.py` - 3D特徴のワーピング
|
43 |
+
- 他多数の処理モジュール
|
44 |
+
- `/aux_models` - 補助モデル(顔検出、ランドマーク検出等)
|
45 |
+
- `face_mesh.py` - 顔メッシュ検出
|
46 |
+
- `hubert_stream.py` - HuBERT音声モデル
|
47 |
+
- `mediapipe_landmark478.py` - MediaPipeランドマーク
|
48 |
+
- `/models` - メインモデル実装
|
49 |
+
- `appearance_extractor.py` - 外観特徴抽出
|
50 |
+
- `decoder.py` - デコーダー
|
51 |
+
- `lmdm.py` - ランドマーク拡散モデル
|
52 |
+
- `/modules` - モデルモジュール群
|
53 |
+
- `/utils` - ユーティリティ関数
|
54 |
+
- `crop.py` - 画像クロップ処理
|
55 |
+
- `load_model.py` - モデルロード
|
56 |
+
- `tensorrt_utils.py` - TensorRT最適化
|
57 |
+
|
58 |
+
### `/docs` ドキュメント
|
59 |
+
- `APIドキュメント.md` - API仕様書(日本語)
|
60 |
+
- `api_documentation.md` - API仕様書(英語)
|
61 |
+
- `deployment_guide.md` - デプロイメントガイド
|
62 |
+
- `model_upload_guide.md` - モデルアップロードガイド
|
63 |
+
|
64 |
+
### `/example` サンプルファイル
|
65 |
+
- `audio.wav` - サンプル音声
|
66 |
+
- `image.png` - サンプル画像
|
67 |
+
- `2025-07-17_22-12-46.mp4` - 生成サンプル動画
|
68 |
+
|
69 |
+
### `/scripts` ユーティリティスクリプト
|
70 |
+
- `cvt_onnx_to_trt.py` - ONNXからTensorRTへの変換
|
71 |
+
|
72 |
+
### `/stream_pipeline_*` リアルタイム処理
|
73 |
+
- `stream_pipeline_online.py` - オンラインストリーミング処理
|
74 |
+
- `stream_pipeline_offline.py` - オフライン処理
|
75 |
+
|
76 |
+
## 主要な機能
|
77 |
+
|
78 |
+
### 1. 基本的な動画生成
|
79 |
+
- 音声ファイル(WAV)とソース画像(PNG/JPG)から動画生成
|
80 |
+
- 自然な口の動きと表情の同期
|
81 |
+
- 高品質な動画出力
|
82 |
+
|
83 |
+
### 2. API機能
|
84 |
+
- REST APIエンドポイント(`/process_talking_head`)
|
85 |
+
- 画像の事前アップロード機能(`/prepare_avatar`)
|
86 |
+
- 非同期処理とキャッシュサポート
|
87 |
+
|
88 |
+
### 3. パフォーマンス最適化(Phase 3で実装予定)
|
89 |
+
- 解像度320×320固定による高速化
|
90 |
+
- 画像埋め込みの事前計算とキャッシュ
|
91 |
+
- TensorRT/ONNX最適化
|
92 |
+
- 目標: 16秒の音声を10秒以内で処理
|
93 |
+
|
94 |
+
## 使用方法
|
95 |
+
|
96 |
+
### Web UI経由
|
97 |
+
1. Hugging Face Spaces(https://huggingface.co/spaces/O-ken5481/talkingAvater_bgk)にアクセス
|
98 |
+
2. 音声ファイルと画像をアップロード
|
99 |
+
3. 「生成」ボタンをクリック
|
100 |
+
|
101 |
+
### API経由
|
102 |
+
```python
|
103 |
+
from gradio_client import Client, handle_file
|
104 |
+
|
105 |
+
client = Client("O-ken5481/talkingAvater_bgk")
|
106 |
+
result = client.predict(
|
107 |
+
audio_file=handle_file("path/to/audio.wav"),
|
108 |
+
source_image=handle_file("path/to/image.png"),
|
109 |
+
api_name="/process_talking_head"
|
110 |
+
)
|
111 |
+
```
|
112 |
+
|
113 |
+
## 技術スタック
|
114 |
+
- **モデル**: Ditto TalkingHead(Ant Group Research)
|
115 |
+
- **フレームワーク**: PyTorch, ONNX Runtime, TensorRT
|
116 |
+
- **WebUI**: Gradio
|
117 |
+
- **インフラ**: Hugging Face Spaces(GPU: A100)
|
118 |
+
- **補助モデル**: HuBERT(音声特徴)、MediaPipe(顔ランドマーク)
|
119 |
+
|
120 |
+
## 今後の展開
|
121 |
+
- Phase 3の高速化実装(TensorRT最適化、キャッシュシステム)
|
122 |
+
- リアルタイムストリーミング対応
|
123 |
+
- 複数話者の対応
|
124 |
+
- より高解像度での生成オプション
|
test_performance.py
ADDED
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
パフォーマンステストスクリプト
|
4 |
+
動画生成の各ステップの実行時間を計測
|
5 |
+
"""
|
6 |
+
|
7 |
+
import time
|
8 |
+
import logging
|
9 |
+
from test_api_client import TalkingHeadAPIClient
|
10 |
+
import os
|
11 |
+
|
12 |
+
# ロギング設定
|
13 |
+
logging.basicConfig(
|
14 |
+
level=logging.INFO,
|
15 |
+
format='%(asctime)s - %(message)s',
|
16 |
+
datefmt='%Y-%m-%d %H:%M:%S'
|
17 |
+
)
|
18 |
+
|
19 |
+
class TimingStats:
|
20 |
+
def __init__(self):
|
21 |
+
self.stats = {}
|
22 |
+
self.start_times = {}
|
23 |
+
|
24 |
+
def start(self, name):
|
25 |
+
self.start_times[name] = time.time()
|
26 |
+
|
27 |
+
def end(self, name):
|
28 |
+
if name in self.start_times:
|
29 |
+
duration = time.time() - self.start_times[name]
|
30 |
+
self.stats[name] = duration
|
31 |
+
return duration
|
32 |
+
return None
|
33 |
+
|
34 |
+
def report(self):
|
35 |
+
print("\n=== パフォーマンス計測結果 ===")
|
36 |
+
total_time = sum(self.stats.values())
|
37 |
+
for name, duration in self.stats.items():
|
38 |
+
percentage = (duration / total_time) * 100 if total_time > 0 else 0
|
39 |
+
print(f"{name}: {duration:.2f}秒 ({percentage:.1f}%)")
|
40 |
+
print(f"\n合計時間: {total_time:.2f}秒")
|
41 |
+
|
42 |
+
# 音声ファイルの長さを取得
|
43 |
+
try:
|
44 |
+
import librosa
|
45 |
+
audio_path = "example/audio.wav"
|
46 |
+
y, sr = librosa.load(audio_path, sr=None)
|
47 |
+
audio_duration = len(y) / sr
|
48 |
+
print(f"音声ファイルの長さ: {audio_duration:.2f}秒")
|
49 |
+
print(f"処理時間比率: {total_time/audio_duration:.2f}x")
|
50 |
+
except Exception as e:
|
51 |
+
print(f"音声長さの取得失敗: {e}")
|
52 |
+
|
53 |
+
def test_performance():
|
54 |
+
"""パフォーマンステストを実行"""
|
55 |
+
timer = TimingStats()
|
56 |
+
|
57 |
+
# 全体の開始時間
|
58 |
+
timer.start("全体処理")
|
59 |
+
|
60 |
+
# クライアント初期化
|
61 |
+
timer.start("API接続")
|
62 |
+
try:
|
63 |
+
client = TalkingHeadAPIClient()
|
64 |
+
timer.end("API接続")
|
65 |
+
except Exception as e:
|
66 |
+
logging.error(f"クライアント初期化失敗: {e}")
|
67 |
+
return
|
68 |
+
|
69 |
+
# サンプルファイル
|
70 |
+
audio_path = "example/audio.wav"
|
71 |
+
image_path = "example/image.png"
|
72 |
+
|
73 |
+
# ファイル情報を表示
|
74 |
+
audio_size = os.path.getsize(audio_path) / 1024 / 1024 # MB
|
75 |
+
image_size = os.path.getsize(image_path) / 1024 / 1024 # MB
|
76 |
+
print(f"\n入力ファイル情報:")
|
77 |
+
print(f"- 音声: {audio_path} ({audio_size:.2f} MB)")
|
78 |
+
print(f"- 画像: {image_path} ({image_size:.2f} MB)")
|
79 |
+
|
80 |
+
# 動画生成
|
81 |
+
timer.start("動画生成(API呼び出し)")
|
82 |
+
try:
|
83 |
+
result = client.generate_video(audio_path, image_path)
|
84 |
+
video_data, status = result
|
85 |
+
timer.end("動画生成(API呼び出し)")
|
86 |
+
|
87 |
+
if video_data:
|
88 |
+
# 保存処理
|
89 |
+
timer.start("動画保存")
|
90 |
+
if isinstance(video_data, dict) and 'video' in video_data:
|
91 |
+
saved_path = client.save_with_timestamp(video_data['video'])
|
92 |
+
timer.end("動画保存")
|
93 |
+
|
94 |
+
# 出力ファイル情報
|
95 |
+
output_size = os.path.getsize(saved_path) / 1024 / 1024 # MB
|
96 |
+
print(f"\n出力ファイル情報:")
|
97 |
+
print(f"- 動画: {saved_path} ({output_size:.2f} MB)")
|
98 |
+
|
99 |
+
timer.end("全体処理")
|
100 |
+
timer.report()
|
101 |
+
|
102 |
+
print(f"\n✅ テスト成功!")
|
103 |
+
print(f"ステータス: {status}")
|
104 |
+
else:
|
105 |
+
print(f"\n❌ テスト失敗")
|
106 |
+
print(f"ステータス: {status}")
|
107 |
+
|
108 |
+
except Exception as e:
|
109 |
+
logging.error(f"エラー発生: {e}")
|
110 |
+
import traceback
|
111 |
+
traceback.print_exc()
|
112 |
+
|
113 |
+
def test_multiple_runs(runs=3):
|
114 |
+
"""複数回実行して平均時間を計測"""
|
115 |
+
print(f"\n=== {runs}回連続実行テスト ===")
|
116 |
+
|
117 |
+
times = []
|
118 |
+
for i in range(runs):
|
119 |
+
print(f"\n--- 実行 {i+1}/{runs} ---")
|
120 |
+
start = time.time()
|
121 |
+
|
122 |
+
try:
|
123 |
+
client = TalkingHeadAPIClient()
|
124 |
+
result = client.generate_video("example/audio.wav", "example/image.png")
|
125 |
+
if result[0]:
|
126 |
+
duration = time.time() - start
|
127 |
+
times.append(duration)
|
128 |
+
print(f"実行時間: {duration:.2f}秒")
|
129 |
+
except Exception as e:
|
130 |
+
print(f"エラー: {e}")
|
131 |
+
|
132 |
+
if times:
|
133 |
+
avg_time = sum(times) / len(times)
|
134 |
+
min_time = min(times)
|
135 |
+
max_time = max(times)
|
136 |
+
print(f"\n=== 統計 ===")
|
137 |
+
print(f"平均時間: {avg_time:.2f}秒")
|
138 |
+
print(f"最小時間: {min_time:.2f}秒")
|
139 |
+
print(f"最大時間: {max_time:.2f}秒")
|
140 |
+
|
141 |
+
def analyze_bottlenecks():
|
142 |
+
"""ボトルネック分析のための詳細テスト"""
|
143 |
+
print("\n=== ボトルネック分析 ===")
|
144 |
+
|
145 |
+
# ローカルファイルの読み込み時間
|
146 |
+
start = time.time()
|
147 |
+
with open("example/audio.wav", "rb") as f:
|
148 |
+
audio_data = f.read()
|
149 |
+
with open("example/image.png", "rb") as f:
|
150 |
+
image_data = f.read()
|
151 |
+
local_read_time = time.time() - start
|
152 |
+
print(f"ローカルファイル読み込み: {local_read_time:.3f}秒")
|
153 |
+
|
154 |
+
# ネットワーク遅延の推定(Hugging Face Spaceへのping相当)
|
155 |
+
import requests
|
156 |
+
start = time.time()
|
157 |
+
try:
|
158 |
+
response = requests.get("https://o-ken5481-talkingavater-bgk.hf.space", timeout=10)
|
159 |
+
network_time = time.time() - start
|
160 |
+
print(f"ネットワーク遅延(推定): {network_time:.3f}秒")
|
161 |
+
except:
|
162 |
+
print("ネットワーク遅延の測定失敗")
|
163 |
+
|
164 |
+
if __name__ == "__main__":
|
165 |
+
print("DittoTalkingHead パフォーマンステスト")
|
166 |
+
print("=" * 50)
|
167 |
+
|
168 |
+
# 1. 詳細な時間計測
|
169 |
+
test_performance()
|
170 |
+
|
171 |
+
# 2. 複数回実行テスト
|
172 |
+
# test_multiple_runs(3)
|
173 |
+
|
174 |
+
# 3. ボトルネック分析
|
175 |
+
analyze_bottlenecks()
|