diff --git a/labo5.py b/labo5.py
new file mode 100644
index 0000000000000000000000000000000000000000..fabf8e274943ac021574d33f9fc210007c64dfae
--- /dev/null
+++ b/labo5.py
@@ -0,0 +1,130 @@
+import numpy as np
+import random
+import numpy.typing as npt
+import matplotlib.pyplot as plt
+from PIL import Image
+from enum import Enum
+
+Img = npt.NDArray[np.uint8]
+Fft = npt.NDArray[np.complex128]
+
+
+class Isotropy(Enum):
+    ISO_90 = 1
+    ISO_45 = 2
+
+
+class Orientation(Enum):
+    HORIZONTAL = 1
+    VERTICAL = 2
+    X_Y = 3
+
+
+def load_img(path: str) -> Img:
+    img = np.array(Image.open(path))
+
+    return img
+
+
+def show_image(img: Img) -> None:
+    if len(img.shape) == 2:
+        plt.imshow(img, cmap="gray", vmin=0, vmax=255)
+    else:
+        plt.imshow(img)
+
+    plt.show()
+
+
+def thresholding(img: Img, value: int) -> Img:
+    new_img = img.copy()
+
+    for y in range(img.shape[0]):
+        for x in range(img.shape[1]):
+
+            if new_img[y, x] > value:
+                new_img[y, x] = 255
+            else:
+                new_img[y, x] = 0
+
+    return new_img
+
+
+def xcorr(img: Img, kernel: Img) -> Img:
+    res = np.zeros(img.shape)
+
+    it = np.nditer(res, flags=['multi_index'], op_flags=['writeonly'])
+
+    h = kernel.shape[0] // 2
+
+    for elem in it:
+        row, col = it.multi_index
+        roi = img[row - h:row + h + 1, col - h:col + h + 1]
+
+        if roi.shape == kernel.shape:
+            mul = np.multiply(kernel, roi)
+            elem[...] = np.sum(mul)
+
+    return res
+
+
+def normalize(img: Img, new_range: tuple[int, int] = (0, 255)) -> Img:
+    new_img = img.copy()
+
+    min, max = (np.amin(img), np.amax(img))
+
+    for y in range(img.shape[0]):
+        for x in range(img.shape[1]):
+            intensity = new_img[y, x]
+
+            new_img[y, x] = (intensity - min) * ((new_range[1] -
+                                                  new_range[0]) / (max - min))\
+                + new_range[0]
+
+    return new_img
+
+
+def get_fft(img: Img) -> Fft:
+    return np.fft.fft2(img)
+
+
+def get_fft_spectrum(fft: Fft) -> Img:
+    # Shift the zero-frequency component to the center of the spectrum
+    fft_shifted = np.fft.fftshift(fft)
+
+    # Compute magnitude spectrum (absolute value of the shifted FFT)
+    magnitude_spectrum = np.abs(fft_shifted)
+
+    return np.log1p(magnitude_spectrum)
+
+
+def get_img(fft: Fft) -> Img:
+    reconstructed_img = np.fft.ifft2(fft).real
+
+    return normalize(reconstructed_img)
+
+
+if __name__ == "__main__":
+    img = load_img("resources/testpattern1024.png")
+    fft = get_fft(img)
+    spectrum = get_fft_spectrum(fft)
+
+    plt.figure(figsize=(12, 6))
+
+    # Plot FFT magnitude spectrum
+    plt.subplot(1, 3, 1)
+    plt.imshow(img, cmap="gray", vmin=0, vmax=255)
+    plt.title('Original Image')
+    plt.axis('off')
+
+    plt.subplot(1, 3, 2)
+    plt.imshow(spectrum, cmap="gray")
+    plt.title('FFT spectrum')
+    plt.axis('off')
+
+    plt.subplot(1, 3, 3)
+    plt.imshow(get_img(fft), cmap="gray", vmin=0, vmax=255)
+    plt.title('Inverse FFT')
+    plt.axis('off')
+
+    plt.tight_layout()
+    plt.show()