Merge pull request #44 from mfaisyal/master

Add Unit Test and PyPI
This commit is contained in:
Andre Daniel Paredes
2022-09-27 10:52:28 -04:00
committed by GitHub
20 changed files with 8478 additions and 22 deletions

1
.gitignore vendored
View File

@@ -17,7 +17,6 @@ docs/website/api/*api.md
.github/brew-colima
.github/brew-docker
# Distribution / packaging
speech/
.Python
build/
develop-eggs/

19
pytest.ini Normal file
View File

@@ -0,0 +1,19 @@
[pytest]
python_files = test_*
python_classes = *Test
python_functions = test_*
filterwarnings =
ignore::DeprecationWarning
markers =
api: test all end-user api
speech: testing speech api
movement: testing movement api
facial: testing facial api
acoustic: testing acoustic api
docker: testing api that using docker
non_docker: testing api that not using docker
debug1: debug1

57
setup.cfg Normal file
View File

@@ -0,0 +1,57 @@
[metadata]
name = opendbm
# $version will be replaced automatically by Github Actions pipeline
version = $version
description = AiCures OpenDBM is a software package that allows for calculation of digital biomarkers of health and functioning from video or audio of an individuals behavior. It integrates existing tools for measurement of behavioral characteristics such as facial activity, voice, and movement into a single package for measurement of overall behavior. OpenDBM is designed for ease of use, expanding the availability of such tools to the wider scientific community.
long_description = file: README.md
long_description_content_type = text/markdown
url = https://github.com/AiCure/open_dbm
author = The OpenDBM Development Team
author_email = opendbm@aicure.com
license = AGPL-3.0
license_files = LICENSE
platforms = any
classifiers =
Intended Audience :: Science/Research
License :: OSI Approved :: GNU Affero General Public License v3
Operating System :: OS Independent
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Topic :: Scientific/Engineering
project_urls =
Bug Tracker = https://github.com/AiCure/open_dbm/issues
Documentation = https://AiCure.github.io/open_dbm
Source Code = https://github.com/AiCure/open_dbm
[options]
packages = find:
install_requires =
numpy>=1.17
pandas>=1.1.5
matplotlib==3.5.2
praat-parselmouth
watchtower
webrtcvad
imutils
more_itertools
scipy
pyyaml==5.4.1
pydub
deepspeech==0.9.3
nltk
lexicalrichness
vaderSentiment
opencv-python>=4.5.5
cmake; "Windows" not in platform_system
dlib>=19.13.0; "Windows" not in platform_system
python_requires = >=3.7
include_package_data = True
[options.extras_require]
test =
pytest>=6.0
coverage
pre-commit

View File

@@ -1,23 +1,13 @@
import setuptools
# Copyright (C) 2012-2022 james jameson
with open('requirements.txt') as fp:
install_requires = fp.read()
setuptools.setup(
name="open_dbm",
version="0.0.1",
author="Vijay Yadav",
author_email='vijay.yadav@aicure.com',
description="openDBM",
license='',
packages=setuptools.find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires='>=3.6',
py_modules=["open_dbm"],
#package_dir={'':'open_dbm'}, # Directory of the source code of the package
install_requires = install_requires
)
if __name__ == "__main__":
import sys
from setuptools import find_packages, setup
if sys.version_info[:2] < (3, 7):
raise RuntimeError("opendbm requires python >= 3.7.")
setup()

42
tests/conftest.py Normal file
View File

@@ -0,0 +1,42 @@
import importlib
import sys
from pytest import fixture
from opendbm import FacialActivity, Movement, Speech, VerbalAcoustics
# sys.path.append("")
# Movement = importlib.import_module("api_lib.movement")
# Speech = importlib.import_module("api_lib.speech")
# Facial = importlib.import_module("api_lib.facial_activity")
# Verbal_acoustics = importlib.import_module("api_lib.verbal_acoustics")
class Model:
def __init__(self, movement, speech, facial, verbal_acoustics):
self._movement = movement
self._speech = speech
self._facial = facial
self._verbal_acoustics = verbal_acoustics
@property
def movement(self):
return self._movement()
@property
def speech(self):
return self._speech()
@property
def facial(self):
return self._facial()
@property
def verbal_acoustics(self):
return self._verbal_acoustics()
@fixture(scope="session")
def get_model():
m = Model(Movement, Speech, FacialActivity, VerbalAcoustics)
return m

11
tests/facial/conftest.py Normal file
View File

@@ -0,0 +1,11 @@
from pytest import fixture
path1 = "tests/test_data/facial_speech_verbal_video_test.mp4"
@fixture(scope="class")
def processing_facial_activity(get_model):
m = get_model.facial
m.fit(path1)
yield m

View File

@@ -0,0 +1,39 @@
# import numpy as np
import pandas as pd
from numpy.testing import assert_allclose
from pytest import mark
# @mark.smoke
# @mark.body
@mark.facial
class FacialTest:
def test_get_landmark(self, processing_facial_activity):
df_act = pd.read_csv("tests/test_data/landmark.csv")
res = processing_facial_activity.get_landmark()
assert_allclose(df_act.mean(), res.mean(), rtol=0.1, atol=1e-8)
assert_allclose(df_act.std(), res.std(), rtol=0.1, atol=1e-8)
def test_get_action_unit(self, processing_facial_activity):
df_act = pd.read_csv("tests/test_data/action_unit.csv")
res = processing_facial_activity.get_action_unit()
assert_allclose(df_act.mean(), res.mean(), rtol=0.1, atol=1e-8)
assert_allclose(df_act.std(), res.std(), rtol=0.1, atol=1e-8)
def test_get_asymmetry(self, processing_facial_activity):
actual_mean = [2.58260995, 3.34416172, 3.0563894, 2.94777878]
actual_std = [1.74161635, 2.17995634, 2.19173686, 1.82435901]
res = processing_facial_activity.get_asymmetry()
assert_allclose(actual_mean, res.mean(), rtol=0.1, atol=1e-8)
assert_allclose(actual_std, res.std(), rtol=0.1, atol=1e-8)
def test_get_expressivity(self, processing_facial_activity):
df_act = pd.read_csv("tests/test_data/expressivity.csv")
res = processing_facial_activity.get_expressivity()
assert_allclose(df_act.mean(), res.mean(), rtol=0.35, atol=1e-8)
assert_allclose(df_act.std(), res.std(), rtol=0.35, atol=1e-8)

View File

@@ -0,0 +1,11 @@
from pytest import fixture
path1 = "tests/test_data/movement_video_test.mp4"
@fixture(scope="class")
def processing_movement(get_model):
m = get_model.movement
m.fit(path1)
yield m

View File

@@ -0,0 +1,83 @@
# import numpy as np
from numpy.testing import assert_allclose
from pytest import mark
# @mark.smoke
# @mark.body
@mark.movement
class MovementTest:
def test_get_head_movement(self, processing_movement):
actual_mean = [1.3946, 0.3011, -0.1183, 0.003, 0.0094]
actual_std = [1.2644, 0.0786, 0.0649, 0.0342, 0.008]
res = processing_movement.get_head_movement()
assert_allclose(actual_mean, res.mean(), rtol=0.1, atol=1e-8)
assert_allclose(actual_std, res.std(), rtol=0.1, atol=1e-8)
def test_get_eye_blink(self, processing_movement):
actual_mean = [0.1101, 455.5, 2.2931, 29.0]
actual_std = [0.0241, 311.8611, 1.1407, 0.0]
res = processing_movement.get_eye_blink()
assert_allclose(actual_mean, res.mean(), rtol=0.1, atol=1e-8)
assert_allclose(actual_std, res.std(), rtol=0.1, atol=1e-8)
def test_get_eye_gaze(self, processing_movement):
actual_mean = [0.2292, 0.4174, -0.8761, 0.0209, 0.4191, -0.9046, 0.0145, 0.0132]
actual_std = [0.0546, 0.048, 0.0218, 0.0462, 0.0542, 0.0243, 0.0156, 0.0169]
res = processing_movement.get_eye_gaze()
assert_allclose(actual_mean, res.mean(), rtol=0.1, atol=1e-8)
assert_allclose(actual_std, res.std(), rtol=0.1, atol=1e-8)
def test_get_facial_tremor(self, processing_movement):
actual_mean = [
8.5948,
3.8759,
0.7286,
0.2546,
3.7195,
2.8068,
0.7231,
0.4562,
6.7215,
3.5861,
0.8253,
0.3912,
2.8608,
2.1741,
0.8614,
0.6464,
3.6781,
2.6698,
0.887,
0.5783,
0.0,
0.0,
0.6772,
1.0,
0.7655,
0.5476,
0.7504,
0.8978,
1.9713,
1.4991,
0.9381,
0.7761,
2.706,
2.019,
0.9885,
0.7138,
]
res = processing_movement.get_facial_tremor()
assert_allclose(actual_mean, res.mean(), rtol=0.1, atol=1e-8)
def test_get_vocal_tremor(self, processing_movement):
actual_mean = [4.23, 9.437, 7.634, 7.38, 61.642, 54.287]
res = processing_movement.get_vocal_tremor()
assert_allclose(actual_mean, res.mean(), rtol=0.1, atol=1e-8)

19
tests/speech/conftest.py Normal file
View File

@@ -0,0 +1,19 @@
from pytest import fixture
test_path = "tests/test_data/"
path_mp4 = test_path + "facial_speech_verbal_video_test.mp4"
path_wav = test_path + "facial_speech_verbal_audio_test.wav"
@fixture(scope="class")
def processing_speech_mp4(get_model):
m = get_model.speech
m.fit(path_mp4)
yield m
@fixture(scope="class")
def processing_speech_wav(get_model):
m = get_model.speech
m.fit(path_wav)
yield m

View File

@@ -0,0 +1,73 @@
import numpy as np
from pytest import mark
@mark.non_docker
@mark.speech
class SpeechTest:
def test_get_transcribe(self, processing_speech_mp4, processing_speech_wav):
actual_totaltime = 87.978685
len_words_count = 57
res_mp4 = processing_speech_mp4.get_transcribe().to_dataframe()
audio_duration_mp4 = res_mp4["nlp_totalTime"].item()
transcribed_text_mp4 = res_mp4["nlp_transcribe"].item()
res_wav = processing_speech_wav.get_transcribe().to_dataframe()
audio_duration_wav = res_wav["nlp_totalTime"].item()
transcribed_text_wav = res_wav["nlp_transcribe"].item()
# test if duration is matched
assert np.isclose(audio_duration_mp4, actual_totaltime, rtol=0.1, atol=1e-8)
assert np.isclose(audio_duration_wav, actual_totaltime, rtol=0.1, atol=1e-8)
# test if there is transcribed text or not
assert type(transcribed_text_mp4) == str
assert type(transcribed_text_wav) == str
# test the length of the text
assert np.isclose(
len(transcribed_text_mp4.split(" ")), len_words_count, rtol=0.5, atol=1e-8
)
assert np.isclose(
len(transcribed_text_wav.split(" ")), len_words_count, rtol=0.5, atol=1e-8
)
def test_get_speech_features(self, processing_speech_mp4, processing_speech_wav):
# actual = [
# 1.0,
# 2.0,
# 2.0,
# 1.0,
# 1.0,
# 6.0,
# 6.0,
# 11.0,
# 11.0,
# 5.0,
# 5.0,
# 15.0,
# 15.0,
# -0.8256,
# 0.08860759493670886,
# 38.873052120437336,
# 87.97868480725624,
# ]
res_mp4 = (
processing_speech_mp4.get_speech_features()
.to_dataframe()
.drop(columns="dbm_master_url")
)
res_wav = (
processing_speech_wav.get_speech_features()
.to_dataframe()
.drop(columns="dbm_master_url")
)
desired_mp4 = np.array((res_mp4.iloc[0]))
desired_wav = np.array((res_wav.iloc[0]))
# check if there is any zero value or not
for v1, v2 in zip(desired_mp4, desired_wav):
assert bool(v1)
assert bool(v2)

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

2639
tests/test_data/landmark.csv Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,19 @@
from pytest import fixture
test_path = "tests/test_data/"
path_mp4 = test_path + "facial_speech_verbal_video_test.mp4"
path_wav = test_path + "facial_speech_verbal_audio_test.wav"
@fixture(scope="class")
def processing_verbal_acoustics_mp4(get_model):
m = get_model.verbal_acoustics
m.fit(path_mp4)
yield m
@fixture(scope="class")
def processing_verbal_acoustics_wav(get_model):
m = get_model.verbal_acoustics
m.fit(path_wav)
yield m

View File

@@ -0,0 +1,177 @@
import numpy as np
from numpy.testing import assert_allclose
from pytest import mark
@mark.non_docker
@mark.acoustic
class AcousticTest:
def test_get_audio_intensity(
self, processing_verbal_acoustics_mp4, processing_verbal_acoustics_wav
):
actual_mean = 52.867660826299
actual_std = 6.357796
res_mp4 = processing_verbal_acoustics_mp4.get_audio_intensity()
res_wav = processing_verbal_acoustics_wav.get_audio_intensity()
# testing mean actual vs desired
assert np.isclose(res_mp4.mean().item(), actual_mean, rtol=0.1, atol=1e-8)
assert np.isclose(res_wav.mean().item(), actual_mean, rtol=0.1, atol=1e-8)
# testing std actual vs desired
assert np.isclose(res_mp4.std().item(), actual_std, rtol=0.1, atol=1e-8)
assert np.isclose(res_wav.std().item(), actual_std, rtol=0.1, atol=1e-8)
def test_get_pitch_frequency(
self, processing_verbal_acoustics_mp4, processing_verbal_acoustics_wav
):
actual_mean = 27.270735
actual_std = 58.073703
res_mp4 = processing_verbal_acoustics_mp4.get_pitch_frequency()
res_wav = processing_verbal_acoustics_wav.get_pitch_frequency()
assert np.isclose(res_mp4.mean().item(), actual_mean, rtol=0.1, atol=1e-8)
assert np.isclose(res_wav.mean().item(), actual_mean, rtol=0.1, atol=1e-8)
assert np.isclose(res_mp4.std().item(), actual_std, rtol=0.1, atol=1e-8)
assert np.isclose(res_wav.std().item(), actual_std, rtol=0.1, atol=1e-8)
def test_get_formant_frequency(
self, processing_verbal_acoustics_mp4, processing_verbal_acoustics_wav
):
actual_mean = [679.47914618, 1788.237625, 2931.83885151, 4075.29506138]
actual_std = [366.35888699, 472.92129736, 543.15256087, 431.39331643]
res_mp4 = processing_verbal_acoustics_mp4.get_formant_frequency()
res_wav = processing_verbal_acoustics_wav.get_formant_frequency()
assert_allclose(res_mp4.mean(), actual_mean, rtol=0.1, atol=1e-8)
assert_allclose(res_wav.mean(), actual_mean, rtol=0.1, atol=1e-8)
assert_allclose(res_mp4.std(), actual_std, rtol=0.1, atol=1e-8)
assert_allclose(res_wav.std(), actual_std, rtol=0.1, atol=1e-8)
def test_get_harmonic_noise(
self, processing_verbal_acoustics_mp4, processing_verbal_acoustics_wav
):
actual_mean = 3.154794
actual_std = 7.389723
res_mp4 = processing_verbal_acoustics_mp4.get_harmonic_noise()
res_wav = processing_verbal_acoustics_wav.get_harmonic_noise()
assert np.isclose(res_mp4.mean().item(), actual_mean, rtol=0.1, atol=1e-8)
assert np.isclose(res_wav.mean().item(), actual_mean, rtol=0.1, atol=1e-8)
assert np.isclose(res_mp4.std().item(), actual_std, rtol=0.1, atol=1e-8)
assert np.isclose(res_wav.std().item(), actual_std, rtol=0.1, atol=1e-8)
def test_get_glottal_noise(
self, processing_verbal_acoustics_mp4, processing_verbal_acoustics_wav
):
actual_mean = 0.86287177
actual_std = 0.106516901
res_mp4 = processing_verbal_acoustics_mp4.get_glottal_noise()
res_wav = processing_verbal_acoustics_wav.get_glottal_noise()
assert np.isclose(res_mp4.mean().item(), actual_mean, rtol=0.1, atol=1e-8)
assert np.isclose(res_wav.mean().item(), actual_mean, rtol=0.1, atol=1e-8)
assert np.isclose(res_mp4.std().item(), actual_std, rtol=0.1, atol=1e-8)
assert np.isclose(res_wav.std().item(), actual_std, rtol=0.1, atol=1e-8)
def test_get_jitter(
self, processing_verbal_acoustics_mp4, processing_verbal_acoustics_wav
):
actual_mean = 0.041403506
actual_std = 0.026209854
res_mp4 = processing_verbal_acoustics_mp4.get_jitter()
res_wav = processing_verbal_acoustics_wav.get_jitter()
assert np.isclose(res_mp4.mean().item(), actual_mean, rtol=0.1, atol=1e-8)
assert np.isclose(res_wav.mean().item(), actual_mean, rtol=0.1, atol=1e-8)
assert np.isclose(res_mp4.std().item(), actual_std, rtol=0.1, atol=1e-8)
assert np.isclose(res_wav.std().item(), actual_std, rtol=0.1, atol=1e-8)
def test_get_shimmer(
self, processing_verbal_acoustics_mp4, processing_verbal_acoustics_wav
):
actual_mean = 0.2018721891
actual_std = 0.0584668629
res_mp4 = processing_verbal_acoustics_mp4.get_shimmer()
res_wav = processing_verbal_acoustics_wav.get_shimmer()
assert np.isclose(res_mp4.mean().item(), actual_mean, rtol=0.1, atol=1e-8)
assert np.isclose(res_wav.mean().item(), actual_mean, rtol=0.1, atol=1e-8)
assert np.isclose(res_mp4.std().item(), actual_std, rtol=0.1, atol=1e-8)
assert np.isclose(res_wav.std().item(), actual_std, rtol=0.1, atol=1e-8)
def test_get_pause_characteristics(
self, processing_verbal_acoustics_mp4, processing_verbal_acoustics_wav
):
actual_mean = [84.76, 28.84, 32, 55.92, 0.65974516]
res_mp4 = processing_verbal_acoustics_mp4.get_pause_characteristics()
res_wav = processing_verbal_acoustics_wav.get_pause_characteristics()
assert_allclose(res_mp4.mean(), actual_mean, rtol=0.1, atol=1e-8)
assert_allclose(res_wav.mean(), actual_mean, rtol=0.1, atol=1e-8)
def test_get_voice_prevalence(
self, processing_verbal_acoustics_mp4, processing_verbal_acoustics_wav
):
actual_mean = [1865.0, 8794.0, 21.207641573800316]
res_mp4 = processing_verbal_acoustics_mp4.get_voice_prevalence()
res_wav = processing_verbal_acoustics_wav.get_voice_prevalence()
assert_allclose(res_mp4.mean(), actual_mean, rtol=0.1, atol=1e-8)
assert_allclose(res_wav.mean(), actual_mean, rtol=0.1, atol=1e-8)
def test_get_mfcc(
self, processing_verbal_acoustics_mp4, processing_verbal_acoustics_wav
):
actual_mean = [
491.835,
-37.714,
82.164,
64.293,
-33.829,
54.35,
6.563,
-6.669,
31.392,
-8.672,
9.302,
8.096,
]
actual_std = [
71.555,
83.91,
65.506,
32.296,
38.059,
35.283,
30.122,
23.462,
21.292,
21.183,
18.607,
17.101,
]
res_mp4 = processing_verbal_acoustics_mp4.get_mfcc()
res_wav = processing_verbal_acoustics_wav.get_mfcc()
assert_allclose(res_mp4.mean(), actual_mean, rtol=0.1, atol=1e-8)
assert_allclose(res_wav.mean(), actual_mean, rtol=0.1, atol=1e-8)
assert_allclose(res_mp4.std(), actual_std, rtol=0.1, atol=1e-8)
assert_allclose(res_wav.std(), actual_std, rtol=0.1, atol=1e-8)