¿Puede GPT-3 dibujar?

Vamos a usar GPT-3 para dibujar, o intentarlo. Y no hablo de usar DALL-E, si no de usar el propio GPT-3 para que genere el dibujo, para que esto sea posible el dibujo debería de cumplir las siguientes condiciones:

  • Tiene que estar realizado con texto. GPT-3 solo genera texto.
  • Tiene que poderse incluir una descripción del mismo. Para poder pasarla a GPT-3 como texto a completar

SVG

SVG es un lenguaje de descripción de gráficos vectoriales. Permite describir dibujos usando un lenguaje basado en XML. Es decir, un lenguaje de etiquetas. Como SVG puede ir directamente en el código HTML vamos a pasar a DALL-E el principio de una web con una etiqueta abierta de SVG (a la que podemos añadir el ancho y el alto fijando las dimensiones del dibujo). Para describir el dibujo que queremos, justo antes de esa etiqueta pondrrmos un comentario HTML (<!– –>) describiendo el dibujo.

Por ejemplo le pedimos una estrella

<!DOCTYPE html>
<html>
<body>
<!-- svg code to draw a star -->
<svg height="210" width="500"

Le damos a autocompletar

<!DOCTYPE html>
<html>
<body>
<!-- svg code to draw a star -->
<svg height="210" width="500"
 xmlns="http://www.w3.org/2000/svg">
<!-- Define five points. -->
<polygon points="100,10 40,198 190,78 10,78 160,198"
style="fill:lime;stroke:purple;stroke-width:5;fill-rule:nonzero;" />
<!-- Define the top right point. -->
<circle cx="100" cy="10" r="4" fill="red" />
<!-- Define the left middle point. -->
<circle cx="40" cy="198" r="4" fill="red" />
<!-- Define the bottom point. -->
<circle cx="190" cy="78" r="4" fill="red" />
<!-- Define the top left point. -->
<circle cx="10" cy="78" r="4" fill="red" />
<!-- Define the right middle point. -->
<circle cx="160" cy="198" r="4" fill="red" />
</svg>
</body>
</html>

¡Correcto!

Ahora probaremos a pedir un coche

<!DOCTYPE html>
<html>
<body>
<!-- svg code to draw a car-->
<svg height="210" width="500" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
      <stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
    </linearGradient>
  </defs>
  
  <rect x="1" y="1" width="498" height="208" fill="url(#grad1)" />
  <text x="280" y="150" font-family="Verdana" font-size="35">My car</text>
</svg>
</body>
</html>

No ha salido bien

Demosle otra oportunidad

<!DOCTYPE html>
<html>
<body>
<!-- svg code to draw a car-->
<svg height="210" width="500" xmlns="http://www.w3.org/2000/svg">
  <rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
  <rect x="200" y="10" width="100" height="30" style="fill:rgb(0,255,0);stroke-width:3;stroke:rgb(0,0,0)" />
   <circle cx="100" cy="100" r="80" fill="green" />
  <rect x="20" y="20" width="10" height="10" style="fill:rgb(255,0,0);stroke-width:3;stroke:rgb(0,0,0)" />
  <rect x="20" y="80" width="10" height="10" style="fill:rgb(255,0,0);stroke-width:3;stroke:rgb(0,0,0)" />
  <rect x="40" y="20" width="10" height="10" style="fill:rgb(255,0,0);stroke-width:3;stroke:rgb(0,0,0)" />
  <rect x="40" y="80" width="10" height="10" style="fill:rgb(255,0,0);stroke-width:3;stroke:rgb(0,0,0)" />
  Sorry, your browser does not support inline SVG.
</svg>

</body>
</html>

Al menos parece que intenta dibujar un coche.

Ascii art

Lo primero que se me ocurrió es Ascii Art, un tipo de dibujo realizado con caracteres de texto, la idea era buena pero el resultado fue….curioso, a diversa peticiones:

A dog in ascii art
A bunny in ascii art

