save code
This commit is contained in:
+1
-1
@@ -20,7 +20,7 @@ def _json_request(url: str, method: str = "GET", payload: dict | None = None, ti
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Quick QA for text pipeline and metrics")
|
||||
parser.add_argument("--base-url", default="https://127.0.0.1:8080")
|
||||
parser.add_argument("--base-url", default="http://127.0.0.1:8080")
|
||||
parser.add_argument("--rounds", type=int, default=5)
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
@@ -6,11 +6,24 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
PY="/home/xsl/miniconda3/envs/MuseTalk/bin/python"
|
||||
|
||||
if [[ -f ".env" ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source ./.env
|
||||
set +a
|
||||
fi
|
||||
|
||||
SCHEME="http"
|
||||
if [[ -n "${SSL_CERTFILE:-}" && -n "${SSL_KEYFILE:-}" ]]; then
|
||||
SCHEME="https"
|
||||
fi
|
||||
BASE_URL="${BASE_URL:-${SCHEME}://127.0.0.1:${WEBRTC_PORT:-8080}}"
|
||||
|
||||
echo "[1/6] service status"
|
||||
bash scripts/service.sh status || true
|
||||
|
||||
echo "[2/6] quick pipeline qa"
|
||||
"$PY" scripts/qa_check.py --base-url "https://127.0.0.1:8080" --rounds 5
|
||||
"$PY" scripts/qa_check.py --base-url "$BASE_URL" --rounds 5
|
||||
|
||||
echo "[3/6] llm fallback"
|
||||
"$PY" scripts/llm_fallback_check.py
|
||||
|
||||
+13
-1
@@ -16,7 +16,19 @@ if [[ -f "$ENV_FILE" ]]; then
|
||||
set +a
|
||||
fi
|
||||
|
||||
HOST="${WEBRTC_HOST:-0.0.0.0}"
|
||||
normalize_host() {
|
||||
local host="${1:-0.0.0.0}"
|
||||
case "$host" in
|
||||
127.0.0.1|localhost|::1)
|
||||
echo "0.0.0.0"
|
||||
;;
|
||||
*)
|
||||
echo "$host"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
HOST="$(normalize_host "${WEBRTC_HOST:-0.0.0.0}")"
|
||||
PORT="${WEBRTC_PORT:-8018}"
|
||||
SSL_CERTFILE="${SSL_CERTFILE:-$ROOT_DIR/certs/dev-cert.pem}"
|
||||
SSL_KEYFILE="${SSL_KEYFILE:-$ROOT_DIR/certs/dev-key.pem}"
|
||||
|
||||
+64
-16
@@ -35,34 +35,82 @@ async def _write_avatar_video(
|
||||
avatar: AvatarService, audio: np.ndarray, sr: int, out_path: str, fps: int
|
||||
) -> tuple[int, float]:
|
||||
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
|
||||
writer = cv2.VideoWriter(out_path, fourcc, float(fps), (avatar.width, avatar.height))
|
||||
width = 720
|
||||
height = 720
|
||||
writer = cv2.VideoWriter(out_path, fourcc, float(fps), (width, height))
|
||||
if not writer.isOpened():
|
||||
raise RuntimeError(f"failed to open video writer: {out_path}")
|
||||
|
||||
duration_s = float(audio.shape[0]) / float(sr) if sr > 0 else 0.0
|
||||
total_frames = max(1, int(math.ceil(duration_s * fps)))
|
||||
samples_per_frame = max(1, int(sr / fps))
|
||||
payload = await avatar.build_controls_from_audio(audio, sr, text="smoke-test")
|
||||
frames = payload.get("frames", [])
|
||||
duration_ms = int(payload.get("duration_ms", 0))
|
||||
|
||||
for i in range(total_frames):
|
||||
s = i * samples_per_frame
|
||||
e = min(audio.shape[0], (i + 1) * samples_per_frame)
|
||||
seg = audio[s:e]
|
||||
if seg.size == 0:
|
||||
level = 0.0
|
||||
else:
|
||||
rms = float(np.sqrt(np.mean(seg.astype(np.float32) ** 2)))
|
||||
level = float(np.clip(rms * 10.0, 0.0, 1.0))
|
||||
frame = await avatar.render_frame(mouth_open=level, speaking=True)
|
||||
for frame_payload in frames:
|
||||
controls = frame_payload.get("controls", {})
|
||||
frame = _render_debug_frame(width, height, controls, speaking=bool(frame_payload.get("speaking", False)))
|
||||
writer.write(frame)
|
||||
|
||||
# add 0.4s idle tail for easy visual check
|
||||
tail_frames = max(1, int(0.4 * fps))
|
||||
for _ in range(tail_frames):
|
||||
frame = await avatar.render_frame(mouth_open=0.0, speaking=False)
|
||||
frame = _render_debug_frame(width, height, {}, speaking=False)
|
||||
writer.write(frame)
|
||||
|
||||
writer.release()
|
||||
return total_frames + tail_frames, duration_s + 0.4
|
||||
return len(frames) + tail_frames, (duration_ms / 1000.0) + 0.4
|
||||
|
||||
|
||||
def _render_debug_frame(width: int, height: int, controls: dict, speaking: bool) -> np.ndarray:
|
||||
jaw_open = float(np.clip(controls.get("jawOpen", 0.0), 0.0, 1.0))
|
||||
mouth_pucker = float(np.clip(controls.get("mouthPucker", 0.0), 0.0, 1.0))
|
||||
viseme_aa = float(np.clip(controls.get("viseme_aa", jaw_open), 0.0, 1.0))
|
||||
viseme_ee = float(np.clip(controls.get("viseme_ee", 0.0), 0.0, 1.0))
|
||||
viseme_oh = float(np.clip(controls.get("viseme_oh", 0.0), 0.0, 1.0))
|
||||
head_yaw = float(np.clip(controls.get("headYaw", 0.0), -1.0, 1.0))
|
||||
head_pitch = float(np.clip(controls.get("headPitch", 0.0), -1.0, 1.0))
|
||||
head_roll = float(np.clip(controls.get("headRoll", 0.0), -1.0, 1.0))
|
||||
|
||||
frame = np.zeros((height, width, 3), dtype=np.uint8)
|
||||
frame[:] = (16, 12, 10)
|
||||
|
||||
center_x = width // 2 + int(head_yaw * 48)
|
||||
center_y = height // 2 + int(head_pitch * 42) - 24
|
||||
head_radius = 190
|
||||
|
||||
cv2.circle(frame, (center_x, center_y), head_radius + 26, (42, 28, 18), -1, lineType=cv2.LINE_AA)
|
||||
cv2.circle(frame, (center_x, center_y), head_radius, (198, 214, 238), -1, lineType=cv2.LINE_AA)
|
||||
|
||||
eye_y = center_y - 44
|
||||
eye_offset = 64
|
||||
cv2.circle(frame, (center_x - eye_offset, eye_y), 12, (26, 31, 43), -1, lineType=cv2.LINE_AA)
|
||||
cv2.circle(frame, (center_x + eye_offset, eye_y), 12, (26, 31, 43), -1, lineType=cv2.LINE_AA)
|
||||
|
||||
brow_dx = int(head_roll * 28)
|
||||
cv2.line(frame, (center_x - 100 - brow_dx, eye_y - 34), (center_x - 42 - brow_dx, eye_y - 40), (48, 58, 74), 6, lineType=cv2.LINE_AA)
|
||||
cv2.line(frame, (center_x + 42 - brow_dx, eye_y - 40), (center_x + 100 - brow_dx, eye_y - 34), (48, 58, 74), 6, lineType=cv2.LINE_AA)
|
||||
|
||||
mouth_center = (center_x, center_y + 86)
|
||||
mouth_width = int(64 + viseme_oh * 36 + mouth_pucker * 18)
|
||||
mouth_height = int(12 + jaw_open * 98 + viseme_aa * 18)
|
||||
lip_color = (96, 54, 130) if speaking else (88, 74, 108)
|
||||
inner_color = (36, 22, 54)
|
||||
cv2.ellipse(frame, mouth_center, (mouth_width, max(6, mouth_height)), 0, 0, 360, lip_color, -1, lineType=cv2.LINE_AA)
|
||||
cv2.ellipse(frame, mouth_center, (max(12, mouth_width - 16), max(4, mouth_height - 8)), 0, 0, 360, inner_color, -1, lineType=cv2.LINE_AA)
|
||||
|
||||
ee_width = int(max(20, 86 - viseme_ee * 46))
|
||||
cv2.line(frame, (mouth_center[0] - ee_width, mouth_center[1]), (mouth_center[0] + ee_width, mouth_center[1]), (220, 224, 232), 3, lineType=cv2.LINE_AA)
|
||||
|
||||
metrics = [
|
||||
f"jawOpen={jaw_open:.2f}",
|
||||
f"viseme_aa={viseme_aa:.2f}",
|
||||
f"viseme_ee={viseme_ee:.2f}",
|
||||
f"viseme_oh={viseme_oh:.2f}",
|
||||
f"headYaw={head_yaw:.2f}",
|
||||
]
|
||||
for index, text in enumerate(metrics):
|
||||
cv2.putText(frame, text, (28, 42 + index * 28), cv2.FONT_HERSHEY_SIMPLEX, 0.72, (210, 220, 235), 2, lineType=cv2.LINE_AA)
|
||||
|
||||
return frame
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
|
||||
+18
-3
@@ -8,11 +8,26 @@ cd "$ROOT"
|
||||
PY="/home/xsl/miniconda3/envs/MuseTalk/bin/python"
|
||||
|
||||
if [[ -f ".env" ]]; then
|
||||
# shellcheck disable=SC2046
|
||||
export $(grep -E '^[A-Za-z_][A-Za-z0-9_]*=' .env | xargs)
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source ./.env
|
||||
set +a
|
||||
fi
|
||||
|
||||
ARGS=(main:app --host "${WEBRTC_HOST:-0.0.0.0}" --port "${WEBRTC_PORT:-8080}")
|
||||
normalize_host() {
|
||||
local host="${1:-0.0.0.0}"
|
||||
case "$host" in
|
||||
127.0.0.1|localhost|::1)
|
||||
echo "0.0.0.0"
|
||||
;;
|
||||
*)
|
||||
echo "$host"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
HOST="$(normalize_host "${WEBRTC_HOST:-0.0.0.0}")"
|
||||
ARGS=(main:app --host "$HOST" --port "${WEBRTC_PORT:-8080}")
|
||||
|
||||
if [[ -n "${SSL_CERTFILE:-}" && -n "${SSL_KEYFILE:-}" ]]; then
|
||||
ARGS+=(--ssl-certfile "$SSL_CERTFILE" --ssl-keyfile "$SSL_KEYFILE")
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
MODE="${1:-auto}"
|
||||
|
||||
if [[ -f ".env" ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source ./.env
|
||||
set +a
|
||||
fi
|
||||
|
||||
normalize_host() {
|
||||
local host="${1:-0.0.0.0}"
|
||||
case "$host" in
|
||||
127.0.0.1|localhost|::1)
|
||||
echo "0.0.0.0"
|
||||
;;
|
||||
*)
|
||||
echo "$host"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
pick_python() {
|
||||
if [[ -n "${VISUAL_CHAT_PYTHON:-}" && -x "${VISUAL_CHAT_PYTHON}" ]]; then
|
||||
echo "$VISUAL_CHAT_PYTHON"
|
||||
return 0
|
||||
fi
|
||||
if [[ -n "${VIRTUAL_ENV:-}" && -x "${VIRTUAL_ENV}/bin/python" ]]; then
|
||||
echo "${VIRTUAL_ENV}/bin/python"
|
||||
return 0
|
||||
fi
|
||||
if [[ -x "/home/xsl/miniconda3/envs/MuseTalk/bin/python" ]]; then
|
||||
echo "/home/xsl/miniconda3/envs/MuseTalk/bin/python"
|
||||
return 0
|
||||
fi
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
command -v python3
|
||||
return 0
|
||||
fi
|
||||
command -v python
|
||||
}
|
||||
|
||||
PY="$(pick_python)"
|
||||
HOST="$(normalize_host "${WEBRTC_HOST:-0.0.0.0}")"
|
||||
PORT="${WEBRTC_PORT:-8080}"
|
||||
SCHEME="http"
|
||||
ARGS=(main:app --host "$HOST" --port "$PORT")
|
||||
|
||||
case "$MODE" in
|
||||
auto)
|
||||
if [[ -n "${SSL_CERTFILE:-}" && -n "${SSL_KEYFILE:-}" ]]; then
|
||||
SCHEME="https"
|
||||
ARGS+=(--ssl-certfile "$SSL_CERTFILE" --ssl-keyfile "$SSL_KEYFILE")
|
||||
fi
|
||||
;;
|
||||
https)
|
||||
if [[ -z "${SSL_CERTFILE:-}" || -z "${SSL_KEYFILE:-}" ]]; then
|
||||
echo "SSL_CERTFILE / SSL_KEYFILE 未配置,无法以 https 模式启动" >&2
|
||||
exit 1
|
||||
fi
|
||||
SCHEME="https"
|
||||
ARGS+=(--ssl-certfile "$SSL_CERTFILE" --ssl-keyfile "$SSL_KEYFILE")
|
||||
;;
|
||||
http)
|
||||
;;
|
||||
*)
|
||||
echo "usage: bash scripts/start-test.sh [auto|http|https]" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "repo: $ROOT"
|
||||
echo "python: $PY"
|
||||
echo "mode: $MODE"
|
||||
echo "bind: ${HOST}:${PORT}"
|
||||
echo "listen: ${SCHEME}://${HOST}:${PORT}"
|
||||
echo "browser test page: ${SCHEME}://<server-ip>:${PORT}/"
|
||||
|
||||
exec "$PY" -m uvicorn "${ARGS[@]}"
|
||||
Reference in New Issue
Block a user