Ojos

Ojos

He estado con lío esta semana, pero hace tiempo que quería hacer algo divertido como un generador de caras.. como eso lo veo complejo, vamos a empezar por un generador de ojos.

Me sorprendió lo sencillo que ha resultado hacer un ojo humano con funciones gráficas en R, casi en 5 minutos teníamos una ecuación que daba el pego, resultaba aparente y bastante fácil, hasta mi hija de 7 años se quedó encantada y empezó a jugar con los colores del iris.

R no es un lenguaje de diseño gráfico, otros como processing son fantásticos para eso, pero a veces me sorprenden los resultados con R.

Como veis tengo especial predilección por los gráficos generativos, podéis buscar bajo el hashtag #generative, son diseños que dependen de funciones matemáticas y con cierto grado de aleatoriedad. En este blog hemos hecho varios ejemplos con estilo propio como:

Hoy vamos a hacer un ensayo del generador de caras, y lo empezaremos haciendo una función creadora de la forma de un ojo humano a partir de funciones simples de R, como las que pintan círculos, líneas y splines.

Programar una gráfica de un ojo

Aunque pensé hacerlo con un circulo simple, la primera prueba con splines ha quedado tan perfecta que así se quedó. La idea es crear el ojo a partir de unos puntos básicos que definan la forma y trazar un spline entre ellos.

Un spline es una curva suavizada que pasa por los puntos definidos.

El iris y la pupila si que son dos círculos simples centrados y para dar un poco de gracia pintamos un circulo dentro de la pupila como un reflejo de luz.

Veamos el código de esta primera prueba, los círculos en el paquete base se dibujan con la función symbols:

# parametros del lienzo:
par(bg = 'white')
par(oma=c(0,0,0,0)+ 1.2)  # all sides have 3 lines of space  
par(mar=c(0,0,0,0) + 1)
# Creamos un OJO con splines
# Definimos los puntos base del ojo
    x<-c(0,1,2,3,3,2,1,0)
    y<-c(0,0.8,0.8,0,0,-0.7,-0.9,0)
    # creamos un spline de los párpados
    ojo <- xspline(x, y, 1, draw = FALSE)
    # pintamos los puntos base anteriores
    plot(x, y, pch = 19, col="red",main="Pintamos un ojo con R")
    # tambien el poligono que forma el blanco del ojo
    polygon(ojo, col=gray(0.90))
    # y el spline
    lines(ojo, lwd=2)
    
    # iris
    symbols(1.5, y=0, circles=.6,add = TRUE,bg="lightblue",lwd=2, inches = FALSE)
    # pupila
    symbols(1.5, y=0, circles=.2,add = TRUE,bg="black", inches = FALSE)
    # reflejo
    symbols(1.45, y=0.12, circles=.05,add = TRUE,bg="white", inches = FALSE)
    #párpados
      parpados <- xspline(x, y, shape =0.5, draw = FALSE)
    lines(parpados, lwd=2)
Ojo inicial, con los puntos de control marcados en rojo

Ojo inicial, con los puntos de control marcados en rojo

Vemos que el resultado es gracioso y se parece a un ojo de verdad.

La función xsplines crea una curva a partir de unos puntos de control, en este caso los 8 puntos que hemos definido, y el parámetro shape toma valores de -1 a 1 y nos marca la forma del spline respecto a esos puntos de control. Puedes probar diferentes valores para ver cómo cambia la línea.

Iris

Para añadir más realismo vamos a hacer las marcas del iris mediante lineas radiales. Haremos una función en la que generaremos un número determinado de radios entre el círculo interior de la pupila y el exterior del iris. El color de las líneas lo haremos aleatorio con diferentes tonos de grises.

La función iris tiene varios argumentos: el radio del iris, el de la pupila, el punto central del ojo y el número de líneas radiales generadas:

# Función genera rayas en iris
  iris<-function(rext=.6,rint=0.2,x0=1.5,y0=0,n_lineas=30){
      # dividimos el círculo en tantos sectores como lineas
      p_lin<-runif(n_lineas,0,(2*pi))
      # para cada punto trazamos una linea
      for(i in 1:n_lineas){
          ang<-p_lin[i] # angulo de la linea
          plong<-runif(1) # parametro aleatorio de a longitud de la linea
          #plong<-ifelse(any(plong>1,plong<(rint/rext)) ,0.9,plong)
          
          #calculamos los puntos inicial y final de la linea
          x1<-x0 + rint*cos(ang)
          y1<-y0 +  rint*sin(ang)
          x2<- x0 + plong*(rext*cos(ang))
          y2<- y0 +  plong*(rext*sin(ang))
          # pintamos la linea
          lines(x=c(x1,x2),y=c(y1,y2),col=gray(plong))
        }
  }

# pintamos en la imagen
iris(rext=0.6,x0=1.5,n_lineas=150)
Mismo ojo, con manchas-rayas en el iris

Mismo ojo, con manchas-rayas en el iris

Pestañas

Por último pintamos unas pestañas, aunque no han quedado nada bien. La idea es, sobre la línea de arriba del párpado, dividirla en puntos y pintar en cada uno una línea vertical.

# Funcion pestañas
pestanas<-function(linea_pes,n_lineas=50, long=0.2){
  # numeor de puntos o pestañas
  p_lin<-sample(seq(10,length(lin_arriba$x)-10),size=n_lineas,replace = TRUE)
        
  for(i in p_lin){
    plong<-abs(rnorm(1,0.1))
    # dos puntos que definen la linea de cada pestaña
    x1<-linea_pes$x[i]
    y1<-linea_pes$y[i]
    x2<- x1#+0.04
    y2<- y1+long*plong
    # pintamos cada pestaña
    lines(c(x1,x2),c(y1,y2))
    }
}

