Programmer en langage C avec Imagemagick


imagemagick

La présente page montre comment programmer en langage C avec Imagemagick sous Ubuntu.
Imagemagick est une suite logicielle gratuite et open source, utilisée pour éditer et manipuler des images numériques. Elle peut être utilisé pour créer, modifier, composer ou convertir des images bitmap et prend en charge un large éventail de formats de fichiers, notamment JPEG, PNG, GIF, TIFF.
ImageMagick est écrit en langage C et peut être utilisé sur divers systèmes d'exploitation, notamment Linux, Windows et macOS. Elle comprend une interface de ligne de commande pour exécuter des tâches de traitement d'image complexes, ainsi que qu'une API (API MagickWand) pour intégrer ses fonctionnalités dans des applications écrites en langage C.

1) Installation de Imagemagick

Pour installer l'API MagickWand d'ImageMagick sous Ubuntu, on peut utiliser le gestionnaire de paquets apt. Voici les étapes à suivre :

1. Mise à jour des informations des paquets :
sudo apt-get update

2. Installation de l'API MagickWand d'ImageMagick :
sudo apt-get install libmagickwand-dev
Cette commande installe les fichiers d'en-tête nécessaires et les bibliothèques partagées pour le développement avec MagickWand.3. Vérification de l'installation :

3. Vérification de l'installation :
On peut facilement vérifier si l'installation s'est déroulée correctement en utilisant la commande suivante :
dpkg-query -l
Cette commande qui affiche la liste de tous les paquets installés sur l'ordinateur et permet de vérifier, en particulier, la présence des paquets relatifs à Imagemagick.
ii imagemagick 8:6.9.10.23+dfsg-2.1ubuntu11.9 amd64 image manipulation programs -- binaries
ii imagemagick-6-common 8:6.9.10.23+dfsg-2.1ubuntu11.9 all image manipulation programs -- infrastructure
ii imagemagick-6.q16 8:6.9.10.23+dfsg-2.1ubuntu11.9 amd64 image manipulation programs -- quantum depth Q16

4. Exemple de programme
On peut vérifier le bon fonctionnement de l'API en compilant et en exécutant un petit programme de test. Par exemple, un fichier test.c avec le contenu suivant :

#include <stdio.h>
#include <wand/MagickWand.h>
int main() {
MagickWandGenesis();
printf("L'API Magickwand fonctionne !\n");
MagickWandTerminus();
return 0;
}

On compile le programme avec la commande :
gcc -o test test.c $(pkg-config --cflags --libs MagickWand)

On exécute le programme avec la commande :
./test

Les exemples utilisés ici sont basés sur la version 6.9 de imagemagick. Pour connaître la version de imagemagick installée, il suffit de taper la commande suivante dans le terminal:
convert -version

2) Exemple 1 : Tracé d'un cadre noir autour d'une image

Voici un exemple de programme (programme source "cadre.c", exécutable "cadre") écrit en langage C utilisant l'API MagickWand d'ImageMagick pour tracer un cadre noir de 1 pixel sur une image "image.png" qui se trouve dans le même répertoire que l'éxécutable "cadre".

Ce programme utilise MagickWand pour charger une image, ajouter un cadre noir de 1 pixel autour d'elle, sauvegarder l'image modifiée, puis libérer les ressources utilisées.

Pour compiler le programme, il suffit de taper la commande suivante, avec le terminal ouvert dans le répertoire où se trouve l'exécutable "cadre'.
gcc -o cadre cadre.c $(pkg-config --cflags --libs MagickWand)