Respondió con el siguiente dibujo

                 ,#####,
                 #_   _#
                 |a` `a|
                 |  u  |
                 \  =  /
                 |\___/|
        ___ ____/:     :\____ ___
      .'   `.-===-\   /-===-.`   '.
     /      .-"""""-.-"""""-.      \
    /'             =:=             '\
  .'  ' .:    o   -=:=-   o    :. '  `.
  (.'   /'. '-.....-'-.....-' .'\   '.)
  /' ._/   ".     --:--     ."   \_. '\
 |  .'|      ".  ---:---  ."      |'.  |
 |  : |       |  ---:---  |       | :  |
  \ : |       |_____._____|       | : /
  /   (       |----|------|       )   \
 /... .|      |    |      |      |. ...\
|::::/'' jgs /     |       \     ''\::::|
'""""       /'    .L_      `\       """"'
           /'-.,__/` `\__..-'\
          ;      /     \      ;
          :     /       \     |
          |    /         \.   |
          |`../           |  ,/
          ( _ )           |  _)
          |   |           |   |
          |___|           \___|
          :===|            |==|
           \  /            |__|
           /\/\           /"""`8.__
           |oo|           \__.//___)
           |==|
           \__/

Como poco inquitante.

A otras muchas peticiones respondió con cosas sin sentido o espacios en blanco

Emojis

Este era fácil y supuse que lo haría bien, usar emoticonos para representar ideas.

JS Canvas

Otra forma de dibujar es usando un canvas en una web y usar código JS para dibujar en el lo que quieras. la idea es parecida a SVG, pero con código JS.

Primero preparamos la web;

<!DOCTYPE html>
<html>
<body>
  <canvas id="star" width="600" height="600"></canvas>
  </body>
<script>
 //Aquí ira el código generado por JS
</script>

La completaremos con el código que genere GPT-3

En lugar de pasarle la cabecera de la web le pasamos solo un comentario:

/* js code to draw a star in a canvas */

Le pedimos una estrella:

<!DOCTYPE html>
<html>
<body>
  <canvas id="star" width="600" height="600"></canvas>
  </body>
<script>
  /* js code to draw a star in a canvas */
// Define the points of the star.
  var points = 5;
  var innerRadius = 50;
  var outerRadius = 100;
  var angle = Math.PI / points;
  var x, y;
 
// Get the context of the canvas.
  var canvas = document.getElementById('star');
  var ctx = canvas.getContext('2d');
 
// Begin the path.
  ctx.beginPath();
 
// Move to the first point.
  x = canvas.width / 2 + Math.cos(0) * outerRadius;
  y = canvas.height / 2 + Math.sin(0) * outerRadius;
  ctx.moveTo(x, y);
 
// For each point draw a line to it.
  for (i = 1; i <= points * 2; i++) {
    x = canvas.width / 2 + Math.cos(angle * i) * (i % 2 === 0 ? innerRadius : outerRadius);
    y = canvas.height / 2 + Math.sin(angle * i) * (i % 2 === 0 ? innerRadius : outerRadius);
    ctx.lineTo(x, y);
  }
 
// Close the path.
  ctx.closePath();
 
// Fill the path.
  ctx.fillStyle = '#000';
  ctx.fill();
</script>

¡Casi! Y es fácil corregir el problema (basta con comentar el calculo del primer punto). Ahora, y esto le da nota extra, el método sirve para estrellas de cualquier numero de puntas, basta con cambiar la variable «points».

Ahora le pedimos un coche.

<!DOCTYPE html>
<html>
<body>
  <canvas id="canvas" width="600" height="600"></canvas>
  </body>
<script>
  /* js code to draw a car in a canvas */
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
// draw car body
context.beginPath();
context.moveTo(75, 25);
context.quadraticCurveTo(25, 25, 25, 62.5);
context.quadraticCurveTo(25, 100, 50, 100);
context.quadraticCurveTo(50, 120, 30, 125);
context.quadraticCurveTo(60, 120, 65, 100);
context.quadraticCurveTo(125, 100, 125, 62.5);
context.quadraticCurveTo(125, 25, 75, 25);
context.stroke();
// draw car wheels
context.beginPath();
context.arc(75, 100, 10, 0, 2 * Math.PI);
context.fillStyle = "black";
context.fill();
context.lineWidth = 1;
context.strokeStyle = "black";
context.stroke();
context.closePath();
context.beginPath();
context.arc(125, 100, 10, 0, 2 * Math.PI);
context.fill();
context.lineWidth = 1;
context.stroke();
context.closePath();
</script>

La idea está ahí pero no es lo que queríamos

Vamos a darle más detalles a ver… «Un coche deportivo rojo»

<!DOCTYPE html>
<html>
<body>
  <canvas id="canvas" width="1600" height="1600"></canvas>
  </body>
<script>
/* js code to draw a red car in a canvas with id "canvas" */
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// draw a rectangle for the car body
ctx.fillStyle = "red";
ctx.fillRect(30, 30, 150, 50);
// draw a triangle for the car roof
ctx.beginPath();
ctx.moveTo(40, 30);
ctx.lineTo(140, 30);
ctx.lineTo(90, 10);
ctx.fill();
// draw two circles for the car wheels
ctx.beginPath();
var centerX = 50;
var centerY = 75;
var radius = 20;
// draw left wheel
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
// move center coordinates to right wheel
centerX += 100;
// draw right wheel
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
// color the wheels
ctx.fillStyle = "black";
ctx.fill();
// reset the current path
ctx.beginPath();
// draw a line in the middle of the car to separate the two colors
ctx.moveTo(50, 30);
ctx.lineTo(50, 80);
ctx.stroke();
// stroke the current path with a thick line
ctx.lineWidth = 3;
ctx.stroke();
</script>

No es lo que tenía en mente, pero bueno un coche es, no sé si deportivo, y rojo también.

Conclusiones

Con dibujos muy sencillos e insistiendo a base de prueba y error se puede conseguir algo. Menos en Ascii Art que es una causa pérdida. No es un sistema cómodo, ni práctico. Eso no quita lo sorprendente que es que entienda lo que le pides y trate de dibujarlo, aunque sea con resultados irregulares.

Es posible que con un entrenamiento adecuado se puedan mejorar estos resultados. Hay que tener en cuenta que esta forma de dibuja obliga a entender como se relacionan los distintos elementos del dibujo entre ellos. no solo es dibuja la ruedas con dos círculos es colocarlas correctamente respecto al resto del dibujo.

Puedes ver un vídeo sobre el mismo tema en mi canal de Youtube:

Haz click para ver el vídeo en mi canal de Youtube

Stable Diffusion se enfrenta a un examen de dibujo.

Ya hemos visto a DALL-E enfrentarse a varios retos. Hoy enfrentamos a otro modelo, está vez código libre, Stable Diffusion. Le he pedido a un profesor de dibujo que le ponga un par de ejercicios y valoremos el resultado.

Ejercicio 1: Mándala

El enunciado del ejercicio es:

Crea un zendala. Elige una gama cromática de colores fríos o cálidos y crea una armonía cromática con colores afines. Usa un mínimo de dos colores y un máximo de tres.

Cambiamos «crear» por «dibuja» y «zendala» por «mandala», palabra mucho más conocida. Lo traducimos al inglés:

Draw a mandala. Choose a chromatic range of cold or warm colors and create a chromatic harmony with similar colors. Use a minimum of two colors and a maximum of three.

Y el resultado es:

Varios puntos a tener en cuenta:

  • Simula ser un dibujo a mano, coloreado y es un mandala. Lo damos por válido.
  • Escala cromática. Falla, no es lo que le hemos pedido, lo intenta pero no sabe que hacer. Incorrecto.

Ejercicio 2: Camaleón puntillista

El siguiente ejercicio tiene dos partes  (iba a ser solo una pero el profesor sugirió un cambio)

Dibuja un camaleón con puntillismo en una jungla

Nos encontramos con la duda de ¿Cuál es la traducción de puntillismo? Google sugirió «pointillism» no estábamos seguros pero continuamos. El texto quedó:

draw a chameleon with pointillism in a jungle
  • Lo esperábamos en blanco y negro, pero no sé lo especificamos. El profesor da por bueno el resultado.
  • Solo es puntillista el camaleón, no la jungla. Puede ser culpa nuestra por no indicarlo ya que puntillismo hace referencia al camaleón
  • Falta un «trozo» de camaleón. Algo habitual en este tipo de IA, no se les da muy bien encuadrar la imagen

La pega es que el profesor esperaba un dibujo en blanco y negro, por lo que había que especificarlo en el prompt. Además pidió otro cambio. No se fiaba mucho de que el algoritmo no estuviera «copiando» imágenes. Por lo que en lugar de en la jungla, algo típico para un camaleón, pidió algo diferente, una escalera.

Cómo cuando usamos «stairs» dibujo un camaleón con un lápiz en la mano ¿? Usamos la palabra «ladder».

Dibuja un camaleón con puntillismo bajando una escalera. Blanco y negro
draw a chameleon with pointillism going down a ladder. Black and white
  • El profesor le da el visto bueno y lo considera como superado
  • Yo señalo que no esta «bajando la escalera». Pero no parece tener importancia

Resultado:

No es fácil dar una nota numérica ya que por un lado los dibujos son espectaculares, por otro comete errores muy simples como el usar más de tres colores en el mandala o cortar al figura del camaleón. El resultado seria claramente más que aprobado aunque no llegaría a sobresaliente.

Puedes ver la explicación en vídeo en mi canal de Youtube:

Haz click para ver el vídeo en Youtube

DALL-E vs Diseñador Gráfico, crear un logo

Vamos a enfrentar a DALL-E contra un diseñador profesional a ver quién diseña mejor logo para mi blog. Haremos dos enfrentamientos:

Primero pasaremos a DALL-E una descripción de la temática de la web y un poco de información sobre la misma. La idea es que DALL-E tenga, más o menos, la misma información que el diseñador. No se le va pasar ninguna descripción del logo deseado. Queremos ver la «creatividad» de DALL-E. Sobre los logos que más me gusten le pediré que genere variaciones.

En el segundo le pasaremos el logo generado por el diseñador y le pediremos que genere variaciones, a ver si puede mejorarlo.

Antes de empezar la confrontación hay que dejar claro que a DALL-E no se le van a valorar los textos, ya que tiene problemas para generar textos con sentido. Si que valoraremos el diseño de los mismos.

El logo con el que competir es el de este blog:

Generando un logo desde cero

Vamos a pasar a DALL-E una descripción del blog y su temática. También quién es Chispas, información que le sirvió al diseñador para crear el logo.

Un logo para una web sobre inteligencia artificial, electrónica y programación. El nombre de la web es "Chispas". "Chispas" es una inteligencia artificial.

En inglés:

A logo for a website about artificial intelligence, electronics and programming. The name of the website is "Chispas". "Chispas" is an artificial intelligence

Dejo Chispas sin traducir para evitar que influya añadiendo chispas electricas al logo.

No es el resultado esperado, quizás sirvan para portada de un disco indy, pero no valen como logotipo. Voy a probar el «menos es más» y a simplificar la prompt.

A logo for a website about artificial intelligence, electronics and programming.

Veamos los resultados:

Como me parecían muy «típicos» decidí darle una segunda oportunidad:

Los dos primeros casos de esta segunda prueba se parece más a lo que me paso el diseñador gráfico para comprobar como queda el logo con distintos fondos:

Aunque lo lógico sería elegir la imagen 1 y 2 para generar variaciones, no me convence mucho la imagen 1 con ese cuadrado queriendo ser la letra a, así que voy a elegir las imágenes 2 y 4 y pedirle a DALL-E que me genere variaciones a ver cuál es el mejor resultado de cada. De la 2 me gusta que parece una mezcla entre el logo de ahora (coincidencia) y algo orgánico. De la 4 la idea de mezclar medio cerebro «orgánico» con elementos claramente artificiales.

De la variaciones de la imagen 2 me quedo con esta:

Me gusta el estilo orgánico de estos diseños, me gusta tanto que me los guardo para usarlos. Un punto en contra es que en esta variación no ha entendido que la imagen debería ser la misma en cada cuadrante. Debo decir que son demasiado pequeños para usarlos como logo del blog.

De la variaciones de la imagen 4 he elegido esta:

En este caso me gustaría una mezcla del logo original (imagen 4) con este. Me gusta que este cerebro luzca más orgánico pese a mantener las lineas rectas. Prefiero los hexágonos a los círculos. Si tuviera que quedarme con una de las dos me quedaría con la original.

Humano: 1 DALL-E: 0.5 (no se merece un 0)

Variaciones del logo de un diseñador

En este caso partimos del logo ya creado por un humano y le pedimos modificaciones para ver qué ideas sugiere.

Debo reconocer que algunas de estas variaciones me gustan. No las veo como para sustituir la imagen original. Pero si como para tener variaciones para algunas secciones.

Humano: 1 DALL-E: 0.5 (no se merece un 0)

Conclusiones

DALL-E es capaz de crear logos, puede que no sean los mejores logos, pero para un apaño sirven. El mayor problema es que solo genera imágenes de 1024 x 1024, lo cual puede ser pequeño para usos profesionales. Los resultados no son llegar y usar, es necesaria la intervención humana para «retocar» el resultado. Creo que DALL-E puede ser más útil como fuente de inspiración, tanto para el cliente como para el diseñador, para crear una idea base de la partir que como herramienta de diseño completa.

Es importante señalar que diseñar un logo de forma profesional es más complicado que lo que hemos visto aquí. No hay tanta libertad ya que el cliente puede imponer restricciones, hay códigos de estilo que seguir, por ejemplo colores que definen la marca o la web. Ademas de la incapacidad de generar imágenes vectoriales que limita mucho su uso. Es cierto que hay herramientas que pueden convertir una imagen en vectorial y resolver este punto y el del tamaño.

Puedes ver un vídeo sobre este articulo en mi canal de Youtube:

Haz click para ver el vídeo en Youtube

¿Puede DALL-E dibujar un cómic?

Tras un intento fallido de generar sprites de un videojuego (aunque realmente lo interesante era hacer uncrop de la imagen). Vamos a intentar generar un cómic para ver si con DALL-E se puede narrar una historia en varias imágenes de forma secuencial. Me preocupa especialmente la capacidad de DALL-E para mantener la consistencia entre las distintas viñetas. Y ya se que no le puedo pedir que me ponga el texto. Por lo tanto el objetivo se reduce a una historia muda o a una que le añadiremos el texto después.

Primer intento

El más sencillo para mi. Escribo el guión, se lo paso. Me genera el comic y a partir de ahí solo es cuestión de tiempo hacerme una artista famoso y millonario.

El guion es el siguiente:

Una tira cómica de 4 paneles estilo Calvin Y Hobbes. La tira cómica es protagonizada por Kelvin, un niño, y el gato Fred. 
Panel 1: Kelvin baja del autobús escolar y se dirige a la casa de su madre. Es invierno, el suelo está nevado, Kelvin lleva un abrigo y un gorro en la cabeza.
Panel 2: Fred le espera detrás tumbado en la entrada detrás de la puerta de la casa. El suelo es de láminas de madera, las paredes blancas y vemos un marco de fotos en una de las paredes.
Panel 3: Kelvin abre la puerta de la casa.
Panel 4: Fred salta sobre Kelvin y lo tira al suelo.

Ahora solo queda traducirlo, introducir algunos cambios pensando en que DALL-E lo entienda mejor y ver el resultado.

Primer problema, el prompt de DALL-E solo permite 400 caracteres. Así que hay que hacer una «poda» del texto original, al reducir el número de caracteres reducimos la descripción de las viñetas que pasamos a DALL-E. El resultado es el siguiente:

A 4 panels page with a comic strip in the style of Calvin and Hobbes. The characters are Kelvin, a kid, and Fred, a cat.
Panel 1: Kelvin gets off the school bus. It is winter, the ground is snowy, Kelvin is wearing a coat and a wool cap.
Panel 2: Fred is lying in the entrance behind the door of the house.
Panel 3: Kelvin, from outside, opens the door of the house.
Panel 4: Fred jumps on Kelvin.

Tras realizar varios intentos me encuentro con los mismos problemas, vamos a ver unos pocos ejemplos:

El resultado se «parece» a lo que quiero conseguir….pero no es exactamente lo que quiero conseguir. Los problemas:

  • No tiene claro cual es el estilo que quiero.
  • No siempre me genera 4 viñetas y cuando lo hace las dibuja cortadas
  • No hace cuatro viñetas distintas, mas bien mezcla una o dos de las descripciones y las representa en las viñetas.
  • Pone cosas que parecen diálogos, no es un gran problema porque suele ponerlos donde es fácil borrarlos.
  • ¿Donde esta el gato? Apenas aparece.

Explincadole a DALL-E lo que son las viñetas

Primero vamos a por el tema de las viñetas. Vamos a indicarle a DALL-E que no queremos que se salga de una estructura de pagina ¿Cómo? Con una plantilla como esta:

Subiremos esta plantilla, marcaremos como «borrar» las zonas grises y DALL-E solo podrá dibujar dentro de ellas.

Como no reconoce el estilo de dibujo que le pido le pongo uno que si que conoce: render 3D

También le facilito la vida eliminado los nombres de Kelvin y Fred, poniendo directamente niño (kid) y gato (cat)

A comic strip with 4 panels. Render 3D 
Panel 1: A kid wearing a coat and a wool cap gets off the school bus. It is winter, the ground is snowy.
Panel 2: A cat is lying in the entrance behind the door of the house.
Panel 3: kid, from outside, opens the door of the house.
Panel 4: cat jumps on kid

Veamos algunos de los resultados:

Tiene mejor pinta pero las viñetas siguen siendo variaciones de una misma sentencia. Otro problema que se ha resuelto al decirle que trate de hacer algo con estilo render 3D , en lugar de un comic es que no pone «pseudopalabras» sueltas por el texto.

¿Entiende DALL-E que cada sentencia es una viñeta?

Vamos a probar las capacidades de compresión de DALL-E. Para ello le voy a pedir que me ponga una figura geométrica sencilla en cada viñeta. Se lo voy a pedir de dos formas, con números (1,2,3,4) y con ordinales (primero, segundo, tercero, cuatro).

A comic strip with 4 panels. Render 3D. 
Panel 1: a circle.
Panel 2: a triangle.
Panel 3: a square.
Panel 4: a hexagon.
A comic strip with 4 panels. Render 3D. 
First Panel: a circle.
Second Panel: a triangle.
Third Panel: a square.
Fourth Panel: a hexagon.

Selecciono un par de casos como ejemplo:

Esta claro que DALL-E no tiene una idea clara de lo que le pido.

Dibujo viñeta a viñeta

Siguiente prueba, si es incapaz de entender las cuatro viñetas a la vez, que pasa si se la explico de una en una. Empiezo por la plantilla que he creado antes, marco la primera viñeta como «borrar» y le paso la descripción de esa viñeta. Elijo el mejor resultado y lo uso para crear la segunda viñeta, elijo el mejor resultado y paso a la tercera y luego a la cuarta.

Me preocupa si mantendrá la coherencia entre viñetas o cada una será con un estilo.

Resumo el proceso poniendo cada una de las cuatro sentencias usadas:

[1] A kid gets off the school bus. It is winter, the ground is snowy, Kelvin is wearing a coat and a wool cap. 3D render
[2] A cat is lying in the entrance behind the door of the house. 3D render
[3] kid, from outside, opens the door of the house.
[4] when the kid open the door a cat jumps on kid

El resultado de este proceso:

No creo que le den un premio Eisner. Pero funciona y mantiene cierta coherencia entre viñetas.

Hay varias cosas que podría mejorar:

  • Dedicar más tiempo a los prompts que en este ejemplo son muy poco detallados
  • Elegir un estilo mejor que render 3D
  • Se pueden introducir correcciones a mano en cada viñeta antes de generar las siguiente
  • Generar mayor número de pruebas, yo solo he generado 3 casos y elegido el mejor.
  • Marcar mejor los límites de la viñeta para evitar esa niebla gris que rodea cada viñeta (y que viene de los restos de color gris que no he marcado completamente para borrar)

Conclusiones

Por lo tanto ¿Es capaz de dibujar un comic?. Si, es capaz, pero no será un gran comic. Hay que indicarle la estructura de las viñetas a mano y cuidar mucho los prompts. Por otro lado se puede llegar a narrar una historia lo suficientemente bien como para entenderla.

Otro punto son los diálogos, a DALL-E no se le dan bien las palabras y hay que añadirlos a parte.

Puedes ver un vídeo sobre este tema en mi canal de Youtube:

Haz click para ver el vídeo en mi canal de Youtube

Cómo hacer «uncrop» de una imagen con DALL-E

Uncrop, palabra que tiene un difícil traducción al español, seria algo así como «desrecortar», lo que hace es, a partir de una imagen, extender esta hacia donde le indiquemos completando la «parte que falta» de la misma.

Veamos como hacerlo usando DALL-E

Para empezar a probarlo necesitaremos una imagen o dibujo para extenderlo. En este caso vamos a usar una nave espacial estilo pixel art. Se la pediremos a DALL-E:

pixel art spaceship for videogame

Nave generada por DALL-E

El resultado es una imagen de 1024×1024, ahora queremos que DALL-E use esta imagen como base para crear sprites para videojuegos. Para ellos, partiendo de nuestra nave, la reducimos a un 25% de su tamaño y la ponemos sobre un fondo negro de 1024×1024, realmente el color da igual, por comodidad es mejor que sea un color oscuro ya que la herramienta que tiene DALL-E para editar imágenes pinta de blanco las zonas a modificar.

La zona negra se marcará para borrar

Como puede verse hemos colocado la nave en la esquina superior izquierda, podemos colocarla donde queramos, sabiendo que la zona negra es la que se va a completar.

Subimos la imagen a DALL-E, marcarmos la zona negra como «para borrar» e introducimos la descripción de la nueva imagen que queremos obtener, en nuestro caso:

sprites of a spaceship for a video game: with engines on, with engines off, firing, with force field, damaged

El resultado:

No era lo deseado pero aun así se aproxima a la idea

Por una vez voy a poner un resultado que deja a DALL-E mal. Ha hecho algo que se parece a lo que le pedimos, pero no lo que le pedimos exactamente y es que el abuso de artículos, vídeos y demás donde se ven resultados increíbles (seleccionados, obviamente) no transmiten la realidad que es un punto intermedio entre lo sublime y lo fallido. Hace cosas increíbles, pero también a veces falla en otras mucho más simples y eso no quiere decir que no haya manera de usarlo para lo que le he pedido. Seguramente exista, quizás usando esta técnica para generar cada uno de los sprites uno a uno. O adaptando el texto introducido, usando prueba y error. Pero ya no es tan intuitivo como «pedirle lo que quieres».

Puedes ver otro uncrop en el siguiente vídeo de mi canal de Youtube:

Haz click para ver el vídeo en Youtube

Usar Serial Plotter con Arduino

Todos los que usamos el editor de Arduino estamos acostumbrados al Monitor Serie o Serial Monitor para ver los datos que se envían por el puerto serie del Arduino. Pero junto a él, en el menú «Tools», hay otra herramientas que permite ver estos datos forma visual el Serial Plotter que dibuja una gráfica (o varias) con los datos que le devuelva el puerto serie.

Su uso es muy sencillo los datos tiene que seguir ir codificados de la siguiente manera:

  • En cada linea van los datos separados por coma y espacio «, «
  • Cada nueva linea va separada de la anterior por un salto de linea

La forma mas sencilla de entenderlo es con este código:

Serial.print(var1);
Serial.print(", ");
Serial.print(var2);
Serial.println();

Si bien no es una herramienta muy avanzada y tiene bastantes limitaciones sirve para visualizar datos visuales de una forma sencilla. Solo puede representar un tipo de gráfica, el tiempo en el eje horizontal y el valor de la variable en el eje vertical. Con este tipo de gráficas tienes el problema de que algunos datos son muy rápidos y serial plotter no permite almacenar la gráfica para revisarla.

Veamos un ejemplo donde se muestran varias gráficas simultáneamente:

float t = 0;
float oldDataT = 0;
float f = 1; //frecuencia
unsigned long oldMillis = 0;
unsigned long nowMillis = 0;

void setup() {
  Serial.begin(9600); //iniciamos el Serial para mostrar la gráfica
  while (!Serial) {}
}

void loop() {
  nowMillis = millis();
  double dt = double(nowMillis - oldMillis)/1000;
  
  t += dt;
  double tf = t*f;
  double sawtoothW = tf;
  double sinW = sin(tf*2*PI);
  double posSinW = sin(tf*PI);
  double squareW = (sin(tf*2*PI) > 0) ? 1: 0;
  double triangleW = (sin(tf*2*PI) > 0) ? 2*tf: 2-(2*tf);

  if(t - oldDataT > 0.05){ //si no lo datos van demasiado rápidos 
    oldDataT = t;
    Serial.print(sawtoothW);
    Serial.print(", ");
    Serial.print(sinW);
    Serial.print(", ");
    Serial.print(posSinW);
    Serial.print(", ");
    Serial.print(squareW);
    Serial.print(", ");
    Serial.print(triangleW);
    Serial.println();
  }

  oldMillis = nowMillis;    

  if(t >= 1/f){ //cuando alcanza un periodo se reinicia
    t = 0;
    oldDataT = 0;
  }
}

El resultado es el siguiente:

Gráficas en serial plotter

En la imagen se pueden ver gráficas de varias formas generadas desde Arduino.

Un ventaja que tiene este sistema es que los datos son compatibles con el formato CVS, habitual cuando se trabaja con datos.

Añadir guías a las gráficas

Hay algún truco que podemos usar para mejorar la visualización de las gráficas. Establecer guías, una guía no es nada más que una línea recta paralela al movimiento del gráfico y que tiene siempre un valor fijo. Se puede establecer una guía poniendo un valor fijo en los datos que se devuelven. Se usan como referencia visual. Si conoces los máximos y mínimos del valor puedes establecer otras dos guías una en cada valor y así evitar que la gráfica vaya dando «saltos» como a veces ocurre cuando varia mucho el valor.

Puedes ver el vídeo sobre este tema en mi canal de Youtube:

Haz click para ver el vídeo en Youtube

Filtro de media móvil exponencial

Este es posiblemente uno de los filtros más sencillos de implementar que hay. Su funcionamiento es similar al filtro de media móvil, pero solo tiene en cuenta el valor actual V y el anterior Vant. El filtro consiste en multiplicar Vant por un valor a y V por (1-a) y luego sumarlos.

V = ((1-a) * V) + (a * Vant)

Siendo a un valor entre 0 y 1. Cuanto mayor sea el valor de a mayor importancia se le dan a los valores anteriores, sería como aumentar la n, el número de muestras, del filtro de media móvil. Al igual que con la n, a mayor valor tenga a, mayor retardo se produce en la señal. Los valores habituales para a suelen oscilar entre 0,2 y 0,6.

Veamos en nuestro simulador como usarlo para, partiendo de una señal sinusoidal (verde)a la que añadimos ruido (azul) la filtramos usando un filtro de media móvil (naranja):

Filtro de media móvil exponencial

Es un filtro simple, rápido y no requiere prácticamente recursos, pero hay un un caso especial que es computacionalmente aun más rápido. Cuando a vale 0,5, en ese caso el filtro se resume en:

V = (V + Vant) / 2 

La división por dos se puede reemplazar por un desplazamiento a la derecha lo que permite usar números enteros. De esta forma el coste de aplicar este filtro es mínimo.

Filtro elimina banda o stop banda por software

Un filtro elimina banda es un filtro con dos frecuencias de corte, el filtro atenúa las frecuencias entre esas dos frecuencias de corte. Se podría ver cómo lo contrario a un filtro paso banda.

Una de las formas más simples de hacerlo es usar un filtro paso alto que deje solo las frecuencias por encima a la frecuencia de corte superior y uno paso bajo que deje solo las frecuencias inferiores a la de corte inferior. Para obtener el resultado sería necesario sumar de ambos filtros.

Veamos un poco de código:

//filtro paso bajo
double a_low = dt / (RC_low + dt)
output_low[t] = output_low[t-1] + a * (sensor[t] - output_low[t-1]); 

//filtro paso alto
double a_high = RC_high / (RC_high + dt);
output_high[t] = a_high * (output_high[t-1] + sensor[t] - sensor[t-1]);

//sumamos ambos
output = output_high[t] + output_low[t];

En nuestro simulador podemos usarlo para hacer una curiosa prueba, partiendo de una señal sinusoidal (verde), le añadimos ruido (azul) y usamos el filtro elimina banda para eliminar la señal y dejar solo el ruido (naranja):

Filtro elimina banda

Filtro paso banda por software

Un filtro paso banda es un filtro que tiene dos frecuencias de corte. Una por debajo, atenúa las frecuencias por debajo de ella y otra encima, atenúa toda frecuencia por encima de ella.

Hay muchas formas de implementarlo, una de las más sencillas es usar dos filtros paso bajo, cada uno de ellos ajustado a una de las frecuencias. La forma de funcionar de este filtro es muy sencilla. Un filtro atenúa todo lo que hay por encima de la frecuencia superior y el otro filtro atenúa todo lo que hay por encima de la frecuencia inferior. Un momento, si queremos quedarnos con las frecuencias entre ambos filtros y el filtro inferior atenúa todo lo que hay por encima de su frecuencia de corte. ¿No está atenuando las frecuencias que queremos conservar? Efectivamente. La idea es que solo queden las frecuencias por debajo de la frecuencia inferior para restarlas del resultado obtenido por el filtro superior. Eso nos dejara solo las frecuencias comprendidas entre ambos filtros.

Veamos un poco de código:

//filtro paso bajo frecuencia inferior
double a_low = dt / (RC_low + dt)
output_low[t] = output_low[t-1] + a * (sensor[t] - output_low[t-1]); 

//filtro paso bajo frecuencia superior
double a_high = dt / (RC_high + dt)
output_high[t] = output_high[t-1] + a * (sensor[t] - output_high[t-1]); 

//restamos
output[t] = output_high[t] - output_low[t]; 

Y ahora usaremos nuestro simulador para hacer una prueba y eliminar ruido. En la imagen resultante podemos ver como la señal original (verde) es afectada por el ruido (azul) y que tras el filtrado (naranja) se elimina gran parte del mismo acercando la forma de la señal a la original.

Filtro paso banda

Filtro paso alto por software

Un filtro paso alto es un filtro que fija una frecuencia de corte y deja pasar cualquier frecuencia superior a esa, mientras que atenúa frecuencias inferiores, Al menos esa es la teoría, en la realidad «el corte» no es tan limpio y se ven afectadas otras frecuencias. En realidad la frecuencia de corte e inferiores no sufren una caída completa a cero si no una gran atenuación.

En electrónica el modelo más sencillo de filtro paso alto es un filtro RC usando una resistencia y un condensador. En la imagen inferior se puede ver como montarla y como afecta a una señal ruidosa reduciendo el ruido de baja frecuencia, dejando solo la señal de alta frecuencia.

Filtro paso lato RC

Para realizar nuestro filtro paso bajo por software vamos a modelar matemáticamente el filtro RC.

double a = RC / (RC + dt);
output[t] = a * (output[t-1] + sensor[t] - sensor[t-1]);

Donde:

  • dt – Es el tiempo, en sg, entre las muestras t-1 y t
  • RC – Resistencia * Capacidad del condensador
  • output[t] – Es la señal filtrada en el momento t.
  • sensor[t] – Es la entrada sin filtrar en el momento t.

RC en nuestro caso es un valor numérico que emula el comportamiento Para calcular RC partiremos de la frecuencia de corte «fc» que deseemos en Herzios:

RC = 1 / (2 * Pi * fc)

Tras esta visión general vamos a los detalles.

Cuando t = 0 se puede usar como output:

output[0] = sensor[0]

Si no necesitamos guardar los valores no hace falta que usemos un array, podemos usar una sola variable output que almacena el valor del resultado anterior:

output = a * (output + sensor[t] – sensor[t-1]);

Veamos un ejemplo de señal (verde) sinusoidal sometida a ruido de baja frecuencia (azul) que la hace oscilar y filtrada usando un filtro de paso bajo (naranja):

Ejemplo de filtrado con filtro de paso alto

Puedes probar aquí un simulador que cree para mostrar como funcionan los diferentes filtros.

Puedes ver esta misma explicación para filtros de paso bajo.