[C++]Utilisation des génériques

Sting_6322
[C++]Utilisation des génériques

Bonjour,

dans le cadre d'un exo, je dois créer une classe générique Matrice(dont le paramètre de généricité sera la classe Pixel).
Pixel est une classe qui désigne un point d'une image.

template<Pixel>
class Matrice{
...
};

Ce que je voudrai savoir c'est quel attributs mettre dans cette classe et comment l'utiliser dans les méthodes
Cette classe aura comme méthodes : rotation, translation, homothétie qui permettent de faire des transformations géométriques sur un point

Merci

fredericmazue

Salut Sting,

C'est sympa de te voir poster en C++ :)
Ca change de Java ;)

Quote:
dans le cadre d'un exo, je dois créer une classe générique Matrice(dont le paramètre de généricité sera la classe Pixel).

Pardonnes moi mais si "le paramètre de généricité" est connu d'avance alors ... ta classe Matrice n'est pas générique....
Il me semble que soit tu n'as pas tout dit sur l'énoncé, soit tu n'as pas bien compris, soit l'énoncé est mauvais.
Pourrais tu détailler un peu plus cet énoncé ?

Quote:
Cette classe aura comme méthodes : rotation, translation, homothétie qui permettent de faire des transformations géométriques sur un point

Même chose ici, ça ne va pas. rotation et translation pour un point ça peut se concevoir. Homothétie sur un point moi je ne vois pas. Ou alors faut que je retourne à l'école. C'est vrai que je n'y suis pas allé beaucoup :D

Je vais quand même essayer de t'aider un peu concrètement.
Suppose que tu as Pixel comme ça (je prends une structure c'est plus pratique pour écrire. Tu transposeras à classe sans difficulté. Idem si ça doit être en 3D ;) ))

struct Pixel
{
  int x;
  int y;
};

Ta class matrice pourrait avoir cette tête là pour la translation

class Matrice
{
private:
  Pixel px;
public:
  Pixel translation(VecteurTranslation);
};

avec

Pixel Matrice::translation(VecteurTranslation vt)
{
   Pixel px_new = Pixel(px);
   px_new.x = px.x + vt.x;
   px_new.y = px.y + vt.y;

   return px_new;
}

Je suis parti du principe que translation retourne un nouveau Pixel. Ca parait logique si la classe s'appelle Matrice. Mais l'énoncé peut voir ça autrement.

Je n'ai pas écrit une classe générique non plus pour aller plus vite. Mais c'est une formalité de mettre un template dans la déclaration.
Je n'ai pas mis de constructeur non plus. En fait la classe telle que tu présentes l'énnoncé devrait être une classe normale contenant uniquement des méthodes statiques qui elles devraient être génériques. Enfin il me semble ;)

Bref je ne t'ai donné qu'un point de départ, mais j'espère quand même que ça t'aidera à démarrer. Affine la question, précise nous l'énoncé surtout, écris un peu de code, et reviens parmi nous. On essaiera d'avancer :)

Sting_6322

Bonjour fréderic,

il n'y a pas d'erreur de ma part.
L'énoncé parle bien d'une classe Matrice dont le paramètre de généricité sera la classe Pixel.

Pour écrire les méthodes de transformations, il n'y a pas besoin d'avoir en attribut une matrice représenté par un tableau ?
Il me semblait que ces calculs se faisaient à l'aide de matrice

L'idée de l'exo est d' écrire un programme qui permet d'effectuer des transformations géométriques sur une image. C'est pour ça que j'ai parlé de rotation ou de translation pour un point.
Par rotation d'un point, je voulais dire : déplacer le point d'un angle de X degré.

Pour ce qui est de l'homothétie, c'est vrai qu'il sera difficile de l'appliquer aux points :lol: mais comment faire dans ce cas ?

fredericmazue

Quote:
il n'y a pas d'erreur de ma part.
L'énoncé parle bien d'une classe Matrice dont le paramètre de généricité sera la classe Pixel.

