Skip to content

Commit

Permalink
Added more datasets and metrics to the analysis.
Browse files Browse the repository at this point in the history
- Added missing includes.
- Added more garbage to .gitignore.
- Added more doc comments.
- Fixed many small bugs.
- Fixed wording in various places.
- Made the calculation tool optionally able to skip calculating
  already-existing metrics.
- Added leaderboard flag to the analysis tool that shows top list of
  metrics.
- Made the table output be Markdown-compatible.
- Added an optional error handler to the worker pool.
- Added many more external metrics.
- Added ingestion tool for more datasets.
  • Loading branch information
Martin Bruse committed May 3, 2024
1 parent 645cfd1 commit e5ccf39
Show file tree
Hide file tree
Showing 10 changed files with 945 additions and 274 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ debug_build
asan_build
.vscode
python/__pycache__/**
**/METADATA
**/METADATA
# Some files produced by one of the external metrics.
pesq_results.txt
analized
Testing/*
1 change: 1 addition & 0 deletions cpp/zimt/goohrli.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <cstddef>
#include <utility>

#include "absl/status/statusor.h"
#include "absl/types/span.h"
#include "hwy/aligned_allocator.h"
#include "hwy/base.h"
Expand Down
9 changes: 6 additions & 3 deletions go/bin/perceptual_audio/perceptual_audio.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
// limitations under the License.

// perceptual_audio creates a study from https://github.com/pranaymanocha/PerceptualAudio/blob/master/dataset/README.md.
//
// Download and unpack the dataset ZIP, and provide the unpacked directory
// as -source when running this binary.
package main

import (
Expand Down Expand Up @@ -56,7 +59,7 @@ func populate(source string, dest string, workers int) error {

lineReader := bufio.NewReader(res.Body)
err = nil
bar := progress.New("Recoding")
bar := progress.New("Transcoding")
pool := worker.Pool[*data.Reference]{
Workers: workers,
OnChange: bar.Update,
Expand Down Expand Up @@ -95,10 +98,10 @@ func populate(source string, dest string, workers int) error {
})
lineIndex++
}
bar.Finish()
if err := pool.Error(); err != nil {
log.Println(err.Error())
}
bar.Finish()
refs := []*data.Reference{}
for ref := range pool.Results() {
refs = append(refs, ref)
Expand All @@ -112,7 +115,7 @@ func populate(source string, dest string, workers int) error {
func main() {
source := flag.String("source", "", "Directory containing the unpacked http://percepaudio.cs.princeton.edu/icassp2020_perceptual/audio_perception.zip.")
destination := flag.String("dest", "", "Destination directory.")
workers := flag.Int("workers", runtime.NumCPU(), "Number of workers downloading sounds.")
workers := flag.Int("workers", runtime.NumCPU(), "Number of workers transcoding sounds.")
flag.Parse()
if *source == "" || *destination == "" {
flag.Usage()
Expand Down
32 changes: 30 additions & 2 deletions go/bin/score/score.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import (
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"sort"

"github.com/google/zimtohrli/go/data"
"github.com/google/zimtohrli/go/goohrli"
Expand All @@ -37,22 +39,42 @@ const (
func main() {
details := flag.String("details", "", "Path to database directory with a study to show the details from.")
calculate := flag.String("calculate", "", "Path to a database directory with a study to calculate metrics for.")
force := flag.Bool("force", false, "Whether to recalculate scores that already exist.")
calculateZimtohrli := flag.Bool("calculate_zimtohrli", false, "Whether to calculate Zimtohrli scores.")
calculateViSQOL := flag.Bool("calculate_visqol", false, "Whether to calculate ViSQOL scores.")
calculatePipeMetric := flag.String("calculate_pipe", "", "Path to a binary that serves metrics via stdin/stdout pipe. Install some of the via 'install_python_metrics.py'.")
zimtohrliFrequencyResolution := flag.Float64("zimtohrli_frequency_resolution", goohrli.DefaultFrequencyResolution(), "Smallest bandwidth of the Zimtohrli filterbank.")
zimtohrliPerceptualSampleRate := flag.Float64("zimtohrli_perceptual_sample_rate", goohrli.DefaultPerceptualSampleRate(), "Sample rate of the Zimtohrli spectrograms.")
correlate := flag.String("correlate", "", "Path to a database directory with a study to correlate scores for.")
leaderboard := flag.String("leaderboard", "", "Glob to directories with databases to compute leaderboard for.")
accuracy := flag.String("accuracy", "", "Path to a database directory with a study to provide JND accuracy for.")
workers := flag.Int("workers", runtime.NumCPU(), "Number of concurrent workers for tasks.")
failFast := flag.Bool("fail_fast", false, "Whether to panic immediately on any error.")
flag.Parse()

if *details == "" && *calculate == "" && *correlate == "" && *accuracy == "" {
if *details == "" && *calculate == "" && *correlate == "" && *accuracy == "" && *leaderboard == "" {
flag.Usage()
os.Exit(1)
}

if *leaderboard != "" {
databases, err := filepath.Glob(*leaderboard)
if err != nil {
log.Fatal(err)
}
studies := make(data.Studies, len(databases))
for index, path := range databases {
if studies[index], err = data.OpenStudy(path); err != nil {
log.Fatal(err)
}
}
board, err := studies.Leaderboard()
if err != nil {
log.Fatal(err)
}
fmt.Println(board)
}

if *details != "" {
study, err := data.OpenStudy(*details)
if err != nil {
Expand Down Expand Up @@ -107,7 +129,13 @@ func main() {
log.Print("No metrics to calculate, provide one of the -calculate_XXX flags!")
os.Exit(2)
}
if err := study.Calculate(measurements, pool); err != nil {
sortedTypes := sort.StringSlice{}
for scoreType := range measurements {
sortedTypes = append(sortedTypes, string(scoreType))
}
sort.Sort(sortedTypes)
log.Printf("*** Calculating %+v (force=%v)", sortedTypes, *force)
if err := study.Calculate(measurements, pool, *force); err != nil {
log.Fatal(err)
}
bar.Finish()
Expand Down
163 changes: 163 additions & 0 deletions go/bin/sebass_db/sebass_db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Copyright 2024 The Zimtohrli Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// sebass_db creates a study from https://www.audiolabs-erlangen.de/resources/2019-WASPAA-SEBASS/.
//
// It currently supports SASSEC, SiSEC08, SAOC, and PEASS-DB. SiSEC18 at
// that web site doesn't contain all audio, and is not currently supported.
//
// Download and unpac one of the supported ZIP files, and use the directory
// it unpacked as -source when running this binary.
package main

import (
"encoding/csv"
"flag"
"fmt"
"log"
"math"
"os"
"path/filepath"
"reflect"
"runtime"
"strconv"

"github.com/google/zimtohrli/go/aio"
"github.com/google/zimtohrli/go/data"
"github.com/google/zimtohrli/go/progress"
"github.com/google/zimtohrli/go/worker"
)

func populate(source string, dest string, workers int, failFast bool) error {
study, err := data.OpenStudy(dest)
if err != nil {
return err
}
defer study.Close()

csvFiles, err := filepath.Glob(filepath.Join(source, "*.csv"))
if err != nil {
return err
}
for _, csvFile := range csvFiles {
signals := "Signals"
switch filepath.Base(csvFile) {
case "SAOC_1_anonymized.csv":
signals = "Signals_1"
case "SAOC_2_anonymized.csv":
signals = "Signals_2"
case "SAOC_3_anonymized.csv":
signals = "Signals_3"
}
fileReader, err := os.Open(csvFile)
if err != nil {
return err
}
defer fileReader.Close()
csvReader := csv.NewReader(fileReader)
header, err := csvReader.Read()
if err != nil {
return err
}
if !reflect.DeepEqual(header, []string{"Testname", "Listener", "Trial", "Condition", "Ratingscore"}) {
return fmt.Errorf("header %+v doesn't match expected SEBASS-DB header", header)
}
err = nil
bar := progress.New(fmt.Sprintf("Transcoding from %q", csvFile))
pool := worker.Pool[*data.Reference]{
Workers: workers,
OnChange: bar.Update,
FailFast: failFast,
}
var loopLine []string
lineIndex := 0
for loopLine, err = csvReader.Read(); err == nil; loopLine, err = csvReader.Read() {
line := loopLine
if len(line) == 0 {
continue
}
if line[3] == "anchor" {
line[3] = "anker_mix"
}
if line[3] == "hidden_ref" {
line[3] = "orig"
}
if line[3] == "SAOC" {
continue
}
mos, err := strconv.ParseFloat(line[4], 64)
if err != nil {
return err
}
if math.IsNaN(mos) {
continue
}
refIndex := lineIndex
pool.Submit(func(f func(*data.Reference)) error {
ref := &data.Reference{
Name: fmt.Sprintf("ref-%v", refIndex),
}
var err error
path := filepath.Join(source, signals, "orig", fmt.Sprintf("%s.wav", line[2]))
ref.Path, err = aio.Recode(path, dest)
if err != nil {
return fmt.Errorf("unable to fetch %q", path)
}
dist := &data.Distortion{
Name: fmt.Sprintf("dist-%v", refIndex),
Scores: map[data.ScoreType]float64{
data.MOS: mos,
},
}
path = filepath.Join(source, signals, line[3], fmt.Sprintf("%s.wav", line[2]))
dist.Path, err = aio.Recode(path, dest)
if err != nil {
return fmt.Errorf("unable to fetch %q", path)
}
ref.Distortions = append(ref.Distortions, dist)
f(ref)
return nil
})
lineIndex++
}
if err := pool.Error(); err != nil {
log.Println(err.Error())
}
bar.Finish()
refs := []*data.Reference{}
for ref := range pool.Results() {
refs = append(refs, ref)
}
if err := study.Put(refs); err != nil {
return err
}
}
return nil
}

func main() {
source := flag.String("source", "", "Directory containing one of the unpacked datasets from https://www.audiolabs-erlangen.de/resources/2019-WASPAA-SEBASS/.")
destination := flag.String("dest", "", "Destination directory.")
workers := flag.Int("workers", runtime.NumCPU(), "Number of workers transcoding sounds.")
failFast := flag.Bool("fail_fast", false, "Whether to exit immediately at the first error.")
flag.Parse()
if *source == "" || *destination == "" {
flag.Usage()
os.Exit(1)
}

if err := populate(*source, *destination, *workers, *failFast); err != nil {
log.Fatal(err)
}
}
Loading

0 comments on commit e5ccf39

Please sign in to comment.