Está en la página 1de 23

5/4/2021 Fourier2D

Transformada de Fourier. Aplicación al


procesamiento de imágenes

Contenidos
Transformada 2D de Fourier
Aplicación: compresión de imágenes
Aplicación: orientación de vehículos

Transformada 2D de Fourier

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 1/23
5/4/2021 Fourier2D

Sea f (x, y) una imagen M × N , con x = 0, 1, 2, … , M − 1 (columnas) y


y = 0, 1, 2, … , N − 1 (filas).

La Transformada Discreta de Fourier (DFT en inglés) de f, que llamaremos


F (m, n), viene dada por

M −1 N −1
1 x y
F (m, n) = ∑ ∑ f (x, y) exp(−2πi( m + n)),
MN M N
x=0 y=0

con m = 0, 1, 2, … , M − 1 y n = 0, 1, 2, … , N − 1.

La región rectangular M × N definida para (m, n) se llama dominio de frecuencias,


y los valores de F (m, n) se llaman coeficientes de Fourier.

La Transformada Inversa Discreta de Fourier viene dada por

M −1 N −1
m n
f (x, y) = M N ∑ ∑ F (m, n) exp(2πi( x + y)),
M N
m=0 n=0

con x = 0, 1, 2, … , M − 1 y y = 0, 1, 2, … , N − 1 .

Nota. Hay otras formas de normalizar la DFT y su inversa por medio de los factores
1/M N y M N . El que hemos escogido es el que se usa habitualmente en Python.

Incluso si f (x, y) es real, su transformada F (m, n) es, en general, una función


compleja. Para visualizarla utilizaremos el espectro de potencias,
¯
P (m, n) = F (m, n)F (m, n),

donde ¯
F es el conjugado del complejo F.

Propiedad: Si la imagen es periódica, es decir, si toma los mismos valores en los


bordes superiores en inferiores y en los bordes derecho e izquierdo, entonce, la DFT es
periódica también y el espectro de potencia es simétrico respecto del origen:
P (m, n) = P (−m, −n).

Si la imagen no es periódica, entonces F puede tener discontinuidades. Esto se conoce


como el fenómeno de Gibbs.

Gracias a la propiedad mencionada, podemos representar la DFT en un cuadrado


centrado en (0, 0), lo cual es más conveniente para su visualización.

La DFT y su inversa se obtienen en la práctica usando el algoritmo de la


Transformada Rápida de Fourier (Fast Fourier Transform, FFT). Las funciones
correspondientes de Numpy para la transformada directa y la inversa son fft2 y ifft2.

Para calcular el espectro de potencia usamos la función Numpy abs que nos da el
módulo del complejo. Para mover el origen de la transformada al centro del rectángulo
de frecuencias usamos fftshift. Finalmente, si queremos ver mejor el resultado,
usaremos una escala logarítmica.

Ejemplo

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 2/23
5/4/2021 Fourier2D

In [1]:

from __future__ import division # hace que se utilice la división en p


unto flotante
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from numpy.fft import fft2, fftshift, ifft2

Muestra los gráficos insertados.

In [2]:

%matplotlib inline

Empezamos creando una imagen periódica de tamaño (601, 1201). El periodo, 10.5,

aparece en la dirección horizontal. En vertical, la imagen es constante:

In [3]:

hW, hH = 600, 300


hFrec = 10.5

# Crea una malla en el cuadrado de dimensiones [0,1)x[0,1)


x = np.linspace( 0, 2*hW/(2*hW +1), 2*hW+1) # columnas (Anchura)
y = np.linspace( 0, 2*hH/(2*hH +1), 2*hH+1) # filas (Altura)

[X,Y] = np.meshgrid(x,y)
A = np.sin(hFrec*2*np.pi*X)

plt.imshow(A, extent=[0,1,0,1], cmap ='gray');


H,W = np.shape(A) # Dimensiones de la imagen A

Si entendemos que estamos representando la función A = f (X, Y ) como una superficie,


un corte por un plano paralelo al plano OXZ de dicha superficie sería

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 3/23
5/4/2021 Fourier2D

In [4]:

xx = np.linspace(0,1,1200)
plt.plot(xx,np.sin(hFrec*2*np.pi*xx))
plt.show()

1
que es una función periódica de frecuencia f = 10.5 Hz y periodo T =
f