Alors l'énoncé est mal formulé je pense, justement je revenais mettre un message complémentaire, parce que je me suis dit que ce que je t'avais dit tombait sans doute à côté.
Je dois dire que ton énoncé m'a déstabilisé. :lol: Et justement tu touches les points justes dans ton dernier message.

Quote:
Pour écrire les méthodes de transformations, il n'y a pas besoin d'avoir en attribut une matrice représenté par un tableau ?
Il me semblait que ces calculs se faisaient à l'aide de matrice

Très très bien dit. C'est justement là que ce situe le noeud du problème et c'est ce qui m'a gêné dans la question initiale. Mais peut être qu'une des difficultés de l'exercice c'est de bien comprendre ce qu'il faut faire
Tu as 100% raison, les calculs se font avec une matrice. Et justement la matrice *est* en tant que telle. Par exemple une matrice carré en 2D c'est quatre nombres. Les nombres sont des nombres et c'est tout. Et selon la valeur de ces nombres on aura une matrice rotation ou autre.
La matrice est générique en tant que telle car tu peux l'appliquer à ce que tu veux. Mathématiquement je parle. Un point, un segment de droite, un triangle, toutes formes géométriques.
Pour le codage ça ne fait pas de sens de dire qu'elle doit prend une paramètre générique Pixel. C'est là que je ne comprends pas.

Quote:
L'idée de l'exo est d' écrire un programme qui permet d'effectuer des transformations géométriques sur une image. C'est pour ça que j'ai parlé de rotation ou de translation pour un point.
Par rotation d'un point, je voulais dire : déplacer le point d'un angle de X degré.

Bon c'est d'accord pour tout ça. Alors que faire ? Pour moi de deux choses l'une.

- Il faut écrire une classe matrice générique. Mais alors qu'est-ce qui va être générique ? le caractère de la matrice (rotation, identité, etc) dans ce cas tu écris quelque chose comme:

template<class Type_Matrice> classe Matrice {};

Et selon Type_Matrice, la classe se spécialise en rotation, identité, etc.

- ou bien la matrice est une classe qui comporte 4 nombres. Donc une matrice déjà définie (i.e spécialisée du point de vue programmation)
Dans ce cas il s'agit d'une simple classe, mais dont la méthode de calcul (d'application) de la matrice transforme des formes géométriques quelconques et c'est alors là que se situerait la généricité.

class Matrice
{
 int a,b,c,d; les coeffs de la matrice // ou bien un tableau comme tu disais
template <class Forme_Geo> Forme_Geo application(Forme_Geo la_forme); 
};

Enfin c'est comme ça que ma petite tête voit les choses. (Et encore, car mettre des templates ici je ne suis pas sûr que ça soit le mieux à faire... mais bon)

Est-ce que j'ai été clair ?
Mais une classe Matrice générique qui prend un paramètre générique Pixel (dont le type est connu et donc pas générique), je ne comprends pas. Selon moi ça n'a aucun sens ...

Sting_6322

Je mets une plus grosse partie de l'énoncé car je me suis aperçu que les méthodes de transformations ne pouvaient pas se trouver dans la classe Matrice :

Quote:
Les classes
Dans le cadre de cet exercice, vous êtes amenés à manipuler des instances d'objet des classes

* Couleur (les couleurs sont codées sur 24 bits)
* Pixel qui est une classe dérivée de la classe Couleur
* Image

Conceptuellement, la classe Image devra hériter de la classe Image2D et de la classe générique Matrice (dont le paramètre de généricité sera la classe Pixel). La classe Image2D devra contenir les données et les méthodes qui ne concernent que les images en 2D. Idem pour les classes Matrice ou Pixel.

Toutes les transformations géométriques doivent être mises dans la classe Image2D et non dans Matrice car ces transformations ne s'appliquent pas forcément à une matrice "Pixel" d'ou le fait que la matrice devait être générique.

je n'arrive pas à savoir quel attribut mettre dans la classe Image2D pour l'utiliser dans ses méthodes.

fredericmazue

Quote:

Je mets une plus grosse partie de l'énoncé car je me suis aperçu que les méthodes de transformations ne pouvaient pas se trouver dans la classe Matrice

Hier soir, ton post était différent. (Je n'ai pas eu le temps d'y répondre).
Tu peux bien changer tes posts si tu veux, mais pour que ceux qui essaient de suivre la discussion ou la prennent en cours comprennent bien, je te demande de laisser tes posts comme ils étaient en y ajoutant ce que tu veux après. Et même si possible en indiquant que tu as édité ton post en mettant Edit: ou quelque chose comme ça.

Bon hier je me suis marré parce que quand tu as mis un bout de l'énoncé, je l'ai reconnu. Regarde dans ce même forum, un certain austinpower a le même problème que toi. Et lui non plus ne comprend pas bien l'énoncé je crois. Il avait mis un lien sur l'énoncé et j'avais lu rapîdement. Mais malheureusement je me m'en souviens plus clairement et le lien n'est plus valable maintenant. Regarde quand même la réponse que je lui ai faite.
Bon faudrait le donner en entier cet énoncé et qu'on voit ça. Je dois dire que quand je l'ai lu, je ne l'ai pas très bien compris non plus :)

Quote:

Toutes les transformations géométriques doivent être mises dans la classe Image2D et non dans Matrice car ces transformations ne s'appliquent pas forcément à une matrice "Pixel" d'ou le fait que la matrice devait être générique.

Dans l'énoncé dont je parle il était question d'héritage multiple, ce qui selon moi n'est pas approprié non plus. Cela dit matrice générique, ça na pas de sens selon moi.
Mais l'exercice m'amuse. Tu peux poster l'énoncé en entier ici ?
Sting_6322

Les soucis que j'ai c'est pour mettre les attributs et méthodes qu'il doit y avoir dans ces différentes classes.
Pour le codage, il ne devrait pas y avoir de soucis.
Si tu pouvais m'aider à organiser, tout ça, ça m'aiderait beaucoup.

je fais un récapitulatif pour voir si j'ai bien compris.
Pixel :
attribut double x,y;
méthode : getteur pour récupérer les coordonnées

Couleur hérite de Pixel
attribut int red, green, blue;
méthode : getteur pour récupérer les couleurs

Matrice classe génerique qui est une représensentation de l'image.
Cette matrice doit donc être de taille : largeur_image * hauteur_image
attribut : T**contenu
int largeur_image, hauteur_image
méthode : getteur et setteur

Image2D : classe abstraite définissant toutes les méthodes que devra implémenter la classe qui en héritera -> ici la classe Image.
Toutes les méthodes modifiant l'image initiale seront déclarées ici
attribut : aucun
méthode : rotation, translation, ...

Image : classe qui hérite de Image2D et de Matrice(je ne comprends pas trop pourquoi cette classe doit être héritée et non en attribut de la classe Image)
attribut : aucun sauf si Matrice est en attribut et non en héritage
méthode : implémente toutes les méthodes qui ont été déclarées dans Image2D + ajouter le constructeur pour le chargement de l'image et une méthode pour la sauvegarde.
Le constructeur se chargera de parcourir l'image (largeur * hauteur) et de remplir la matrice.

Si tu as des choses à ajouter ou à modifier, n'hésites pas.

Merci

fredericmazue

Bon ça devient plus clair :)

Quote:
Voici l'énoncé :

C'est bien le même que celui d'austinpower
Vous êtes dans la même classe ? Et tu en es à quel niveau d'étude ?

Bon je vais essayer de t'aider.

Quote:

Pixel :
attribut double x,y;
méthode : getteur pour récupérer les coordonnées

Ok sur le principe. mais moi je mettrais x et y en int.
Un pixel correspond quand même à une valeur entière. A moins que le besoin des doubles ne se fasse sentir lors des manipulations. Il faut peut être aussi prévoir des setteurs si les pixels doivent être transformés par les méthodes d'IMage2D.
Quote:

Couleur hérite de Pixel
attribut int red, green, blue;
méthode : getteur pour récupérer les couleurs

Non tu as mal lu ton énoncé. c'est pixel qui hérite de couleur. C'est débile mais moins que l'inverse ;)
C'est débile de faire hériter Pixel de couleur. Pixel devrait avoir une couleur comme membre (ce que vous appelez un attribut