# calculamos la linea de arriba del parpado con los puntos base anteriores
    x<-c(0,1,2,3)
    y<-c(0,0.8,0.8,0)
    lin_arriba <- xspline(x, y, 1, draw = FALSE)

    # llamamos a la fucnión pestaña
pestanas(linea_pes = lin_arriba,n_lineas=120)
Prueba de pestaña… que no me gusta

Prueba de pestaña… que no me gusta

Juntamos todo

Nos falta generalizar el punto de inicio de coordenadas del ojo, por lo que ahora creamos una nueva función que añade x0,y0, también hemos cambiado, solo por probar la función symbols para pintar círculos por draw.circle del paquete plotrix.

Esta librería tienen multitud de funciones gráficas, que nos pueden ser útiles como la que pinta lineas radiales. Puedes encontrar más información aquí

Hemos añadido alguna aleatoriedad con funciones como rnorm() en los puntos generadores. También un parámetro p que define la escala general y otro pap que define la longitud de la línea de parpado secundaria.

Para probarla además de la función, creamos una pintura con varios ojos aleatorios:

library(plotrix)

# Función general que pinta un ojo
f.ojo1<-function(x0=5,y0=5,p=1, color="lightblue",pap=0.2){
    x<-c(0,1,2,3,3,2,1,0)*p
    y<-c(0,0.8+rnorm(1,0,0.1),0.8+rnorm(1,0,0.1),0,0,-0.7+rnorm(1,0,0.1),-0.9+rnorm(1,0,0.1),0)*p
    #max(x)-min(y)
    ojo <- xspline(x, y, 1, draw = FALSE)
    x<-x0+x
    y<-y0+y
    ojo$x<-x0+ojo$x
    ojo$y<-y0+ojo$y
    #points(x, y, pch = 19, col="red")
    polygon(ojo, col=gray(runif(1,0.20,1)))
    lines(ojo, lwd=1)
    
    # iris
    riris<-p*(0.55)#+rnorm(1,0,0.1))
    #symbols(x0+1.5, y=y0, circles=.5*p,add = TRUE,bg="lightblue",lwd=2, inches = T)
    draw.circle(x0+1.5*p,y=y0,radius=riris,col=color,lwd=1)
    # pupila
    #symbols(x0+1.5, y=y0, circles=.2*p,add = TRUE,bg="black", inches = T)
    iris(rext=riris,rint=0.2*p,x0=x0+1.5*p,y0=y0,n_lineas=105)
    draw.circle(x0+1.5*p,y=y0,0.2*p,col="black",lwd=2)
    # reflejo
    #symbols(x0+1.45, y=y0+0.12*p, circles=.05,add = TRUE,bg="white", inches = T)
    draw.circle(x0+1.45*p,y=y0+0.12*p,0.05*p,col="white",lwd=1)
    #parpados
    parpados <- xspline(x, y, 0.5, draw = FALSE)
    lines(parpados, lwd=2)
    #lines(parpados$x,parpados$y*1.2, lwd=2,col="red")
    parpa_y<-c(y[1],y[2]+pap,y[3]+pap,y[4])
    parpa_x<-c(x[1],x[2],x[3],x[4])
    #parpado superior
     lines(xspline(parpa_x, parpa_y, 1, draw = FALSE), lwd=3,col="black")
    # pestanas(linea_pes = xspline(parpa_x, parpa_y, 1, draw = FALSE),n_lineas=100)
    parpa_y<-c(y[1],y[7]-pap,y[6]-pap,y[4])  
    #parpado superior
     lines(xspline(parpa_x, parpa_y, 1, draw = FALSE), lwd=3,col="black")
}

# vamos con un ejemplo

# hacemos un vector de colores
  colores<-c("lightblue","aliceblue","burlywood4","bisque3","bisque4","azure2","darkolivegreen3","aquamarine1","aquamarine3","antiquewhite3", "red", "blue","violet")

  plot(1, type="n", xlab="", ylab="", xlim=c(0, 7), ylim=c(0, 7))
    for (i in 1:6){
        f.ojo1(x0=runif(1,0,5-2),y0=runif(1,0.5,5.5),color=sample(colores,1),p=runif(1,0.5,2))
    }

Cuadro de iris

Otro obra artística que hemos hecho de ejemplo, con nuestras funciones, es un cuadro con varios iris y pupilas juntos, solo por diversión:

Lo haremos haciendo una nueva función irisE() que pinta solo el centro del ojo sin párpados, después con un bucle pintaremos varios iris sobre el lienzo en colores aleatorios:

irisE<-function(x0=2,y0=2,p=1, color="lightblue"){
        # iris
    riris<-p*(0.6)
    draw.circle(x0,y=y0,radius=riris,col=color,lwd=1)
    # pupila
     iris(rext=riris,rint=0.2*p,x0=x0,y0=y0,n_lineas=100)
    draw.circle(x0,y=y0,0.2*p,col="black",lwd=2)
    # reflejo
    draw.circle(x0-0.035,y=y0+0.1,0.05*p,col="white",lwd=1)
}

colores<-c("lightblue","aliceblue","burlywood4","bisque4","azure2","darkolivegreen3","aquamarine3","antiquewhite3", "red", "blue","violet")
par(bg = 'black')
par(oma=c(0,0,0,0))  # all sides have 3 lines of space  
par(mar=c(0,0,0,0) + 0.1)
plot(1, type="n", xlab="", ylab="", xlim=c(1, 6), ylim=c(1, 6))
for (i in 1:6){
    for (j in 1:6){
     irisE(x0=i,y0=j,color=sample(colores,1),p=0.7)    
    }
}

Esto es todo por hoy, espero que os guste el OJO!!!