fixed merged conflicts

This commit is contained in:
dscripka 2023-10-11 21:43:48 -04:00
commit b2a3ee6c3e
28 changed files with 127 additions and 77 deletions

2
.gitattributes vendored
View file

@ -1,2 +0,0 @@
*.onnx filter=lfs diff=lfs merge=lfs -text
*.tflite filter=lfs diff=lfs merge=lfs -text

View file

@ -13,8 +13,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
with:
lfs: true
- name: Set up Python 3.8 - name: Set up Python 3.8
uses: actions/setup-python@v3 uses: actions/setup-python@v3
with: with:

View file

@ -8,6 +8,7 @@ on:
branches: [ "main" ] branches: [ "main" ]
pull_request: pull_request:
branches: [ "main" ] branches: [ "main" ]
workflow_dispatch:
jobs: jobs:
unit_tests_linux: unit_tests_linux:
@ -18,8 +19,6 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with:
lfs: true
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3 uses: actions/setup-python@v3
with: with:
@ -42,8 +41,6 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with:
lfs: true
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3 uses: actions/setup-python@v3
with: with:

View file

@ -1,2 +0,0 @@
recursive-include openwakeword *.onnx
recursive-include openwakeword *.tflite

View file

@ -41,16 +41,20 @@ Many thanks to [TeaPoly](https://github.com/TeaPoly/speexdsp-ns-python) for thei
# Usage # Usage
For quick local testing, clone this repository and use the included [example script](examples/detect_from_microphone.py) to try streaming detection from a local microphone. **Important note!** The model files are stored in this repo using [git-lfs](https://git-lfs.com/); make sure it is installed on your system and if needed use `git-lfs fetch --all` to make sure the the models download correctly. For quick local testing, clone this repository and use the included [example script](examples/detect_from_microphone.py) to try streaming detection from a local microphone. You can individually download pre-trained models from current and past [releases](https://github.com/dscripka/openWakeWord/releases/), or you can download them using Python (see below).
Adding openWakeWord to your own Python code requires just a few lines: Adding openWakeWord to your own Python code requires just a few lines:
```python ```python
import openwakeword
from openwakeword.model import Model from openwakeword.model import Model
# Instantiate the model # One-time download of all pre-trained models (or only select models)
openwakeword.utils.download_models()
# Instantiate the model(s)
model = Model( model = Model(
wakeword_models=["path/to/model.onnx"], # can also leave this argument empty to load all of the included pre-trained models wakeword_models=["path/to/model.tflite"], # can also leave this argument empty to load all of the included pre-trained models
) )
# Get audio data containing 16-bit 16khz PCM audio data from a file, microphone, network stream, etc. # Get audio data containing 16-bit 16khz PCM audio data from a file, microphone, network stream, etc.

View file

@ -5,24 +5,48 @@ from openwakeword.custom_verifier_model import train_custom_verifier
__all__ = ['Model', 'VAD', 'train_custom_verifier'] __all__ = ['Model', 'VAD', 'train_custom_verifier']
models = { FEATURE_MODELS = {
"embedding": {
"model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/embedding_model.tflite"),
"download_url": "https://github.com/dscripka/openWakeWord/releases/download/v0.5.1/embedding_model.tflite"
},
"melspectrogram": {
"model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/melspectrogram.tflite"),
"download_url": "https://github.com/dscripka/openWakeWord/releases/download/v0.5.1/melspectrogram.tflite"
}
}
VAD_MODELS = {
"silero_vad": {
"model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/silero_vad.onnx"),
"download_url": "https://github.com/dscripka/openWakeWord/releases/download/v0.5.1/silero_vad.onnx"
}
}
MODELS = {
"alexa": { "alexa": {
"model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/alexa_v0.1.tflite") "model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/alexa_v0.1.tflite"),
"download_url": "https://github.com/dscripka/openWakeWord/releases/download/v0.5.1/alexa_v0.1.tflite"
}, },
"hey_mycroft": { "hey_mycroft": {
"model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/hey_mycroft_v0.1.tflite") "model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/hey_mycroft_v0.1.tflite"),
"download_url": "https://github.com/dscripka/openWakeWord/releases/download/v0.5.1/hey_mycroft_v0.1.tflite"
}, },
"hey_jarvis": { "hey_jarvis": {
"model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/hey_jarvis_v0.1.tflite") "model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/hey_jarvis_v0.1.tflite"),
"download_url": "https://github.com/dscripka/openWakeWord/releases/download/v0.5.1/hey_jarvis_v0.1.tflite"
}, },
"hey_rhasspy": { "hey_rhasspy": {
"model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/hey_rhasspy_v0.1.tflite") "model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/hey_rhasspy_v0.1.tflite"),
"download_url": "https://github.com/dscripka/openWakeWord/releases/download/v0.5.1/hey_rhasspy_v0.1.tflite"
}, },
"timer": { "timer": {
"model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/timer_v0.1.tflite") "model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/timer_v0.1.tflite"),
"download_url": "https://github.com/dscripka/openWakeWord/releases/download/v0.5.1/timer_v0.1.tflite"
}, },
"weather": { "weather": {
"model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/weather_v0.1.tflite") "model_path": os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources/models/weather_v0.1.tflite"),
"download_url": "https://github.com/dscripka/openWakeWord/releases/download/v0.5.1/weather_v0.1.tflite"
} }
} }
@ -40,6 +64,6 @@ model_class_mappings = {
def get_pretrained_model_paths(inference_framework="tflite"): def get_pretrained_model_paths(inference_framework="tflite"):
if inference_framework == "tflite": if inference_framework == "tflite":
return [models[i]["model_path"] for i in models.keys()] return [MODELS[i]["model_path"] for i in MODELS.keys()]
elif inference_framework == "onnx": elif inference_framework == "onnx":
return [models[i]["model_path"].replace(".tflite", ".onnx") for i in models.keys()] return [MODELS[i]["model_path"].replace(".tflite", ".onnx") for i in MODELS.keys()]

View file

@ -67,7 +67,7 @@ class Model():
with VAD scores above the threshold will be returned. The default value (0), with VAD scores above the threshold will be returned. The default value (0),
disables voice activity detection entirely. disables voice activity detection entirely.
custom_verifier_models (dict): A dictionary of paths to custom verifier models, where custom_verifier_models (dict): A dictionary of paths to custom verifier models, where
the keys are the model names (corresponding to the openwakeword.models the keys are the model names (corresponding to the openwakeword.MODELS
attribute) and the values are the filepaths of the attribute) and the values are the filepaths of the
custom verifier models. custom verifier models.
custom_verifier_threshold (float): The score threshold to use a custom verifier model. If the score custom_verifier_threshold (float): The score threshold to use a custom verifier model. If the score
@ -85,7 +85,7 @@ class Model():
wakeword_model_names = [] wakeword_model_names = []
if wakeword_models == []: if wakeword_models == []:
wakeword_models = pretrained_model_paths wakeword_models = pretrained_model_paths
wakeword_model_names = list(openwakeword.models.keys()) wakeword_model_names = list(openwakeword.MODELS.keys())
elif len(wakeword_models) >= 1: elif len(wakeword_models) >= 1:
for ndx, i in enumerate(wakeword_models): for ndx, i in enumerate(wakeword_models):
if os.path.exists(i): if os.path.exists(i):

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6ff566a01d12670e8d9e3c59da32651db1575d17272a601b7f8a39283dfbae3e
size 854246

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7333a317a790070a7f3432b81d9439c779481cc4ebd67c73da7174ea3cf48397
size 855312

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:70d164290c1d095d1d4ee149bc5e00543250a7316b59f31d056cff7bd3075c1f
size 1326578

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c0aea21eb84a4ce90a08c870da41b7a7173b45269e6a3207c71d67c40f3a59d8
size 1330312

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:94a13cfe60075b132f6a472e7e462e8123ee70861bc3fb58434a73712ee0d2cb
size 1271370

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:14bff778604985e1b5c19f0f7bbe477a69cf281d8db34b232b3b972411f710e2
size 1278912

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c2a311e8fa1338de89c31b3b46dc4dffd4af2f9a8d6ddead48893c2d301b1f18
size 857691

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bf9e43136afd3ca323698820a6e32a47f885ef4c30a3b8b577ec71688a9d64d8
size 860300

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5a9b3ed3be2910e35780e097905aa9f35a9c10038df47914cf2b3ec4d670f6ea
size 204081

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:01d2526b45068f565aa3849d6ec2b7abae099154fc1b496f9ef20de9ef241fe9
size 416140

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ba2b0e0f8b7b875369a2c89cb13360ff53bac436f2895cced9f479fa65eb176f
size 1087958

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:96fa0adccb6e8cf95cb14465409a1a2898ee4a96a85bb9ed3c7eb0e68bf163e8
size 1092516

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a35ebf52fd3ce5f1469b2a36158dba761bc47b973ea3382b3186ca15b1f5af28
size 1807522

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:371e44535470a29248b3b8f1bbbbaf2525c86417fd8f75c67fcf02ae0b9626df
size 1742475

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:21d5b0267e97df64870b7aca312e2043ebed248d365698926a115a3694ff9626
size 1743316

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8441da8e746899e8d969528d5bad5651cdd563079c05962788f77753041f60e7
size 1149158

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4178991c7aeb76670f5a56559eb4129a6f3ae6207886db8bd8094fea7d362c3f
size 1150224

View file

@ -25,6 +25,7 @@ from tqdm import tqdm
import openwakeword import openwakeword
from numpy.lib.format import open_memmap from numpy.lib.format import open_memmap
from typing import Union, List, Callable, Deque from typing import Union, List, Callable, Deque
import requests
# Base class for computing audio features using Google's speech_embedding # Base class for computing audio features using Google's speech_embedding
@ -590,6 +591,79 @@ def compute_features_from_generator(generator, n_total, clip_duration, output_fi
trim_mmap(output_file) trim_mmap(output_file)
# Function to download files from a URL with a progress bar
def download_file(url, target_directory, file_size=None):
"""A simple function to download a file from a URL with a progress bar using only the requests library"""
local_filename = url.split('/')[-1]
with requests.get(url, stream=True) as r:
if file_size is not None:
progress_bar = tqdm(total=file_size, unit='iB', unit_scale=True, desc=f"{local_filename}")
else:
total_size = int(r.headers.get('content-length', 0))
progress_bar = tqdm(total=total_size, unit='iB', unit_scale=True, desc=f"{local_filename}")
with open(os.path.join(target_directory, local_filename), 'wb') as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
progress_bar.update(len(chunk))
progress_bar.close()
# Function to download models from GitHub release assets
def download_models(
model_names: List[str] = [],
target_directory: str = os.path.join(pathlib.Path(__file__).parent.resolve(), "resources", "models")
):
"""
Download the specified models from the release assets in the openWakeWord GitHub repository.
Uses the official urls in the MODELS dictionary in openwakeword/__init__.py.
Args:
model_names (List[str]): The names of the models to download (e.g., hey_jarvis_v0.1). Both ONNX and
tflite models will be downloaded. If not provided (the default),
the latest versions of all models will be downloaded.
target_directory (str): The directory to save the models to. Defaults to the install location
of openWakeWord (i.e., the `resources/models` directory).
Returns:
None
"""
if not isinstance(model_names, list):
raise ValueError("The model_names argument must be a list of strings")
# Always download melspectrogram and embedding models, if they don't already exist
if not os.path.exists(target_directory):
os.makedirs(target_directory)
for feature_model in openwakeword.FEATURE_MODELS.values():
if not os.path.exists(os.path.join(target_directory, feature_model["download_url"].split("/")[-1])):
download_file(feature_model["download_url"], target_directory)
download_file(feature_model["download_url"].replace(".tflite", ".onnx"), target_directory)
# Always download VAD models, if they don't already exist
for vad_model in openwakeword.VAD_MODELS.values():
if not os.path.exists(os.path.join(target_directory, vad_model["download_url"].split("/")[-1])):
download_file(vad_model["download_url"], target_directory)
# Get all model urls
official_model_urls = [i["download_url"] for i in openwakeword.MODELS.values()]
official_model_names = [i["download_url"].split("/")[-1] for i in openwakeword.MODELS.values()]
if model_names != []:
for model_name in model_names:
url = [i for i, j in zip(official_model_urls, official_model_names) if model_name in j]
if url != []:
if not os.path.exists(os.path.join(target_directory, url[0].split("/")[-1])):
download_file(url[0], target_directory)
download_file(url[0].replace(".tflite", ".onnx"), target_directory)
else:
print(official_model_urls)
for official_model_url in official_model_urls:
if not os.path.exists(os.path.join(target_directory, official_model_url.split("/")[-1])):
download_file(official_model_url, target_directory)
download_file(official_model_url.replace(".tflite", ".onnx"), target_directory)
# Handle deprecated arguments and naming (thanks to https://stackoverflow.com/a/74564394) # Handle deprecated arguments and naming (thanks to https://stackoverflow.com/a/74564394)
def re_arg(kwarg_map): def re_arg(kwarg_map):
def decorator(func): def decorator(func):

View file

@ -32,7 +32,8 @@ setuptools.setup(
'tflite-runtime>=2.8.0,<3; platform_system == "Linux"', 'tflite-runtime>=2.8.0,<3; platform_system == "Linux"',
'tqdm>=4.0,<5.0', 'tqdm>=4.0,<5.0',
'scipy>=1.3,<2', 'scipy>=1.3,<2',
'scikit-learn>=1,<2' 'scikit-learn>=1,<2',
'requests>=2.0,<3',
], ],
extras_require={ extras_require={
'test': [ 'test': [
@ -44,7 +45,8 @@ setuptools.setup(
'types-requests', 'types-requests',
'types-PyYAML', 'types-PyYAML',
'mock>=5.1,<6', 'mock>=5.1,<6',
'types-mock>=5.1,<6' 'types-mock>=5.1,<6',
'types-requests>=2.0,<3'
], ],
'full': [ 'full': [
'mutagen>=1.46.0,<2', 'mutagen>=1.46.0,<2',

View file

@ -34,6 +34,9 @@ import scipy.io.wavfile
import tempfile import tempfile
import pytest import pytest
# Download models needed for tests
openwakeword.utils.download_models(model_names=["alexa_v0.1", "hey_mycroft_v0.1"])
# Tests # Tests
class TestModels: class TestModels:

View file

@ -40,6 +40,9 @@ import pickle
import tempfile import tempfile
import mock import mock
# Download models needed for tests
openwakeword.utils.download_models()
# Tests # Tests
class TestModels: class TestModels: