CLASSE DE TRAITEMENT D'IMAGE

neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 - 9 avril 2008 à 20:04
bayahiassem Messages postés 2 Date d'inscription mardi 30 octobre 2007 Statut Membre Dernière intervention 5 septembre 2008 - 5 sept. 2008 à 14:32
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/46324-classe-de-traitement-d-image

bayahiassem Messages postés 2 Date d'inscription mardi 30 octobre 2007 Statut Membre Dernière intervention 5 septembre 2008
5 sept. 2008 à 14:32
Salut, voici une fonction que vous pouvez ajouter à la classe et qui permet d'inverser l'image horizontalement:

public function mirrorEffect()
{
for($x=0;$x<$this->width/2;$x++)
{
for($y=0;$y<$this->height;$y++)
{
$rgb = imagecolorat($this->source,$x,$y);
$r = (($rgb>>16)& 0xFF);
$g = (($rgb>>8)& 0xFF);
$b = ($rgb & 0xFF);

$rgb2 = imagecolorat($this->source,$this->width-$x - 1,$y);
$r2 = (($rgb2>>16)& 0xFF);
$g2 = (($rgb2>>8)& 0xFF);
$b2 = ($rgb2 & 0xFF);

$color1 = imagecolorallocate($this->source,$r,$g,$b);
$color2 = imagecolorallocate($this->source,$r2,$g2,$b2);

if(!imagesetpixel($this->source,$this->width-$x,$y,$color1) | !imagesetpixel($this->source,$x,$y,$color2))
return false;
}
}

return true;
}

et une autre fonction qui permet d'enlever l'effet des yeux rouges (il faut envoyer à cette fonction la position ainsi que la taille de la partie à traiter dans l'image)

public function removeRed($startX,$startY,$width,$height)
{
for($x=$startX;$x<$startX+$width;$x++)
{
for($y=$startY;$y<$startY+$height;$y++)
{
$rgb = imagecolorat($this->source,$x,$y);
$r = (($rgb>>16)& 0xFF);
$g = (($rgb>>8)& 0xFF);
$b = ($rgb & 0xFF);

while($r > $g + $b - 30)
{
$r = $r - 2;
}

$color = imagecolorallocate($this->source,$r,$g,$b);
if(!imagesetpixel($this->source,$x,$y,$color))
return false;
}
}
return true;
}
bayahiassem Messages postés 2 Date d'inscription mardi 30 octobre 2007 Statut Membre Dernière intervention 5 septembre 2008
1 août 2008 à 12:23
bonjour,

ce code est magnifique, merci Grunkz. Vous pouvez ajouter à votre class ces fonctions:

/**
* Ajoute ou diminue la teinte de chaque couleur
* @access public
*/
public function colorize($red,$blue,$green,$alpha)
{
imagefilter($this->source,IMG_FILTER_COLORIZE,$red,$blue,$green,$alpha);
}

/**
* Modifie le facteur gamma
* @access public
* @param int $factor Facteur de gamma
*/
public function gammaCorrect($factor)
{
imagegammacorrect($this->source,1.0,$factor);
}

/**
* Retourne l'image
* @access public
* @param int $degrees Degree de rotation
*/
public function rotate($degrees)
{
$this->source = imagerotate($this->source, $degrees, -1);
}

juste une petite remarque: pour le contraste le sens des valeurs est inversé: -100 c'est le maximum contrast, 100 c'est le minimum.
lonva6 Messages postés 1 Date d'inscription vendredi 7 décembre 2007 Statut Membre Dernière intervention 23 avril 2008
23 avril 2008 à 12:07
Elle est bien sympa cette class quand meme et super utile
jvais essayer de l'optimiser un peu.. et aussi rajouter des fonctions
genre :
- Au lien de $r,$v,$b mettre une couleur en hexa type #8FEC34 ou 8FEC34 ou 255:0:150
- Des autres effets comme Negatif ou bien Colorisation ou encore Sepia
- Une gestion des erreurs.. etc..

bref j'essaye de faire ca dès que j'ai un peu de temp..
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
17 avril 2008 à 15:32
Salut,

http://fr.php.net/manual/fr/image.setup.php

Note: Depuis PHP 4.3, il existe une version de GD qui est distribuée avec PHP. Cette version contient des fonctionnalités supplémentaires, comme les canaux alpha, et il est recommandé de l'utiliser de préférence à la bibliothèque externe, car elle est mieux supportée, et bien plus stable.

embarquée != externe
brunoperel Messages postés 14 Date d'inscription jeudi 2 novembre 2000 Statut Membre Dernière intervention 6 octobre 2008
17 avril 2008 à 15:21
qu'est-ce que ça veut dire "version embarquée" ? :-$
Si il faut avoir accès au serveur en tant qu'admin, c'est fichu pour moi :'-(
grunkz Messages postés 6 Date d'inscription mercredi 30 novembre 2005 Statut Membre Dernière intervention 3 juillet 2008
17 avril 2008 à 09:51
Doc php : Note: Cette fonction n'est disponible que si PHP(5) est compilé avec la version embarquée de la bibliothèque GD.
Donc vos problèmes viennent surement de là
etaty Messages postés 1 Date d'inscription mercredi 12 décembre 2007 Statut Membre Dernière intervention 16 avril 2008
16 avril 2008 à 13:01
Fatal error: Call to undefined function imagefilter() in /demo/traitementImage.class.php on line 232
je confirme pour cette erreur avec l'image d'exemple !
brunoperel Messages postés 14 Date d'inscription jeudi 2 novembre 2000 Statut Membre Dernière intervention 6 octobre 2008
15 avril 2008 à 11:21
Si pourtant je suis bien en PHP5 :
http://mpteu.kelio.org/info.php
Bon, j'essaierai avec un autre serveur...
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
15 avril 2008 à 01:02
Salut,

Moi j'aurais tendance à dire que tu n'es pas en PHP 5... http://fr.php.net/imagefilter
Ou alors, GD n'est pas installé comme il faut.
brunoperel Messages postés 14 Date d'inscription jeudi 2 novembre 2000 Statut Membre Dernière intervention 6 octobre 2008
14 avril 2008 à 23:13
Salut,

Au risque de passer pour un newbie complet : saurais-tu pourquoi j'obtiens l'erreur :
Fatal error: Call to undefined function imagefilter() in /demo/traitementImage.class.php on line 232
?
Parce que mon GD 2 est activé et tout...
J'ai envie de dire "bonne source" mais c'est encore prématuré je crois :-D
Eregon Messages postés 17 Date d'inscription lundi 3 septembre 2007 Statut Membre Dernière intervention 26 octobre 2009
14 avril 2008 à 21:36
Ok, je n'ai rien dit pour l'appli.

Néanmoins je trouve ce genre de commentaires /* * * */ encombrants et ça double souvent le nombre de lignes.

Quand à phpDoc, j'ai l'impression que sa réputation est surtout basée sur son utilité en Java(d'après ce que j'ai lu phpDoc en dérive). Car je pense que le module pourrait lui même remarquer les "private,public,protected" devant les fonctions.

Mais je dévie du sujet, et comme je me heurte probablement à tous les utilisateurs de phpDoc, je ne vais pas continuer. Néanmoins, pour moi, la meilleure documentation est faite main en commentaires dans le code ou si plus complexe dans des fichiers à part.
Quand à l'utilité de détecter le hiérarchie des classes, perso je l'indique toujours en début de script.

Donc je comprends l'attrait de phpDoc, mais la notation dans un code me semble exagérée pour certaines propriétés.
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
14 avril 2008 à 20:27
Je crois que t'as pas compris...
C'est pas l'appli qui fait les commentaires, mais les commentaires qui servent à générer la doc via une appli.
En l'occurrence,

@access private

permet d'indiquer dans la doc que la méthode ou la propriété a une visibilité privée. Générer des commentaires avec une appli, voilà bien une drôle d'idée... Renseigne toi sur phpDoc, visiblement, tu ne sais pas à quoi ça sert...
Eregon Messages postés 17 Date d'inscription lundi 3 septembre 2007 Statut Membre Dernière intervention 26 octobre 2009
14 avril 2008 à 20:23
J'ai dit que je préférais une autre notation, donc j'apprécie les codes commentés, et je ne lis pas ceux qui le sont trop peu.

Je sais que c'est une convention, mais je ne vois pas l'intérêt des @access et certaines autres propriétés.

Si certains pensent que ça facilite de faire les commentaires avec une appli, je ne pense pas que les descriptions et types(en php) puissent être générés automatiquement... Donc les seules propriétés intéressantes sont celles qu'on entre soi-même.(C'est mon avis)
Alors autant le faire soi même...
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
13 avril 2008 à 20:13
Salut,

