To be able to edit code and run cells, you need to run the notebook yourself. Where would you like to run the notebook?

This notebook takes about 30 seconds to run.

In the cloud (experimental)

Binder is a free, open source service that runs scientific notebooks in the cloud! It will take a while, usually 2-7 minutes to get a session.

On your computer

(Recommended if you want to store your changes.)

  1. Download the notebook:
  2. Run Pluto

    (Also see: How to install Julia and Pluto)

  3. Open the notebook file

    Type the saved filename in the open box.

Frontmatter

If you are publishing this notebook on the web, you can set the parameters below to provide HTML metadata. This is useful for search engines and social media.

Author 1

Cálculo científico con Julia 🧪

¿Qué es Julia?

Julia es un moderno lenguaje de programación especialmente diseñado para el cálculo científico que destaca principalmente en la construcción de modelos matemáticos.

👀 Reading hidden code
md"""
# Cálculo científico con Julia 🧪

!!! question "¿Qué es Julia?"
Julia es un moderno lenguaje de programación especialmente diseñado para el cálculo científico que destaca principalmente en la construcción de modelos matemáticos.
"""
9.5 ms

Filtros para imágenes

En este taller veremos como las imágenes pueden representarse matemáticamente como matrices y cómo podemos diseñar filtros para cambiar el aspecto de las imágenes con Julia.

👀 Reading hidden code
md"""
# Filtros para imágenes

En este taller veremos como las imágenes pueden representarse matemáticamente como **matrices** y cómo podemos diseñar filtros para cambiar el aspecto de las imágenes con Julia.
"""
372 μs

Representación de imágenes como matrices

Si miras con lupa una imagen digital, verás está formada por pequeños puntitos de colores llamados píxeles que se organizan en forma de tabla o matriz conocida como mapa de bits. El número de filas y columnas de esa matriz es la resolución de la imagen.

El color de cada pixel se obtiene mediante la combinación de los colores básicos R (red), G (green) y B (blue) y esa combianción se puede expresar como un código numérico.

👀 Reading hidden code
244 μs

Mapa de bits

👀 Reading hidden code
55.8 ms

En este taller trabajaremos con imágenes en blanco y negro que son más sencillas de representar matricialmente.

Mapa de bits en escala de grises.

👀 Reading hidden code
261 μs

Podemos definir fácilmente una variable en Julia para representar un pixel con un nivel de gris determiando por un parámetro interactivo.

Observa cómo cambia de color el píxel de más abajo cuando movelos el deslizador del nivel del gris.

👀 Reading hidden code
288 μs
0.0
👀 Reading hidden code
278 ms
👀 Reading hidden code
8.2 ms

Ahora podemos definir una matriz de 3 x 3 (3 filas y 3 columnas) con números entre 0 y 1 que representan el nivel de gris.

En Julia las matrices se representan entre corchetes [ ], separando sus filas con punto y coma ; y las columnas con espacios.

👀 Reading hidden code
425 μs
3×3 Matrix{Float64}:
 0.1  0.2  0.3
 0.4  0.5  0.6
 0.7  0.8  0.9
matriz = [0.1 0.2 0.3; 0.4 0.5 0.6; 0.7 0.8 0.9]
👀 Reading hidden code
17.9 ms

A partir de esta matriz podemos usar el paquete Images de Julia para generar fácilmente una imagen de 3x3 con pixels cuyos niveles de gris se corresponden con los valores de la matriz anterior.

👀 Reading hidden code
287 μs
img = Gray.(matriz)
👀 Reading hidden code
92.6 ms

Ahora que ya entiendes cómo se representa una imagen matemáticamente mediante una matriz, vamos a cargar una imagen real con la que trabajaremos. Selecciona la imagen que más prefieras.

👀 Reading hidden code
288 μs
👀 Reading hidden code
3.4 s
👀 Reading hidden code
1.7 ms

A continuación se muestra la matriz que representa esta imagen.

