MY.PICS : TRAITEMENT D'IMAGES NON DESTRUCTIF

cs_twisteurwin Messages postés 167 Date d'inscription mardi 21 septembre 2004 Statut Membre Dernière intervention 2 mai 2009 - 12 nov. 2009 à 12:10
inwebo Messages postés 380 Date d'inscription lundi 12 novembre 2007 Statut Membre Dernière intervention 23 octobre 2014 - 19 mai 2011 à 13:57
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/50834-my-pics-traitement-d-images-non-destructif

inwebo Messages postés 380 Date d'inscription lundi 12 novembre 2007 Statut Membre Dernière intervention 23 octobre 2014
19 mai 2011 à 13:57
Pour neigedhiver,

La seul chose que l'on ne peut sérialiser en PHP c'est les données de type ressources :

"The value to be serialized. serialize() handles all types, except the resource-type. You can even serialize() arrays that contain references to itself. Circular references inside the array/object you are serializing will also be stored. Any other reference will be lost. "
cs_aKheNathOn Messages postés 575 Date d'inscription dimanche 23 décembre 2001 Statut Membre Dernière intervention 23 octobre 2012
25 mai 2010 à 09:30
J'ai explosé ta classe en plein de classes annexes comme je l'ai expliqué dans mes commentaires, du coup ça ne ressemble plus trop à la classe de départ, parcontre je n'ai pas toutes tes fonctionnalités ce qui est un peu dommage (je pense aux masques notamment)... reste encore à finaliser mais pour mon projet j'ai ce qu'il me faut : redimensionnement des vignettes.

Le projet qui est en cours de réalisation et un système de gestion eCommerce tout ce qu'il y a de plus classique, dès que j'aurais une version beta je le mettrais sur un SVN pour partager le code - je te tiendrais au courant
inwebo Messages postés 380 Date d'inscription lundi 12 novembre 2007 Statut Membre Dernière intervention 23 octobre 2014
24 mai 2010 à 12:14
Okay,

Pas de problème, par curiosité quel est ton projet ?
Je mettrais à jour sur phpcs.

Bon prog'
cs_aKheNathOn Messages postés 575 Date d'inscription dimanche 23 décembre 2001 Statut Membre Dernière intervention 23 octobre 2012
22 mai 2010 à 22:16
Salut Inwebo,

Je compte utiliser ton code dans un de mes projets :) que je vais le distribuer en GPL / je pense que ça doit être compatible avec ta licence.

Je te propose que j'y apporte également quelques améliorations que tu pourrais publier en mise à jour à cette source si tu le souhaite.

Je te tiens au courant,
akh
inwebo Messages postés 380 Date d'inscription lundi 12 novembre 2007 Statut Membre Dernière intervention 23 octobre 2014
17 nov. 2009 à 14:14
Compris et très bonne idée. Cela serait d'ailleurs une valeurs ajoutée par rapport aux autres class qui font peu ou proue la même chose. Plutôt que de réinventer la roue autant lui mettre de nouveaux pneus (image naze mais c'est tout ce que j'avais sous la main....... désolé).

Merci à vous deux en tout cas !
cs_aKheNathOn Messages postés 575 Date d'inscription dimanche 23 décembre 2001 Statut Membre Dernière intervention 23 octobre 2012
17 nov. 2009 à 13:23
T'as raison neige, c'est à quoi je pensait. L'idée est de faire un script de retouche d'images, or quand tu sauvegarde l'image en jpg ou PNG tu ne peux plus revenir sur une modification du/des filtres ou bien de sa composition si tu y incruste plusieurs images.

Il faut donc en plus de l'image de pouvoir enregistrer les filtres et les infos de ta classe et garder les ressources d'origine.

Du coup tu sauvegarde ton image au format sérialisé de la classe. Plus tard, tu peux re-ouvrir ton fichier sérialisé comme indique neige,
y modifier des paramètres de blur ou autre puis tu peux ré-exporter l'image au format que tu veux...

Parcontre à coup sûr le pointeur d'instance GD ne sera pas bon, il faudra le ré-ouvrir lors de la déserialisation. Si tu veux tu peux aller à fond dans le truc ... tu te fait une classe qui représente le ptr GD. En constructeur lui passes soit un fichier ou une taille, il initialize le pointeur GD.