@Eregon : "Pour les commentaires, je déteste ce genre à la /* * */, surtout avec les @access public, alors qu'il suffit de savoir lire qqs lignes plus bas."

C'est pas un caprice de l'auteur, c'est le respect de phpDOc qui permet de générer la documentation d'une classe ou d'un package automatiquement grâce à une appli spécialisée.
Le résultat se voit sur phpdoc.org, c'est aussi le même genre avec doxygen qui a généré la doc de la SPL.

Alors si tu détestes les codes documentés qui permettent de s'y retrouver et de générer la doc soi-même à partir du code source, ben... moi j'apprécie... Et je pense, très sincèrement, ne pas être le seul...
Eregon Messages postés 17 Date d'inscription lundi 3 septembre 2007 Statut Membre Dernière intervention 26 octobre 2009
13 avril 2008 à 19:56
J'ai commencé une classe de création d'image à partir de GD (40 de méthodes et 500lignes),
et donc je ne peux qu'approuver l'idée.

Sauf que l'on voit assez vite le défaut de GD quand on travaille sur l'épaisseur...
Impossible de modifier celle des ellipses, incohérences graves lors d'une épaisseur supérieure à 2 à proximité des angles 45°+k90°
Pour faire simple:
http://eregon.franceserv.com/tmp/q1gd.php.png

J'ai donc complètement changé d'optique pour passer en SVG. Plus besoin d'extension...
La classe est beaucoup plus orientée objet(car SVG, c'est du xml...).
Qualité infinie, calcul chez l'utilisateur donc pas d'explosion de mémoire...
http://eregon.franceserv.com/tmp/test.png

Je rentre un peu trop dans un débat, donc revenons à ta classe:
-Le code est assez clair, et les fonctions explicites
-Les fonctions sont de 2 types: il y a de la gestion globale d'image(recadrage, constraste) et puis interne(texte).
=>J'avais plus approfondi le texte car on peut voir que ttfbox ne renvoie pas souvent le même valeur pour l'ordonnée du point haut gauche et haut droite. De même avec la rotation, il faut modifier différemment l'ordonnée.

...
Je sens que je vais poster ma classe ... Trop de choses à dire ...
...

Pour les commentaires, je déteste ce genre à la /* * */, surtout avec les @access public, alors qu'il suffit de savoir lire qqs lignes plus bas. Il est parfois utile de préciser le type, mais je trouve plus simple de mettre des noms explicites et de mettre ça en une ligne en dessous de la première ligne de fonction:
public function fill_arc(Point $p, $w, $h, $start, $end, $color, $style)
{//filled arc(Point $origin, int $width, int $height, float $start_angle, float $end_angle, color $color, IMG_ARC_ CONSTANT(PIE,CHORD,NOFILL,EDGED) $style)

Pour les types d'images, en 1 switch:
switch(strtolower(substr($name, -3, 3)))
{
case 'jpg':
case 'peg':
ImageJpeg($this->img, $name, 100*$quality);
break;
case 'gif':
ImageGif($this->img, $name);
break;
case 'png':
ImagePng($this->img, $name, 9*(1-$quality));
break;
}

Quand aux idées de factory, p-e si les librairies sont proches, mais entre SVG et GD, ce n'est pas vraiment possible, le style est défini de manière bien différente.

Bon dès que j'ai le temps de commenter un peu, je poste ma classe ;)
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
10 avril 2008 à 15:20
Si tu savais... Je m'inquiète déjà, alors imagine dans 30 autres années... Mon dieu...
codefalse Messages postés 1123 Date d'inscription mardi 8 janvier 2002 Statut Modérateur Dernière intervention 21 avril 2009 1
10 avril 2008 à 15:12
C'est tout bonnement le principe de la Poo :)

Alzheimer, vraiment Neige ? à 30 ans ? !!! ;)
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
10 avril 2008 à 14:40
Salut,

Concernant le if/else et le switch, il me semble me souvenir que le case demande plus de ressources à la "création", mais est plus rapide pour les tests suivants. Donc, toujours d'après mes souvenirs (et ceux-ci sont peut-être erronés), il me semble que le if/else est plus intéressant pour un petit nombre de cas, et le case pour un plus grand nombre. Il me semble (toujours ces souvenirs, merci Alzheimer...) que c'est ce qu'avaient donnés mes tests persos.

Concernant la Factory, l'idée est d'appeler une méthode statique qui va procéder à un minimum d'opérations sur l'image et va :
- déterminer le type d'après son extension
- instancier un objet ImgTraitement approprié (ImgPNG, ImgJPG, ...)
- retourner cet objet

Ensuite, la question des if/else et des case ne se posera plus, puisque le test aura été fait au départ.
On ne gagne pas, ainsi, que quelques lignes de code. On y gagne vraiment beaucoup en modularité.
Comme je le disais, on peut ensuite rajouter des objets pour n'importe quel type d'image (gd, jpg, png, gif, xpn, etc). L'idée est que si la classe est assez bien foutue (plus précisément la méthode statique d'instanciation) on n'a par la suite aucune ligne de code à rajouter dans cette classe pour qu'un nouveau type soit pris en charge : il suffit d'écrire le code du "module" propre à ce type.
Plus loin : au lieu de se limiter à GD, qui finalement ne gère pas tous les formats, les classes concrètes pourraient utiliser soit GF, ImageMagick, voire une autre librairie. Cela, de manière tout à fait transparente, et toujours sans toujours une ligne de code dans la classe principale.
On n'y gagne pas que quelques lignes de code avec des if/else : on y gagne des heures de travail pour plus tard.
Du coup, il faut une interface qui définisse quelles méthodes les classes spécialisées doivent implémenter.
Finalement, c'est rigoureusement le même principe que les classes d'abstraction de base de données : on peut ajouter une classe pour n'importe quel SGBDR sans toucher une ligne de code de ce qui a déjà été fait.
codefalse Messages postés 1123 Date d'inscription mardi 8 janvier 2002 Statut Modérateur Dernière intervention 21 avril 2009 1
10 avril 2008 à 11:55
En effet, le switch à été testé et prouvé comme étant plus rapide donc apportant un gain de performance. Pour des cas tels qu'énnoncés comme ici, c'est vrai qu'il serait bien approprié. En fait il faut l'utiliser dans ces circonstances là, et utiliser if dans les autres circonstances. C'est pas une histoire de quantité.