👀 Reading hidden code
223 μs
512×512 reinterpret(reshape, N0f8, ::Array{Gray{N0f8},2}) with eltype N0f8:
 0.569  0.22   0.192  0.349  0.537  0.353  …  0.271  0.518  0.443  0.6    0.694
 0.455  0.396  0.157  0.263  0.353  0.212     0.498  0.475  0.51   0.584  0.49
 0.302  0.447  0.18   0.18   0.384  0.235     0.482  0.384  0.392  0.345  0.318
 0.278  0.533  0.373  0.188  0.325  0.471     0.333  0.435  0.286  0.294  0.275
 0.341  0.478  0.518  0.196  0.298  0.341     0.365  0.259  0.231  0.314  0.337
 0.322  0.298  0.624  0.239  0.282  0.376  …  0.267  0.208  0.333  0.596  0.714
 0.169  0.2    0.525  0.51   0.275  0.478     0.239  0.357  0.725  0.776  0.643
 ⋮                                  ⋮      ⋱                       ⋮      
 0.412  0.435  0.518  0.569  0.58   0.506     0.4    0.365  0.322  0.325  0.325
 0.376  0.447  0.541  0.596  0.584  0.506     0.416  0.38   0.361  0.361  0.333
 0.435  0.525  0.596  0.541  0.549  0.498     0.4    0.341  0.388  0.353  0.349
 0.553  0.569  0.553  0.573  0.494  0.482     0.345  0.361  0.325  0.349  0.306
 0.612  0.553  0.514  0.467  0.404  0.467  …  0.333  0.322  0.31   0.314  0.271
 0.043  0.043  0.051  0.055  0.059  0.059     0.02   0.016  0.027  0.016  0.016
👀 Reading hidden code
41 ns

Filtros de imágenes

Ahora veremos como podemos aplicar un filtro a la imagen seleccionada para provocar un cambio en su aspecto.

¿Qué es un filtro?

Un filtro es una transformación que se aplica a cada uno de los pixels de la imagen.

Una transformación habitual es una convolución que consiste en multiplicar cada pixel y sus pixels vecinos por una pequeña matriz. El nuevo valor del pixel se obtienen sumando los valores de estos productos.

Dependiendo de los valores de la matriz de convolución, podemos obtener filtros que provocarán distintos cambios de aspecto de la imagen.

La siguiente función de Julia aplica la transformación de convolución. Esta función recorre los pixels de la matriz original y aplica la transformación de convolución a cada uno de ellos, pero no te preocupes si no la entiendes del todo.

👀 Reading hidden code
510 μs
convolucion (generic function with 1 method)
function convolucion(imagen, filtro)

# Otenemos las dimensiones de la matriz de la imagen original
imagen_filas, imagen_columnas = size(imagen)
filtro_filas, filtro_columnas = size(filtro)

# Inicializamos la matriz de la imagen resultante con ceros
filtrada = zeros(imagen_filas-filtro_filas+1, imagen_columnas-filtro_columnas+1)
filtrada_filas, filtrada_columnas = size(filtrada)

# Aplicamos el filtro a cada pixel de la matriz original iterando sobre cada pixel por filas y columnas.
for i in 1:filtrada_filas
for j in 1:filtrada_columnas
filtrada[i,j] = sum(imagen[i:i+filtro_filas-1, j:j+filtro_columnas-1] .* filtro)
end
end
return filtrada
end
👀 Reading hidden code
2.2 ms

Filtro para difuminar

Vamos a empezar con un filtro sencillo para difuminar la imagen uniformemente. Para ello utilizaremos una matriz de convolución de 3x3 con los mismos valores. La intensidad del difuminado dependerá del valor de la matriz de convolución.

Como todos los valores de la matriz de convolución son iguales, el nuevo valor del pixel es la media de los valores del pixel y de sus vecinos.

A continuación definimos la matriz de un filtro de difuminado cuyos valores dependen del pámetro interactivo intensidad.

👀 Reading hidden code
445 μs
👀 Reading hidden code
107 ms
3×3 Matrix{Float64}:
 0.1  0.1  0.1
 0.1  0.1  0.1
 0.1  0.1  0.1
👀 Reading hidden code
502 ns

Y tomamos una submatriz cualquiera de nuestra imagen con las mismas dimensiones que la matriz del filtro.

👀 Reading hidden code
237 μs
3×3 Matrix{Float64}:
 1.0  0.5  1.0
 0.5  0.0  0.5
 1.0  0.5  1.0
submatriz = [1 0.5 1; 0.5 0 0.5; 1 0.5 1]
👀 Reading hidden code
15.2 ms

Ahora aplicamos la transformación de convolución de este filtro a la submatriz de la imagen.

👀 Reading hidden code
208 μs
3×3 Matrix{Float64}:
 0.1   0.05  0.1
 0.05  0.0   0.05
 0.1   0.05  0.1
submatriz .* filtro_dispersion
👀 Reading hidden code
6.6 μs

El resultado de la convolución es la suma de estos valores, que es una combinación lineal de los valores originales.

👀 Reading hidden code
279 μs
0.6
resultado = sum(submatriz .* filtro_dispersion)
👀 Reading hidden code
4.3 μs
Matriz originalFiltro de convoluciónResultado
👀 Reading hidden code
447 μs

Esta operacion se reliza sobre cada posible submatriz de la matriz original.

A continuación se ve el resultado para la imagen seleccionada.

👀 Reading hidden code
247 μs
510×510 Matrix{Float64}:
 0.291765  0.238431  0.259608  0.286667  …  0.372157  0.397255  0.425098  0.437647
 0.312157  0.271765  0.240392  0.261176     0.382353  0.379608  0.370588  0.349412
 0.345098  0.309412  0.264314  0.261961     0.363922  0.316863  0.294118  0.279216
 0.376471  0.344706  0.304314  0.271765     0.321961  0.271765  0.295686  0.338039
 0.347451  0.358824  0.346667  0.299608     0.284314  0.298431  0.38      0.467059
 0.290196  0.363137  0.383137  0.356863  …  0.310588  0.382745  0.475294  0.543137
 0.22902   0.333725  0.393725  0.420392     0.314902  0.426275  0.508235  0.540784
 ⋮                                       ⋱                                
 0.429804  0.462745  0.496471  0.50902      0.34902   0.334902  0.307059  0.288627
 0.415686  0.462745  0.501961  0.505098  …  0.355686  0.343529  0.318824  0.30549
 0.428627  0.476863  0.507451  0.492941     0.342353  0.337255  0.319608  0.311765
 0.459608  0.494118  0.502745  0.482353     0.336863  0.331765  0.321961  0.312549
 0.49098   0.48902   0.46902   0.447451     0.315686  0.312549  0.306275  0.296471
 0.34902   0.337647  0.316863  0.305882     0.211373  0.205882  0.203922  0.193333
convolucion(imagen, filtro_dispersion)
👀 Reading hidden code
40.6 ms

Aquí puedes ver la imagen original y el resultado de la aplicación del filtro.

👀 Reading hidden code
297 μs
aplicar_filtro(imagen, filtro_dispersion)
👀 Reading hidden code
44.8 ms

¿Notas la diferencia?

Prueba a cambiar el deslizador con la intensidad del filtro.

👀 Reading hidden code
203 μs

Más filtros

A continuación puedes elegir distintos filtros de convolución habituales en los programas de edición de imágenes.

👀 Reading hidden code
252 μs
👀 Reading hidden code
347 ms
3×3 Matrix{Float64}:
 0.0  0.0  0.0
 0.0  1.0  0.0
 0.0  0.0  0.0
👀 Reading hidden code
27 ns
👀 Reading hidden code
48.2 ms

Experimenta

Ahora prueba a definir tus propios filtros y observa como se transforma la imagen.

👀 Reading hidden code
234 μs

Matriz de convolución

👀 Reading hidden code
76.2 ms



👀 Reading hidden code
383 μs
👀 Reading hidden code
36.0 ms

¿Cuál es el filtro que desplaza la imagen hacia la derecha?

👀 Reading hidden code
177 μs

Pista

Para desplazar la imagen hacia la derecha el nuevo valor del pixel debe ser el valor del pixel a su izquierda.

👀 Reading hidden code
398 μs

¿Cuál es el filtro para hacer más nítida la imagen?

👀 Reading hidden code
150 μs

Pista

El filtro de nitidez se consigue aplicando el filtro de detección de bordes sobre el filtro original.

👀 Reading hidden code
939 μs
👀 Reading hidden code
3.3 s
👀 Reading hidden code
944 μs
👀 Reading hidden code
581 μs
👀 Reading hidden code
520 μs