Evaluación de la pérdida de calidad del audio usando el codec Opus en tareas de auscultación remota¶
Juan M. Fonseca-Solís · agosto 2020 (última vez actualizado en julio 2026)
Introducción¶
Con el fin de suplir la demanda de hospitales y clínicas que buscan auscultar a los pacientes remotamente, la industria médica se ha dedicado al desarrollo de estetoscopios digitales. El desarrollo de estos aparatos involucra no solo la construcción del hardware, sino también el desarrollo de la infraestructura necesaria para transferir los datos hacia la consulta médica tradicional o automatizada (mediante inteligencia artificial). Parte de esta infraestructura involucra el uso del códec Opus, que también se emplea en plataformas de videollamada como Zoom, MS Teams y Skype, por su rendimiento frente a otros algoritmos de compresión de audio "con pérdida". En este notebook determinamos una tasa de bits que puede emplearse para evitar distorsiones producidas por aliasing (pliegamiento de las frecuencias altas hacia abajo). Realizamos experimentos con barridos de frecuencia y de voz.
Figura 1. Tomada de https://pixnio.com/es/ciencia/ciencia-medica/estetoscopio-telefono-movil-diagnostico-pulso-cardiologia-medicina-atencion-medica.
Transmisión del audio¶
Transmisión sin pérdida¶
Para auscultar a un paciente remotamente, idealmente se desearía transmitir audio en formato crudo, es decir, sin ninguna compresión. Esto se podría lograr transmitiendo audio en formato WAV, pero esto requiere utilizar 1 canal, 44100 muestras por segundo y 32 bits de cuantización, es decir, 1,35 Mbps (megabits por segundo). Para una clínica rural con una conexión de $1,4$ Mbps (el máximo permitido para 3.5G) esto significaría consumir casi el 96% del ancho de banda y quedarse sin video durante la llamada [17].1 Para ilustrar lo molesto que podría ser esto, proponemos el siguiente diálogo imaginario entre una clínica y un médico:
Médico (o el sistema): —Muy bien, vamos a realizar la auscultación. Clínica, apague su cámara (yo voy a apagar la mía). Muy bien, ahora espere un momento... voy a activar el modo "estetoscopio". Listo, proceda a colocar el estetoscopio digital en el espacio intercostal izquierdo de la línea medioclavicular e indíqueme para grabar.—
Clínica: —Listo doctor, el aparato está ubicado en la zona.—
Médico: —Ok, no puedo ver, pero confío en que ubicó el aparato en la zona que le indiqué.—
Clínica: —Listo doctor, puede comenzar a grabar.—
Médico (1 minuto después): —Ok, ahora repitamos en esta otra zona...—
Terrible, ¿no es así? Y confiemos en que la clínica no va a recibir un correo electrónico que pueda cortar la transmisión mientras se está auscultando al paciente.
Figura 5. Posiblemente cómo se vería nuestro médico auscultando remotamente y sin video a nuestro paciente. Tomada de https://output.com/blog/9-sound-design-tips-to-hack-your-listeners-ears.
Transmisión con pérdida¶
A diferencia de WAV, Opus transmite audio realizando una compresión "con pérdida", es decir, reduciendo el tamaño de las grabaciones eliminando información inaudible para el oído humano. Esta compresión funciona aprovechando la forma en la que está diseñada la cóclea, que, como muestra la figura 3, es un órgano en forma cónica encargado de percibir las frecuencias y transmitirlas como impulsos eléctricos al cerebro. La cóclea se encuentra "arrollada" dentro del oído interno y recibe estímulos del oído externo e intermedio mediante la ventana oval. La ventana oval se conecta a su vez con los huesecillos que reciben estímulos del tímpano y que flotan en un medio acuoso.
Figura 3. Tomada de https://en.wikipedia.org/wiki/Auditory_system#/media/File:Anatomy_of_the_Human_Ear.svg.
La cóclea, como explica la teoría psicoacústica, sufre dos efectos llamados enmascaramiento frecuencial y enmascaramiento temporal. El primero ocurre cuando dos tonos están lo suficientemente cercanos y el de mayor energía logra cubrir con su envolvente espectral al de menor energía, enmascarándolo. La figura 4 ilustra que este efecto solo se logra cuando ambos tonos comparten la misma banda crítica, que está dividida en 32 regiones en las que está dividida la cóclea, y que "resuenan" con mayor facilidad a una frecuencia característica. Dos ejemplos de bandas críticas son: $[500, 7000]$ Hz con frecuencia central de $500$ Hz y $[8900,10400]$ Hz con frecuencia central de $9600$ Hz [10].
Figura 4. Ilustración del audio interno, medio y externo (tomada de https://output.com/blog/9-sound-design-tips-to-hack-your-listeners-ears).
La longitud y la localización de las bandas críticas han sido aproximadas en unas escalas numéricas llamadas Bark y ERB. Estas escalas se basan en el hecho de que el oído humano realiza un mejor trabajo diferenciando tonos graves de tonos agudos (como siguiendo una distribución logarítmica). La escala Equivalent Rectangular Bandwidth (ERB) es una versión mejorada de la escala Bark y su ecuación es la siguiente[9]:
$$\text{ERB}(f)=21.4 \log_{10}{(0.00437f+1)}.$$
import numpy as np
from numpy import ndarray
import matplotlib.pylab as plt
def erb(F:float) -> ndarray:
return 21.4 * np.log10(0.00437*F+1)
F = np.linspace(0,20000,1000) # 0 a 20k Hz es el rango de audición humana
E = erb(F)
plt.plot(F/1e3,E)
plt.ylabel('Frequency (kHz)')
plt.xlabel('ERB (kHz)')
plt.show()
E = np.linspace(0,erb(20000),32) # 32 puntos equidistantes
F = (np.power(10,E/21.4)-1)/0.00437 # invertimos el logaritmo de la escala ERB
print(F.astype(int))
plt.stem(F,np.ones(len(F)))
plt.xlabel('Frecuencia central de cada banda crítica (Hz)')
plt.show()
[ 0 35 76 124 179 242 315 400 498 611 742 893 1068 1270 1503 1772 2083 2443 2859 3339 3894 4536 5277 6134 7123 8267 9589 11116 12881 14920 17276 20000]
El códec Opus¶
Tradicionalmente, la compresión de audio se ha realizado usando MP3, Advance Audio Coding (AAC) y Vorbis, pero como muestra la figura 2, se ha preferido OPUS por su rendimiento superior en todo el rango de transmisión de bits. En 2013, Valin et al. publicaron el algoritmo en la conferencia 135 de la Audio Engineering Society y explicaron que Opus se basa en dos algoritmos desarrollados previamente: SILK, para la compresión de voz (originalmente desarrollado por Skype), y Constrained Energy Lapped Transform (CELT), para la compresión de música (así como un modo híbrido) [3, 4, 13].2
Figura 2. Rendimiento del algoritmo Opus (tomado del sitio de Opus https://opus-codec.org/static/comparison/quality.svg).
Compresión a diferentes tasas de bits¶
Opus puede ajustarse para funcionar a diferentes tasas de bits, por ejemplo, con 8 kbps puede transmitir una videollamada entre dos personas y con 128 kbps puede enviar audio en calidad estéreo de banda completa [12]. A continuación, realizamos una prueba de compresión precisamente en esas dos tasas de bits. Transmitimos audio de un barrido de frecuencias en el rango de $[10, 20000]$ Hz, pues abarca el espectro de sonidos del corazón ($[20, 500]$ Hz), los pulmones ($[10, 2000]$ Hz) y la voz ($[20, 20000]$ Hz) [14, 15].3
Construimos el barrido usando un tono puro senoidal de la forma $x[t] = \sin(2\pi F(t) t)$, con la tasa de cambio de la frecuencia fundamental, $F(t)$, como sigue [6]:4 $$ F(t) = \Big(\frac{F_1-F_0}{T}\Big)t + F_0. $$
Nota: a partir de este momento se recomienda bajar el volumen del audio si se están usando audífonos.
from scipy.io import wavfile
from IPython.display import Audio
import numpy as np
import matplotlib.pyplot as plt
import os
rango = [10, 20e3] # el rango promedio de audicion humano en Hz
Fs = 44.1 * 1e3 # la tasa de muestreo de los equipos comerciales
T = 1.0 # segundos (t1-t0)
N = int(T*Fs)
n = np.arange(0,N)
F0 = (rango[1]-rango[0])*n/N + rango[0]
x = np.sin(2*np.pi*F0/(2*Fs)*n) # f=F0/Fs: frecuencia discreta
if not os.path.exists('tmp'):
ok.makedir('tmp')
wavfile.write('tmp/barrido.wav',int(Fs),x.astype(np.float32))
Audio(x, rate=Fs)
Para evaluar la calidad de las compresiones del audio calculamos la magnitud del espectro de frecuencias y el espectrograma del audio sin distorsión, para usarlo como referencia:
def plotSpecgram(x:ndarray, Fs:float, f_max:float=None):
# graficar el espectrograma
fig, ax = plt.subplots(nrows=1)
ax.specgram(x, NFFT=1024, Fs=Fs, noverlap=900)
plt.xlabel('Tiempo (s)')
plt.ylabel('Frecuencia (Hz)')
if f_max != None:
plt.ylim([0,f_max])
plt.show()
def plotFFT(x:np.ndarray, Fs:float, x_lim:list=None):
plt.figure()
N2=int(len(x)/2)
F = np.linspace(0,Fs/2,N2)/1e3
X = np.sqrt(np.abs(np.fft.fft(x)[0:N2]))
plt.plot(F, X)
X[0] = 0 # remover el valor 0 que contiene el promedio de la energía de la magnitud espectral
plt.xlabel('Frecuencia (kHz)')
plt.ylabel(r'$\sqrt{|S(f)|}$')
if x_lim != None:
plt.xlim(x_lim)
plt.show()
plotFFT(x,Fs)
plotSpecgram(x,Fs)
Vemos que la magnitud del espectro (figura de arriba) muestra una recta horizontal casi en todo el rango, indicando que todas las frecuencias están presentes; la segunda figura (espectrograma) muestra la tasa de incremento de la frecuencia en el tiempo como una recta ascendente.
Compresión a 8 kbps¶
Ahora, procedemos a codificar y decodificar el barrido de frecuencias usando Opus a una tasa de bits de 8 kbps para comprobar la distorsión de la señal.
def readAudio(inputFile:str):
Fs, x = wavfile.read(inputFile)
y = np.array(x)/max(x)
return Fs, y
# sudo apt-get install opus-tools ffmpeg
!ffmpeg -loglevel error -y -i tmp/barrido.wav -qscale 0 tmp/wavRaw.wav
!opusenc --quiet --bitrate 8 tmp/wavRaw.wav tmp/opusEnc_8kbps.opus
!opusdec --quiet tmp/opusEnc_8kbps.opus tmp/opusDec_8kbps.wav
Fs, x = readAudio('tmp/opusDec_8kbps.wav')
plotFFT(x,Fs)
plotSpecgram(x, Fs)
Audio(x, rate=Fs)
Skipping chunk of type "LIST", length 26
La magnitud del espectro muestra que la energía se conserva en el rango $[10, 4500]$ Hz, pero el resto fue atenuado. El espectrograma muestra lo mismo, pero se observa además que el rango $[4500, 20000]$ Hz ha sufrido una distorsión llamada aliasing, donde se pliegan las frecuencias altas hacia abajo. Para mitigar el aliasing se podría emplear un filtro pasa-bajas con frecuencia de corte de $4500$ Hz antes de realizar la compresión. Sin embargo, esto permitiría transmitir solamente audio del corazón y los pulmones, dejando por fuera la voz humana.
Compresión a 128 kbps¶
Probemos ahora con 128 kbps (fullband stereo), a ver si la calidad mejora.
!ffmpeg -loglevel error -y -i tmp/barrido.wav -qscale 0 tmp/wavRaw.wav # opus-tools
!opusenc --quiet --bitrate 128 tmp/wavRaw.wav tmp/opusEnc_128kbps.opus
!opusdec --quiet tmp/opusEnc_128kbps.opus tmp/opusDec_128kbps.wav
Fs, x = readAudio('tmp/opusDec_128kbps.wav')
plotFFT(x,Fs)
plotSpecgram(x, Fs)
Audio(x, rate=Fs)
Skipping chunk of type "LIST", length 26
En efecto, la calidad del espectro ha mejorado. El espectrograma sigue mostrando que hay cierto efecto aliasing, pero con bastante menor energía; y además, los artefactos introducidos no afectan el rango $[10, 20000]$ Hz (hay cierta distorsión mínima luego de los $10000$ Hz, pero es acotada). La magnitud espectral muestra una recta horizontal similar a la que vimos con la señal cruda. Por lo que podemos decir que con full-band se obtiene una menor distorsión visible y es más apto que 8 kbps para realizar auscultación remota.5
Analizando un audio real¶
Analizamos también una grabación real tomada de la Saarbrücken Voice Database usando Opus con la misma tasa de bits de 128 kbps [16]. La grabación está en formato crudo y corresponde a un paciente masculino con reflujo gastroesofágico (GERD, por sus siglas en inglés) pronunciando "buenos días" en alemán. Escuchemos la grabación original.
Nota: a partir de este momento se recomienda usar audífonos.
Fs, x = readAudio('saarbruecken_voice_database_1117-phrase.wav')
plotFFT(x,Fs)
plotSpecgram(x, Fs)
Audio(x, rate=Fs)
Observamos que las armónicas son claramente reconocibles en el rango $[0, 10000]$ Hz. El resto de espectro también contiene información.
# Opus
!ffmpeg -loglevel error -y -i saarbruecken_voice_database_1117-phrase.wav -qscale 0 tmp/wavRaw.wav # same quality
!opusenc --quiet --bitrate 128 tmp/wavRaw.wav tmp/opusEnc.opus
!opusdec --quiet tmp/opusEnc.opus tmp/opusDec.wav
Fs, x = readAudio('tmp/opusDec.wav')
plotFFT(x,Fs)
plotSpecgram(x, Fs)
Audio(x, rate=Fs)
Skipping chunk of type "LIST", length 26
El espectrograma muestra que con la tasa de bits elegida los formantes siguen siendo claros y no hay distorsión apreciable hasta los $20000$ Hz.
Conclusiones¶
En este trabajo explicamos el problema de transmitir audio crudo para tareas de auscultación remota. Estudiamos el códec Opus como alternativa al audio crudo y explicamos por qué es un algoritmo de compresión de audio "con pérdida" y cómo realiza esa compresión a nivel psicoacústico. Realizamos dos experimentos a 8 y 128 kbps para transmitir un barrido de frecuencias y confirmamos que 128 kbps podría ser más conveniente para auscultar sonidos del corazón, los pulmones y la voz. Como trabajo futuro, se propone crear un método para cuantificar la distorsión apreciada con una prueba objetiva usando POLQA o PESQ.
Apéndice¶
Podemos verificar que para dos tonos con frecuencia $595$ Hz y $605$ Hz (ubicados en la misma banda crítica), solo el de $605$ Hz se escucha si la energía del de $595$ Hz está por debajo de cierto umbral. Podemos construir una señal de dos tonos como sigue:
y = 0.4*np.sin(2*np.pi*595/Fs*n) + np.sin(2*np.pi*605/Fs*n)
plotSpecgram(y,Fs)
plotFFT(y,Fs,x_lim=[500/1e3,700/1e3])
wavfile.write('tmp/enmascaramiento.wav',int(Fs),y.astype(np.float32))
Audio(y, rate=Fs)
Ahora procedemos a realizar la compresión y de-compresión a una tasa de 8 kbps:
!ffmpeg -loglevel error -y -i tmp/enmascaramiento.wav -qscale 0 tmp/wavRaw.wav # opus-tools
!opusenc --quiet --bitrate 8 tmp/wavRaw.wav tmp/opusEnc.opus
!opusdec --quiet tmp/opusEnc.opus tmp/opusDec.wav
Fs, x = readAudio('tmp/opusDec.wav')
plotFFT(x,Fs, x_lim=[500/1e3,700/1e3])
plotSpecgram(x, Fs)
Audio(x, rate=Fs)
Skipping chunk of type "LIST", length 26
El espectrograma muestra que hay información nueva a partir de los $5000$ Hz que no contenía la grabación original. Esto no parece ser producto de un aliasing sino de información agregada por el Opus, y podría eliminarse usando un filtrado pasa bajas de postprocesamiento. Se observa que el tono de $605$ Hz sigue siendo dominante y que el de $595$ Hz ha sido enmascarado completamente. Veamos qué pasa con 128 kbps:
!ffmpeg -loglevel error -y -i tmp/enmascaramiento.wav -qscale 0 tmp/wavRaw.wav # opus-tools
!opusenc --quiet --bitrate 128 tmp/wavRaw.wav tmp/opusEnc.opus
!opusdec --quiet tmp/opusEnc.opus /tmp/opusDec.wav
Fs, x = readAudio('tmp/opusDec.wav')
plotFFT(x,Fs, x_lim=[500/1e3,700/1e3])
plotSpecgram(x, Fs)
Audio(x, rate=Fs)
Skipping chunk of type "LIST", length 26
Aún se observa información que no existía y el enmascaramiento se ha revertido. De nuevo, lo que se puede hacer en este caso es aplicar un filtro pasa-bajas para atenuar la información extra en el rango $[5000, 20000]$ Hz [18].
from scipy import signal
sos = signal.butter(30, 2500, 'lp', fs=Fs, output='sos')
y = signal.sosfilt(sos, x)
plotFFT(y,Fs, x_lim=[500/1e3,700/1e3])
plotSpecgram(y, Fs)
Audio(y, rate=Fs)
Aún queda alguna información extra que podría removerse usando un filtrado mejor, pero un buen algoritmo de reconocimiento de patrones debería ser capaz de tolerar cierto nivel de ruido.
Notas¶
1 El cálculo es $1,35 = 1 \cdot 44100 \cdot 32 / 1024^2$ Mbps.
2 La razón por la cual elegimos estudiar a Opus es porque es el algoritmo predeterminado del framework WebRTC, porque es de código libre, y porque según la literatura científica ha sido empleado desde el 2015 en labores de telemedicina [19]. Igualmente, la teoría explicada aquí se puede aplicar a otros códecs de compresión del audio.
3 De acuerdo con la sugerencia de Arturo Camacho, dado que el contenido frecuencial está por debajo de 1000 Hz y suponiendo que los algoritmos de reconocimiento de patrones puedan trabajar bien en el rango $[0,1000]$ Hz, también se podría enviar, por aparte, el audio del estetoscopio en formato crudo. De acuerdo con el teorema de Nyquist, esto significaría usar una tasa de muestreo de 2000 Hz, lo que en términos de Mbps correspondería a consumir $0,06~\text{Mbps}$ cada segundo ($1~\text{canal} \cdot 2000~\text{Hz} \cdot 32~\text{bits de cuantización } \cdot 1024^{-2}$). Algo factible que no afectaría la calidad de la videollamada.
4 También podría haberse usado ruido blanco en lugar de un barrido de frecuencias.
5 En este punto valdría la pena preguntarse cuál de los tres métodos de Opus realizó la compresión y si los resultados varían al forzar algún método en particular (SILK, CELT o híbrido).
Referencias¶
- Colaboradores de Wikipedia. Códec de audio [en línea]. Wikipedia, la enciclopedia libre, 2020 [fecha de consulta: 4 de noviembre del 2020]. Disponible en https://es.wikipedia.org/w/index.php?title=C%C3%B3dec_de_audio&oldid=129362034.
- Hong Kong Polytechnic University. Department of Electronic and Information Engineering. Perceptual Coding and MP3. Disponible en http://www.eie.polyu.edu.hk/~enyhchan/DAP-lab-PModel-v3.pdf.
- López Monfort José Javier. Opus codec | 22/23 | UPV. Universitat Politècnica de València - UPV. Disponible en https://youtu.be/2-yv1bCDL94.
- Wikipedia contributors. (2020, October 11). SILK. In Wikipedia, The Free Encyclopedia. Retrieved 15:18, November 8, 2020, from https://en.wikipedia.org/w/index.php?title=SILK&oldid=982904860
- Wikipedia contributors. (2020, November 2). Linear predictive coding. In Wikipedia, The Free Encyclopedia. Retrieved 15:22, November 8, 2020, from https://en.wikipedia.org/w/index.php?title=Linear_predictive_coding&oldid=986664067
- EPFL. Room impulse responses. Última vez consultado el 27 de diciembre de 2020 en: https://nbviewer.jupyter.org/github/LCAV/SignalsOfTheDay/blob/master/Room_Acoustics/Room%20Impulse%20Response.ipynb
- López Monfort José Javier. Enmascaramiento Temporal | 4/23 | UPV. Disponible en https://youtu.be/7NQvDoZMBM8 (consultado por última vez el 3 de enero de 2021).
- International Telecomunication Union (ITU-T). P.800.1, MOS.
- Julius O. Smith III, Jonathan S. Abel. Equivalent Rectangular Bandwidth. Center for Computer Research in Music and Acoustics (CCRMA), Stanford University. Disponible en https://ccrma.stanford.edu/~jos/bbt/Equivalent_Rectangular_Bandwidth.html (consultado por última vez el 5 de enero de 2021).
- LCAV. Python MP3 encoder. Disponible en https://github.com/LCAV/MP3Lab/blob/master/mp3python.ipynb (consultado por última vez el 6 de enero de 2021).
- XIPH ORG. OpusFAQ. Disponible en https://wiki.xiph.org/OpusFAQ#Why_not_keep_the_SILK_and_CELT_codecs_separate.3F (consultado por última vez el 6 de enero de 2021).
- Tyler Abbott. How Much Data Does a Zoom Meeting Use?. Reviews ORG. Disponible en https://www.reviews.org/internet-service/how-much-data-does-zoom-use/ (consultado por última vez el 16 de enero de 2021).
- Xiph.Org Foundation. The Opus Codec. 135th AES Convention2013 October 17–20, New York, USA. Disponible en https://arxiv.org/pdf/1602.04845.pdf (consultado por última vez el 21 de enero de 2021).
- GROSS, V., DITTMAR, A., PENZEL, T., SCHÜTTLER, F., & von WICHERT, P. (2000). The Relationship between Normal Lung Sounds, Age, and Gender. American Journal of Respiratory and Critical Care Medicine, 162(3), 905–909. https://doi.org/10.1164/ajrccm.162.3.9905104 (consultado por última vez el 23 de enero de 2021).
- McGee, S. (2018). Chapter 39 - Auscultation of the Heart: General Principles. In S. McGee (Ed.), Evidence-Based Physical Diagnosis (Fourth Edition) (Fourth Edition, pp. 327-332.e1). Elsevier. https://doi.org/https://doi.org/10.1016/B978-0-323-39276-1.00039-1 (consultado por última vez el 23 de enero de 2021).
- Pützer, M., & Barry, W. J. (2008). Saarbruecken Voice Database [Data set]. Zenodo. Disponible en: https://zenodo.org/records/16874898?preview_file=GERD.zip (consultado por última vez el 26 de junio de 2026).
- Carlos Cordero Pérez. Claro con precios rebajados de Internet móvil prepago en Panamá.Tecnología, El Financiero. Disponible en: http://wvw.elfinancierocr.com/ef_archivo/2010/junio/27/tecnologia2420649.html (consultado por última vez el 30 de enero de 2021).
- The SciPy community. scipy.signal.butter. Disponible en: https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.butter.html (consultado por última vez el 30 de enero de 2021).
- Jang-Jaccard, J., Nepal, S., Celler, B. et al. WebRTC-based video conferencing service for telehealth. Computing 98, 169–193 (2016). https://doi.org/10.1007/s00607-014-0429-2

Este obra está bajo una licencia de Creative Commons Reconocimiento-NoComercial-SinObraDerivada 4.0 Internacional. El sitio juanfonsecasolis.github.io es un blog dedicado a la investigación independiente en temas relacionados al procesamiento digital de señales. Para reutilizar este artículo y citar la fuente por favor utilice el siguiente Bibtex:
@online{Fonseca2020,
author = {Juan M. Fonseca-Solís},
title = {Evaluación de la pérdida de calidad del audio usando el codec Opus en tareas de auscultación remota},
year = 2020,
url = {https://juanfonsecasolis.github.io/blog/JFonseca.evaluacionPerdidaCalidadAudioOpus.html},
urldate = {}
}