Apres ca marche très bien donc pas de soucis :p

Pour la factory, je pense que Neige voulait plutot dire que tu fait une classe ImgFactory avec comme constructeur le type (entre autre) et quand tu fait un
new ImgFactory ('png');

Celui ci va instancier ImgPng (par exemple) et passer en parametre à ta classe :
__construct ($sType) {
switch ($sType) {
case 'png':
$oImgType = new ImgPng ();
break;
case 'jpg':
$oImgType = new ImgJpg ();
break;
//etc...
}
return new ImgTraitement ($oImgType);
}

Du coup dans ta classe ImgTraitement tu n'aura plus à faire :
return imagejpeg($this->source,'',$qualite); (par exemple)

mais
return $this->_oImgType->createImage ($this->source,'',$qualite);

et c'est ton instance de $_oImgType, instance de ImgPng ou ImgJpg, ou etc, qui fera le imagejpeg ou imagepng en fonction du type demandé.

Je sais pas si j'ai été assez clair ?
et Neige, dit moi si c'est bien à cela que tu pensais :p
grunkz Messages postés 6 Date d'inscription mercredi 30 novembre 2005 Statut Membre Dernière intervention 3 juillet 2008
10 avril 2008 à 11:48
C'est juste une préférence perso. Tant que le nombre de possibilités n'est pas élevé je préfère le if/else plutôt que le switch case.
Après sur un grd nombre de possibilité je me force à utiliser le switch case qui sera plus rapide
spipod Messages postés 23 Date d'inscription mercredi 2 février 2005 Statut Membre Dernière intervention 17 juillet 2009
10 avril 2008 à 11:33
Salut,

Très bon travail.

Mais une petite question : pourquoi dans ton code (exemple fonction save) utilises-tu des if / else plutôt qu'un switch ?

N'est-ce pas plus lisible ?

Genre :

public function save($file,$qualite=95)
{
$ret=false;
switch($this->type)
{
case IMG_GIF :
if (imagegif($this->source,$file))
$ret = true;
break;
case IMG_JPG :
if( imagejpeg($this->source,$file,$qualite))
$ret = true;
break;
case IMG_PNG :
if (imagepng($this->source,$file))
$ret = true;
break;
}
return $ret;
}
grunkz Messages postés 6 Date d'inscription mercredi 30 novembre 2005 Statut Membre Dernière intervention 3 juillet 2008
10 avril 2008 à 10:10
Merci de vos commentaires.
Pour les constantes c'est pris en compte. En revanche au niveau de la factory, j'ai comme un doute sur le bénéfice dans ce cas là.
Mes connaissances en design pattern étant plus que limitée je vais peut être dire une annerie mais :

