No suelo ser demasiado fotogénico pero de vez en cuando disfruto de guardar recuerdos de lo que me rodea en forma de fotografías y videos, sobretodo si estoy de viaje, quién sabe cuándo volveré a ese lugar. Normalmente el teléfono es suficiente para esos deberes pero, a veces, prefiero un dispositivo dedicado que pueda usar en su totalidad para capturar imágenes. Para ese fin cuento con dos cámaras: una de acción (DJI Action 3) y una de vlogging (DJI Osmo Pocket). Son excelentes para el uso que les doy, no soy ningún tipo de profesional, sólo quiero fotografías y video con un estabilización y calidad decente, el resto es más o menos secundario, o eso pensé.

Alquilé un auto y grabé el camino hacia unas tiendas de segunda mano que quedaban a las afueras de la ciudad en la que me estaba quedando. Más o menos unos 90 minutos de ida y una cantidad similar de vuelta.
Cuando llegó el momento de transferir los videos a un medio de almacenamiento más seguro, recordé algo que había olvidado, cada video de aproximadamente 7 minutos pesaba unos 4 GB. Se veía bien, era lo que importaba entonces.

Unas semanas más tarde, cuando me dispuse a transferir dichos archivos a mi servidor para poder visualizarlos en diferentes dispositivos gracias a Immich, a parte de tardar una eternidad en la transferencia en sí, encontré un par de problemas:

  • Como mencioné más arriba, las cámaras seccionan la grabación para que los archivos de video no pesen más de 4 GB. DJI dice que usan FAT32 (sistema que presenta precisamente esta limitación), lo cual explica el fenómeno.
  • Dada la configuración elegida al momento de la grabación (1080p 60 fps HQ), los videos tenían un bitrate de unos 80 Mbps, u 80000 Kbps, lo cual es más del doble de lo que se encuentra en algunos discos de Blu-ray convencional y un poco menos de lo que se puede encontrar en discos de Blu-ray 4K.
  • Estas dos cámaras no tienen GPS incorporado, lo cual no es un problema ni para la mayoría de situaciones ni de personas, yo incluido, pero justo en ese caso, dada la naturaleza de la grabación, era un feature más que bienvenido.

Veamos…

FAT32

El primer problema no es tan problemático, ni siquiera es un problema, es hasta una buena característica de este tipo de cámaras. El hecho de que los de DJI usen FAT32 y los videos individualmente no pesen más de 4 GB facilita trabajar con ellos y podría prevenir problemas serios de corrupción.

Transferir archivos MPEG-4 (o sea, MP4) demasiado grandes puede ser complicado. Superando los 36 GB por hora de grabación, una transferencia de ese tipo podría demorar horas entre dispositivos inalámbricos y al no ser un formato de contenedor que se lleve muy bien con los errores y la corrupción si el proceso se interrumpiera por algún motivo se tendría que reiniciar, lo cual es terriblemente engorroso.

Por otro lado, si los errores ocurrieran en el momento de la grabación o almacenamiento en las tarjetas MicroSD en las cámaras, al estar seccionada la grabación, se incrementan las probabilidades de recuperar una mayor porción del proyecto original. Bueno, un poco inconveniente en algunos aspectos pero nada muy malo. Next.

Lista de archivos originales
Lista de videos originales

Bitrate

Este sí es un problema, aunque uno de esos que no está tan mal tener. A ver, no tendría por qué ser un problema, yo entiendo que mientras más grande sea el archivo, lo más probable es que más información contenga el video y sea mejor cuando se trate de editar, pero creo que en este caso los archivos masivos no son de demasiada ayuda.
Con un sensor de 1/2.3 pulgadas, la cámara no está capturando tanta luz (comparativamente hablando, claro está) y todo ese peso adicional viene de ¿el ruido?, no lo sé. Aún así, más información no suele caer mal, pero en este caso creo que puedo deshacerme de unos cuantos bytes sin sacrificar mucha calidad de imagen. Por lo menos es la ida.