In [5]:

print "T = ", 1./hFrec

T = 0.0952380952381

El paso siguiente es calcular la DFT centrada en el origen y mostrar la figura del espectro
de potencia (su raíz cuadrada).

In [6]:

F = fft2(A)/(W*H)
F = fftshift(F)
P = np.abs(F)
plt.imshow(P, extent = [-hW,hW,-hH,hH]);

Hacemos zoom (nos acercamos) para ver mejor los valores altos de P a las frecuenccias
±hF req

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 4/23
5/4/2021 Fourier2D

In [7]:

plt.imshow(P[hH-25:hH+25,hW-25:hW+25], extent=[-25,25,-25,25]);

Si cortamos la superficie representada por un plano vertical que contenga el eje OX

tenemos

In [8]:

plt.plot(range(-25,25),P[hH,hW-25:hW+25])
plt.show()

Otros ejemplos: Con la malla anterior, creamos otras imágenes.

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 5/23
5/4/2021 Fourier2D

In [9]:

hFrec = 10.5 # Frecuencia horizontal


vFrec = 20.5 # Frecuencia vertical

A1 = np.sin(hFrec*2*np.pi*X) + np.sin(vFrec*2*np.pi*Y)

plt.figure()
plt.imshow(A1, cmap = 'gray', extent=[0,1,0,1]);

Si cortamos esta superficie por el plano y = 0.5 tenemos la curva

In [10]:

xx = np.linspace(0,1,1200)
yy = 0.5*np.ones(1200)
plt.plot(xx,np.sin(hFrec*2*np.pi*xx) + np.sin(vFrec*2*np.pi*yy))
plt.show()

y si cortamos por el plano x = 0.5

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 6/23
5/4/2021 Fourier2D

In [11]:

yy = np.linspace(0,1,1200)
xx = 0.5*np.ones(1200)
plt.plot(yy,np.sin(hFrec*2*np.pi*xx) + np.sin(vFrec*2*np.pi*yy))
plt.show()

In [12]:

F1 = fft2(A1)/(W*H)
F1 = fftshift(F1)
P1 = np.abs(F1)

plt.figure()
plt.imshow(P1[hH-25:hH+25,hW-25:hW+25], extent=[-25,25,-25,25]);

Si cortamos la superficie con un plano perpendicular al plano OXY que contiene al eje
OX

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 7/23
5/4/2021 Fourier2D

In [13]:

plt.plot(range(-25,25),P1[hH,hW-25:hW+25])
plt.show()

Y con un plano que contiene a OY

In [14]:

plt.plot(range(-25,25),P1[hH-25:hH+25,hW])
plt.show()

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 8/23
5/4/2021 Fourier2D

In [15]:

hFrec = 10.5 # Frecuencia horizontal


vFrec = 20.5 # Frecuencia vertical

A2 = np.sin(hFrec*2*np.pi*X + vFrec*2*np.pi*Y)

plt.figure()
plt.imshow(A2, cmap = 'gray',extent=[0,1,0,1]);

F2 = fft2(A2)/(W*H)
F2 = fftshift(F2)
P2 = np.abs(F2)

r = 25
plt.figure()
plt.imshow(P2[hH-r:hH+r,hW-r:hW+r], extent=[-r,r,-r,r]);

Finalmente vamos a usar una imagen real, con una frecuencia alta a lo largo del eje
horizontal, lo que se puede ver porque el máximo relativo se alínea con el eje horizontal:

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 9/23
5/4/2021 Fourier2D

In [16]:

I = Image.open("canes.jpg")
I = I.convert('L') # 'L' para convertir a escala de grises
A3 = np.asarray(I, dtype = np.float32) # Convertimos la imagen I en
# un array Numpy A3 con elementos tipo flo
at32
H,W = np.shape(A3)
plt.imshow(A3, cmap = 'gray');

In [17]:

F3 = fft2(A3)/float(W*H)
F3 = fftshift(F3)
P3 = np.abs(F3)

r = 100
mW = int(np.fix(0.5*W)) # Entero que vale, aproximadamente, la mitad de W
mH = int(np.fix(0.5*H)) # Entero que vale, aproximadamente, la mitad de H
plt.figure()
plt.imshow(np.log(1+P3[mH-r:mH+r,mW-r:mW+r]), extent=[-r,r,-r,r]);

