Files
SpeechGenderAnalysis/experiment/validate_cn_celeb.py
T
2022-03-24 23:30:57 -04:00

169 lines
5.1 KiB
Python

import json
import os
import warnings
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
import pygame
from inaSpeechSegmenter import Segmenter
from ina_main import process, get_result_percentages
from utils import color, printc
def segment_all():
# Create segmenter
seg = Segmenter()
np.seterr(invalid='ignore')
# Loop through all celebrities
for id in ids:
id_dir = data_dir.joinpath(id)
# Loop through all recordings (Exclude singing for now)
utters = [r for r in os.listdir(id_dir) if r.endswith('.flac')
and not r.startswith('singing')]
# Exclude existing
utters = [id_dir.joinpath(u) for u in utters]
utters = [u for u in utters if not u.with_suffix('.json').exists()]
if len(utters) == 0:
continue
# Analyze
results = process(seg, [str(u) for u in utters], verbose=True)
# Write results
total = [0, 0, 0, 0, 0]
type_totals = {}
for result in results.results:
file = Path(result.file).with_suffix('.json')
# Get results
# f: Frames, r: Ratios
ratios = [round(r, 3) for r in get_result_percentages(result)]
stored = {'f': result.frames, 'r': ratios}
# Count type total (type_totals[utter_type][-1] is the count)
file_name = file.name
utter_type = file_name[:file_name.index('-')]
type_totals.setdefault(utter_type, [0, 0, 0, 0, 0])
for i in range(4):
type_totals[utter_type][i] += ratios[i]
total[i] += ratios[i]
type_totals[utter_type][-1] += 1
total[-1] += 1
# Write result
file.write_text(json.dumps(stored))
# Write type averages
type_averages = {t: [r / type_totals[t][-1] for r in type_totals[t][:-1]] for t in type_totals}
total_average = [r / total[-1] for r in total[:-1]]
obj = {'type_averages': type_averages, 'total_averages': total_average}
id_dir.joinpath('total.json').write_text(json.dumps(obj))
def graph_histogram():
closest_to_half = 1000
closest_to_half_id = ''
id_pf_map = {}
for id in ids:
id_dir = data_dir.joinpath(id)
json_path = id_dir.joinpath('total.json')
if not json_path.exists():
continue
obj = json.loads(json_path.read_text())
f, m, o, pf = obj['total_averages']
# Recalculate pf (pf is actually calculated incorrectly)
if f + m == 0:
continue
pf = f / (f + m)
id_pf_map[id] = pf
# Save fixed json
obj['total_averages'][3] = pf
json_path.write_text(json.dumps(obj))
# Find pf closest to .5
dist = abs(pf - .5)
if dist < closest_to_half:
closest_to_half = dist
closest_to_half_id = id
data_dir.joinpath('id_pf_map.json').write_text(json.dumps(id_pf_map))
plt.hist(id_pf_map.values(), bins=50)
plt.show()
print(closest_to_half_id)
def manually_label_data():
"""
Since CN-Celeb isn't labelled with the speaker's gender, this script is used to manually label
them.
"""
pygame.mixer.init()
# Load existing labels
labels_json = data_dir.joinpath('id_labels.json')
id_labels = json.loads(labels_json.read_text()) if labels_json.exists() else {}
# Load pf table
id_pfs = json.loads(data_dir.joinpath('id_pf_map.json').read_text())
# Loop through all speaker
for id in sorted(ids):
id_dir = data_dir.joinpath(id)
# Skip already identified labels
if id in id_labels:
continue
# Get ina choice
pf = id_pfs.get(id, -1)
ina_choice = 'f' if pf > 0.5 else 'm'
# Loop through all tracks until identified
tracks = [f for f in os.listdir(id_dir) if f.endswith('.flac')]
for track_i, audio in enumerate(tracks):
# Play track
sound = pygame.mixer.Sound(id_dir.joinpath(audio))
sound.play()
i = input(color(
f'\n&7Playing speaker {id[-3:]}/{len(ids)} - track {track_i}/{len(tracks)} - {audio}&r'
f'\n- Press f / m, or anything else to play next track: '))\
.lower().strip()
sound.stop()
# Skip
if i == 's':
break
# Labeled
if i == 'f' or i == 'm':
id_labels[id] = i
labels_json.write_text(json.dumps(id_labels))
# Print choice match
if pf != -1:
agree = '&aINA agrees' if ina_choice == i else '&cINA disagree'
printc(f'{agree} with confidence {abs(pf - 0.5) * 200:.0f}%')
else:
printc(f"&7INA didn't identify any voice")
break
if __name__ == '__main__':
cn_celeb_root = Path('C:/Users/me/Workspace/Data/CN-Celeb_flac')
data_dir = cn_celeb_root.joinpath('data')
ids = [id for id in os.listdir(data_dir) if id.startswith('id0')]
# segment_all()
# graph_histogram()
manually_label_data()