Con eso en mente, la tasa de ~36 GB por hora (80000 Kbps) es un innecesariamente alta para videos caseros o personales que seguramente querríamos subir a redes sociales, con esas tasas de bits demoraremos mucho en hacer cualquier cosa con los videos, ya sea editarlos, subirlos a alguna nube o simplemente moverlos a un disco externo. Es aquí donde entra FFmpeg al juego.

FFmpeg (pronunciado /‘ɛfɛfɛm’pɛɡ/ 😉), me ayudó a reducir el tamaño de los archivos sin reducir su calidad de forma muy apreciable. Estos son los parámetros que usé:

ffmpeg -hide_banner -loglevel error -hwaccel qsv -c:v h264_qsv -i {input} -c:v h264_qsv -global_quality 25 {output}
  • -hide_banner previene la impresión de cosas como el banner, cosas del copyright y opciones con las que fue compilada la librería, datos que son de poco uso la mayoría de veces.
  • -loglevel error muestra todos los errores.
  • -hwaccel qsv hace uso de los codificadores de hardware de los procesadores Intel Quick Sync Video (qsv) que aceleran el proceso significativamente.
  • -c:v h264_qsv especifica el códec que vamos a usar, uso h.264 por compatibilidad.
  • -global_quality 25 establece el nivel de calidad que obtendremos, mientras menos sea ese número, mayor será la calidad, la idea es tener un equilibrio y 25 está bastante bien para la reducción.

El resultado es bastante prometedor, los videos se reducen de alrededor de 4 GB a unos nada despreciables ~850 MB, una reducción de hasta el 80%.

Original
Fotograma original
Procesado
Fotograma después de la dieta

Imágenes comprimidas para mejorar el rendimiento de la página. Aquí y aquí se pueden encontrar los archivos originales.

Pero no confíen simplemente en mis (o sus) ojos, existen algoritmos de comparación de imágenes y videos como VMAF, que desarrolló Netflix, o SSIM, desarrollado en universidades de Estados Unidos, que pueden ayudarnos a tomar decisiones sobre cuáles son los mejores parámetros de codificación para nuestros videos según las necesidades.

Por ahora elegiré SSIM debido a que para usar el algoritmo de Netflix (por cierto, recomiendo visitar su blog técnico de vez en cuando) es necesario completar un par de pasos con anterioridad, como convertirlos al espacio de color YUV, por ejemplo. La flojera me gana en estos momentos.

SSIM, el índice de similitud estructural, funciona simulando la percepción que tendría un ser humano sobre dos imágenes y su similitud, incluyendo aspectos como la luminosidad y el contraste. Hay más de una forma de calcularlo, yo opté por usar FFmpeg que lo trae incluido de “fábrica”:

ffmpeg.exe -i {input1} -i {input2} -lavfi ssim=stats_file={output} -f null -

Este comando nos devuelve un archivo largo en donde podemos ver la comparación entre cada uno de los fotogramas de los dos videos, dándonos un valor general y valores parciales por los tres componentes del espacio YUV (parece que no había que convertir los videos antes de usar VMAF después de todo 😛).

Tiene una estructura similar a esta:

SSIM

Al hacer un pequeño análisis estadístico de los resultados tanto para valores de 24 como de 25 para la configuración -global_quality del comando usado para reducir la calidad, 24 gana por poco, un margen que me pareció aceptable. Esta corta distancia numérica, sumado a que en nivel 25 el cambio no era drástico comparado con la reducción de tamaño me hizo ratificar mi decisión.

Aquí unos cuantos números cortesía de CalculatorSoup (Wolfram Alpha no pudo con tanta data en su versión gratuita ☹️) del promedio de similitud de fotogramas entre videos, así como la desviación estándar de los valores. A mayor promedio y menos desviación estándar (σ), mejor.

Calidad nivel 24
Calidad nivel 24
Calidad nivel 25
Calidad nivel 25

Teniendo en cuenta que, en general, un valor de MSSIM (SSIM promedio) mayor o igual a 0.99 significa que dos imágenes o videos son indistinguibles (Sec. 4.1 Luminance), los valores resultantes parecen satisfactorios.

