campplus.AXERA / python /example.py
HY-2012's picture
Upload folder using huggingface_hub
906ada2 verified
Raw
History Blame Contribute Delete
4.04 kB
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright 2026 AXERA-TECH (authors: Magnetar)
#
# CAMPPlus speaker embedding demo (AXera NPU).
#
# Mirrors the Python flows of 3D-Speaker-MT.axera:
# 1) 1:1 speaker verification (export_campplus_onnx.py demo):
# python3 example.py --models-dir models --wav1 a.wav --wav2 b.wav
# same speaker ~0.67, different speakers ~0.06
# 2) chunked embedding extraction + speaker clustering (ax_cam_bin.py):
# python3 example.py --models-dir models --audio wav/vad_example.wav \
# --diarize --speaker-num 0
import argparse
import os
import time
import numpy as np
from campplus_sdk import CampplusModel, chunk, cosine_similarity
from campplus_sdk.inference import load_wav
def parse_args():
parser = argparse.ArgumentParser(description="CAMPPlus speaker embedding demo")
parser.add_argument("--models-dir", type=str, default="models",
help="Directory containing campplus.axmodel")
parser.add_argument("--wav1", type=str, default=None,
help="First wav for 1:1 speaker verification")
parser.add_argument("--wav2", type=str, default=None,
help="Second wav for 1:1 speaker verification")
parser.add_argument("--audio", type=str, default=None,
help="Audio for chunked embedding extraction / diarization")
parser.add_argument("--diarize", action="store_true",
help="Run speaker clustering (requires clustering deps)")
parser.add_argument("--speaker-num", type=int, default=0,
help="Known speaker count (0 = auto, requires clustering deps)")
return parser.parse_args()
def load_wav_16k(wav_file):
"""Read wav, resample if needed, return mono [T] tensor."""
return load_wav(wav_file, target_sr=16000)
def main():
args = parse_args()
if not args.wav1 and not args.audio:
raise SystemExit("Specify --wav1/--wav2 or --audio (see --help)")
t0 = time.time()
model = CampplusModel(args.models_dir)
print(f"Model loaded in {time.time() - t0:.2f}s")
if args.audio:
speech = load_wav_16k(args.audio)
speech = speech.numpy()
if speech.ndim > 1:
speech = speech[0]
fs = 16000
audio_duration = len(speech) / fs
print(f"Audio duration: {audio_duration:.2f}s")
# VAD-free chunking identical to ax_meeting_transc_demo.py
chunks = chunk(0, audio_duration)
t1 = time.time()
embeddings = model(speech, fs, chunks=chunks)
cost = time.time() - t1
print(f"Embedding extraction: {cost:.2f}s for {len(chunks)} chunks "
f"({cost / max(len(chunks), 1) * 1000:.2f} ms/chunk)")
print(f"Embeddings shape: {embeddings.shape}")
if args.diarize:
try:
from campplus_sdk.clustering import do_clustering
except ImportError as e:
raise SystemExit(
f"clustering deps missing ({e}); run: "
"pip install scipy scikit-learn fastcluster umap-learn hdbscan")
t1 = time.time()
speaker_num, diar_results = do_clustering(
chunks, embeddings,
speaker_num=args.speaker_num if args.speaker_num else None)
print(f"Clustering: {time.time() - t1:.2f}s, "
f"speakers: {speaker_num}")
for seg_st, seg_ed, spk in diar_results:
print(f" Speaker_{spk}: [{seg_st:.2f} {seg_ed:.2f}]")
return
# 1:1 speaker verification
wav1 = load_wav_16k(args.wav1)
wav2 = load_wav_16k(args.wav2)
emb1 = model.extract(wav1)
emb2 = model.extract(wav2)
sim = cosine_similarity(emb1, emb2)
print(f"\n{args.wav1} vs {args.wav2}")
print(f"cosine similarity: {sim:.4f}")
print("note: the higher the similarity, the more likely the two audios "
"belong to the same speaker")
if __name__ == "__main__":
main()