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