“Fórmulas de la regla 68-95-99.7”
Fórmulas de la regla 68-95-99.7

Por un lado, con calidad 24, la más alta de las dos, tenemos un promedio de 0.9862 con un σ de 0.0084. Esto significa que, según la fórmula de arriba, el ~95% del tiempo los videos tienen un nivel de similitud de entre 96.94% y 100% y que más del 99% del tiempo ese nivel está entre 96.1% y 100%. Además, en promedio son 98.62% similares. Bastante bien.

Por el otro, con calidad 25, el promedio es de 0.9844 con un σ de 0.0109, lo cual significa que el ~95% del tiempo los videos tienen un nivel de similitud de entre 96.26% y 100% y que más del 99% del tiempo ese nivel está entre 95.17% y 100%. Además, en promedio son 98.44% similares.

Parece un precio justo a pagar en términos de calidad por ahorrar varios bytes aquí y allá.

¿Fue innecesario todo esto? Muy probablemente, pero me hacía ilusión tener números respaldando o desaprobando mi decisión; felizmente, sucedió algo más cercano a lo primero.

GPS

Me di cuenta de la falta de información de geolocalización de los videos al añadirlos a Immich, en donde se indica en un mapa en dónde fueron capturados si tienen los metadatos disponibles. Lo bueno es que cuenta con un editor de metadatos, el cual usé para guardar las coordenadas del punto de inicio de cada video; tuve que verlos y contrastar con Google Maps para poder ubicarlos exactamente.
Una vez geolocalizados los videos, Immich creó archivos Exif externos correspondientes a cada uno de los videos, todos ellos compartían el nombre con su contraparte videográfica aunque con una extensión distinta: xmp.

Hasta ahí todo bien, si bien en este caso los metadatos Exif son externos, al tener el mismo nombre de archivo pueden ser recogidos por diversos reproductores de video, no debería ser un problema; sin embargo, yo quería que estuvieran todos los datos en los mismos videos, es más fácil organizarlos de esa forma.

Usé ExifTool para solucionarlo:

exiftool -gpsposition -c "%.7f" {input}
  • -gpsposition -c "%.7f" extrae los datos de geolocalización en el formato “%.7f”, que es un número entero con siete cifras decimales (ej. -31.8267316).
exiftool -TagsFromFile {input} -All:All -XMP -ExtractEmbedded -overwrite_original {output}
  • -TagsFromFile ayuda a tomar las etiquetas Exif guardadas en los archivos que creó Immich previamente.
  • -All:All indica a la herramienta tomar en cuenta todas las etiquetas Exif que pueda.
  • -XMP lo mismo que lo anterior pero con etiquetas XMP, que suelen ser metadatos no estándar definidos por cada fabricante de cámaras o desarrollador de software de video.

Había un pequeñísimo problema adicional, desgraciadamente: al parecer, las cámaras DJI necesitan conectarse a un teléfono o tablet por Bluetooth después de haber estado desconectadas por mucho tiempo para poder obtener la fecha y hora adecuadas. Esta característica causó que la hora de mis videos estuviera atrasada unos 90 minutos. No sé la cantidad exacta de minutos porque no tenía una buena referencia más que una grabación que hice con mi teléfono, así que calculo que debo tener un error de 3 minutos como máximo. Aceptable.

Tenía que cambiar esas horas en los archivos y en las etiquetas Exif, ya que si sólo lo hacía con los archivos, reproductores de video que leen dichas etiquetas mostrarían mal las marcas de tiempo y software de organización de medios, como Immich, no colocarían de forma adecuada los videos en las líneas de tiempo.

Cambiar de fecha los archivos era simple. En Windows (PowerShell) sería más o menos así:

(Get-Item {input}).LastWriteTime = {new_date}

No olvidar que new_date debe tener el formato correcto, en este caso opté por MM/dd/yyyy HH:mm ya que fue el que funcionó en mis pruebas en mi instalación de Windows.