//gcc -o cadre cadre.c $(pkg-config --cflags --libs MagickWand)
#include <stdio.h>
#include <wand/MagickWand.h>
int main() {
// Initialiser l'API MagickWand
MagickWandGenesis();
// Créer un objet MagickWand
MagickWand *wand = NewMagickWand();
// Charger l'image depuis le fichier "image.png"
if (MagickReadImage(wand, "image.png") == MagickFalse) {
fprintf(stderr, "Erreur lors de la lecture de l'image.\n");
DestroyMagickWand(wand);
MagickWandTerminus();
return 1;
}
// Ajouter un cadre noir de 1 pixel autour de l'image
PixelWand *border_color = NewPixelWand();
PixelSetColor(border_color, "black");
if (MagickBorderImage(wand, border_color, 1, 1) == MagickFalse) {
fprintf(stderr, "Erreur lors de l'ajout du cadre noir.\n");
DestroyMagickWand(wand);
DestroyPixelWand(border_color);
MagickWandTerminus();
return 1;
}
// Sauvegarder l'image modifiée avec le nom "image-sauv-bordure.png"
if (MagickWriteImage(wand, "image-sauv-bordure.png") == MagickFalse) {
fprintf(stderr, "Erreur lors de la sauvegarde de l'image modifiée.\n");
DestroyMagickWand(wand);
DestroyPixelWand(border_color);
MagickWandTerminus();
return 1;
}
// Libérer les ressources
DestroyMagickWand(wand);
DestroyPixelWand(border_color);
MagickWandTerminus();
printf("traitement effectué\n");
return 0;
}

1- Initialisation de l'API MagickWand :
MagickWandGenesis();
Cette fonction initialise l'API MagickWand. C'est la première étape nécessaire pour utiliser les fonctionnalités de la bibliothèque MagickWand.

2- Création d'un objet MagickWand :
MagickWand *wand = NewMagickWand();
On crée un objet qu'on appelle wand, de type MagickWand. Cet objet représente une image ou une séquence d'images. Toutes les opérations sur l'image seront effectuées sur cet objet.

3- Chargement de l'image depuis le fichier "image.png" :
if (MagickReadImage(wand, "image.png") == MagickFalse) {
fprintf(stderr, "Erreur lors de la lecture de l'image.\n");
DestroyMagickWand(wand);
MagickWandTerminus();
return 1;
}

L'instruction MagickReadImage charge l'image depuis le fichier "image.png" dans l'objet MagickWand. Si le chargement échoue, une erreur est affichée sur la sortie d'erreur, et le programme se termine avec un code d'erreur.

4- Ajout d'un cadre noir de 1 pixel autour de l'image :
PixelWand *border_color = NewPixelWand();
PixelSetColor(border_color, "black");
if (MagickBorderImage(wand, border_color, 1, 1) == MagickFalse) {
fprintf(stderr, "Erreur lors de l'ajout du cadre noir.\n");
DestroyMagickWand(wand);
DestroyPixelWand(border_color);
MagickWandTerminus();
return 1;
}

On crée un objet qu'on appelle border_color, de type PixelWand, représentant la couleur noire. Ensuite, la fonction MagickBorderImage ajoute un cadre noir de 1 pixel autour de l'image dans l'objet wand de type MagickWand. Si l'ajout échoue, une erreur est affichée, les ressources sont libérées, et le programme se termine avec un code d'erreur.

5- Sauvegarde de l'image modifiée avec le nom "image-sauv-bordure.png" :
if (MagickWriteImage(wand, "image-sauv-bordure.png") == MagickFalse) {
fprintf(stderr, "Erreur lors de la sauvegarde de l'image modifiée.\n");
DestroyMagickWand(wand);
DestroyPixelWand(border_color);
MagickWandTerminus();
return 1;
}

L'instruction MagickWriteImage sauvegarde l'image modifiée dans un nouveau fichier "image-sauv-bordure.png". Si la sauvegarde échoue, une erreur est affichée, les ressources sont libérées, et le programme se termine avec un code d'erreur.

6- Libération des ressources :
DestroyMagickWand(wand);
DestroyPixelWand(border_color);

Ces instructions libèrent la mémoire allouée pour l'objet MagickWand et l'objet PixelWand.

7- Terminaison de l'API MagickWand :
MagickWandTerminus();
Cette instruction termine l'API MagickWand. C'est la dernière fonction qu'on appelle avant la fin du programme.

8- Affichage d'un message de traitement effectué :
printf("traitement effectué\n");
Si le programme parvient à cette étape, cela signifie que le traitement s'est déroulé avec succès, et un message est affiché.

9- Retour avec succès :
return 0;
Le programme se termine avec un code de retour 0, indiquant qu'il s'est exécuté correctement.

2) Exemple 2 : Tracé d'un cadre noir autour d'un lot d'images

