vocal-render / src /vocalrender /evaluation /visualization.py
multimodalart's picture
multimodalart HF Staff
Upload folder using huggingface_hub
5ed07ee verified
Raw
History Blame Contribute Delete
4.7 kB
"""
Visualization utilities for SVS evaluation.
- create_score_condition_figure: matplotlib bar chart for TensorBoard display
These functions accept structured metadata (bpm, word, pitch, note lists)
that are stored directly in the preprocessed Arrow dataset, avoiding
redundant reverse-parsing of SVS prompt strings.
"""
import os
from typing import Dict, List, Optional, Tuple
import numpy as np
def _load_chinese_font():
from matplotlib import font_manager
user_font_path = os.path.expanduser("~/.fonts/NotoSansSC-Regular.ttf")
if os.path.exists(user_font_path):
font_manager.fontManager.addfont(user_font_path)
return font_manager.FontProperties(fname=user_font_path)
return font_manager.FontProperties(family='sans-serif')
def _aligned_lyrics(words: List[str], n_pitches: int) -> List[str]:
return list(words[:n_pitches]) + ["·"] * max(0, n_pitches - len(words))
def _draw_score_strip(
ax_lyric, ax_midi, durs, pitches, lyrics,
lyric_color, midi_color, edge_color, text_color, chinese_font,
*, ylabel_lyric='Lyric', ylabel_midi='MIDI',
lyric_fontsize=14, midi_fontsize=12, label_fontsize=11,
placeholder=None,
):
if not durs:
if placeholder is not None:
for ax in (ax_lyric, ax_midi):
ax.text(0.5, 0.5, placeholder, ha='center', va='center',
fontsize=12, color=text_color)
ax.axis('off')
return
positions = np.cumsum([0.0] + list(durs[:-1]))
total_width = sum(durs)
import matplotlib.pyplot as plt
for pos, dur, lyric in zip(positions, durs, lyrics):
ax_lyric.add_patch(plt.Rectangle(
(pos, 0), dur, 1, facecolor=lyric_color,
edgecolor=edge_color, linewidth=1.5,
))
ax_lyric.text(pos + dur / 2, 0.5, lyric,
ha='center', va='center', fontsize=lyric_fontsize,
fontweight='bold', color=text_color,
fontproperties=chinese_font)
for pos, dur, pitch in zip(positions, durs, pitches):
ax_midi.add_patch(plt.Rectangle(
(pos, 0), dur, 1, facecolor=midi_color,
edgecolor=edge_color, linewidth=1.5,
))
ax_midi.text(pos + dur / 2, 0.5, str(pitch),
ha='center', va='center', fontsize=midi_fontsize,
fontweight='bold', color=text_color)
for ax, ylabel in ((ax_lyric, ylabel_lyric), (ax_midi, ylabel_midi)):
ax.set_xlim(0, total_width)
ax.set_ylim(0, 1)
ax.set_ylabel(ylabel, fontsize=label_fontsize, fontweight='bold',
color=text_color)
ax.set_xticks([])
ax.set_yticks([])
for spine in ax.spines.values():
spine.set_visible(False)
def create_score_condition_figure(
bpm: int,
words: List[str],
pitches: List[int],
notes: List[str],
step: Optional[int] = None,
):
"""
Create a bar chart visualization of score control conditions for TensorBoard.
Shows:
- Top row: lyric characters
- Bottom row: MIDI pitch values
- Bar width proportional to note duration
Args:
bpm: Beats per minute.
words: List of lyric characters (one per pitch segment).
pitches: List of MIDI pitch values.
notes: List of note token strings (e.g. "<NOTE_4>", "<NOTE_DOT_8>").
step: Training step (shown in the title if provided).
Returns:
matplotlib Figure.
"""
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from vocalrender.model.svs_utils import get_svs_token_maps
_, _, _, dur_units, _ = get_svs_token_maps()
chinese_font = _load_chinese_font()
if not pitches:
fig, ax = plt.subplots(figsize=(8, 2))
ax.text(0.5, 0.5, 'No score data', ha='center', va='center', fontsize=14)
ax.axis('off')
return fig
note_durations = [dur_units.get(n, 1.0) for n in notes]
lyrics = _aligned_lyrics(words, len(pitches))
total_width = sum(note_durations)
fig, (ax_lyric, ax_midi) = plt.subplots(
2, 1, figsize=(max(12, total_width * 1.5), 3),
gridspec_kw={'height_ratios': [1, 1], 'hspace': 0.05},
)
_draw_score_strip(
ax_lyric, ax_midi, note_durations, pitches, lyrics,
lyric_color='#E8A038', midi_color='#F5D03A',
edge_color='#8B6914', text_color='#5A4A1A',
chinese_font=chinese_font,
)
step_str = f" @ Step {step}" if step is not None else ""
fig.suptitle(f'Score Condition (BPM={bpm}){step_str}', fontsize=12, fontweight='bold', y=0.98)
plt.tight_layout(rect=[0, 0, 1, 0.95])
return fig