j'aurais besoin d'une factory qui créera un objet d'une de mes classe (par exemple new traitementImgJPG, ou traitementImgGIF) qui elle même dériveront de la classe ci dessus (les méthodes spécifique au format en moins).

Donc au final ça va faire bcp de code pour éviter 3 if/else nan ?

Après sémentiquement c'es tcertainement plus "propre" je vous l'accorde
codefalse Messages postés 1123 Date d'inscription mardi 8 janvier 2002 Statut Modérateur Dernière intervention 21 avril 2009 1
10 avril 2008 à 09:40
Yop :)
Je suis d'accord avec Neige sur ce qu'il dit, sauf la partie des commentaires, ou c'est subjectif :p Après comme il dit, c'est vraiment comme on veux, et ca ne change rien à la qualité du code :)

Juste pour donner un contre exemple :

const IMG_GIF = 1; // Type Gif
const IMG_JPG = 2; // Type Jpg
const IMG_PNG = 3; // Type Png

Bon, dans ce cas ca sert à rien, mais c'est juste pour indiquer que le retour à la ligne dépends de la longueur du code (à mon avis, donc subjectif :p) :)

Quant à ton code, rien à redire, il est propre, reste plus qu'à réduire la répétition de code tel que l'a souligné Neige, et tout sera bon ! :)

Bon courage ! :)
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
9 avril 2008 à 20:04
Salut,

J'ai pas regardé en détails, mais après un rapide coup d'oeil, y'a deux choses qui ont retenu mon attention :
- j'apprécie la documentation au format phpDoc (ou Doxygen, ou autre, bref), mais je déplore juste l'absence de commentaires non parsés dans le reste du code (en même temps, si ça se trouve, c'est pas justifié parce que suffisament simple et lisible). Corrolaire : j'apprécie la clarté du code. Au moins, on sait où on en est dans ce qu'on lit.
- pour les comparaisons du genre : if($this->type==1) tu devrais utiliser des constantes, c'est plus facile à lire que des commentaires qui indiquent à quoi correspondent les valeurs 1, 2 etc

const IMG_GIF = 1;
const IMG_JPG = 2;
const IMG_PNG = 3;

if($this->type == self::IMG_GIF)

Je trouve ça plus lisible...

Bon encore un autre truc (parce que j'ai survolé une deuxième fois) : tu mets des commentaires avec // en fin de ligne... Syntaxiquement, c'est tout à fait valable. Personnellement (et là, ça n'engage que moi, c'est une question de goût), je préfère mettre des commentaires sur une ligne rien que pour eux, avant la ligne qu'ils commentent. Je trouve que c'est encore plus lisible. Par exemple :

$rectText = imageftbbox($size,0,$this->font,$texte); // Retourne les coordoonées du text
$wText = abs($rectText[4]-$rectText[0]); // Largeur du texte$hText abs($rectText[1]-$rectText[5]); // hauteur du texte >

//Retourne les coordoonées du texte
$rectText = imageftbbox($size,0,$this->font,$texte);
// Largeur du texte
$wText = abs($rectText[4]-$rectText[0]);
// hauteur du texte
$hText = abs($rectText[1]-$rectText[5]);

C'est vraiment une question de convention, hein. Je sais que pour certains, ce sont des détails à la con, mais quand on veut un code clair, autant qu'il le soit le plus possible.

Maintenant, une toute dernière chose, peut-être une piste pour améliorer la source (qui ne me semble pas mauvaise, même si elle ne me parait pas non plus révolutionnaire) : l'abstraction du traitement.
Certaines portions de ton code sont récurrentes : pour un gif, jpeg ou png. Juste parce que certaines fonctions sont spécifiques au format.
Tu gagnerais peut-être à avoir une classe abstraite pour la gestion de l'image, et des classes concrètes, une pour chaque format. Avec une factory qui instancie le bon objet selon le type d'image.
Tu économises du code et tu peux ensuite rajouter autant de formats que tu le souhaites, sans changer une seule ligne de ta classe principale.

Euh voilà pour un premier coup d'oeil...
Rejoignez-nous