1 - Si c'est un fichier tu mets le contenu de celui-ci dans une propriété de la classe.
2 - Si c'est un nouveau fichier, tu mets width et height ...

Lors de la désérialisation, tu vérifies si :

1 - Contenu du fichier dans la classe, dans ce cas tu l'enregistres dans un temporaire et tu initialise GD à partir de
2 - Sinon construire une nouvelle instance.

Du coup en sérialisant, tu stockes dans ton fichier la structure et paramétrage de ta transformation sur l'image, ainsi que les contenus. Le format serait alors distribuable en tant que tel.

C'est peut-être un peu compliqué mais ça serait pratique pour faire des modifs à la volée sans avoir besoin d'un bloc de code pour instancier et configurer les filtres.

Bonne prog,
akh
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
17 nov. 2009 à 12:44
Ah d'accord ok d'accord.
L'idée est simplement de linéariser la classe (ou plus exactement une instance de la classe) et de l'enregistrer sous cette forme dans un fichier. Ca te permet, d'une certaine manière d'avoir ton format d'image "propriétaire", propre à ta classe. Ainsi, pour ouvrir un "fichier image" de ta classe, il suffirait d'une méthode statique qui délinéariserait le contenu du fichier et le retournerait en tant qu'instance de la classe, restaurant ainsi l'instance précédemment enregistrée avec toutes ses propriétés (donc, a priori, y compris l'image GD stockée dans une propriété).
La seule question que je me pose est : peut-on linéariser une image GD ? A creuser...
Ou alors, j'ai rien compris à ce que disait Akh...
inwebo Messages postés 380 Date d'inscription lundi 12 novembre 2007 Statut Membre Dernière intervention 23 octobre 2014
17 nov. 2009 à 12:22
Pour l'alias, j'ai fait l'erreur d'inattention d'écrire set_save_jpg_pics au lieu de set_save_jpeg_pics, et je me suis demandé si il n'y avait pas possibilité de faire un alias sans re-écrire toute la fonction. Car cela pourrait être une erreur courante d'utilisation.

Oui Neige après relecture tu parlais bien de tes commentaires. ^^
Sinon 80 caractères par ligne, mais 120 tolérés.

http://framework.zend.com/manual/en/coding-standard.php-file-formatting.html#coding-standard.php-file-formatting.max-line-length

Sinon, j'avais compris l'idée d'AKH, pour l'instanciation de la class, de passer en paramètre, soit un chemin, soit des dimensions , c'est juste cela qui m'avait posé problème :

"D'autre part, ce serait pas mal du coup de faire une fonction d'enregistrement du contenu de la classe sous un fichier dans le style file_put_contents(serialize($this ..."
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
16 nov. 2009 à 23:04
En PHP, on ne peut pas déclarer d'alias de fonction... Pour ma part, j'ignorais que c'était possible dans d'autres langagtes (en fait, je ne m'étais jamais posé cette question).
En quoi est-ce que ça consiste exactement ?

Pour la mise en forme, je parlais de mon code à moi que j'ai posté : dans les commentaires, il s'affiche sans indentation.
En python, les recommandations sont de ne pas dépasser les 80 caractères, retour à la ligne compris. La raison : pour éditer un script en ligne de commande (avec VI par exemple), on ne perd pas l'indentation et la lisibilité du code. Cela implique que Python fournit une syntaxe spécifique pour "écrire une ligne sur plusieurs lignes". Bref, on s'égare.

Ce que voulais dire Akh c'est de ne pas utiliser le pointeur comme argument pour chaque méthode. Au lieu de ça, il faut considérer une instance de la classe comme un objet image que l'on manipule grâce à ses méthodes.
En gros, lors de l'instanciation de la classe, deux cas peuvent se présenter :
- on "charge" une image
- on crée une nouvelle image

Dans le premier cas, il convient d'accéder au fichier et d'en faire une image GD. Il faut alors stocker dans une propriété (par exemple $this->gd_image) le pointeur vers cette image GD. A partir de là, l'instance de cette classe ne manipule plus que cette image.
Dans le second, on crée simplement une image GD dont on stocke le pointeur dans ladite propriété.
Quand une méthode nécessite de manipuler le pointeur, elle utilise simplement $this->gd_image. Inutile de passer le pointeur en argument : c'est une tâche dont l'utilisateur de ta classe devrait être soulagé.
J'espère avoir été clair, sinon, il faudra un exemple de code ^^
inwebo Messages postés 380 Date d'inscription lundi 12 novembre 2007 Statut Membre Dernière intervention 23 octobre 2014
16 nov. 2009 à 15:51
AKH :

Ton idée de passer les paramètres lors de l'instanciation est tout simplement "logique". C'est une faute que je vais corriger dès que possible.
Pour les fonctions de dessins ou d'écriture sur l'image l'idée c'était de faire une class fille pour écrire sur l'image, et une pour ajouter des dessins (formes etc), et dans ce cas, la fonction image_set_filters n'a plus sa place dans la class Pictures. Je voulais juste éviter d'avoir une class monstre.

Par contre je n'ai pas compris l'idée de l'enregistrement du contenu de la class dans un fichier. Dans quel but ?

Neige :

Je ne connaissais tout simplement pas cette fonction (extension_loaded()), tu as entièrement raison, je corrige.
Je ne connaissais pas cette syntax pour le switch dans (image_set_filters() ). C'est plus propre et compact.
Effectivement pas besoin de re-tester les valeurs pour le return avec un if. Pour les fonctions set_save_*_pics, j'étais partis sur une unique fonctions, mais je me retrouve avec pas mal d'arguments, je voulais faire quelques chose de "simple" à utiliser, pour ne pas que l'utilisateur est à jongler avec trop de paramètres.

Et pour la mise en page des commentaires un peu moche je plaide non coupable (j'ai vu qu'il ne fallait pas dépasser les 120 caractères par ligne (dans la doc du zend framwork) mais je trouve cela un peu juste.

Je vais essayer de mettre en pratique tout cela.

J'ai une question, comment déclarer un alias de fonction ?
En tout cas merci à vous deux pour ces conseils avisés !
neigedhiver Messages postés 2480 Date d'inscription jeudi 30 novembre 2006 Statut Membre Dernière intervention 14 janvier 2011 19
16 nov. 2009 à 11:28
Salut,

private function get_gd_installed() {

Ta méthode est bizarrement faite... Pas vraiment optimisée.
Pourquoi ne pas utiliser, plus simplement, la fonction extension_loaded() qui sert justement à savoir si une extension particulière est chargée ?
D'une manière générale, pour ce genre de fonction, il est pertinent de ne pas réexécuter le code entier à chaque appel : autant stocker le résultat dans une variable statique et renvoyer celle-ci quand elle est définie. Exemple :

private function get_gd_installed() {
static $flag = NULL;
if (NULL !== $flag) {
$flag = extension_loaded('gd');
}
return $flag;
}

Mais pour le cas présent, c'est superflu... Comme tu l'utilise dans dans ton constructeur, tu pourrais aussi bien te dispenser de cette méthode qui ne sert, en fait, à rien :

public function __construct() {
if (!extension_loaded('gd')) {
throw new Exception(_('GD disabled'));
}
}

Là, j'utilise la fonction _(), alias de gettext qui permet de laisser l'utilisateur traduire le texte s'il le souhaite.

Ta méthode set_filters_pics pourrait gagner en clarté :
public function set_filters_pics($pictures, $filtre, $filtre_param_1 '', $filtre_param_2 '', $filtre_param_3 = '', $filtre_param_4 = '') {
if( !is_resource($pictures) ) {
$pictures = self::get_open_pics($pictures);
}
switch( $filtre ) {
case IMG_FILTER_NEGATE :
case IMG_FILTER_GRAYSCALE :
case IMG_FILTER_EDGEDETECT :
case IMG_FILTER_EMBOSS :
case IMG_FILTER_GAUSSIAN_BLUR :
case IMG_FILTER_SELECTIVE_BLUR :
case IMG_FILTER_MEAN_REMOVAL :
return (imagefilter($pictures, $filtre) !== FALSE);
break;
case IMG_FILTER_BRIGHTNESS :
case IMG_FILTER_CONTRAST :
case IMG_FILTER_SMOOTH :
return (imagefilter($pictures, $filtre, $filtre_param_1) !== FALSE);
break;
case IMG_FILTER_COLORIZE :
return (imagefilter($pictures, IMG_FILTER_CONTRAST, $filtre_param_1, $filtre_param_2, $filtre_param_3, $filtre_param_4) !== FALSE);
break;
case IMG_FILTER_PIXELATE :
return (imagefilter($pictures, IMG_FILTER_PIXELATE, $filtre_param_1, $filtre_param_2) !== FALSE);
break;
default:
return $pictures;
}
}

Bon la mise en page est pas géniale dans les commentaires...
Les idées sont :
- ta fonction renvoit le plus souvent un booléen, la fonction imagefilter aussi : inutile de faire un if pour tester et renvoyer un booléen... Utilise directement le résultat de la fonction imagefilter pour la valeur à renvoyer
- de nombreux cas nécessitent le même traitement : inutile de les séparer, autant les passer dans la même moulinette. La variable $filtre contient une valeur qui permet le traitement, puisqu'elle est "filtrée" par le bloc switch

D'une manière générale, tu pourrais économiser quelques lignes en factorisant un peu plus. Par exemple, les méthodes set_save_*_pics() qui font toutes la même chose pour chaque type d'image. Il serait intéressant d'avoir une certaine abstraction pour l'utilisateur, qu'il n'ait pas besoin absolument de connaître le type d'image pour appeler la bonne méthode : une seule méthode qui appellerait une méthode spécialisée en fonction du type d'image et avec certains paramètres selon ceux qui sont nécessaires.

L'idée d'Akh est à considérer sérieusement, c'est là qu'est la clé. Elle permettrait de faire de l'abstraction (avec atomisation du code), histoire de ne choisir le type d'image que quand on fait une conversion d'un type à un autre ou quand on enregistre l'image sur le disque, mais pas quand on la manipule.
Essaye aussi d'éviter le plus possible la redondance de code : quand deux fonctions ont le même code, à peu de choses près, autant les rassembler en une seule avec des paramètres, éventuellement avec deux fonctions spécialisées qui appellent la fonction "générique" avec les bons paramètres (ça évite aussi les entrées utilisateur qui peuvent provoquer des erreurs).

Voilà rapidement quelques conseils comme ça, sans avoir vraiment regardé le code en profondeur.
cs_aKheNathOn Messages postés 575 Date d'inscription dimanche 23 décembre 2001 Statut Membre Dernière intervention 23 octobre 2012
16 nov. 2009 à 09:22
Le sujet de ta source est très intéressant, et c'est agréable de voir que tout est bien documenté. Pas mal le côté filtre. J'ai une remarque qui pourrais changer ton code. Je constate que tu as fait une classe, ce qui est très bien, mais tu utilises des fonctions en passant en paramètres la ressource de l'image. Pour résumer, tu as fait une classe qui utilise l'astuce du pointeur de ressources dans les versions en mode fonctions.

Ma question / proposition serait de dire, autant faire une classe image, qui représente une image. A l'instanciation tu lui indiques soit un chemin, ce qui charge l'image, soit une taille, ce qui créé une image.

Du coup tu utiliserais toutes les fonctions sans avoir à passer le pointeur de la ressource image qui serait stocké dans l'instance de la classe.

Ce qui serait le top, serait d'avoir en complément des fonctions permettant de dessiner l'image.

Tu peux par exemple y mettre : du texte, des formes, des bouts d'images, bref des calques style photoshop, et ce n'est qu'au final en sauvegardant l'image au format png ou jpg ou gif que les pixels seraient dessinés. Entre temps tu stockerait tous les éléments dans ton instance de classe. D'autre part, ce serait pas mal du coup de faire une fonction d'enregistrement du contenu de la classe sous un fichier dans le style file_put_contents(serialize($this ...

En tout cas c'est une bonne base fonctionnelle, avec une orientation plus objet ce serait encore mieux.

Bonne prog, akh
inwebo Messages postés 380 Date d'inscription lundi 12 novembre 2007 Statut Membre Dernière intervention 23 octobre 2014
12 nov. 2009 à 12:50
Au plaisir, merci d'avoir pris le temps d'y jeter un œil !
cs_twisteurwin Messages postés 167 Date d'inscription mardi 21 septembre 2004 Statut Membre Dernière intervention 2 mai 2009
12 nov. 2009 à 12:10
salut Inwebo c'est une très bonne source complète et utile merci de nous l'avoir partagée, si je vois des améliorations je posterai :)
merci bcp
Rejoignez-nous