Skip to content
Snippets Groups Projects
Commit 275115f8 authored by Pierre Kunzli's avatar Pierre Kunzli
Browse files

exercices deplaces dans des dossiers et ajout serie 11

parent 027d70c0
Branches
No related tags found
No related merge requests found
Showing
with 262536 additions and 0 deletions
File added
Programmation/Exercices/serie_11_traitement_dimages/figs/mandrill.png

233 KiB

Programmation/Exercices/serie_11_traitement_dimages/figs/mandrill_centr.png

1.02 MiB

Programmation/Exercices/serie_11_traitement_dimages/figs/mandrill_crop.png

675 KiB

Programmation/Exercices/serie_11_traitement_dimages/figs/mandrill_hor.png

1.01 MiB

Programmation/Exercices/serie_11_traitement_dimages/figs/mandrill_negative.png

78.9 KiB

Programmation/Exercices/serie_11_traitement_dimages/figs/mandrill_sym.png

3.13 MiB

Programmation/Exercices/serie_11_traitement_dimages/figs/mandrill_vert.png

1.02 MiB

This diff is collapsed.
---
title: "Programmation séquentielle"
subtitle: "Série 11 - Traitement d'images"
date: 10.01.2023
autoSectionLabels: false
autoEqnLabels: true
eqnPrefix:
- "éq."
- "éqs."
chapters: true
numberSections: false
chaptersDepth: 1
sectionsDepth: 3
lang: fr
documentclass: article
papersize: A4
cref: false
urlcolor: blue
toc: false
---
\newcommand{\mat}[1]{\underline{\underline{#1}}}
# Cahier des charges
## Buts
* Manipulation de tableaux à deux dimensions en `C`.
* Utilisation de `git`.
* Utilisation de pointeurs.
* Lecture et écriture de fichiers texte.
* Créer une librairie logiciell et l'utiliser dans un programme.
* (Bonus) Affichage graphique avec la librairie `SDL2`.
## Énoncé
Créer une librairie de manipulation d'images en *niveaux de gris*. Cette librairie devra permettre d'effectuer les opérations suivantes:
1. Lecture d'une image au format PGM depuis un format texte.
2. Sauvegarde d'une image dans le format PGM texte.
3. Effectuer des opérations sur les images:
1. Symétrie *horizontale*, *verticale*, et *centrale* d'une image.
2. *Filtre* d'une image.
3. *Rogner* une image.
4. Calculer le *négatif* d'une image.
4. (Bonus) Visualiser une image PGM obtenue à l'aide de la librairie SDL.
Une fois votre librairie implémentée et testée, vous devez la mettre en oeuvre dans un programme utilisateur. Ce programme devra disposer d'une interface avec l'utilisateur (en ligne de commande ou graphique) permettant d'exploiter les fonctionnalités de la librairie.
Vous êtes libre de décider de la façon dont votre programme s'utilise. Il pourra par exemple prendre les noms de fichiers et les opérations à effectuer en argument, ou vous pouvez implémenter une interface interactive textuelle ou graphique.
Pour ce travail, en plus de la réalisation de la librairie de traitement d'images et du programme utilisateur, vous devez
utiliser le logiciel de gestion de version `git` et évidemment compiler le projet à l'aide d'un `Makefile`.
### Gestion d'erreurs
Un grand nombre des fonctions de ce travail peuvent échouer. Pour simplifier,
nous nous limiterons à deux retour possibles du type `error_code` :
```
typedef enum _error_code {
ok, err
} error_code;
```
### Représentation informatique des images
Dans ce travail pratique, une image n'est rien d'autre qu'une matrice de nombres entiers,
au sens du type `matrix` que vous avez implémenté dans un précédent travail
pratique. Pour simplifier les images seront uniquement représentées en *niveaux de gris*.
Ainsi chaque élément de la matrice représente un pixel. Les valeurs des pixels
devront être limitées entre `0` et une valeur maximale `max`.
Pour simplifier encore, les images seront supposées être données au format PGM. Le format PGM (portable graymap file format)
est un format de fichier très simple permettant de stocker des images en niveau de gris.
Nous utiliserons le format PGM **textuel**.
Vous trouverez une définition du format sur la
page [https://fr.wikipedia.org/wiki/Portable_pixmap](https://fr.wikipedia.org/wiki/Portable_pixmap).
Une image PGM peut être représentée à l'aide de la structure de données suivante:
```C
typedef struct _pgm {
int max;
matrix pixels;
} pgm;
```
Le format PGM implique la lecture dans un premier temps d'une entête contenant le texte
`P2` sur la première ligne, puis les dimensions de l'image sur la deuxième ligne. Sur la troisième ligne
se trouve le niveau de gris maximal. Finalement, les pixels de l'image en format texte sont stockés dans les lignes restantes.
Nous supposerons dans ce travail, pour simplifier, qu'il n'existe pas de commentaires dans les fichier (pas de lignes commençant par `#`).
### Lecture et écriture d'une image PGM en format texte
Afin de lire et écrire un fichier texte, il faut utiliser
le type pointeur de fichier:
```C
FILE *f;
```
et les fonctions permettant de manipuler les fichiers:
* `fopen()`{.C} avec les option `r` et `w` pour ouvrir un fichier en mode lecture ou écriture.
* `fclose()`{.C} pour fermer un fichier.
* `fprintf()`{.C} pour écrire.
* `fgets()`{.C} pour lire une ligne d'un fichier texte.
A l'aide de ces fonctions vous devriez être capables de lire et écrire des fichiers PGM.
Afin de tester vos fonctions vous pouvez utiliser l'image du mandrill.
![Image d'un mandrill.](figs/mandrill.png){#fig:mandrill width=50%}
### Fonctions à implémenter
Il faut implémenter **au minimum** les fonctions suivantes:
* la fonction
```C
error_code pgm_read_from_file(pgm *p, char *filename);
```
lisant le fichier `filename` et écrivant son contenu dans la variable `p`. Cette fonction retourne un `error_code`.
* la fonction
```C
error_code pgm_write_to_file(pgm *p, char *filename);
```
écrivant le contenu de l'image `p` dans le le fichier `filename`. Cette fonction retourne un `error_code`.
# Les transformations d'images
## Le négatif
Le négatif d'une image consiste à *inverser* la valeur des pixels de l'image par rapport à la valeur
maximale permise. Ainsi si on représente `max` niveaux de gris, le négatif d'un pixel de niveau de gris, `p`, est donné par `max-p`.
![L'original (image de gauche) et le négatif (image de droite) de la photo du mandrill.](figs/mandrill_negative.png){#fig:mandrill_negative}
### Fonctions à implémenter
Il faut implémenter **au minimum** la fonction suivante:
* la fonction
```C
error_code pgm_negative(pgm *neg, pgm *orig);
```
calculant le négatif de l'image `orig` et la stockant dans `neg` (qui est également allouée dans cette fonction). L'image `orig` n'est pas modifiée. Cette fonction retourne un `error_code`.
## Les symétries
Une symétrie verticale ou horizontale consiste à inverser l'ordre des pixels verticalement ou horizontalement
respectivement. La symétrie centrale consiste à échanger les lignes et les colonnes d'une image.
![Exemple de symétrie horizontale.](figs/mandrill_hor.png){#fig:mandrill_hor width=70%}
![Exemple de symétrie verticale.](figs/mandrill_vert.png){#fig:mandrill_vert width=70%}
![Exemple de symétrie centrale.](figs/mandrill_centr.png){#fig:mandrill_centr width=70%}
### Fonctions à implémenter
Il faut implémenter **au minimum** les fonctions suivantes:
* la fonction
```C
error_code pgm_symmetry_hori(pgm *sym, pgm *orig);
```
calculant la symétrie horizontale de l'image `orig` et la stockant dans `sym` (qui est également allouée dans cette fonction). L'image `orig` n'est pas modifiée. Cette fonction retourne un `error_code`.
* la fonction
```C
error_code pgm_symmetry_vert(pgm *sym, pgm *orig);
```
calculant la symétrie verticale de l'image `orig` et la stockant dans `sym` (qui est également allouée dans cette fonction). L'image `orig` n'est pas modifiée. Cette fonction retourne un `error_code`.
* la fonction
```C
error_code pgm_symmetry_cent(pgm *sym, pgm *orig);
```
calculant la symétrie centrale de l'image `orig` et la stockant dans `sym` (qui est également allouée dans cette fonction). L'image `orig` n'est pas modifiée. Cette fonction retourne un `error_code`.
## Rogner
Le rognage d'une image est une opération assez simple. Elle consiste à extraire une sous partie rectangulaire
des pixels de l'image d'origine.
![Rognage de la photo de mandrill.](figs/mandrill_crop.png){#fig:mandrill_crop width=100%}
### Fonctions à implémenter
Il faut implémenter **au minimum** la fonction suivante:
* la fonction
```C
error_code pgm_crop(pgm *crop, pgm *orig,
int32_t x0, int32_t x1,
int32_t y0, int32_t y1);
```
calculant la sous-image de `orig` aux coordonnées `x0` à `x1` (non-inclu), et `y0` à `y1` (non-inclu). Le résultat est stocké dans `crop` (qui est également allouée dans cette fonction). L'image `orig` n'est pas modifiée. Cette fonction retourne un `error_code`.
## Convolution et filtres [^1]
Explication dans la documentation de gimp: [https://docs.gimp.org/2.8/fr/plug-in-convmatrix.html](https://docs.gimp.org/2.8/fr/plug-in-convmatrix.html)
Les matrices de convolutions sont particulièrement utiles dans le traitement d'images.
On les appelle également *noyaux* ou *masques*. L'image traitée est obtenue
en faisant la *convolution* entre la matrice et l'image. Ce genre d'opération
est effectuée tout le temps dans vos téléphones portables pour appliquer des filtres sur vos photos (floutage, vieillissement, ...).
Il existe une grande quantité de noyaux (vous pouvez en trouver des exemple sur la page
<https://en.wikipedia.org/wiki/Kernel_(image_processing)>), mais l'opération pour effectuer le traitement de l'image reste toujours la même.
Une image peut se représenter sous la forme d'une matrice $m\times n$
\begin{equation}
\mat{A}=
\begin{pmatrix}
a_{11} & \dots & a_{1,j-1} & a_{1,j} & a_{1,j+1} & \dots & a_{1n}\\
\vdots & \ddots & \vdots & \ddots & \vdots & \ddots & \vdots \\
a_{i-1,1} & \dots & a_{i-1,j-1} & a_{i-1,j} & a_{i-1,j+1} & \dots & a_{i-1,n}\\
a_{i, 1} & \dots & a_{i,j-1} & a_{i,j} & a_{i,j+1} & \dots & a_{i-1,n}\\
a_{i+1,1} & \dots & a_{i+1,j-1} & a_{i+1,j} & a_{i+1,j+1} & \dots & a_{i+1,n}\\
\vdots & \ddots & \vdots & \ddots & \ddots & \ddots & \vdots \\
a_{m,1} & \dots & a_{m,j-1} & a_{m,j+1} & \dots & \dots & a_{mn}\\
\end{pmatrix}.
\end{equation}
Si nous choisissons une matrice de convolution, $\mat{C}$, $3\times 3$ (cela se généralise pour toutes les tailles), de la forme
\begin{equation}
\mat{C}=\begin{pmatrix}
c_{11} & c_{12} & c_{13} \\
c_{21} & c_{22} & c_{23} \\
c_{31} & c_{32} & c_{33}
\end{pmatrix},
\end{equation}
nous pouvons écrire la transformation de tous les éléments $a_{i,j}$ de la matrice $\mat{A}$, que nous noterons $b_{i,j}$, comme
\begin{align}
b_{i,j}&=\left[\begin{pmatrix}
c_{11} & c_{12} & c_{13} \\
c_{21} & c_{22} & c_{23} \\
c_{31} & c_{32} & c_{33}
\end{pmatrix}\ast
\begin{pmatrix}
& a_{i-1,j-1} & a_{i-1,j} & a_{i-1,j+1}\\
& a_{i,j-1} & a_{i,j} & a_{i,j+1} \\
& a_{i+1,j-1} & a_{i+1,j} & a_{i+1,j+1}\\
\end{pmatrix}\right]\nonumber\\
&=c_{11}a_{i-1,j-1}+c_{12}a_{i-1,j}+c_{13}a_{i-1,j+1}\nonumber\\
&\quad\quad+c_{21}a_{i,j-1}+c_{22}a_{i,j}+c_{23}a_{i,j+1}\nonumber\\
&\quad\quad+c_{31}a_{i+1,j-1}+c_{32}a_{i+1,j}+c_{33}a_{i+1,j+1}.
\end{align}
On voit donc que la convolution est une combinaison linéaire de tous les éléments d'une sous matrice de $\mat{A}$ dont les poids sont donnés
par la matrice de convolution. La somme des éléments de la matrice de convolution est en général de $1$ (on dit qu'elle est normalisée à 1)
pour éviter de modifier la luminosité des pixels.
---
### Illustration (Floutage)
Si la matrice de convolution est donnée par
\begin{equation}
\mat{C}=\frac{1}{9}\begin{pmatrix}
1 & 1 & 1 \\
1 & 1 & 1 \\
1 & 1 & 1
\end{pmatrix},
\end{equation}
on voit que son effet est de moyenner la valeur de chaque pixel avec tous ses voisins
\begin{align}
b_{ij}&=\frac{1}{9}(a_{i-1,j-1}+a_{i-1,j}+a_{i-1,j+1}+a_{i,j-1}+a_{i,j}\nonumber\\
&\quad\quad+a_{i,j+1}+a_{i+1,j-1}+a_{i+1,j}+a_{i+1,j+1}).
\end{align}
---
Pour l'implémentation que nous vous proposons ici, les poids sont tous entiers (la `struct matrix` ne contient que des entiers).
Afin de pouvoir implémenter un filtre quelconque malgré tout, nous avons rajouté une normalisation
dans la signature de la fonction. Dans le cas de la matrice de convolution
\begin{equation*}
\mat{C}=\frac{1}{9}\begin{pmatrix}
1 & 1 & 1 \\
1 & 1 & 1 \\
1 & 1 & 1
\end{pmatrix},
\end{equation*}
la variable `kernel` de la fonction
```C
error_code pgm_conv(pgm *conv, pgm *orig,
matrix *kernel, double norm);
```
contiendrait
\begin{equation*}
\mathrm{kernel}=\begin{pmatrix}
1 & 1 & 1 \\
1 & 1 & 1 \\
1 & 1 & 1
\end{pmatrix},
\end{equation*}
et la normalisation, `norm`, serait
\begin{equation*}
\mathrm{norm}=\frac{1}{9}.
\end{equation*}
On peut aisément constater que ces opérations sont mal définies pour les pixels se trouvant sur les bords de l'image (aux endroits où les pixels n'ont pas suffisamment de voisins
pour effectuer l'opération de convolution). Il existe différentes solutions possibles:
1. Diminuer la taille de l'image traitée. L'image sera plus petite que l'image de départ de $n$ pixels, où $n\times n$ est la taille de la matrice de convolution.
2. La taille de l'image est étendue en copiant le dernier pixel sur tous les pixels manquants dans une direction donnée.
3. On effectue une réflexion miroir de l'image dans toutes les directions.
Dans notre cas, on considérera simplement que les pixels absents valent `0`.
### Remarques
Il se peut que suite à l'application d'un filtre, vous dépassiez la valeur maximale autorisée pour un pixel, ou obteniez
une valeur plus petite que zéro. Dans ces cas il faudra ramener les valeurs dans l'intervalle `[0, max]`:
1. Si la valeur du pixel est inférieure à `0`, alors le pixel vaudra `0`.
2. Si la valeur du pixel est supérieure `max`, alors le pixel vaudra `max`.
Par ailleurs, il se peut que la valeur des pixels ne soit plus entière. Il faudra donc
tronquer le nombre obtenu pour en faire un entier.
### Fonctions à implémenter
Il faut implémenter **au minimum** la fonction suivante:
* la fonction
```C
error_code pgm_conv(pgm *conv, pgm *orig,
matrix *kernel, double norm);
```
calculant la convolution entre `orig` et le noyau `kernel` ainsi que sa normalisation `norm`. Le résultat est stocké dans `conv` (qui est également allouée dans cette fonction). L'image `orig` n'est pas modifiée. Cette fonction retourne un `error_code`.
## (Bonus) Afficher l'image avec la librairie SDL
Uniquement après avoir réalisé **intégralement** toutes les parties qui précèdent,
afficher les images en niveau de gris à l'aide de la librairie `SDL2`.
Vous trouverez toutes les fonctions nécessaires dans l'exemple se trouvant sur [ce lien](https://malaspinas.academy/prog_seq_c_tp/tableaux_unidimensionnels/gfx_example.tar.gz). Cet exemple affiche du bruit (des valeurs de gris aléatoires
sur un grand nombre de pixels). Vous **devez** réutiliser les fonctions se trouvant dans cet exemple qui sont là pour vous faciliter la vie (et non tenter de réinventer la roue).
Le code dont vous devez vous inspirer est dans le fichier `gfx_example.c` (qui utilise `gfx.h/c`).
Outre les fonctions de création/destruction du contexte, la fonction importante
est `render()`{.C} qui affiche un pixel à la position `x`, `y` à un niveau de gris `color`,
à l'aide de la fonction `put_pixel()`{.C}.
<!-- # Travail à rendre
* Ce travail est à réaliser individuellement.
* Vous devez rendre votre code sur votre répo `git` au plus tard le mardi 1er décembre 2020 à 23h30 (la version antérieure ou égale à cette date sera récupérée sur votre git).
* Tout le monde n’ayant pas le même nom de repo git, veuillez écrire l’adresse de votre git, ainsi que votre nom sur le wiki sur `cyberlearn`. N’oubliez pas de laisser votre répo public pour qu'on puisse le cloner.
* Suite à ce rendu, vous devrez effectuer une présentation orale de votre travail d’une durée d’au maximum 20 min avec des diapositives.
* La présentation orale aura lieu les mercredi 2 et 9 décembre pendant les heures de cours.
* Vous serez notés sur la présentation et sur le code que vous avez rendu (pondération 1/4 - 3/4). -->
[^1]: Repris du cours de math de N. Eggenberg et O. Malaspinas
File added
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment