{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Matricule & noms : " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###
ELE8812 - Traitement et analyse d'images
\n", "
Travail practique No. 2
\n", "
Hiver 2021
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "Ce lien vous permettra de voir les différences fondamentales entre Python et Matlab. Ne passez pas forcement par les étapes d'installation.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ce travail pratique a pour objet de vous permettre de vous familiariser avec la manipulation de représentations fréquentielles discrètes des images. Il vous donnera également l’occasion de mettre en œuvre certains traitements vus en cours et de les comparer avec leur équivalent dans le domaine spatial. Trois points seront abordés:\n", "\n", "1. La représentation fréquentielle\n", "2. Le rehaussement dans le domaine de fourier\n", "3. Le détramage par réjection de fréquences\n", "\n", "\n", "Les données à utiliser pour effectuer ce travail pratique se trouvent dans l’archive TP2.zip disponible sur le site web du cours. \n", "\n", "Pondération \n", "- Représentation fréquentielle : 4 pts \n", "- Rehaussement dans le domaine de Fourier: 7 pts \n", "- Détramage par réjection de fréquences : 7 pts \n", "- Qualités de la langue et du rapport : 2 pts \n", "- Total : 20 points \n", "\n", "Date de remise \n", "- Groupe 1: 13 février 23h59 \n", "- Groupe 2: 20 février 23h59 " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "\n", "La cellule ci dessous effectue des import cela s'assimile a unaddpath en matlab. Le but est d'amener des fonctions extérieures (packages) dans le notebook afin que Python les reconnaisse. \n", " import package as pkg permet de raccourcir le nom de ce dernier. Les fonctions contenues dans 'package' seront alors appelées ainsi pkg.function()
\n", " \n", "Vous pouvez chercher sur google chacun des package appelés ci-dessous afin d'obtenir leur documentation.
\n", "\n", "N'oubliez pas de relancer cette cellule si jamais vous redemarrez le Kernel !!\n", "
" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import matplotlib.image as mpimg\n", "from matplotlib.pyplot import imread\n", "import cv2\n", "import numpy as np\n", "from pylab import ginput\n", "from scipy import signal\n", "import matplotlib\n", "from scipy.ndimage import convolve\n", "\n", "%matplotlib inline\n", "plt.rcParams[\"figure.figsize\"] = (12, 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Représentation fréquentielle (4 pts)\n", "\n", "Représenter et interpréter correctement une image dans le domaine fréquentiel est un \n", "préalable indispensable à tout traitement dans ce domaine. Cette question vous permettra \n", "de vous familiariser avec la représentation spectrale des images. \n", "\n", "### 1.1. Indexation des axes fréquentiels (2 pts)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "\n", "\n", "\n", "Chargez l’image Voiture.tif et représentez-la dans le domaine spectral (fft2) selon \n", "la convention usuelle (fréquence nulle au voisinage du centre de l’image). Indexez les axes \n", "en fréquences réduites (comprises entre −1/2 et 1/2).\n", "\n", "Note :\n", "Par convention, la fonction fft2 de numpy ordonne les fréquences de 0 → N/2, puis de –N/2 + 1 → -1 pour chaque dimension de la matrice transformée. On pourra utiliser la fonction fftshift de numpy pour placer la fréquence nulle au centre de l’image. Consulter l’aide de cette fonction pour plus de détails (doc [fftshift](https://numpy.org/doc/stable/reference/generated/numpy.fft.fftshift.html)). Vous pouvez appeler la fonction comme suit: \n", " \n", " np.fft.fft2 np.fft.fftshift\n", "\n", "Pour augmenter le contraste de la fft, il est possible de normaliser en faisant 1 + log(FFT) pour mieux discerner les intensités.\n", "
" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Inscrivez votre code ici" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "Donnez, en fréquences réduites puis en nombres de pixels, les coordonnées des 9 « points brillants » dont l’amplitude est la plus grande. Vous pouvez utiliser la function `ginput` qui vous permettra de cliquer sur les points les plus brillants de l'image pour en retirer les coordonnées.
" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# Inscrivez votre code ici\n", "# Ceci sera nécessaire pour utiliser ginput dans le jupyter notebook\n", "matplotlib.use('TkAgg')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "DOUBLE-CLIQUEZ POUR INSCRIRE VOTRE RÉPONSE\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1.2. Effet de la parité des dimensions sur l’image (2 pts)\n", "
\n", "\n", "\n", "Appliquez la fonction ifftshift à la représentation fréquentielle de l’image obtenue à la question précédente, puis prenez-en la transformée de Fourier inverse (ifft2). L'image contiendra des nombres complexes. Pour obtenir les intensités, il faut appliquer np.real pour conserver seulement les nombres réels. Comparez le résultat à l’image de départ. Vous pouvez appeler la fonction comme suit: \n", "np.fft.ifftshift np.fft.ifft2 np.real
\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Inscrivez votre code ici\n", "# Garder cette ligne une fois que vous n'utilisez plus ginput\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "Appliquez maintenant la fonction \n", "fftshift (plutôt que ifftshift) de numpy à la représentation fréquentielle de \n", "l’image obtenue à la question précédente, puis prenez-en la transformée de Fourier inverse. \n", "Comparez le résultat à l’image de départ.
" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# Inscrivez votre code ici" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "- Que constatez-vous ? \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "DOUBLE-CLIQUEZ POUR INSCRIRE VOTRE RÉPONSE \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "\n", "Supprimez une ligne et une colonne de l’image Voiture.tif et effectuez de nouveau les opérations décrites dans le paragraphe précédent.\n", "\n", "Vous pouvez vous aider de la function ```numpy.delete(arr, obj, axis=None)```
" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# Inscrivez votre code ici" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "- Que constatez-vous maintenant? Expliquez
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "DOUBLE-CLIQUEZ POUR INSCRIRE VOTRE RÉPONSE\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Rehaussement dans le domaine de Fourier (7 pts)\n", "### 2.1. Rehaussement par masquage flou (3 pts) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "Chargez l’image Lune2.tif et mettez en œuvre un rehaussement par masquage flou dans le domaine de Fourier (voir section 3.2 du TP1 pour un rappel sur le rehaussement par masquage flou. Formule: G = 𝐹 + 𝑎 [ 𝐹 − 𝐹′ ]). Pour cela, utilisez un filtre Butterworth ou gaussian passe-bas, puis faites varier la fréquence de coupure et le coefficient de masquage. Utilisez les fonctions fournies:```filtpb_gaus``` et ```filtpg_butter```.
" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "def filtpb_gauss(A, fc):\n", " \"\"\" Application d'un filtre gaussien sur une image im à une fréquence de coupure fc dans \n", " le domaine fréquentiel\n", " \n", " A (ndarray): Image d'entrée\n", " fc (float): Fréquence de coupure\n", " \"\"\"\n", " M, N = A.shape\n", "\n", " # Image dans le domaine fréquentiel\n", " AA = np.fft.fftshift(np.fft.fft2(A))\n", "\n", " M0 = np.ceil((M+1) / 2)\n", " N0 = np.ceil((N+1) / 2)\n", "\n", " U, V = np.mgrid[1:M+1, 1:N+1]\n", " D2 = (U - M0)**2 + (V - N0)**2\n", " \n", " # Réponse fréquentielle du filtre gaussien\n", " HH = np.exp(-D2 / (2 * fc**2))\n", " \n", " # Application du filtre et retour au domaine spatial\n", " BB = np.fft.ifftshift(AA * HH)\n", " B = np.fft.ifft2(BB)\n", " B = np.real(B)\n", " return B \n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def filtpb_butter(A, fc, ordre):\n", " \"\"\" Application d'un filtre Butterworth sur une image im à une fréquence de coupure fc dans \n", " le domaine fréquentiel\n", " \n", " A (ndarray): Image d'entrée\n", " fc (float): Fréquence de coupure\n", " ordre (int): Ordre du filtre\n", " \"\"\"\n", " M,N=A.shape\n", "\n", " # Image dans le domaine fréquentiel\n", " AA = np.fft.fftshift(np.fft.fft2(A))\n", "\n", " M0 = np.ceil((M+1) / 2)\n", " N0 = np.ceil((N+1) / 2)\n", "\n", " U, V = np.mgrid[1:M+1, 1:N+1]\n", " D2 = (U - M0)**2 + (V - N0)**2\n", "\n", " # Réponse fréquentielle du filtre Butterworth\n", " HH = 1 / (1 + (D2 / fc**2)**ordre)\n", " \n", " # Application du filtre et retour au domaine spatial\n", " BB = np.fft.ifftshift(AA * HH)\n", " B = np.fft.ifft2(BB)\n", " B = np.real(B)\n", " return B " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Inscrivez votre code ici" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "- Qu'observez-vous ?
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "DOUBLE-CLIQUEZ POUR INSCRIRE VOTRE RÉPONSE\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "Utilisez le script unsharp_filter.m, qui met en œuvre le rehaussement par masquage \n", "flou dans le domaine spatial étudié lors du TP1, et appliquez le filtrage à l’image de la lune. \n", "Comparez les deux approches (spatiale & fréquentielle), en ce qui concerne tant la mise en \n", "œuvre que les résultats. \n", "
" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# On utilise à titre d'exemple l'image filtre gaussian (Low-Pass Filter)\n", "def unsharp_filter(img, taille_masque, coefficient_de_rehaussement):\n", " \"\"\" Rehaussement par masquage par flou dans le domaine spatial\n", " \n", " img (ndarray): Image d'entrée\n", " taille_masque (int): Taille du masque gaussien\n", " coefficient_de_rehaussement (float): a dans la formule du masquage par flou G = 𝐹 + 𝑎 [ 𝐹 − 𝐹′ ] \n", " \"\"\"\n", " a = coefficient_de_rehaussement\n", " Fprime = cv2.blur(img, (taille_masque, taille_masque))\n", " norm_Fprime = Fprime / np.max(Fprime)\n", " return img + a * (img - norm_Fprime)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "DOUBLE-CLIQUEZ POUR INSCRIRE VOTRE RÉPONSE\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.2. Rehaussement par Laplacien (4 pts) \n", "
\n", "\n", "\n", "Mettez en œuvre le rehaussement par Laplacien dans le domaine spectral en programmant directement la forme donnée en cours.Il peut être implémenté à l’aide de l’équation suivante:\n", "\n", "\\begin{equation*}\n", "\\triangledown^2I(x,y)\\xrightarrow{\\text{F}}-4\\pi^2D^2(u,v)\\bullet I_F(u,v)\n", "\\end{equation*}\n", "\n", "Où $𝐼_𝐹 (𝑢, 𝑣)$ est la représentation fréquentielle avec fréquence centrée de l’image $𝐼(𝑥, 𝑦)$ et $𝐷^2 (𝑢, 𝑣)$ la distance euclidienne au carrée entre les fréquences $(𝑢, 𝑣)$ et la fréquence centrale. Cette formulation est équivalente à la forme spatiale du filtre Laplacien en raison \n", "de la théorie de convolution de la transformée de Fourier. Pour plus de détails, consulter les diapositives du cours 4. \n", " \n", "Vous pouvez vous inspirez du code des fonctions `filtpb_gauss` et `filtpb_butter` pour vous aider.\n", "
" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# Inscrivez votre code ici" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "Mettez ensuite en œuvre le rehaussement par Laplacien dans le \n", "domaine spatial. Il peut être implémenté à l’aide des noyaux de convolution suivants : \n", " \n", "
\n", "\n", "$$\n", "\\left[\\begin{array}{ccc} \n", "0 &1 &0\\\\\n", "1 &-4 &1\\\\\n", "0 &1 &0\\;\n", "\\end{array}\\right]\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\\;\n", "\\left[\\begin{array}{ccc} \n", "1 &1 &1\\\\\n", "1 &-8 &1\\\\\n", "1 &1 &1\n", "\\end{array}\\right]\n", "$$ \n", " \n", "Pour rehausser les contours dans le domaine spatial, le Laplacien doit être soustrait à l'image originale:\n", "$I_{rehaussée} = I_{original} - I_{original} * noyau_{laplacien} $\n", " \n", "Pour mieux visualiser l'effet du filtre laplacien, vous pouvez ajouter les paramètres `vmin=0` et `vmax=1` à la fonction plt.imshow pour obtenir l'affichage entre ces valeurs.\n", "
\n", " \n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "# Inscrivez votre code ici\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "\n", "\n", "- Comparez les deux approches (spatial et spectral), en ce qui concerne tant la mise en œuvre que les résultats.\n", " \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "DOUBLE-CLIQUEZ POUR INSCRIRE VOTRE RÉPONSE\n", "
" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "# Inscrivez votre code ici" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "\n", "\n", "- Enfin, comparez le rehaussement par Laplacien et le rehaussement par masquage flou. \n", " \n", "
" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "# Inscrivez votre code ici" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "DOUBLE-CLIQUEZ POUR INSCRIRE VOTRE RÉPONSE\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Détramage par réjection de fréquences (7 pts)\n", "On a vu à la question 1 que la représentation fréquentielle de certaines images comporte \n", "des « points brillants » que l’on peut associer à des trames qui dégradent l’image de départ. \n", "L’amélioration de telles images peut donc être obtenue en éliminant ces points brillants, ce \n", "qui correspond à la réjection de « zones fréquentielles » situées autour des points brillants. \n", "Cette question a pour but de mettre en œuvre un tel filtrage.\n", "\n", "
\n", "\n", "\n", "Développez une fonction permettant de filtrer les fréquences correspondantes aux points brillants. Attention, le centre du spectre de Fourier correspond à la fréquence moyenne de l'image.\n", " \n", "
" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "# Inscrivez votre code ici" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "\n", "\n", "- Commentez les résultats obtenus. \n", " \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "DOUBLE-CLIQUEZ POUR INSCRIRE VOTRE RÉPONSE\n", "
" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.8" } }, "nbformat": 4, "nbformat_minor": 4 }