C'est quand même terrible ça:

Quote:
Posez-vous constamment la question "est-ce que cette méthode, cet attribut est bien à sa place ici?"

C'est assez culotté de mettre ça dans l'énoncé alors que quand le type écrit Pixel hérite de Couleur, il ne respecte pas son propre principe.
A moins que.... le but de l'exercice soit que l'élève exerce son esprit critique et mettre Couleur en attribut de Pixel au lieu de faire une dérivation à la c..
Donc moi je mettrai Couleur en attribut de Pixel et j'irai quand même demander au prof si j'ai le droit :) Et si je n'ai pas le droit alors je ferai dériver.
Bon nous arrivons à cette fameuse matrice....
En fait maintenant il apparaît clairement qu'il ne s'agit pas d'une matrice au sens mathématique, pour faire des calculs et tout et tout. Ce que l'énoncé appelle matrice, ce n'est fondamentalement qu'un tableau de Pixel. Attention je dis tableau parce que ça dit ce que ça veut dire. Cela n'entraîne pas qu'il faille obligatoirement prendre un tableau pour contenir les pixels.
Je lis dans l'énoncé:
Quote:
classe générique Matrice (dont le paramètre de généricité sera la classe Pixel).

Voilà donc cette histoire de matrice générique. Bon là je regrette, je sais qu'il faut (que l'on doit) respecter les profs. Mais quand même ça veut rien dire cette phrase. Surtout cela n'a rien à voir de près ou de loin avec la généricité comme on peut la coder en C++. Matrice est un tableau de Pixel, point final. Il n'y a rien de générique là dedans.
Alors que comprendre ? Bien que ça ne soit pas de la programmation générique au sens C++, je crois qu'il faut comprendre que quelque soit l'implémentation de Pixel, le programme doit marcher. Mais comme la matrice est un tableau de Pixel on s'en fout là de la généricité. Je crois qu'il faut comprendre en fait que les méthodes de Image2D doivent toujours marcher. Pour cela je crois qu'il faut définir avant tout une classe Pixel abstraite, exposant une interface constante quelle que soit l'implémentation, et que le Pixel dérive de couleur ou pas.
Donc je pense que la première classe à écrire est
class AbstractPixel
{
public:
	virtual int  /* ou double* / getX() =0;
	virtual int getY() =0;
	virtual RGB getRGB() =0;
	virtual ~AbstractPixel(); // peut être important.
};

RGB étant un type à définir. Soit la classe Couleur complète, sois une structure qui contient les valeurs RGB, bref, comme tu voudras.
L'important c'est qu'après tu fais
class Pixel : public AbstractPixel
{
	// implémentation ici
	// dont
public:
	virtual ~Pixel();
};

Et là, quelle que soit l'implémentation, que Pixel dérive aussi de couleur ou qu'il ait un attribut couleur on s'en fout.
Ton programme marchera puisque qu'il fera appel aux méthodes de la classes AbstractPixel. Et du coup on peut dire *par abus de langage* que Matrice est un tableau générique.
Je pense que c'est là que l'énoncé veut en venir.

Quote:

Matrice classe génerique qui est une représensentation de l'image.
Cette matrice doit donc être de taille : largeur_image * hauteur_image
attribut : T**contenu
int largeur_image, hauteur_image
méthode : getteur et setteur

C'est ok sur le principe, mais pas T**contenu. Pas de généricité comme on l'a dit. Le type est parfaitement contenu c'est un (Abstract)Pixel donc
Pixel** contenu;
ou même mieux
AbstractPixel** contenu
Et là ton contenu est "générique" ;) :lol:

Quote:

Image2D : classe abstraite définissant toutes les méthodes que devra implémenter la classe qui en héritera -> ici la classe Image.
Toutes les méthodes modifiant l'image initiale seront déclarées ici
attribut : aucun
méthode : rotation, translation, ...

Et toutes ces méthodes appellent les méthodes de AbstractPixel, communes à toutes espèces de Pixel :) Mais il y selon moi quelque chose qui cloche. Voir plus loin.

Quote:
Image : classe qui hérite de Image2D et de Matrice(je ne comprends pas trop pourquoi cette classe doit être héritée et non en attribut de la classe Image)

Excellente remarque. je dois dire que je ne comprends pas non plus et que je la mettrais en attribut si j'avais le choix.
Quote:
méthode : implémente toutes les méthodes qui ont été déclarées dans Image2D

Non. Je ne vois nulle part dans l'énoncé que image 2D soit abstraite. Et je ne vois pas l'intérêt de faire une classe abstraite. Selon moi les implémentations ont leur place directement dans Image2D. Image hérite de Image2D et peut appeler ses méthodes. C'est même là que l'héritage est véritablement justifié. Image EST une Image2D alors que Image A une matrice :)
Quote:
constructeur pour le chargement de l'image et une méthode pour la sauvegarde.
Le constructeur se chargera de parcourir l'image (largeur * hauteur) et de remplir la matrice.

Ok pour le principe. Attention à ne pas trop mettre de code dans un constructeur. C'est mieux si le constructeur appelle des méthodes. Gaffe aux levées d'exception du genre bad_alloc ou autre.
Lève toi même des exceptions par exemple si le nom de fichier est pas bon. Enfin n'oublie pas de traiter les destructeurs avec soin. Notamment dans Matrice pour libérer la mémoire occupée par le tableau. A moins que tu n'ai droit à la STL, dans ce cas un vector de vector de Pixel pour la matrice c'est sûrement le mieux et le plus simple.
Un vector, ça c'est une *vraie* classe générique ;)

Voilà, yapluka coder :)

Sting_6322

Bonjour frederic,

désolé de n'être pas réapparu avant pour donner des nouvelles mais j'ai plusieurs projets à faire en même temps.
J'ai réglé les problemes d'héritages, des génériques, j'ai chargé mon image en mémoire.
A partir de la matrice, je récrée l'image.
Maitenant il reste juste à savoir comment utiliser les fonctions de la bibiotheque GD pour qu'il soit fini.

J'ai parcouru les différentes fonctions de la bibliothèques GD.
Pour l'écriture de certaines méthodes qu'il nous est demandé d'écrire, je n'arrive pas à trouver quelles fonctions sera utilies

Est-ce quen tu pourrais m'aider à savoir quelles fonctions utilisées pour les différentes méthodes qu'il me reste à écrire.

Merci

Sting_6322

Cette après-midi, j'ai réussi à créer toutes les images exceptés :
La combinaison de deux images
et la pixelisation

Pour la pixelisation, est-ce que tu pourrais m'indiquer les boucles et les incrémentations car j'ai essayé un tas de truc mais je n'arrive jamais à avoir ce qui est demandé.

Merci

fredericmazue

Pour la combinaison d'image, si je comprends bien, il suffit de faire la moyen des valeurs de couleurs pixel à pixel, d'image à image.
Donc tu itères en parallèle les deux images sur tous les points et tu fais la moyenne. Je ne vois pas trop la difficulté là.

Pour la pixelisation tel que je comprends l'énoncé, tu dois écrire des boucles imbriquées

En pseudo code

L = largeur_image
H = hauteur image

for I=0; I<H/N;, I++
  for J=0; j<L/N; i++  
    for K=0; K<N; K++
     faire moyenne de K(I,J), K(I,....), K(I,J+N-1), K(I+1,J), K(I+1,....), K(I+1,J+N-1), K(I+N-1,J), K(I+N-1,....), K(I+N-1,J+N-1)  

Quelque chose comme ça quoi. :lol:
Heu je ne garantis pas ne pas être trompé dans les indices de la troisième boucle :D, j'ai écrit ça vite sans rien vérifier.
Mais c'est l'idée qu'il faut retenir: Les deux premières boucles découpent l'image en un quadrillage de "petits carrés". Le propos de la troisième boucle est de faire la moyenne des point de chacun de ces carrés.
Evidemment une fois cette moyenne calculée, tu dois l'affecter à chaque point du petit carré, ce que je n'ai pas mis dans le pseudo code.

fredericmazue

PS: J'aimerais bien savoir à quel niveau d'études correspond cet exercice.

Sting_6322

fredericmazue wrote:
PS: J'aimerais bien savoir à quel niveau d'études correspond cet exercice.

C'est niveau Licence/Master.
Je suis en Licence mais cet exo avait aussi été donné à faire à des master.

J'ai codé toutes les méthodes qui sont demandées dans l'exo mais j'ai un nouveau soucis.
J'ai codé toutes ces méthodes et constructeur dans la classe Image2D pour des tests sans créer la classe Image.
Miatenant j'essaye de créer la classe Image qui hérite de Image2D et de Matrice mais le soucis, c'est que je n'arrive pas vraiment à savoir quoi mettre dans Image et dans Image2D

fredericmazue

Si je devais faire, je mettrais toutes les fonctions de calculs et de transformation dans Image2D.
Et dans Image les fonctions d'I/O et la surcharge des opérateurs pour sortie sur console (je crois me rappeler de ça dans l'énoncé).

Sting_6322

test

fredericmazue

ostream& operator<< (ostream& os, Image const &im) {
  im.generer_image("monImage");
  cout << system("gqview monImage &");
  return os;
}

Pourquoi cout << system("gqview monImage &"); :?:
Pourquoi "cout <<" je veux dire
Pourquoi pas simplement

system("gqview monImage &");

Sting_6322

fredericmazue wrote:
ostream& operator<< (ostream& os, Image const &im) {
  im.generer_image("monImage");
  cout << system("gqview monImage &");
  return os;
}

Pourquoi cout << system("gqview monImage &"); :?:
Pourquoi "cout <<" je veux dire
Pourquoi pas simplement

system("gqview monImage &");


C'est vrai que le cout ne servait à rien mais ça ne modifiait pas mon problème.
Dans cette ligne :
system("gqview monImage &");
j'ai enlevé le & et ça me donne quelques choses de meilleur mais ce n'est pas enore ça.
A l'execution, j'obtiens une image. Lorsque je la ferme, la seconde apparait et ainsi de suite. Les images obtenues sont toutes différentes.
Le souci c'est que si je ne ferme pas la premiere image, la seconde n'apparait jamais, si je ne ferme pas la seconde, la troisieme n'apparait jamais et ainsi de suite.
Est-ce que c'est un comportement normal ou bien il est possible d'avoir les images les unes à la suite des aurtres.
K-lo

En même temps c'est plutot la surcharge de l'opérateur * dont doit venir le problème :?:

En fait montre peut être ton code a propos de cette surcharge...

Sting_6322

K-lo wrote:
En même temps c'est plutot la surcharge de l'opérateur * dont doit venir le problème :?:

En fait montre peut être ton code a propos de cette surcharge...

Sting_6322

J'ai résolu mon problème.
J'avais oublié de modifié également la signature de cette méthode dans un autre fichier.

fredericmazue

Quote:

j'ai enlevé le & et ça me donne quelques choses de meilleur mais ce n'est pas enore ça.
A l'execution, j'obtiens une image. Lorsque je la ferme, la seconde apparait et ainsi de suite. Les images obtenues sont toutes différentes.
Le souci c'est que si je ne ferme pas la premiere image, la seconde n'apparait jamais, si je ne ferme pas la seconde, la troisieme n'apparait jamais et ainsi de suite.
Est-ce que c'est un comportement normal

Oui c'est normal, la perluette (&) demande à lancer le processus en tâche de fond.
Si tu l'enlèves ton programme doit attendre chaque fois la fin de l'exécution de system("gqview... etc

Pour ton erreur de départ... et bien tu as la réponse ci-dessus :)
Si si! ;)
Regarde bien
Tu lances des tâches
C'est à dire que tut fais de la programmation asynchrone comme M Jourdain, sans le savoir :)
Qu'est-ce qui se passe alors ?
Tu crées un fichier image. Juste après ty lance la commande gqview qui met "un certain temps" pour se charger
Pendant ce temps, tu crées le deuxième fichier image.
A ce moment arrive gqview enfin chargé, qui lit sa ligne d ecommande et qui charge deux fois le dernier fichier :)