(estamos cerca)

Peeeeero la intención era quitarle 90 minutos al tiempo de creación/modificación actual de los archivos, así que tuve que extraer y restar. Lo bueno es que es una operación no destructiva, así que se podía dejar para el último paso una vez que los procesos de FFmpeg y ExifTool hubieran culminado.

Una cosita antes de tener el script completo: cuando se trata de etiquetas exif, por lo menos en mi caso, la zona horaria de las marcas de tiempo siempre era UTC y estaban en el formato yyyy-MM-dd HH:mm:ss, así que había que tener eso en cuenta para ese paso, mas no para el cambio de hora de los archivos en sí.

Bueno, con todo eso en cuenta y algunas cosas más, el script terminaría luciendo así (y ahora con versión bash incluida):

$originals_path = {originals_path}
$processed_path = {processed_path}

Get-ChildItem $originals_path -Filter *.mp4 | Sort-Object -Property Name | Foreach-Object{
    ""
    "Original file: $($_.Fullname)"
    "Reducing video bitrate"
    ""
    ""

    ffmpeg -hide_banner -loglevel error -hwaccel qsv -c:v h264_qsv -i $_.Fullname -c:v h264_qsv -global_quality 25 {original_file}

    # qs stands for Quick Sync
    $qs_file = (Get-Item "$processed_path\$($_.Basename).mp4")

    "Bitrate-reduced file: $($qs_file.Fullname)"
    ""
    
    # Get the current creation datetime (wrong creation date)
    $wrongCreationDateTime = "{0:yyyy-MM-dd HH:mm}" -f $($_.CreationTime)

    # Get the right creation datetime but in Windows format
    $rightDateTimeWindowsFormatted = [datetime]::parseexact($wrongCreationDateTime, 'yyyy-MM-dd HH:mm', $null).AddMinutes(-90).toString('MM/dd/yyyy HH:mm')

    # Get the right creation datetime but in Exif format (UTC with seconds)
    $rightDateTimeExifUTCFormattedWithSeconds = [datetime]::parseexact($rightDateTimeWindowsFormatted, 'MM/dd/yyyy HH:mm', $null).AddMinutes(300).toString('yyyy:MM:dd HH:mm:00')

    ""
    "Original file: $($_.Fullname)"
    "QS file: $($qs_file.Fullname)"
    "Wrong creation datetime: $wrongCreationDateTime"
    "Right creation datetime in Windows format: $rightDateTimeWindowsFormatted"
    "Right datetime in ExifTool UTC format with seconds: $rightDateTimeExifUTCFormattedWithSeconds"
    ""

    "ExifTool copying all tags from original..."

    exiftool -TagsFromFile $_.Fullname -All:All -XMP -ExtractEmbedded -overwrite_original $qs_file.Fullname

    # Extracting coordinates info from XMP files
    $coords = exiftool -gpsposition -c "%.7f" "$($qs_file.Fullname).xmp"
    
    ""
    $coords
    ""

    "ExifTool setting geoposition and updated timestaps..."

    exiftool "-CreateDate=$rightDateTimeExifUTCFormattedWithSeconds" "-ModifyDate=$rightDateTimeExifUTCFormattedWithSeconds" "-Track*Date=$rightDateTimeExifUTCFormattedWithSeconds" "-Media*Date=$rightDateTimeExifUTCFormattedWithSeconds" -gpsposition="$coords" -overwrite_original $qs_file.Fullname

    (Get-Item $qs_file).LastWriteTime = $rightDateTimeWindowsFormatted
    ""
    ""
    "================================================================================================="
}

#!/bin/bash

$originals_path = {originals_path}
$processed_path = {processed_path}