Voici un exemple de programme (programme source "cadres-noirs.c", exécutable "cadres-noirs") écrit en langage C utilisant l'API MagickWand d'ImageMagick pour tracer un cadre noir de 1 pixel sur chaque image PNG d'un lot d'images (de noms quelconques) qui se trouvent dans le même répertoire que l'éxécutable "cadres-noirs".

Le programme fonctionne également si les images sont de type JPG ou autre.

/*
 cadres-noirs.c - auteur Claude Turrier - octobre 2023
 Ce programme ajoute un cadre noir de 1 pixel sur chaque image (png, jpg...) se trouvant
 dans le répertoire de l'exécutable "cadres-noirs" 
 compilation : gcc -o cadres-noirs cadres-noirs.c $(pkg-config --cflags --libs MagickWand)
 exécution : ./cadres-noirs 
 */
#include <stdio.h>
#include <wand/MagickWand.h>
#include <dirent.h>
#include <string.h>
void processImage(const char *filename);
int main() {
// Initialiser l'API MagickWand
MagickWandGenesis();
// Ouvrir le répertoire courant
DIR *dir = opendir(".");
if (!dir) {
fprintf(stderr, "Erreur lors de l'ouverture du répertoire.\n");
MagickWandTerminus();
return 1;
}
// Parcourir tous les fichiers du répertoire
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
// Ignorer les fichiers spéciaux "." et ".."
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
// Ignorer les fichiers sans extension
char *dot = strrchr(entry->d_name, '.');
if (!dot || dot == entry->d_name) {
continue;
}
// Traitement de chaque image
processImage(entry->d_name);
}
// Fermer le répertoire
closedir(dir);
// Terminer l'API MagickWand
MagickWandTerminus();
return 0;
}
void processImage(const char *filename) {
// Créer un objet MagickWand
MagickWand *wand = NewMagickWand();
// Charger l'image depuis le fichier d'entrée
if (MagickReadImage(wand, filename) == MagickFalse) {
fprintf(stderr, "Erreur lors de la lecture de l'image %s.\n", filename);
DestroyMagickWand(wand);
return;
}
// Ajouter un cadre noir de 1 pixel autour de l'image
PixelWand *border_color = NewPixelWand();
PixelSetColor(border_color, "black");
if (MagickBorderImage(wand, border_color, 1, 1) == MagickFalse) {
fprintf(stderr, "Erreur lors de l'ajout du cadre noir à l'image %s.\n", filename);
DestroyMagickWand(wand);
DestroyPixelWand(border_color);
return;
}
// Sauvegarder l'image modifiée dans un fichier de sortie
char outputFilename[256];
snprintf(outputFilename, sizeof(outputFilename), "bordered_%s", filename);
if (MagickWriteImage(wand, outputFilename) == MagickFalse) {
fprintf(stderr, "Erreur lors de la sauvegarde de l'image modifiée %s.\n", outputFilename);
} else {
printf("Image modifiée enregistrée avec succès : %s\n", outputFilename);
}
// Libérer les ressources
DestroyMagickWand(wand);
DestroyPixelWand(border_color);
}

3) Exemple 3 : Tracé d'un cadre noir-blanc autour d'un lot d'images

Voici un exemple de programme (programme source "cadres-noirs-blancs.c", exécutable "cadres-noirs-blancs") écrit en langage C utilisant l'API MagickWand d'ImageMagick pour tracer un cadre noir-blanc de 2 pixels (un cadre noir de 1 pixel extérieur plus un cadre blanc de 1 pixel intérieur) sur chaque image PNG d'un lot d'images (de noms quelconques) qui se trouvent dans le même répertoire que l'éxécutable "cadres-noirs".

Le programme fonctionne également si les images sont de type JPG ou autre.

/*
 cadres-noirs-blancs.c - auteur Claude Turrier - octobre 2023
 Ce programme ajoute un cadre noir-blanc de 2 pixels sur chaque image (png, jpg...) se trouvant
 dans le répertoire de l'exécutable "cadres-noirs-blancs" 
 (cadre noir de 1 pixel extérieur et cadre blanc de 1 pixel intérieur)
 compilation : gcc -o cadres-noirs-blancs cadres-noirs-blancs.c $(pkg-config --cflags --libs MagickWand)
 exécution : ./cadres-noirs-blancs 
 */