Inicio

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 10/23
5/4/2021 Fourier2D

Aplicación: compresión de imágenes

Una imagen se recupera a partir de los coeficientes F usando la Transformada Inversa de


Fourier. Como hemos visto, en general, no todos los coeficientes tienen el mismo valor.
Para usar sólo los más importantes, podemos seleccionar aquellos cuyo módulo sea
mayor que uno dado llamado umbral T . Es decir, hacemos cero todos los coeficientes no
contenidos en el conjunto siguiente:

ST = {0 ≤ m ≤ M − 1, 0 ≤ n ≤ N − 1 : |F (m, n)| ≥ T } .

Llamemos g(T ) al número de elementos del array (m, n) , contenidos en ST . Por una
parte, para T = 0 tenemos g(0) = M N , porque |F (m, n)| son no negativos. Por otra
parte, para T > max |F (m, n)| tenemos g(T ) = 0 . Observar que g es una función de T
que es decreciente. Construiremos un plot de g usando la función count_nonzero, que da
el número de elementos no nulos de una matriz.

In [18]:

puntos = 100
Trango = np.linspace(0.05,1,puntos)
g = np.zeros(puntos, dtype = 'float32')
n = 0

for Tcont in Trango:


g[n] = np.count_nonzero(P3 >= Tcont)
n += 1

plt.plot(Trango,g);
plt.xlabel('T (valor del umbral de corte)',fontsize=14)
plt.title('Elementos no nulos en la matriz P3',fontsize=14);

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 11/23
5/4/2021 Fourier2D

Ahora aplicamos el método del valor umbral, es decir, mandamos a cero los coeficientes
menores que un T dado. ¿Cómo escogemos T ?

T debe ser lo bastante grande para eliminar algún coeficiente,


T debe ser lo bastante pequeño para quedarse con algún coeficiente

Tomemos, por ejemplo, T = 0.1 y veamos el resultado:

In [19]:

T = 0.1
c = F3 * (P3 >= T)
fM = ifft2(c)*W*H
plt.imshow(np.abs(fM), cmap = 'gray');

Finalmente, calcularemos el ratio entre el número de coeficientes de la Transformada de


Fourier distintos de cero y aquellos que superan el umbral contenidos en la matriz c. Así
obtenemos una relación de compresión:

In [20]:

out1 = np.count_nonzero(F3)
out2 = np.count_nonzero(c)
print 'Número inicial de elementos distintos de cero = ',out1
print 'Número final de elementos distintos de cero = ',out2
print 'Relación de compresión = ',out1/out2

Número inicial de elementos distintos de cero = 1228800


Número final de elementos distintos de cero = 5099
Relación de compresión = 240.988429104

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 12/23
5/4/2021 Fourier2D

Ejercicio

Tenemos una imagen de tamaño W × H , y queremos comprimirla de acuerdo con un


ratio dado r manteniendo los coeficientes de la transformada de Fourier más
significativos, es decir, queremos encontrar el umbral, T , tal que el ratio de compresión
sea r.

g(T )
Dicho de otra manera, queremos calcular T de forma que se cumpla que = r.
WH

Para conseguirlo, tenemos que calcular la raíz de la función


h(T ) = g(T ) − rW H .

Para escribir el programa compresion.py:

Cargar la imagen cameraman.tif and definir r = 1/50 .


Calcular su transformada de Fourier, F , junto con el máximo y el mínimo de su
módulo, maxF , minF .
La función h(T ) se podría definir como en los ejemplos anteriores, usando
np.count_nonzero. Para hacerse una idea de cómo es h, dibujar esta función para
el intervalo [minF , 0.6] .
Como h(T ) es decreciente, podemos buscar el T para el que cambia el signo de
h (y por lo tanto, dond h se aproxima a cero) comprobando sus signo desde

T = minF hasta T = maxF a intervalos regulares. Una vez que hemos

determinado gráficamente el valor de T cerca del cula cambia el signo de h,


podemos definir la matriz c obtenida a partir de la matriz F aplicando el umbral y
la imagen comprimida fM como hicimos en este apartado. Dibujar las imágenes
original y comprimida.
Comprobar que el ratio de compresión está próximo a r (out1/out2).
Adaptar cualquiera de los programas del método de Bisección para calcular una
aproximación de la única raíz de h. Por cierto, ¿por qué es única?. Comprobar
que las dos soluciones obtenidas son parecidas.
Finalmente, transformar el programa en función con argumentos de entradas la
imagen y la relación de compresión y con argumentos de salida, el umbral T y
una figura de la imagen original y la comprimida.

