sábado, 21 de febrero de 2026

Transformación trapezoidal de imágenes acelerada con C++

Desde que tengo a ChatGPT trabajando en el sótano me gusta revisitar antiguo código R especialmente lento y acelerarlo, ya que se le da maravillosamente bien replicar código en C++ compilable de forma perfecta y sin necesidad de iteraciones. Esta vez le ha tocado el turno al algoritmo de corrección geométrica que vimos en 'Transformación trapezoidal de imágenes con R (I). Algoritmo'.

He aprovechado para salir de mi zona de confort en lo que respecta a la librería Rcpp: en lugar de incrustar el código C++ en R con la función cppFunction() he empezado a usar la función sourceCpp(). Enseguida me he dado cuenta de que es ahora cuando estoy en una zona confortable y no antes, ya que sourceCpp() tiene ventajas sobre cppFunction() que luego comentaré.

En cuanto al ejercicio de corrección de perspectiva, por el mismo precio he pedido a ChatGPT que sustituya la interpolación básica por nearest neighbour que usaba hasta entonces por una bilinear que, pese a su simplicidad, mejora mucho la calidad del resultado.

Escogemos una escena arquitectónica requerida de una transformación bastante radical para lograr hacer frontal la fachada (hacer clic para ver en alta resolución):

Fuente: galerías DPReview


Tomando los marcos de los rectángulos rojos más extremos como referencia de la transformación, e introduciendo un factor de escala a medida para no perder información de la periferia, aplicamos una corrección que nos traerá toda la fachada a la frontalidad:



La imagen corregida es equivalente a lo que se habría obtenido tomando la foto desde el mismo sitio, pero apuntando la cámara perpendicularmente al edificiio (hacer clic para ver en alta resolución):



En esta comparativa al 400% podemos ver la mejora que logramos realizando una verdadera interpolación (aunque sea con la humilde bilineal) frente al mapeo puro de píxeles nearest neighbour, librándonos del molesto aliasing en forma de dientes de sierra:





La mejora en velocidad de la versión compilada en C++ es de dos órdenes de magnitud, pasando de ejecutar en 108s a 0,99s para nuestra imagen de 26Mpx. Tiempos de 3 ejecuciones de cada rutina (s):


~~~

La librería Rcpp permite integrar de maravilla código C++ en programas R para acelerar partes críticas, incluyendo todos los tipos de objetos y funciones necesarias para resultar compatibles con los formatos numéricos nativos de R tales como vectores y matrices.

He abandonado la función cppFunction(), que permite teclear directamente código C++ entre líneas de nuestro programa en R, para pasarme a la función sourceCpp(), que requiere que el código C++ esté en un fichero externo, disfrutando a cambio de las siguientes ventajas:
  • No existe la limitación de exportar a R una sola función por llamada que sí tiene la función cppFunction(). En cada fichero .cpp invocado por sourceCpp() podemos definir todas las funciones que queramos, hacer que se llamen entre ellas y al final exportar a R las que elijamos.
  • Aunque las llamadas al compilador sean las mismas, los mensajes de error se reportan con un extra de información al invocar con sourceCpp() (p.ej. la línea del error relativa al archivo .cpp), lo que facilita la depuración.
  • Al estar el código C++ separado del código R, el archivo R queda más despejado y el código C++ lo podemos visualizar en cualquier editor con formateado para el lenguaje (incluido RStudio):

Fuente: archivo "keystone.cpp" llamado por sourceCpp()

~~~

Repositorio con el código R: GitHub.

No hay comentarios:

Publicar un comentario

Por claridad del blog, por favor trata de utilizar una sintaxis lo más correcta posible y no abusar del uso de emoticonos, mayúsculas y similares.