#include <stdio.h>
#include <wand/MagickWand.h>
#include <dirent.h>
#include <string.h>
void processImage(const char *filename);
int main() {
// Initialiser l'API MagickWand
MagickWandGenesis();
// Ouvrir le répertoire courant
DIR *dir = opendir(".");
if (!dir) {
fprintf(stderr, "Erreur lors de l'ouverture du répertoire.\n");
MagickWandTerminus();
return 1;
}
// Parcourir tous les fichiers du répertoire
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
// Ignorer les fichiers spéciaux "." et ".."
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
// Ignorer les fichiers sans extension
char *dot = strrchr(entry->d_name, '.');
if (!dot || dot == entry->d_name) {
continue;
}
// Traitement de chaque image
processImage(entry->d_name);
}
// Fermer le répertoire
closedir(dir);
// Terminer l'API MagickWand
MagickWandTerminus();
return 0;
}
void processImage(const char *filename) {
// Créer un objet MagickWand
MagickWand *wand = NewMagickWand();
// Charger l'image depuis le fichier d'entrée
if (MagickReadImage(wand, filename) == MagickFalse) {
fprintf(stderr, "Erreur lors de la lecture de l'image %s.\n", filename);
DestroyMagickWand(wand);
return;
}
// Ajouter un cadre blanc (1 pixel intérieur)
PixelWand *border_color_white = NewPixelWand();
PixelSetColor(border_color_white, "white");
if (MagickBorderImage(wand, border_color_white, 1, 1) == MagickFalse) {
fprintf(stderr, "Erreur lors de l'ajout du cadre blanc à l'image %s.\n", filename);
DestroyMagickWand(wand);
DestroyPixelWand(border_color_white);
return;
}
// Ajouter un cadre noir (1 pixel extérieur)
PixelWand *border_color_black = NewPixelWand();
PixelSetColor(border_color_black, "black");
if (MagickBorderImage(wand, border_color_black, 1, 1) == MagickFalse) {
fprintf(stderr, "Erreur lors de l'ajout du cadre noir à l'image %s.\n", filename);
DestroyMagickWand(wand);
DestroyPixelWand(border_color_white);
DestroyPixelWand(border_color_black);
return;
}
// Sauvegarder l'image modifiée dans un fichier de sortie
char outputFilename[256];
snprintf(outputFilename, sizeof(outputFilename), "bordered_%s", filename);
if (MagickWriteImage(wand, outputFilename) == MagickFalse) {
fprintf(stderr, "Erreur lors de la sauvegarde de l'image modifiée %s.\n", outputFilename);
} else {
printf("Image modifiée enregistrée avec succès : %s\n", outputFilename);
}
// Libérer les ressources
DestroyMagickWand(wand);
DestroyPixelWand(border_color_white);
DestroyPixelWand(border_color_black);
}

1- Commentaires du programme :
Le programme commence par des commentaires expliquant son but, son auteur, et les instructions de compilation et d'exécution.

2- Initialisation de l'API MagickWand :
MagickWandGenesis();
Cette fonction initialise l'API MagickWand, ce qui est nécessaire pour utiliser les fonctionnalités de la bibliothèque MagickWand.

3- Ouverture du répertoire courant :
DIR *dir = opendir(".");
Le programme ouvre le répertoire courant en utilisant la fonction opendir.

4- Vérification de l'ouverture du répertoire :
if (!dir) {
fprintf(stderr, "Erreur lors de l'ouverture du répertoire.\n");
MagickWandTerminus();
return 1;
}

Si l'ouverture du répertoire échoue, une erreur est affichée, les ressources de MagickWand sont libérées, et le programme se termine avec un code d'erreur.

5- Parcours des fichiers du répertoire :
while ((entry = readdir(dir)) != NULL) {
...
// Traitement pour chaque fichier
...
}

Le programme utilise une boucle pour parcourir tous les fichiers du répertoire.

6- Ignorer les fichiers spéciaux "." et ".." :
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}

Les fichiers spéciaux "." et ".." sont ignorés.

