transparents du cours en PDF - IUT d'Arles
transparents du cours en PDF - IUT d'Arles
transparents du cours en PDF - IUT d'Arles
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
Imagerie Numérique<br />
Représ<strong>en</strong>tation et codage des images<br />
2. Implém<strong>en</strong>tation d’une<br />
classe Image <strong>en</strong> C++<br />
E. Remy<br />
<strong>IUT</strong> de Prov<strong>en</strong>ce<br />
7/01/2003
19/10/2003 2<br />
ATTENTION<br />
CE COURS NECESSITE UNE REECRITURE<br />
COMPLETE POUR ETRE EN<br />
SYNCHRONISATION AVEC LE<br />
PROGRAMME UTILISE EN TRAVAUX<br />
PRATIQUES.<br />
ETANT ENCORE UTILE TEL QUEL, IL EST<br />
DONNE A TITRE INDICATIF…
19/10/2003 3<br />
Préliminaires<br />
• 3 façons classiques de coder une image :<br />
• Une image « directe » dont chaque pixel conti<strong>en</strong>t un<br />
vecteur de couleur, par exemple (R,V,B) ;<br />
• Un vecteur d’images « directes » dont les pixels<br />
conti<strong>en</strong>n<strong>en</strong>t une et une seule composante parmi R, V<br />
et B. Il faut donc avoir les 3 images pour pouvoir<br />
interpréter le codage.<br />
• Une palette plus une image cont<strong>en</strong>ant les indices de<br />
couleurs utilisés dans la palette.
19/10/2003 4<br />
Cahier des charges<br />
• Coder toutes les formes d’images bitmap 2D (codage<br />
direct ou codage avec palette, noir et blanc, gris,<br />
couleur, etc).<br />
• Capacité de modifier le cont<strong>en</strong>u de l’image.<br />
• Opérations de conversion faciles d’un type d’image à<br />
un autre.<br />
• Lecture et écriture de l’image dans un fichier sur le<br />
disque <strong>du</strong>r dans plusieurs choix de formats.
19/10/2003 5<br />
Attributs de la classe<br />
Des dim<strong>en</strong>sions :<br />
• Largeur et hauteur<br />
• <strong>en</strong>tières et strictem<strong>en</strong>t positives.<br />
Le tableau de pixel :<br />
• Type de pixel variable : pixel_type<br />
• Taille <strong>du</strong> tableau varie d’une instance à<br />
l’autre, donc allocation dynamique.<br />
• Tableau « à plat » (schéma).<br />
Pour des raisons de performance, on veut que<br />
les classes héritant de Image puiss<strong>en</strong>t avoir<br />
accès directem<strong>en</strong>t aux données :<br />
• Droit d’accès « protected ».<br />
template<br />
class Image {<br />
protected:<br />
unsigned int dimx,dimy<br />
dimy;<br />
pixel_type *array;<br />
};
19/10/2003 6<br />
Méthodes primitives<br />
Consultation de la largeur et de<br />
la hauteur :<br />
• Obt<strong>en</strong>ir la valeur, mais…<br />
• Ne pas pouvoir la changer !<br />
Modification de la largeur et de<br />
la hauteur :<br />
• Pouvoir changer la taille de<br />
l’image ;<br />
• Réallouer le tableau de pixels,<br />
sans préservation de l’image<br />
qui est dedans.<br />
• On suppose que le tableau est<br />
déjà alloué avant, ou qu’il est<br />
bi<strong>en</strong> indiqué comme étant à<br />
l’adresse NULL.<br />
inline unsigned int GetDimX(void<br />
void) const<br />
{ return dimx; ; }<br />
inline unsigned int GetDimY(void<br />
void) const<br />
{ return dimy; ; }<br />
void SetSize(const<br />
unsigned int dimx,<br />
const unsigned int _dimy)<br />
{<br />
delete [] array;<br />
dimx = _dimx;<br />
dimy = _dimy;<br />
array = new pixel_type[dimx<br />
dimx*dimy];<br />
}
19/10/2003 7<br />
Modification d’un pixel<br />
Vérification des<br />
paramètres ;<br />
Gestion d’erreur<br />
avec des<br />
exceptions ;<br />
Adressage d’un<br />
pixel dans le<br />
tableau linéaire ;<br />
Modification <strong>du</strong><br />
pixel si les<br />
paramètres sont<br />
valides.<br />
void SetPixel(const<br />
unsigned int x, const unsigned int y,<br />
const pixel_type pixel_value)<br />
{<br />
if(x >= dimx)<br />
{<br />
std::<br />
::ostringstream<br />
s;<br />
s
19/10/2003 8<br />
Consultation d’un pixel<br />
Méthode constante :<br />
on ne modifie pas<br />
l’image <strong>en</strong> la<br />
consultant.<br />
Vérification des<br />
paramètres ;<br />
Gestion d’erreur<br />
avec des exceptions ;<br />
Adressage d’un pixel<br />
dans le tableau<br />
linéaire ;<br />
R<strong>en</strong>voi d’une<br />
référ<strong>en</strong>ce constante<br />
sur le pixel dans le<br />
tableau afin d’éviter<br />
une construction par<br />
copie.<br />
const pixel_type& GetPixel(const<br />
unsigned int x,<br />
const unsigned int y) const<br />
{<br />
if(x >= dimx)<br />
{<br />
std::<br />
::ostringstream<br />
s;<br />
s
19/10/2003 9<br />
void Copy(const<br />
Image& source)<br />
{<br />
Copie d’une image<br />
SetSize(source.dimx<br />
source.dimx,source.dimy);<br />
for(unsigned<br />
long tmax = dimx*dimy<br />
dimy,t=0; t
19/10/2003 10<br />
Constructeurs<br />
Image(const<br />
unsigned int _dimx=0,<br />
const unsigned int _dimy=0)<br />
: dimx(0),<br />
(0),dimy(0),array(NULL)<br />
{ SetSize(_dim<br />
_dimx, x,_dimy); }<br />
• Valeurs des paramètres par défaut : correspond à une image de taille nulle.<br />
• Par sécurité, on utilise la liste d’initialisation pour mettre des d<br />
valeurs<br />
signifiant que l’image est vide, et on utilise uniquem<strong>en</strong>t la méthode<br />
SetSize()<br />
() pour effectuer l’allocation <strong>du</strong> tableau.<br />
Image(const Image& i)<br />
: dimx(0),dimy(0),array(NULL)<br />
{ Copy(i); }<br />
• Utilise la méthode Copy()<br />
• Ne pas abuser <strong>du</strong> constructeur de copie puisqu’il nécessite la coûteuse<br />
recopie pixel par pixel de l’image source.
19/10/2003 11<br />
Destructeur<br />
~Image()<br />
{ delete [] array; ; }<br />
• Le destructeur est trivial.<br />
• Son seul rôle est de libérer la mémoire prise par le tableau.<br />
• On aurait pu utiliser SetSize(0,0)<br />
qui aurait eu un effet s<strong>en</strong>siblem<strong>en</strong>t<br />
équival<strong>en</strong>t, mais il était inutile de placer les largeurs et hauteur à zéro pour<br />
<strong>en</strong>suite de toutes façons détruire l’instance…<br />
Opérateur d’affectation<br />
inline void operator=(const Image&<br />
src)<br />
{ Copy(src); }<br />
• Très semblable au constructeur de copie ;<br />
• Même coût de recopie.
19/10/2003 12<br />
Conversion implicite<br />
template<br />
void Convert(const<br />
Image& source)<br />
{<br />
SetSize(source.GetDimX<br />
source.GetDimX(), (),source.GetDimY());<br />
for(unsigned<br />
int y=0;y
19/10/2003 13<br />
Conversion explicite<br />
template<br />
void Convert(const<br />
Image& source,<br />
pixel_type (*conversion)(const<br />
const any_type))<br />
{<br />
SetSize(source.GetDimX<br />
source.GetDimX(), (),source.GetDimY());<br />
for(unsigned<br />
int y=0;y
19/10/2003 14<br />
Constructeur (suite)<br />
template<br />
Image(const<br />
Image& i,<br />
pixel_type (*conversion)(const<br />
const any_type))<br />
: dimx(0),<br />
(0),dimy(0),array(NULL)<br />
{ Convert(i,<br />
i,conversion); ; }<br />
• On rajoute un constructeur supplém<strong>en</strong>taire reposant sur la conversion<br />
explicite.<br />
• Il permet de créer directem<strong>en</strong>t une instance d’une image <strong>en</strong> effectuant la<br />
conversion explicite avec une fonction de conversion.
19/10/2003 15<br />
Type byte<br />
typedef unsigned char byte;<br />
const byte byte_MIN(0);<br />
const byte byte_MAX(UCHAR_MAX);<br />
• On définit un type byte correspondant à un <strong>en</strong>tier positif codé sur une<br />
taille de 8 bits (1 octet).<br />
• On définit aussi deux constantes qui sont les bornes supérieures et<br />
inférieures de l’intervalle de définition de ce type byte.<br />
• Ces définitions sont repos<strong>en</strong>t sur la définition de l’unsigned<br />
char dans la<br />
norme C++ 98. Idem pour UCHAR_MAX dont la définition est incorporées<br />
<strong>en</strong> faisant #include<br />
.
19/10/2003 16<br />
Type Gray<br />
typedef byte Gray;<br />
const Gray Gray_MIN(0);<br />
const Gray Gray_MAX(byte_MAX);<br />
• En se servant de byte, , on définit maint<strong>en</strong>ant un type Gray permettant de<br />
mémoriser un niveau de gris compris <strong>en</strong>tre 0 (Gray_MIN(<br />
Gray_MIN) ) et 255<br />
(Gray_MAX).<br />
• La définition <strong>du</strong> type byte était attachée à l’idée de taille 1 octet.<br />
• La définition de ce type Gray est liée au concept de niveau de gris.<br />
• On a vu précédemm<strong>en</strong>t que 255 niveaux de gris étai<strong>en</strong>t bi<strong>en</strong> plus que q<br />
ce<br />
que le système visuel humain est capable de percevoir. Notre codage sera<br />
donc probablem<strong>en</strong>t suffisant dans tous les cas.
Classe Color (1/2)<br />
class Color {<br />
byte red,gre<strong>en</strong>,blue;<br />
public:<br />
Color(byte r=0,byte g=0,byte b=0) : red(r),gre<strong>en</strong>(g),blue(b) ) {}<br />
Color(Gray g): red(g),gre<strong>en</strong>(g),blue(g) ) {}<br />
inline byte GetRed(void) ) const { return red; }<br />
inline byte GetGre<strong>en</strong>(void) ) const { return gre<strong>en</strong>; }<br />
inline byte GetBlue(void) ) const { return blue; }<br />
…<br />
};<br />
• Toujours <strong>en</strong> se servant de byte, , on définit maint<strong>en</strong>ant une classe Color<br />
permettant de mémoriser un vecteur couleur dans l’espace RGB.<br />
• Chaque composante <strong>du</strong> vecteur de couleur est codé par un byte, , et est<br />
donc compris <strong>en</strong>tre 0 (byte_MIN(<br />
byte_MIN) ) et 255 (byte_MAX(<br />
byte_MAX).<br />
• Le constructeur permet de faire par défaut une couleur noire (0,0,0).<br />
0,0).<br />
• 3 accesseurs permett<strong>en</strong>t d’obt<strong>en</strong>ir les valeurs de chaque composante, mais<br />
pas de les modifier. Pour modifier une couleur, <strong>en</strong> fait il faut <strong>en</strong> déclarer<br />
une autre, et se servir des paramètres <strong>du</strong> constructeur.<br />
19/10/2003 17
19/10/2003 18<br />
Classe Color (2/2)<br />
class Color {<br />
…<br />
fri<strong>en</strong>d inline bool operator==(const Color& c1,const Color& c2)<br />
{ return c1.red==c2.red && c1.gre<strong>en</strong>==c2.gre<strong>en</strong> && c1.blue==c2.blue; ue; }<br />
fri<strong>en</strong>d inline bool operator!=(const Color& c1,const Color& c2)<br />
{ return c1.red!=c2.red || c1.gre<strong>en</strong>!=c2.gre<strong>en</strong> || c1.blue!=c2.blue; ue; }<br />
inline Color& operator=(const Color& c)<br />
{ red=c.red<br />
c.red; ; gre<strong>en</strong>=c.gre<strong>en</strong><br />
c.gre<strong>en</strong>; ; blue=c.blue<br />
c.blue; ; return *this; }<br />
fri<strong>en</strong>d std::ostream& & operator
19/10/2003 19<br />
Classe GrayImage<br />
class GrayImage : public Image {<br />
public:<br />
GrayImage(const unsigned int dimx=0,const unsigned int dimy=0)<br />
: Image(dimx,dimy<br />
dimx,dimy)<br />
{}<br />
template<br />
GrayImage(const Image& source)<br />
: Image(0,0)<br />
{ Convert(source); }<br />
…<br />
};<br />
• On la fait hériter de Image afin de bénéficier de tout ce qui est déjà<br />
défini pour une template > Image.<br />
• On rajoutera au niveau de cette classe des méthodes spécifiques à une<br />
image <strong>en</strong> niveau de gris (<strong>en</strong> particulier, des méthodes de lecture et<br />
d’écriture d’image <strong>en</strong> niveau de gris).
19/10/2003 20<br />
Classe ColorImage<br />
class ColorImage : public Image {<br />
public:<br />
ColorImage(const unsigned int dimx=0,const unsigned int dimy=0)<br />
: Image(dimx,dimy<br />
dimx,dimy)<br />
{}<br />
template<br />
ColorImage(const Image& source)<br />
: Image(0,0)<br />
{ Convert(source); }<br />
…<br />
};<br />
• Exactem<strong>en</strong>t le même principe, mais avec Image.<br />
• Ici aussi, on rajoutera des méthodes spécifiques aux images <strong>en</strong> couleurs. c
19/10/2003 21<br />
Classe PaletteImage<br />
template<br />
class PaletteImage : public Image<br />
{<br />
protected:<br />
unsigned int palette_count,palette_max;<br />
pixel_type *palette;<br />
…<br />
};<br />
• Images définies avec une palette ;<br />
• Rester indép<strong>en</strong>dant <strong>du</strong> type des « couleurs » utilisées comme pour<br />
Image.<br />
• On hérite de Image car l’image conti<strong>en</strong>t des numéros de<br />
position des couleurs dans le tableau palette. (schéma)<br />
• On utilise un template pour ne pas verrouiller le type des élém<strong>en</strong>ts <strong>du</strong> tableau<br />
palette.
19/10/2003 22<br />
Constructeur et destructeur de la<br />
classe PaletteImage<br />
PaletteImage(const unsigned int dimx=0,const unsigned int dimy=0,<br />
const unsigned int _palette_max=0)<br />
: Image(dimx,dimy),palette_count(0),<br />
palette_max(_palette_max),palette(NULL)<br />
{ palette = new pixel_type[_palette_max]; }<br />
~PaletteImage()<br />
{ delete [] palette; }<br />
• On décide de contraindre l’utilisateur a donner <strong>en</strong> paramètre <strong>du</strong><br />
constructeur la borne supérieure <strong>du</strong> nombre de couleurs pouvant être ê<br />
mémorisées dans la palette.<br />
• Ce nombre permet d’allouer la palette au mom<strong>en</strong>t de la création de d<br />
l’image.<br />
• Contre partie : la palette est pour l’instant de taille fixe.
19/10/2003 23<br />
Copie d’une PaletteImage<br />
void Copy(const PaletteImage& source)<br />
{<br />
palette_count = source.palette_count;<br />
palette_max<br />
= source.palette_max;<br />
delete [] palette;<br />
palette = new pixel_type[palette_max];<br />
for(unsigned int i=0;i::Copy(source);<br />
}<br />
• Copier une PaletteImage c’est copier la palette…<br />
• Puis copier le tableau d’index.<br />
Consultation d’un pixel<br />
const pixel_type& GetPixel(unsigned int x,unsigned int y) const<br />
{ return palette[Image::<br />
>::GetPixel(x,y)]; }
19/10/2003 24<br />
Modification d’un pixel (1/2)<br />
Pour écrire un pixel avec une couleur donnée, il faut :<br />
1. Trouver si cette couleur est prés<strong>en</strong>te dans la palette actuelle, et<br />
noter son numéro.<br />
2. Si oui, utiliser ce numéro dans le tableau de points.<br />
3. Si non, utiliser une <strong>en</strong>trée libre de la palette pour stocker la<br />
couleur, noter son numéro, et l’utiliser dans le tableau de<br />
points.<br />
4. Si la palette est pleine, il y a échec de l’opération.<br />
On intro<strong>du</strong>it donc deux opérations avant d’écrire PutPixel()<br />
:<br />
• FindPaletteIndex()<br />
et<br />
• AllocatePaletteEntry()<br />
().
19/10/2003 25<br />
Recherche d’une couleur dans la palette<br />
unsigned int FindPaletteIndex(const pixel_type pixel)<br />
{<br />
unsigned int i;<br />
for(i=0;i<<br />
=0;i
19/10/2003 26<br />
Allocation d’une (nouvelle) couleur dans<br />
la palette<br />
unsigned int AllocatePaletteEntry(const pixel_type pixel)<br />
{<br />
}<br />
if(palette_count>=<br />
>=palette_max)<br />
{<br />
std::ostringstream os;<br />
os
19/10/2003 27<br />
Modification d’un pixel (2/2)<br />
void SetPixel(const unsigned int x,const unsigned int y,const pixel_type pixel)<br />
{<br />
}<br />
try { Image::<br />
>::SetPixel(x,y,FindPaletteIndex(pixel)); }<br />
catch(std::string s)<br />
{<br />
}<br />
try { Image::<br />
>::SetPixel(x,y,AllocatePaletteEntry(pixel)); }<br />
catch(std::string s2)<br />
{<br />
std::ostringstream os;<br />
os
19/10/2003 28<br />
Des questions ?