Introduction
This blog post describes on how one can set-up a Docker environment to cross-compile VISQOL for Amazon Linux 2.
What is VISQOL
VISQOL stands for Virtual Speech Quality Objective Listener and is an objective, full-reference metric for perceived audio quality.
Why Visqol?
Contrary to PESQ and POLQA, VISQOL is OpenSource and can be used under Apache-2.0 License.
Step 1 - Set Up
To pull the image, just paste docker pull amazonlinux:2
to the command line. To make sure, that we don’t download the image, each time we build the container paste docker tag amazonlinux:2 local-amazonlinux:2
. Now we can build from the local image.
Step 2 - Building The Image
To build the image, paste docker build --tag="local-amazonlinux:latest" .
to the command line. You could also paste docker build .
. I just provided a new tag for versioning.
Step 3 - Run The Image
To run the image paste docker run -it local-amazonlinux:latest
to the command line. This opens an interactive session. Make sure you delete the container, after you copied all necessary file from the container image to your host system.
Inside the container
When you started the interactive session within the container, just paste the following lines to the command line in the exact same order
1python3.8 -m venv env
2source env/bin/activate
3python3.8 -m pip install numpy scipy protobuf wheel
4bazel build :visqol -c opt
5python3.8 -m pip install .
After that, we just have to copy visqol_lib_py.so
to the host system via docker cp name-of-the-currently-running-container:/visqol/build/visqol/build ./build.
.
For that, just open a second terminal window or kill the currently running container and then copy everything to get the container name. For that just paste docker ps -a or docker ps
to the command line.
Step 4 - Setting Up Folder structure
Before we try to build visqol, we have to setup our project. Just make a new folder and change into the new working directory, for example path\visqol-docker-cross-compile-folder
. In this new directory the following files should be there, namely:
- .bazelrc
- Dockerfile
- file_path.h
- setup.py
- test.py.
Additionally, to test, if we can calculate the MOS Score in the Docker environment via VISQOL, we provide two audio files: callee-dump-2023-04-04-09-42-51-dec.wav (‘degraded’ audio) and caller-dump-2023-04-04-09-42-51-enc.wav (‘reference’ audio). The next sub sections describe the content of each file. Just copy them into your folder!
Content .bazelrc
1# Most of this file is to allow tensorflow to build.
2# Inspired by TensorFlow serving's .bazelrc to build from the source.
3# It also may be useful to refer to TensorFlow .bazelrc for more details:
4# https://github.com/tensorflow/tensorflow/blob/master/.bazelrc
5
6# Optimizations used for TF Serving release builds.
7build:release --copt=-mavx
8build:release --copt=-msse4.2
9
10# Options used to build with CUDA.
11build:cuda --repo_env TF_NEED_CUDA=1
12build:cuda --crosstool_top=@local_config_cuda//crosstool:toolchain
13build:cuda --@local_config_cuda//:enable_cuda
14build:cuda --define=using_cuda=true --define=using_cuda_nvcc=true
15build:cuda --action_env=TF_CUDA_COMPUTE_CAPABILITIES="sm_35,sm_50,sm_60,sm_70,sm_75,compute_80"
16
17# Options used to build with TPU support.
18build:tpu --distinct_host_configuration=false
19build:tpu --define=with_tpu_support=true --define=framework_shared_object=false
20
21# Please note that MKL on MacOS or windows is still not supported.
22# If you would like to use a local MKL instead of downloading, please set the
23# environment variable "TF_MKL_ROOT" every time before build.
24build:mkl --define=build_with_mkl=true --define=enable_mkl=true --define=build_with_openmp=true
25build:mkl --define=tensorflow_mkldnn_contraction_kernel=0
26
27# This config option is used to enable MKL-DNN open source library only,
28# without depending on MKL binary version.
29build:mkl_open_source_only --define=build_with_mkl_dnn_only=true
30build:mkl_open_source_only --define=build_with_mkl=true --define=enable_mkl=true
31build:mkl_open_source_only --define=tensorflow_mkldnn_contraction_kernel=0
32
33# Processor native optimizations (depends on build host capabilities).
34build:nativeopt --copt=-march=native
35build:nativeopt --host_copt=-march=native
36build:nativeopt --copt=-O3
37
38build --keep_going
39build --verbose_failures=true
40build --spawn_strategy=standalone
41build --genrule_strategy=standalone
42
43build --define=grpc_no_ares=true
44
45# Sets the default Apple platform to macOS.
46build --apple_platform_type=macos
47
48build -c opt
49
50# ViSQOL is overridding the default c++14 settings in tensorflow.
51# TF's .bazelrc claims it c++14 required, but it is likely required
52# in the sense that it is backwards compatible with c++17.
53build --cxxopt=-std=c++17
54
55# build --linkopt=-lstdc++fs instead od build --linkopt=-ldl
56build --host_cxxopt=-std=c++17
57build --linkopt=-lstdc++fs
58build --linkopt=-ldl
59
60build --experimental_repo_remote_exec
61
62# Enable platform specific config (e.g. by default use --config=windows when on windows, and --config=linux when on linux)
63build --enable_platform_specific_config
64
65
66## Windows config
67startup --windows_enable_symlinks
68build:windows --enable_runfiles
69
70# These settings below allow for compilation using MSVC
71# It would be nice to use clang to compile for Windows as well,
72# but the bazel instructions did not work.
73build:windows --copt=/D_USE_MATH_DEFINES
74build:windows --host_copt=/D_USE_MATH_DEFINES
75build:windows --cxxopt=-D_HAS_DEPRECATED_RESULT_OF=1
76
77build:windows --cxxopt=/Zc:__cplusplus
78# c++20 needed in MSVC for designated initializers (llvm libc++
79# and gnu stc++ provides them in c++17).
80build:windows --cxxopt=/std:c++20
81build:windows --linkopt=-ldl
82build:windows --host_cxxopt=/std:c++20
83
84# Make sure to include as little of windows.h as possible
85build:windows --copt=-DWIN32_LEAN_AND_MEAN
86build:windows --host_copt=-DWIN32_LEAN_AND_MEAN
87build:windows --copt=-DNOGDI
88build:windows --host_copt=-DNOGDI
89
90# MSVC (Windows): Standards-conformant preprocessor mode
91# See https://docs.microsoft.com/en-us/cpp/preprocessor/preprocessor-experimental-overview
92build:windows --copt=/Zc:preprocessor
93build:windows --host_copt=/Zc:preprocessor
94
95# Misc build options we need for windows according to tensorflow
96build:windows --linkopt=/DEBUG
97build:windows --host_linkopt=/DEBUG
98build:windows --linkopt=/OPT:REF
99build:windows --host_linkopt=/OPT:REF
100build:windows --linkopt=/OPT:ICF
101build:windows --host_linkopt=/OPT:ICF
102# This is a workaround for this magic preprocessor constant/macro not existing
103# in MSVC
104build:windows --host_copt=-D__PRETTY_FUNCTION__=__FUNCSIG__
105build:windows --copt=-D__PRETTY_FUNCTION__=__FUNCSIG__
Content Docker File
1FROM local-amazonlinux:2 AS buildstage0
2
3RUN yum install -y sudo
4RUN yum update -y && \
5 yum groupinstall 'Development Tools' -y && \
6 yum -y install gcc openssl-devel bzip2-devel libffi-devel wget tar which
7
8RUN sudo amazon-linux-extras enable python3.8
9RUN yum install -y python38 python38-devel
10
11# Set Up Bazel
12FROM buildstage0 as buildstage1
13RUN wget https://github.com/bazelbuild/bazelisk/releases/download/v1.8.1/bazelisk-linux-amd64
14RUN chmod +x bazelisk-linux-amd64
15RUN sudo mv bazelisk-linux-amd64 /usr/local/bin/bazel
16
17# Pull visqol repo
18RUN git clone https://github.com/google/visqol.git
19
20# Now we change the working directory!
21WORKDIR /visqol
22
23FROM buildstage1 as buildstage2
24COPY file_path.h src/include/
25COPY .bazelrc /visqol
26COPY setup.py /visqol
27COPY test.py /visqol
28COPY callee.wav /visqol
29COPY caller.wav /visqol
Content file_path.h
1/*
2 * Copyright 2019 Google LLC, Andrew Hines
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef VISQOL_INCLUDE_FILEPATH_H
18#define VISQOL_INCLUDE_FILEPATH_H
19
20#include <experimental/filesystem>
21#include <fstream>
22#include <string>
23
24#include "absl/strings/string_view.h"
25
26namespace Visqol {
27class FilePath {
28 public:
29 FilePath() {}
30
31 FilePath(const FilePath& other) { path_ = other.path_; }
32
33 FilePath(absl::string_view path) {
34 path_ = std::experimental::filesystem::path(std::string(path)).string();
35 }
36
37 const std::string Path() const { return path_; }
38
39 bool Exists() const { return std::experimental::filesystem::exists(path_); }
40
41 static std::string currentWorkingDir() {
42 return std::experimental::filesystem::current_path().string();
43 }
44
45 private:
46 std::string path_;
47};
48
49struct ReferenceDegradedPathPair {
50 FilePath reference;
51 FilePath degraded;
52};
53} // namespace Visqol
54
55#endif // VISQOL_INCLUDE_FILEPATH_H
Content setup.py
1import os
2from setuptools import setup
3
4os.system("bazel build -c opt //:similarity_result_py_pb2")
5os.system("bazel build -c opt //:visqol_config_py_pb2")
6os.system("bazel build -c opt //python:visqol_lib_py.so")
7
8setup(
9 name="visqol",
10 version="3.3.3",
11 url="https://github.com/google/visqol",
12 description="An objective, full-reference metric for perceived audio quality.",
13 packages=["visqol", "visqol.model", "visqol.pb2"],
14 package_dir={
15 "visqol": "bazel-bin/python",
16 "visqol.model": "model",
17 "visqol.pb2": "bazel-bin",
18 "external": "bazel-bin/external"
19 },
20 package_data={
21 "visqol": ["visqol_lib_py.so"],
22 "visqol.model": [
23 "lattice_tcditugenmeetpackhref_ls2_nl60_lr12_bs2048_learn.005_ep2400_train1_7_raw.tflite",
24 "libsvm_nu_svr_model.txt"
25 ]
26 }
27)
Content test.py
1import os
2
3import numpy as np
4# import soundfile as sf
5from scipy.io import wavfile
6
7from visqol import visqol_lib_py
8from visqol.pb2 import visqol_config_pb2
9from visqol.pb2 import similarity_result_pb2
10
11config = visqol_config_pb2.VisqolConfig()
12
13mode = "speech"
14if mode == "audio":
15 config.audio.sample_rate = 48000
16 config.options.use_speech_scoring = False
17 svr_model_path = "libsvm_nu_svr_model.txt"
18elif mode == "speech":
19 config.audio.sample_rate = 16000
20 config.options.use_speech_scoring = True
21 svr_model_path = "lattice_tcditugenmeetpackhref_ls2_nl60_lr12_bs2048_learn.005_ep2400_train1_7_raw.tflite"
22else:
23 raise ValueError(f"Unrecognized mode: {mode}")
24
25config.options.svr_model_path = os.path.join(
26 os.path.dirname(visqol_lib_py.__file__), "model", svr_model_path)
27
28api = visqol_lib_py.VisqolApi()
29api.Create(config)
30
31# Both files have actually a sample rate of fs=8000Hz
32# So normally we would have to resample to 16000Hz
33# (based on the official documentation)
34#
35# ---------------------------------------------------------
36# [1] mos-result w.out 'manual' resampling (enc-dec):
37# moslqo: 4.683368925457326
38#
39# [2] mos-result w.out 'manual' resampling (enc-enc):
40# moslqo: 4.999999943193998
41#
42# [3] mos-result w.out 'manual' resampling (dec-dec):
43# moslqo: 4.999999943193998
44reference: str = "caller.wav"
45degraded: str = "callee.wav"
46
47# Option 1:
48_, ref_file_scipy = wavfile.read(reference)
49_, deg_file_scipy = wavfile.read(degraded)
50print(f"Type of reg_file_scipy: {ref_file_scipy.dtype}")
51print(f"Type of deg_file_scipy: {deg_file_scipy.dtype}")
52print(np.iinfo(np.int16).max, np.iinfo(np.int16).min)
53
54# Here we cast to a float64 and normalize the array values such that the range lies
55# between [-1, 1)
56similarity_result_scipy = api.Measure(
57 ref_file_scipy.astype(dtype=np.float64, copy=False),
58 deg_file_scipy.astype(dtype=np.float64, copy=False)
59)
60
61print(f"MOS w. scipy: {similarity_result_scipy.moslqo: 10}")
Important Notes:
The reason why I invoked the bazel build commands receptively the python3.8 commands manually inside the container was just for debugging purposes. You could definitely automate everything! I just wanted to make sure that I have control over each step in the building process.
Update - 20.09.2023:
I composed a script named invoke.sh, which automatically compiles, executes and duplicates all the mandatory processes. You can find the script at https://github.com/sipfront/visqol-docker-cross-compile
comments powered by Disqus