7- Ignorer les fichiers sans extension :
char *dot = strrchr(entry->d_name, '.');
if (!dot || dot == entry->d_name) {
continue;
}

Les fichiers sans extension (l'exécutable présent dans le répertoire notamment) sont également ignorés.

8- Traitement de chaque image :
processImage(entry->d_name);

Pour chaque fichier image, la fonction processImage est appelée pour effectuer le traitement.

9- Fermer le répertoire :
closedir(dir);
Une fois que tous les fichiers du répertoire ont été traités, le répertoire est fermé.

10- Terminer l'API MagickWand :
MagickWandTerminus();
L'API MagickWand est terminée.

11- Fonction processImage :
La fonction processImage est définie pour traiter chaque image (c'est à dire ajouter un cadre noir-blanc sur chaque image présente dans le répertoire).

12- Création d'un objet MagickWand :
MagickWand *wand = NewMagickWand();
Un nouvel objet MagickWand est créé pour chaque image à traiter.

13- Chargement de l'image depuis le fichier d'entrée :
if (MagickReadImage(wand, filename) == MagickFalse) {
fprintf(stderr, "Erreur lors de la lecture de l'image %s.\n", filename);
DestroyMagickWand(wand);
return;
}

L'image est chargée depuis le fichier d'entrée. En cas d'erreur de lecture, un message d'erreur est affiché et l'objet wand, associé à l'image courante, est supprimé.

14- Ajout d'un cadre blanc (1 pixel intérieur) :
PixelWand *border_color_white = NewPixelWand();
PixelSetColor(border_color_white, "white");
if (MagickBorderImage(wand, border_color_white, 1, 1) == MagickFalse) {
fprintf(stderr, "Erreur lors de l'ajout du cadre blanc à l'image %s.\n", filename);
DestroyMagickWand(wand);
DestroyPixelWand(border_color_white);
return;
}

Un cadre blanc d'un pixel est ajouté à l'intérieur de l'image. En cas d'erreur dans le traitement, un message d'erreur est affiché et les objets (wand et border_color_white) associés à l'image courante, sont supprimés.

15- Ajout d'un cadre noir (1 pixel extérieur) :
PixelWand *border_color_black = NewPixelWand();
PixelSetColor(border_color_black, "black");
if (MagickBorderImage(wand, border_color_black, 1, 1) == MagickFalse) {
fprintf(stderr, "Erreur lors de l'ajout du cadre noir à l'image %s.\n", filename);
DestroyMagickWand(wand);
DestroyPixelWand(border_color_white);
DestroyPixelWand(border_color_black);
return;
}

Un cadre noir d'un pixel est ajouté à l'extérieur de l'image. En cas d'erreur dans le traitement, un message d'erreur est affiché et les objets (wand, border_color_white et border_color_black ) associés à l'image courante, sont supprimés.

16- Sauvegarde de l'image modifiée dans un fichier de sortie :
char outputFilename[256];
snprintf(outputFilename, sizeof(outputFilename), "bordered_%s", filename);
if (MagickWriteImage(wand, outputFilename) == MagickFalse) {
fprintf(stderr, "Erreur lors de la sauvegarde de l'image modifiée %s.\n", outputFilename);
} else {
printf("Image modifiée enregistrée avec succès : %s\n", outputFilename);
}

L'image modifiée est sauvegardée dans un fichier de sortie. Un message est affiché indiquant si la sauvegarde s'est bien passée.

17- Libération des ressources :
DestroyMagickWand(wand);
DestroyPixelWand(border_color_white);
DestroyPixelWand(border_color_black);

Les ressources utilisées (objets de type MagickWand et PixelWand) sont libérées.

18- Affichage des résultats :
printf("Image modifiée enregistrée avec succès : %s\n", outputFilename);
Un message est affiché pour indiquer que l'image a été modifiée avec succès.

19- Terminer la fonction processImage :
La fonction processImage se termine.

20- Retour à la boucle principale :
La boucle principale continue à traiter les fichiers suivants.

21- Terminer la fonction principale main :
return 0;
La fonction principale main se termine avec un code de retour 0, indiquant que le programme s'est exécuté correctement.