tp/scripts/tercera_parte.py (22970B)
1 from utils import * 2 from scipy.signal import firwin, freqz, tf2zpk, get_window 3 from scipy.fft import fftshift 4 from scipy.io.wavfile import write as write_wav 5 import random 6 7 import pickle 8 database_name = 'database.pkl' 9 10 ############################# Tercera parte ################################### 11 12 cutoff = 2650 # frecuencia de corte 13 M = 700 # orden FIR (número de coeficientes) 14 15 def filtro_fir(): 16 fs = 44100 17 18 # Diseño FIR pasabajos con ventana 19 b = firwin(M, cutoff, fs=fs, window='hamming') 20 21 w, H = freqz(b, worN=2048, fs=fs) 22 fase = np.unwrap(np.angle(H))*180/np.pi 23 24 fig, ax1, ax2 = freq_response_plot(w, H, fase, show=False) 25 save_plot(fig, "respuesta_en_frecuencia_pasa-bajos_fir") 26 27 # `a` son los coeficientes de la respuesta al impulso (coinciden con los 28 # coeficientes de respuesta en frecuencia) 29 def filtro_fir_polos_y_ceros(a): 30 zeros, poles, gain = tf2zpk(a, [1]) 31 32 # Crear figura 33 fig, axis = plt.subplots(figsize=(8, 4)) 34 35 axis.scatter(np.real(zeros), np.imag(zeros), 36 s=25, facecolors='none', edgecolors='tab:blue', zorder=10, 37 label='Ceros', linewidth=1.25) 38 39 axis.scatter(np.real(poles), np.imag(poles), 40 s=25, marker='x', color='tab:red', 41 label='Polos') 42 43 axis.set_xlabel("Real", color="black") 44 axis.set_ylabel("Imaginario", color="black") 45 46 # Unidad círculo para referencia 47 # theta = np.linspace(0, 2*np.pi, 100) 48 # plt.plot(np.cos(theta), np.sin(theta)) # círculo unitario 49 50 # axis.yaxis.set_major_locator(MaxNLocator(nbins=5)) 51 52 axis.grid(True, which='major', color='black', linestyle=':', linewidth=1.00) 53 axis.grid(True, which='minor', color='black', linestyle=':', linewidth=0.50) 54 axis.xaxis.set_minor_locator(AutoMinorLocator(2)) 55 56 plt.grid(True) 57 plt.axis('equal') 58 axis.legend() 59 save_plot(fig, "polos_y_ceros_pasa-bajos_fir") 60 61 62 # a diferencia de la funcion `filtro_fir` se genera el filtro mediante 63 # operaciones elementales, como la multiplicacion por ventana, en lugar de usar 64 # una funcion de libreria externa como `firwin` 65 def filtro_fir_deducido(): 66 fs = 44100 67 68 # respuesta ideal pasabajos: sinc centrada en M/2 69 n = np.arange(M + 1) 70 wc = 2*np.pi*cutoff / fs 71 72 # h_ideal = sinc(wc*n)/(pi n); wc = 2pi*fc/fs 73 # se normaliza la ganancia a 1 multiplicando por 2.0*(fc/fs) 74 h_ideal = np.sinc(2.0 * (cutoff/fs) * (n - M/2)) 75 76 # ventana de hamming (de acuerdo a la formula de wikipedia) 77 v_hamming = 0.54 - 0.46 * np.cos(2*np.pi*n/M) 78 79 # respuesta del filtro FIR (version acotada de la sinc) 80 h = h_ideal * v_hamming 81 82 # se normaliza para tener ganancia unitaria para frecuancias <= fc 83 h = h / np.sum(h) 84 return h, fs 85 86 def filtro_fir_analisis(h, fs): 87 fig, ax = dtime_plot(M, h, "respuesta_al_impulso_filtro_fir", 88 f'Respuesta al impulso filtro FIR grado {M}') 89 90 # respuesta en frecuencia del filtro 91 w, H = freqz(h, worN=2048, fs=fs) 92 fase = np.unwrap(np.angle(H)) * 180 / np.pi 93 94 fig, ax1, ax2 = freq_response_plot(w, H, fase, show=False, fc=5e3) 95 save_plot(fig, "respuesta_en_frecuencia_pasa-bajos_fir") 96 97 # polos y ceros 98 filtro_fir_polos_y_ceros(h) 99 100 def analisis_freq_ventanas(): 101 N = 1024 102 103 v_rectangular = np.ones(N) 104 v_hamming = np.hamming(N) 105 106 # freq_plot(44100, v_hamming, "v_hamming_freq", f_max=2000, N=N*8) 107 # freq_plot(44100, v_rectangular, "v_rectangular_freq", f_max=2000, N=N*8) 108 109 v_rect_fft, _ = freq_compute_fft(44100, v_rectangular, N=N*8) 110 v_hamm_fft, _ = freq_compute_fft(44100, v_hamming, N=N*8) 111 112 # se centra el lobulo principal (frecuencia 0 en el centro del arreglo) 113 v_rect_fft = fftshift(v_rect_fft) 114 v_hamm_fft = fftshift(v_hamm_fft) 115 116 v_rect_potencia = np.abs(v_rect_fft)**2 117 v_hamm_potencia = np.abs(v_hamm_fft)**2 118 119 # se agrega 1e-12 para evitar dividir por cero 120 v_rect_potencia_db = 10 * np.log10(v_rect_potencia + 1e-12) 121 v_hamm_potencia_db = 10 * np.log10(v_hamm_potencia + 1e-12) 122 123 # Normalizar para que el pico del lóbulo principal sea 0 dB 124 v_rect_potencia_db = v_rect_potencia_db - np.max(v_rect_potencia_db) 125 v_hamm_potencia_db = v_hamm_potencia_db - np.max(v_hamm_potencia_db) 126 127 # Gráfico de la Ventana Rectangular 128 f = np.linspace(-0.5, 0.5, N*8) 129 130 fig, ax = freq_graph_data_norm(f, v_rect_potencia_db, 131 x_min=-0.04, x_max=0.04, y_min=-200, y_max=10, 132 show=False) 133 save_plot(fig, "potencia_db_espectro_ventana_rectangular") 134 135 fig, ax = freq_graph_data_norm(f, v_hamm_potencia_db, 136 x_min=-0.04, x_max=0.04, y_min=-200, y_max=10, 137 show=False) 138 save_plot(fig, "potencia_db_espectro_ventana_hamming") 139 140 141 data_arr = [v_rect_potencia_db, v_hamm_potencia_db] 142 leg_arr = ["Potencia espectro ventana rectangular", 143 "Potencia espectro ventana Hamming"] 144 145 fig, ax = freq_graph_multiple_data_norm(f, data_arr, leg_arr, 146 x_min=-0.04, x_max=0.04, y_min=-100, y_max=40, 147 show=False) 148 149 save_plot(fig, "potencia_db_espectro_ventana_comparacion_hamming_rect") 150 151 def comparacion_de_espectrogramas_filtrado_vs_original(h): 152 for i, cancion in enumerate(canciones_dataset): 153 spectrogram_plot(cancion.fs, cancion.data, 154 f'{cancion.name}_espectrograma_original_44100Hz', 155 N=1024, ylim=[0, 20000]) 156 157 filter_output = np.convolve(cancion.data, h, mode='same') 158 spectrogram_plot(cancion.fs, filter_output, 159 f'{cancion.name}_espectrograma_filtrado_{cutoff}Hz', 160 N=1024, ylim=[0, 20000]) 161 162 spectrogram_plot(cancion.fs, filter_output, 163 f'{cancion.name}_espectrograma_filtrado_{cutoff}Hz_ylim_3000Hz_inferno', 164 cmap='inferno', ylim=[0, 3000], N=4096, win='hamming') 165 166 def generar_base_de_datos(): 167 h, fs = filtro_fir_deducido() 168 169 DB = { 170 'hash_nbits': 20, # Cantidad de bits de los hashes 171 'n_entries': 20, # Cantidad de columnas de la tabla 172 'ID_nbits': 12, # Cantidad de bits para ID numérico de la canción 173 'tabla': np.zeros((2 ** 20, 20), dtype=np.uint32) 174 } 175 176 for i, cancion in enumerate(canciones_dataset): 177 data_filtered = np.convolve(cancion.data, h, mode='same') 178 179 # Sxx es la matriz de energía donde el eje `y` es frecuencia (m) y 180 # el eje `x` es tiempo (n). 181 # 182 # `nperseg` tamaño de ventana (número de muestras por segmento) 183 # `noverlap` cantidad de solapamiento entre ventanas 184 f, t, E = spectrogram(data_filtered, fs=cancion.fs, 185 nperseg=1024, noverlap=24, window='hamm') 186 187 # E.shape retorna (num_bandas, num_frames) -> (m, n) 188 # num_bandas, num_frames = E.shape 189 # E = obtener_caracteristicas(E) 190 191 H = generar_huella(E, fs) 192 193 # if i == 0: 194 # graficar_huella(H, f'{cancion.name}_huella_acustica_completa') 195 196 # graficar_huella(H, f'{cancion.name}_huella_acustica', xlim=[0, 100]) 197 DB = guardar_huella(i, H, DB) 198 199 with open(f'{database_name}', 'wb') as f: 200 pickle.dump(DB, f) 201 202 print(f'[LOG] Base de datos guardada en `./{database_name}`') 203 return DB 204 205 def generar_base_de_datos_si_no_existe(): 206 if os.path.exists(database_name): 207 print(f'[LOG] Base de datos ya existe en `./{database_name}`, pasando...') 208 try: 209 with open(database_name, 'rb') as db_file: 210 DB = pickle.load(db_file) 211 212 print(f"[LOG] Se cargaron los archivos de `{database_name}`") 213 214 except FileNotFoundError: 215 print(f"[ERROR] No se encontro el archivo `{database_name}`") 216 except EOFError: 217 print(f"[ERROR] El archivo `{database_name}` puede estar incompleto o corrupto") 218 except Exception as e: 219 print(f"[ERROR] Ocurrio un error inesperado: {e}") 220 else: 221 cargar_canciones_dataset() 222 DB = generar_base_de_datos() 223 224 return DB 225 226 # calcula la matriz de características dado un espectrograma `E` 227 def obtener_caracteristicas(E): 228 bandas_totales = 21 # Número de bandas de frecuencia (M) 229 f_min = 300 # Frecuencia inferior de la primera banda (300 Hz) 230 f_max = 2000 # Frecuencia superior de la última banda (2 kHz) 231 232 num_bandas, num_frames = E.shape 233 234 # La matriz F tendrá una fila menos (m+1 en E) y una columna menos (n-1 en E) 235 # num_bandas_F = num_bandas - 1 (para evitar el desbordamiento de m+1) 236 # num_frames_F = num_frames - 1 (para evitar el desbordamiento de n-1) 237 F = np.zeros((num_bandas - 1, num_frames - 1), dtype=int) 238 239 # Se itera sobre todas las bandas y frames que permiten la comparación 240 for n in range(1, num_frames): # n: tiempo (frame actual) 241 for m in range(num_bandas - 1): # m: banda 242 # Diferencia de energía entre banda m y m+1 en el tiempo 'n' 243 diff_actual = E[m, n] - E[m + 1, n] 244 245 # Frame anterior 'n-1' 246 # Diferencia de energía entre banda m y m+1 en el tiempo 'n-1' 247 diff_anterior = E[m, n - 1] - E[m + 1, n - 1] 248 249 # Lógica de la Función Signo F(m, n) 250 if diff_actual > diff_anterior: 251 F[m, n - 1] = 1 # Se mapea n al índice correcto de la matriz F 252 else: 253 F[m, n - 1] = 0 254 255 return F 256 257 def dividir_en_bandas_logaritmicas(Sxx, fs, n_bandas, fmin, fmax): 258 # crear la matriz de filtros logarítmicos (Mel) 259 # `n_fft` para librosa, debe ser el doble del número de filas de frecuencia 260 # menos 2 261 n_fft_librosa = (Sxx.shape[0] - 1) * 2 262 263 mel_basis = librosa.filters.mel( 264 sr=fs, 265 n_fft=n_fft_librosa, 266 n_mels=n_bandas, 267 fmin=fmin, 268 fmax=fmax) 269 270 # multiplicar la matriz de filtros por el espectrograma de potencia 271 # E_banda tendrá dimensiones (21 bandas, N_FRAMES) -> E(m, n) 272 E_banda = np.dot(mel_basis, Sxx) 273 274 # convertir la energía de las bandas a dB para aplicar la función F(m, n) 275 # F(m,n) debe operar sobre la energía en dB para ser robusta. 276 # se usa la referencia absoluta (ref=1.0) para que 0dB sea el nivel absoluto. 277 E_db = librosa.power_to_db(E_banda, ref=1.0) 278 return E_db 279 280 281 def generar_huella(E, fs): 282 E_db = dividir_en_bandas_logaritmicas(E, fs, 21, 300, 2000) 283 284 # E_db tiene dimensiones (m, n) -> (n_bandas, n_frames) 285 num_bandas, num_frames = E_db.shape 286 287 # La huella H(m, n) tendrá dimensiones (n_bandas - 1, n_frames - 1) 288 H = np.zeros((num_bandas - 1, num_frames - 1), dtype=int) 289 290 # n: tiempo (frame), m: banda 291 for n in range(1, num_frames): # n: empieza en 1 (segundo frame) 292 for m in range(num_bandas - 1): # m: va de la primera hasta la penúltima banda 293 diff_actual = E_db[m, n] - E_db[m + 1, n] 294 diff_anterior = E_db[m, n - 1] - E_db[m + 1, n - 1] 295 296 if diff_actual > diff_anterior: 297 H[m, n - 1] = 1 # Se mapea n-1 al índice de la matriz H 298 else: 299 H[m, n - 1] = 0 300 301 return H 302 303 def graficar_huella(H, save_name="", xlim=[], ylim=[], save_dir=""): 304 fig, axis = plt.subplots(figsize=(8, 4)) 305 plt.pcolormesh(H, shading='auto', cmap='binary') 306 307 # plt.title('Huella Digital Acústica H(m, n)') 308 plt.ylabel('Diferencia de Banda Logarítmica') 309 plt.xlabel('Frame de Tiempo') 310 # plt.colorbar(ticks=[0, 1], label='Valor de H(m, n)') 311 plt.gca().invert_yaxis() # invertir el eje y para que la frecuencia baja esté abajo 312 plt.tight_layout() 313 314 if len(xlim) != 0: 315 plt.xlim(xlim) 316 317 if len(ylim) != 0: 318 plt.ylim(ylim) 319 320 if save_name == "": 321 plt.show() 322 else: 323 save_plot(fig, save_name, save_dir=save_dir) 324 325 def guardar_huella(ID, huella, DB): 326 """ 327 Guarda la huella digital acústica 'huella' en la tabla hash de la estructura 328 DB, identificando la canción con el identificador numérico. 329 330 Parámetros 331 ---------- 332 ID : int 333 Identificador numérico del tema que corresponde a esta huella. 334 huella : np.ndarray 335 Huella acústica en forma de matriz binaria (unos y ceros). 336 DB : dict 337 Estructura con los siguientes campos: 338 - 'hash_nbits': número de bits para el hash 339 - 'n_entries': número de columnas de la tabla 340 - 'ID_nbits': número de bits para guardar ID numérico de la canción 341 - 'tabla': tabla hash de tamaño (2**hash_nbits, n_entries) 342 343 Retorna 344 ------- 345 DB : dict 346 Estructura con tabla hash actualizada. 347 """ 348 349 # Verificamos que huella sea una matriz binaria 350 if not np.all((huella == 0) | (huella == 1)): 351 raise ValueError("La matriz HUELLA contiene elementos no binarios") 352 353 # Verificamos que huella tenga hash_nbits filas 354 if huella.shape[0] != DB['hash_nbits']: 355 raise ValueError( 356 f"La matriz HUELLA tiene {huella.shape[0]} filas en lugar de {DB['hash_nbits']}" 357 ) 358 359 # Generamos el elemento val a guardar, concatenando los bits del ID con los 360 # bits del tiempo de cada frame 361 frames_nbits = 32 - DB['ID_nbits'] 362 frames = np.mod(np.arange(1, huella.shape[1] + 1), 2 ** frames_nbits) 363 val = np.uint32(ID + (2 ** DB['ID_nbits']) * frames) 364 365 # Obtenemos las filas a guardar en la tabla, pasando las características de binario a decimal 366 # (bi2de en MATLAB toma bits por fila; aquí usamos huella.T para el mismo comportamiento) 367 hash_vals = np.dot(huella.T, 1 << np.arange(huella.shape[0])) + 1 # +1 por índices base 1 368 369 # Primero grabamos en las filas en que queda espacio 370 tabla = DB['tabla'] 371 fila_ok = tabla[hash_vals - 1, -1] == 0 # Filas que tienen espacio al final 372 hash_ok = hash_vals[fila_ok] 373 374 # Cantidad de elementos no nulos por fila 375 count_nonzero = np.sum(tabla[hash_ok - 1, :] != 0, axis=1) 376 indices = (hash_ok - 1, count_nonzero) # (fila, columna disponible) 377 tabla[indices] = val[fila_ok] 378 379 # Finalmente grabamos en las filas que están llenas, pisando algún valor anterior al azar 380 hash_col = hash_vals[~fila_ok] 381 if len(hash_col) > 0: 382 idx_rand = np.ceil(np.random.rand(len(hash_col)) * DB['n_entries']).astype(int) - 1 383 tabla[hash_col - 1, idx_rand] = val[~fila_ok] 384 385 DB['tabla'] = tabla 386 return DB 387 388 def query_DB(DB, huella): 389 """ 390 Hace un query a la base de datos para obtener el ID de las canciones 391 coincidentes con la huella. Devuelve los 5 primeros resultados que mejor 392 coinciden, ordenados en orden de prioridad descendente. 393 394 Parámetros 395 ---------- 396 DB : dict 397 Estructura con los siguientes campos: 398 - 'hash_nbits': número de bits para el hash 399 - 'n_entries': número de columnas de la tabla 400 - 'ID_nbits': número de bits para guardar ID numérico de la canción 401 - 'tabla': tabla hash (np.ndarray de tamaño [2**hash_nbits, n_entries]) 402 huella : np.ndarray 403 Huella acústica binaria (matriz de 0s y 1s) 404 405 Retorna 406 ------- 407 ID : np.ndarray 408 Identificadores numéricos de los primeros 5 resultados que matchean 409 MATCHES : np.ndarray 410 Número de matches que tuvo cada ID 411 """ 412 413 # Verificamos que HUELLA sea una matriz binaria 414 if not np.all((huella == 0) | (huella == 1)): 415 raise ValueError("La matriz HUELLA contiene elementos no binarios") 416 417 # Verificamos que HUELLA tenga hash_nbits filas 418 if huella.shape[0] != DB['hash_nbits']: 419 raise ValueError( 420 f"La matriz HUELLA tiene {huella.shape[0]} filas en lugar de {DB['hash_nbits']}" 421 ) 422 423 # Obtenemos las filas a buscar en la tabla (binario → decimal) 424 hash_vals = np.dot(huella.T, 1 << np.arange(huella.shape[0])) + 1 # +1 por índice MATLAB base 1 425 426 # Extraemos los elementos de la tabla que corresponden a los hashes dados 427 vals = DB['tabla'][hash_vals - 1, :] # restamos 1 para índice base 0 428 vals = vals[vals != 0] 429 430 # Si todos los elementos eran nulos, devolvemos ceros 431 if vals.size == 0: 432 return np.array([0], dtype=np.uint32), np.array([0], dtype=np.uint32) 433 434 # Extraemos de cada elemento el ID numérico y su frame 435 ID1 = np.mod(vals, 2 ** DB['ID_nbits']) 436 frames = np.floor_divide(vals, 2 ** DB['ID_nbits']) 437 438 # Filtrado temporal: para cada ID, contamos coincidencias dentro del frame_span 439 frame_span = huella.shape[1] 440 unique_IDs = np.unique(ID1) 441 MATCHES = np.zeros(len(unique_IDs), dtype=int) 442 443 for k, uid in enumerate(unique_IDs): 444 frame_aux = frames[ID1 == uid] 445 matches = 0 446 for k2 in range(len(frame_aux)): 447 # Número de matches en intervalo frame_span 448 match_aux = np.count_nonzero( 449 (frame_aux >= frame_aux[k2]) & 450 (frame_aux <= frame_aux[k2] + frame_span) 451 ) 452 if match_aux > matches: 453 matches = match_aux 454 MATCHES[k] = matches 455 456 # Ordenar por número de coincidencias en orden descendente 457 idx = np.argsort(-MATCHES) # orden descendente 458 MATCHES = MATCHES[idx] 459 unique_IDs = unique_IDs[idx] 460 461 # Mantener solo los 5 primeros resultados 462 Nm = min(5, len(unique_IDs)) 463 ID = unique_IDs[:Nm] 464 MATCHES = MATCHES[:Nm] 465 466 return ID.astype(np.uint32), MATCHES.astype(np.uint32) 467 468 def evaluar_cancion_raw(DB, cancion_data, cancion_fs, cancion_name): 469 h, fs = filtro_fir_deducido() 470 471 nro_pruebas = 0 472 data = np.convolve(cancion_data, h, mode='same') 473 print(f'[LOG] Evaluando cancion `{cancion_name}`') 474 duracion_cancion = len(cancion_data) / cancion_fs 475 print(f'[LOG] Duracion cancion: {duracion_cancion:05.02f}s') 476 477 for r in range(0, 10): 478 for T in [5, 10, 20]: # segmentso de 5, 10 y 20 segundos 479 t_inicial = random.uniform(0.0, 1.0) * (duracion_cancion - 1.5*T) 480 t_inicial = 0 if t_inicial < 0 else t_inicial 481 print(f'[LOG] Intervalo {t_inicial:05.02f}:{t_inicial+T:05.02f}s', end='') 482 f, t, E = generate_spectrogram(cancion_fs, data, t=t_inicial, dt=T) 483 H = generar_huella(E, cancion_fs) 484 485 nro_pruebas += 1 486 id, matches = query_DB(DB, H) 487 print(f' mejor coincidencia: `{canciones_dataset[id[0]].name}`') 488 489 print("") 490 491 def evaluar_cancion(DB, cancion): 492 h, fs = filtro_fir_deducido() 493 494 nro_pruebas = 0 495 data = np.convolve(cancion.data, h, mode='same') 496 print(f'[LOG] Evaluando cancion `{cancion.path}`') 497 duracion_cancion = len(data) / cancion.fs 498 print(f'[LOG] Duracion cancion: {duracion_cancion:05.02f}s') 499 500 for r in range(0, 10): 501 for T in [5, 10, 20]: # segmentso de 5, 10 y 20 segundos 502 t_inicial = random.uniform(0.0, 1.0) * (duracion_cancion - 1.5*T) 503 t_inicial = 0 if t_inicial < 0 else t_inicial 504 print(f'[LOG] Intervalo {t_inicial:05.02f}:{t_inicial+T:05.02f}s', end='') 505 f, t, E = generate_spectrogram(cancion.fs, data, t=t_inicial, dt=T) 506 H = generar_huella(E, cancion.fs) 507 508 nro_pruebas += 1 509 id, matches = query_DB(DB, H) 510 print(f' mejor coincidencia: `{canciones_dataset[id[0]].name}`') 511 512 print("") 513 514 def agregar_ruido_a_cancion(data, sigma=0.1): 515 ruido = np.random.normal(loc=0, scale=sigma, size=len(data)) 516 cancion_con_ruido = data + ruido 517 cancion_con_ruido = np.clip(cancion_con_ruido, -1.0, 1.0) 518 return cancion_con_ruido 519 520 def calcular_potencia_ruido(potencia_orig, snr_db): 521 snr_lineal = 10**(snr_db / 10) 522 potencia_ruido = potencia_orig / snr_lineal 523 return potencia_ruido 524 525 def evaluar_aciertos_agregar_ruido(DB): 526 h, fs = filtro_fir_deducido() 527 528 incorrectos = 0 529 nro_pruebas = 0 530 531 for i, cancion in enumerate(canciones_dataset): 532 lista_a_probar = [] 533 lista_a_probar.append(cancion) 534 535 for snr_db in [0, 10, 20]: 536 potencia_cancion = np.mean(cancion.data**2) 537 potencia_ruido = calcular_potencia_ruido(potencia_cancion, snr_db) 538 sigma_ruido = np.sqrt(potencia_ruido) 539 data = agregar_ruido_a_cancion(cancion.data, sigma_ruido) 540 name = cancion.name + f"_ruido_SNR_{snr_db}dB" 541 lista_a_probar.append(SimpleNamespace( 542 name=name, 543 data=data, 544 fs=cancion.fs)) 545 546 for T in [5, 10, 20]: # segmentso de 5, 10 y 20 segundos 547 duracion_cancion = len(cancion.data) / cancion.fs 548 print(f'[LOG] Duracion cancion: {duracion_cancion:05.02f}s') 549 550 t_inicial = random.uniform(0.0, 1.0) * (duracion_cancion - 1.5*T) 551 t_inicial = 0 if t_inicial < 0 else t_inicial 552 print(f'[LOG] Intervalo {T:05.02f}s {t_inicial:05.02f}:{t_inicial+T:05.02f}s') 553 554 for elemento in lista_a_probar: 555 data = np.convolve(elemento.data, h, mode='same') 556 print(f'[LOG] Evaluando cancion `{elemento.name}`', end='') 557 558 f, t, E = generate_spectrogram(elemento.fs, elemento.data, t=t_inicial, dt=T) 559 H = generar_huella(E, elemento.fs) 560 561 nro_pruebas += 1 562 id, matches = query_DB(DB, H) 563 print(f' mejor coincidencia: `{canciones_dataset[id[0]].name}`') 564 if id[0] != i: 565 incorrectos += 1 566 567 print("") 568 569 570 print(f'[LOG] cantidad de pruebas incorrectas: {incorrectos} de {nro_pruebas}') 571 print(f'[LOG] error porcentual: {(incorrectos/nro_pruebas)*100:0.02f}%') 572 573 574 def evaluar_aciertos(DB): 575 h, fs = filtro_fir_deducido() 576 577 incorrectos = 0 578 nro_pruebas = 0 579 for i, cancion in enumerate(canciones_dataset): 580 data = np.convolve(cancion.data, h, mode='same') 581 print(f'[LOG] Evaluando cancion `{cancion.path}`') 582 duracion_cancion = len(data) / cancion.fs 583 print(f'[LOG] Duracion cancion: {duracion_cancion:05.02f}s') 584 for r in range(0, 5): 585 for T in [5, 10, 20]: # segmentso de 5, 10 y 20 segundos 586 t_inicial = random.uniform(0.0, 1.0) * (duracion_cancion - 1.5*T) 587 t_inicial = 0 if t_inicial < 0 else t_inicial 588 print(f'[LOG] Intervalo {t_inicial:05.02f}:{t_inicial+T:05.02f}s', end='') 589 f, t, E = generate_spectrogram(cancion.fs, data, t=t_inicial, dt=T) 590 H = generar_huella(E, cancion.fs) 591 592 nro_pruebas += 1 593 id, matches = query_DB(DB, H) 594 595 print(f' mejor coincidencia: `{canciones_dataset[id[0]].name}`') 596 if id[0] != i: 597 incorrectos += 1 598 599 print("") 600 601 print(f'[LOG] cantidad de pruebas incorrectas: {incorrectos} de {nro_pruebas}') 602 print(f'[LOG] error porcentual: {(incorrectos/nro_pruebas)*100:0.02f}%') 603