In [21]:

%run compresion.py

Relación de compresión = 50.0083937428

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 13/23
5/4/2021 Fourier2D

Inicio

Aplicación: orientación de vehículos


En esta aplicación, tenemos un vehículo que queremos orientar de forma que siga líneas
verticales.

Primero, tomamos la imagen original A y la rotamos:

In [24]:

A4 = (A - A.min())/(A.max() - A.min()) # normalizamos a [0,1]


I = Image.fromarray(np.uint8(A4*255)) # convertimos a imagen PIL

rot_angulo = -65
B = I.rotate(rot_angulo, expand = True) # rotamos sin recortar

B4 = np.asarray(B,dtype=np.float32)
sY,sX = np.shape(B4)

plt.figure()
plt.imshow(B,cmap = 'gray');

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 14/23
5/4/2021 Fourier2D

In [25]:

F4 = fft2(B4)/(sX*sY)
F4 = fftshift(F4)
P4 = np.abs(F4)

cX = int(np.fix(sX/2)) # punto medio de la imagen


cY = int(np.fix(sY/2))

plt.figure()
plt.imshow(P4[cY-25:cY+25,cX-25:cX+25], extent=[-25,25,-25,25]);

Como hay una región grande de valor constante (negro), el valor del espectro de
potencia en el origen es alto, haciendo que no resalten los valores de las frecuencias
importantes. Eliminamos este efecto restando el valor medio de la imagen.

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 15/23
5/4/2021 Fourier2D

In [26]:

F4 = fft2(B4-B4.mean())/(sX*sY)
F4 = fftshift(F4)
P4 = np.abs(F4)

plt.figure()
plt.imshow(P4[cY-25:cY+25,cX-25:cX+25], extent=[-25,25,-25,25]);

Calculamos las coordenadas de los coeficientes de más alta frecuencia y calculamos el


ángulo

In [27]:

indices = np.where(P4 == np.max(P4))


maxY = (indices[0][0]-cY)/sY
maxX = (indices[1][0]-cX)/sX

alpha=np.arctan(maxY/maxX)*180/np.pi

print "alpha = ", alpha

alpha = 65.1189251526

Ahora rotamos y recuperamos la imagen original

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 16/23
5/4/2021 Fourier2D

In [28]:

C=B.rotate(alpha)
plt.figure()
plt.imshow(C,cmap = 'gray');

Ejercicio

Crear una función que tenga como:

Entrada: una imagen (con un comportamiento periódico)


Salida: el ángulo para enderezar la imagen, la imagen enderezada, y el espectro
de potencia

Aplicarlo a las siguientes imágenes:

In [29]:

%run angulo.py

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 17/23
5/4/2021 Fourier2D

In [30]:

I5 = Image.open("surcos1.jpg")

alpha5, C5, P5 = angulo(I5)

print alpha5
plt.figure()
plt.imshow(I5);
plt.figure()
plt.imshow(C5);
plt.figure()
plt.imshow(P5);

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 18/23
5/4/2021 Fourier2D

-30.865815124

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 19/23
5/4/2021 Fourier2D

En este ejemplo, el algoritmo funcionó bien. Fijarse en que los valores altos de P5 están
lejos del origen.

Sin embargo, en el ejemplo siguiente, el algoritmo no funciona. ¿Por qué? ¿Cómo podría
arreglarse?

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 20/23
5/4/2021 Fourier2D

In [31]:

I6 = Image.open("surcos2.jpg")
alpha6, C6, P6 = angulo(I6)

print alpha6
plt.figure()
plt.imshow(I6);
plt.figure()
plt.imshow(C6);
plt.figure()
plt.imshow(P6);

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 21/23
5/4/2021 Fourier2D

-90.0
<string>:26: RuntimeWarning: divide by zero encountered in double_sc
alars

Inicio

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 22/23
5/4/2021 Fourier2D

Imágenes
(http://www.unioviedo.es/compnum/laboratorios_

https://www.unioviedo.es/compnum/laboratorios_py/Fourier/Fourier2D.html 23/23

También podría gustarte