for filename in `$originals_path/*.mp4`; do
  echo
  echo "Original file: $filename"
  echo "Reducing video bitrate"
  echo
  echo

  $qs_filename =  $"$processed_path/$(basename "$filename")"

  ffmpeg -hide_banner -loglevel error -hwaccel qsv -c:v h264_qsv -i $filename -c:v h264_qsv -global_quality 25 $qs_filename

  echo "Bitrate-reduced file: $qs_filename"
  echo

  $wrong_datetime = $(date -r "$filename" +"%F %H:%M")
  $right_datetime = $(date -d ""$wrong_datetime"  90 minutes" +"%F %H:%M")
  $right_datetime_exif_utc_w_seconds = $(date -d ""$right_datetime"  300 minutes" +"%F %H:%M:%S")

  echo
  echo "Original file: $filename"
  echo "QS file: $qs_filename"
  echo "Wrong creation datetime: $wrong_datetime"
  echo "Right datetime: $right_datetime"
  echo "Right datetime in ExifTool UTC format with seconds: $right_datetime_exif_utc_w_seconds"
  echo

  echo "ExifTool copying all tags from original..."

  exiftool -TagsFromFile $filename -All:All -XMP -ExtractEmbedded -overwrite_original $qs_filename

  $coords = exiftool -gpsposition -c "%.7f" "$qs_filename.xmp"

  echo
  echo $coords
  echo

  echo "ExifTool setting geoposition and updated timestaps..."

  exiftool "-CreateDate=$right_datetime_exif_utc_w_seconds" \
          "-ModifyDate=$right_datetime_exif_utc_w_seconds" \
          "-Track*Date=$right_datetime_exif_utc_w_seconds" \
          "-Media*Date=$right_datetime_exif_utc_w_seconds" \
          -gpsposition="$coords" -overwrite_original $qs_filename

  touch -d "$right_datetime" $qs_filename

  echo
  echo
  echo "================================================================================================="
done

Para ir terminando

Todo este proceso me dejó con videos de mucho menor tamaño, con la información correcta sobre en dónde fueron capturados y con archivos con las fechas y horas correctas. Pasamos del panorama de la imagen de portada de este artículo a esto:

Lista de archivos procesados
Lista de videos procesados

De esta forma es más fácil editarlos, transmitirlos o simplemente reproducirlos.

Yo sé que mientras más información haya en un video, más flexibilidad otorgará al momento de ser procesado; al fin y al cabo, las computadoras no pueden mágicamente inventar información prácticamente de la nada. ¿O sí? Es por eso que quizás termine usando todos los fragmentos originales para juntarlos y formar un video largo al que después pueda aplicar todo lo aprendido aquí para terminar con un archivo razonablemente pesado.

Lo importante es que si hubo aprendizaje no fue una pérdida de tiempo, aunque el tiempo invertido en geolocalizar cada fragmento por separado difícilmente sea recompensado, a menos que pueda generar una suerte de ruta descrita por las coordenadas para luego animar un mapa con mi recorrido en tiempo real y superponerlo al video o algo por el estilo.

Quién sabe. 😉

¿Por qué no Premiere, Resolve o simplemente Movie Maker?

Bueno, me parece que FFmpeg es no sólo más versátil si no también más divertido de usar cuando se trabaja con varios archivos a la vez and cuando no se sabe bien qué hacer, es una herramienta que permite la experimentación casi extrema. Además, esto se trata de obtener diversión mientras se resuelven problemas; hasta cierto punto, claro.
Obviamente, es también más difícil que usar Resolve, que uso típicamente, pero a veces me siento más cómodo en la terminal y la iba a usar de todas formas debido a ExifTool.
Además, en esta casa se usa FFmpeg, es gratuito y de código abierto y permite usar la tarjeta de video para la codificación; aparte, tiene una comunidad enorme.

Usaré Resolve para la corrección del color, creo que no quiero tener que lidiar con FFmpeg cuando se trate de usar LUTs. 😔

Para algún día

  • Mejorar el código, seguramente se puede optimizar y no hacer dos llamadas a ExifTool, ya que es una operación destructiva y consume bastante tiempo. Aparte, no es el código más lindo del mundo.
  • Darle un poco de color al video, está un poco feo así.
  • Recordar enlazar las cámaras a un teléfono antes de grabar para evitar desfases temporales.