21/04/2023
Pré-requis
Workboot et machine windows ou linux avec internet.
cours de C , variables, boucles, fichier open / write / close
Partie 1: Présentation de l'image pbm / rappel de cours !
Partie 2: Génération d'une image blanche et une noire
Partie 3: Ecriture de fonction de base pour dessiner.
But
Créer des images pbm ,dans le langage C ansi
Vous disposez d'un espace de travail sur workboot.freeboxos.fr
mise en place de l'outil workboot
ssh étant disponible depuis la ligne de commande sous windows ou Linux
nous allons ici travailler avec le format pbm en ASCII . (pédagogique) format simple en noir et blanc
Le wikipedia est très bien fait.
on va enlever les commentaires pour simplifier notre travail. (plus tard un jour on les remettra )
fichier
J.pbm
vous pouvez récupérer ces fichiers en faisant des copier/coller
P1
7 10
0 0 0 0 0 0 0
0 0 0 0 0 1 0
0 0 0 0 0 1 0
0 0 0 0 0 1 0
0 0 0 0 0 1 0
0 0 0 0 0 1 0
0 0 0 0 0 1 0
0 1 0 0 0 1 0
0 0 1 1 1 0 0
0 0 0 0 0 0 0
on peut voir le résultat avec la plate forme de travail workboot. (attention ici l'auteur est poly1)
en utilisant la commande :
imageviewer J.pbm
poly1@workboot:~/Works/TPC/imagepbm $ imageviewer J.pbm
allez voir sur le site http://workboot.freeboxos.fr/~poly1/convert
poly1@workboot:~/Works/TPC/imagepbm $
on obtient depuis votre navigateur :
on y voit bien le J , refaite cette manipulation depuis votre compte.
Autre format possible , plus pratique pour la suite de notre TP
J_bis.pbm
P1
7 10
0
0
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
1
0
0
0
0
0
0
1
0
0
0
0
0
0
1
0
0
0
0
0
0
1
0
0
0
0
0
0
1
0
0
1
0
0
0
1
0
0
0
1
1
1
0
0
0
0
0
0
0
0
0
On notera ici que J_bis.pbm n'est pas plus gros que J.pbm et donne le même résultat dans les 2 cas .
poly1@workboot:~/Works/TPC/imagepbm $ ls -lh
total 8,0K
-rw-r--r-- 1 poly1 poly1 148 avril 21 15:55 J_bis.pbm
-rw-r--r-- 1 poly1 poly1 148 avril 21 15:55 J.pbm
on voit ici que les 2 fichiers prennent autant d'octets 148
Que faut il retenir sur le format pbm
P1
7 10
0 0...
En rouge on va appeler cela en tête du fichier pbm (image)
En vert ce sont les coordonnées de l'image selon X et Y
et en noir ce sont les pixels de l'image qui sont au nombre de X * Y (70 pixels ici) 0 point blanc / 1 point noir
donner une explication des différences que l'on note ici.
regarder les octets en position 0x11 ou sur le fichier J.pbm on a 0x20 et sur J_bis.pbm on à 0x0a
Et donc pourquoi on a le même nombre d'octets dans nos 2 fichiers! ici 148 pourquoi
développer un calcul pour justifier ce 148.
une image blanche de 800 x 600
dans un répertoire ~/Works/TP_C /TPX/imgblc réaliser le projet suivant. (X étant le numéro de TP de votre formation)
source : imgblc.c
/******************************************/
/** Image blanche de 800 x 600 **/
/** V1.Bogt2020 **/
/** 09/11/2020 **/
/******************************************/
#include<stdio.h>
#include<stdlib.h>
int main()
{
int compteur;
/* en tete de l'image */
printf("P1\n");
/* pas de commentaire */
printf("800 600\n");
/* génération des 480000 pixels */
for (compteur=0;compteur<800*600; compteur++)
printf("0\n");
return EXIT_SUCCESS;
}
Sous linux on peut rediriger un flux vers un fichier avec l’opérateur >
exemple :
Comment récupérer le flux de caractère pour l'écran dans un fichier imgbc.pbm de votre code imgblc ?
A l'aide de la fonction imageviewer visualisé l'image blanche. (comme vu précédemment dans la partie 1)
dans un répertoire ~/Works/TP_C/TPX/imagenoire
Réaliser un code capable de génèrer les caractères pour faire une image noire de 800x600
un plan A B C D E F pour les colonnes
et 1 à 11 pour les lignes
On a mis 2 points dans ce tableau , retrouvez les 1 !
Juste la pour expliquer comment on place des points noirs dans un tableau.
une image est un plan d’éléments binaire (bitmap).
En C nous avons le tableau a 1 dimension , une ligne, aussi dénommé string , chaine de caractères.
char ligne[800]
cela représente une ligne 800 caractères, indicé de 0 à 799.
une image numérique est donc une succession de ligne non?
char image[800][600]
c'est un tableau de char (octet) de 800 x 600 .
On imagine très bien que ce sera notre image
Le projet sera dans un répertoire ~/Works/TP_C/TPX/bitmap
/******************************************/
/** Image avec points de 800 x 600 **/
/** V1.Bogt2020 **/
/** 09/11/2020 **/
/******************************************/
#include<stdio.h>
#include<stdlib.h>
#define largeur 800
#define hauteur 600
char image[largeur][hauteur]; /* representation de notre image */
int main()
{
int x,y;
/* remplir le tableau de '0' pour faire une image blanche */
for (y=0; y <hauteur ; y++) /* pour toutes les lignes */
for (x=0; x<largeur ; x++) /* pour chaque colonnes */
image[x][y]='0'; /* mettre un pixel blanc */
/* on place un point sur l'image coordonnées 320,456 */
image[320][456]='1'; /* il suffit de mettre un 1 dans la bonne case ! */
/* on place un point sur l'image coordonnées 501,322 */
image[501][322]='1';
/*Afficher l'image Pbm en ASCII*/
/* en tete de l'image */
printf("P1\n");
/* pas de commentaire dans l'image */
/* on donne les coordonnées en décimal dans l’entête */
printf("%d %d\n",largeur,hauteur);
/* on place tous les pixels */
for (y=0; y <hauteur ; y++) /* pour toutes les lignes */
for (x=0; x<largeur ; x++) /* pour chaque colonnes */
printf ("%c \n",image[x][y]);
return EXIT_SUCCESS;
}
grâce à ce code générer un fichier image.pbm (redirection de flux)
on désire dessiner des axes (une croix) sur notre image.
pt1 (399,0) à pt2 (399,599) pour l'axe X
pt3 (0,299) à pt4 (799,299)
pour avoir cette image on peut remplir à la main chaque pixel ...
image[399][0]='1';
image[399][1]='1';
image[399][2]='1';
...
etc
image[399][799]='1';
franchement on a inventer l'ordinateur pour ne pas faire des taches aussi fastidieuses trouver une solution autre pour réaliser les axes.
Cette fonction est donnée pour montrer comment faire une fonction en C , rappel surement ..
/** point description *******************************************/
/* point(x,y) , place un point sur l'image a la coordonnée x,y */
/* retourne 0 si ok 1 sinon */
/****************************************************************/
int point(int x,int y)
/******************************************/
/** Image avec points de 800 x 600 **/
/** V1.Bogt2020 **/
/** 09/11/2020 **/
/******************************************/
#include<stdio.h>
#include<stdlib.h>
#define largeur 800
#define hauteur 600
/* variable GLOBALE */
char image[largeur][hauteur]; /* representation de notre image */
/* nos fonctions */
/** point description *******************************************/
/* point(x,y) , place un point sur l'image a la coordonnée x,y */
/* retourne 0 si ok 1 sinon */
/****************************************************************/
int point(int x,int y)
{
/* on teste si nous sommes bien dans le tableau sinon on retourne 1 */
if ((x<0) || (x>=largeur))
{
fprintf(stderr,"On sort de l'image !\n");
return EXIT_FAILURE;
}
if ((y<0) || (y>=hauteur))
{
fprintf(stderr,"On sort de l'image !\n");
return EXIT_FAILURE;
}
image[x][y]='1';
return EXIT_SUCCESS;
}
/************************* principale *****************************/
int main()
{
int x,y;
/* remplir le tableau de '0' pour faire une image blanche */
for (y=0; y <hauteur ; y++) /* pour toutes les lignes */
for (x=0; x<largeur ; x++) /* pour chaque colonnes */
image[x][y]='0'; /* mettre un pixel blanc */
/* on place un point sur l'image coordonnées 320,456 */
point(320,456); /* on utilise notre premiere fonction pour faire un test unitaire*/
/* on place un point sur l'image coordonnées 501,322 pour tester */
point(501,322);
/*Afficher l'image Pbm en ASCII*/
/* en tete de l'image */
printf("P1\n");
/* pas de commentaire */
/* on donne les coordonnées en décimal dans l’entête */
printf("%d %d\n",largeur,hauteur);
/* on place tous les pixels */
for (y=0; y <hauteur ; y++) /* pour toutes les lignes */
for (x=0; x<largeur ; x++) /* pour chaque colonnes */
printf ("%c \n",image[x][y]);
return EXIT_SUCCESS;
}
/* faire un fichier pbm avec ce qui s'affiche */
/* ./bitmap > bitmap.pbm */
Actuellement le main réalise le remplissage de 0 dans le tableau contenant l'image.
Pour améliorer le code on va réaliser une fonction EffaceImage qui sera une fonction qui efface en blanc toute l'image.
Réaliser un main ou on testera cette nouvelle fonction.
Modifier le code précédent pour intégrer dans cette fonction.
void EffaceImage(void)
Utilisation du cours sur open / write / close
Avec vos connaissances sur les fichiers , écrire une fonction qui crée le fichier image.pbm dans le répertoire courant de votre projet.
void SauveImage(void)
Ecrire une fonction qui dessine une ligne horizontale dans l'image , qui utilise la fonction point réalisée précédemment.
réaliser un test unitaire de votre fonction, avec un main approprié.
void LigneHorizontale(int x,int y,int nbpixel)
Ecrire une fonction qui dessine une ligne verticale dans l'image , qui utilise la fonction point réalisée précédemment.
réaliser un test unitaire de votre fonction, avec un main approprié.
void LigneVerticale(int x,int y,int nbpixel)
En utilisant vos fonctions.
permet de tracer une ligne du point 1 au point 2 (mais facultatif dans notre TP pour les plus persévérant uniquement)
Mr Bresenham y a réfléchit déjà pour vous
wikipedia anglais de Bresenham
https://fr.wikipedia.org/wiki/Algorithme_de_trac%C3%A9_de_segment_de_Bresenham
Grâce a cet algorithme on peut tracer des lignes d'un point (x0,y0) a un point (x1,y1)
https://fr.wikipedia.org/wiki/Algorithme_de_trac%C3%A9_de_segment_de_Bresenham
https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm
void line(int x0, int y0, int x1, int y1) {
int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
int err = (dx>dy ? dx : -dy)/2, e2;
for(;;){
setPixel(x0,y0);
if (x0==x1 && y0==y1) break;
e2 = err;
if (e2 >-dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; }
}
}
Rosetta utilise des choses qui ne sont pas très classique!
pour comprendre il faut connaitre les expressions ternaire en C:
int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1; /* expression ternaire */
cette ligne peut s'écrire:
int dx;
dx= abs (x1-x0);
if (x0<x1) sx=1;
else sx=-1;
on remarque que la virgule sépare 2 actions.
Et que l'opérateur ternaire.
est simplement un if avec son else .. plus facile à comprendre
break et for (;;) !! c'est simplement monstrueux ,trouver une autre méthode structurée , while .. par exemple
Donner la fonction ligne plus compréhensible
tester et valider
void image_ligne(int x1,int y1,int x2,int y2)
si x=800 et y=600 mettre les axes au centre de l'image.
donner la fonction:
void Axes(int x,int y)
Dans notre image de 800x600 dessiner un belle fonction sinus , attention la fonction est en pi radian
la librairie mathématique
#include <math.h>
et ajouter -lm comme option à gcc pour utiliser cette librairie. (link avec la lib math.h)
! la fonction sinus et en Radian
man sin
exemple d'usage le fonction sin en C ansi
bruno@bruno-MS-7851:~/Works/Code_C/sinus$ more sinus.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main (int argc,char **argv)
{
printf ("sin(pi)=%f\n",sin(M_PI));
printf ("sin(pi/2)=%f\n",sin(M_PI/2));
return EXIT_SUCCESS;
}
bruno@bruno-MS-7851:~/Works/Code_C/sinus$ gcc sinus.c -o sinus -lm -Wall -pedantic -ansi
bruno@bruno-MS-7851:~/Works/Code_C/sinus$ ./sinus
sin(pi)=0.000000
sin(pi/2)=1.000000
bruno@bruno-MS-7851:~/Works/Code_C/sinus$
man math.h
man sin