IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java Discussion :

Compresser image avant Sérialization


Sujet :

Java

  1. #1
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2011
    Messages
    442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2011
    Messages : 442
    Par défaut Compresser image avant Sérialization
    Bonjour,

    Juste une petite question : j'essaye de compresser une image avant de l'enregistrer. Le cas a déjà été traité plein de fois, mais j'ai un petit problème dans mon cas :

    On trouve facilement comment compresser une image pour l'enregistrer au format png ou jpg. On trouve rarement par contre comment compresser cette image avant de l'enregistrer par le procédé de Sérialization :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Truc implements Serializable {
        String nom = "machin";
        transient Panel panel = new Panel();
        ImageIcon img = new ImageIcon(panel.capturerImage());
    }
     
    class Panel extends JPanel {
        ...
        public Image capturerImage() {
            BufferedImage buffer = new BufferedImage(this.getSize().width, this.getSize().height, BufferedImage.TYPE_3BYTE_BGR);//type choisit au hasard
            Graphics g = buffer.createGraphics(); //On crée un Graphic que l'on insère dans tamponSauvegarde
            this.paint(g);
            return buffer;
        }
    }
    Voilà en gros l'idée. Un panel qui capture l'image de ce qu'il affiche, et une classe qui contient l'image comme attribut et qui va être sérialisée.

    Le problème c'est qu'en l'état actuel, l'image générée est assez conséquente. Comme puis-je la réduire avant la sauvegarde ?

    J'ai envisagé plusieurs solutions :
    1) BufferedImage.TYPE_3BYTE_BGR : est-ce le mode le plus adapté ?
    2) Mon panel contient majoritairement du blanc. Je pourrais créer un liste des pixels non blancs ?
    3) Utiliser un algorithme de compression comme celui des images jpg, mais je sais pas faire... Existe-t-il quelque chose de tout fait ?

    Merci pour votre aide

  2. #2
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 586
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 586
    Par défaut
    Le mieux serait d'enregistrer l'image sous forme de vrai PNG (pas JPEG pour éviter les pertes,) mais dans un byte[] au lieu d'un fichier. La sérialisation enregistrera donc ce byte[].

    Vu que pendant l'exécution un byte[] n'est pas très utile et qu'il vaut mieux un BufferedImage, il vaut mieux régler ce cas avec writeObject()/readObject().
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  3. #3
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2011
    Messages
    442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2011
    Messages : 442
    Par défaut
    Merci beaucoup pour ta réponse. Malheureusement j'ai pas tout compris...

    tu proposes que j'utilise un ByteArrayOutputStream dans le writer. Càd :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    OutputStream out = new ByteArrayOutputStream();
    ImageIO.write(monImage, "png", out);
    byte[] Tbyte = out.toByteArray();
    J'enregistre ensuite le Tbyte comme attribut de ma classe Truc.

    J'ai bien compris ?

    Si c'est ça, est-ce que tu peux éclaircir ces points pour moi ? :
    - comment je récupère mon image à partir du Tbyte ?
    - que veux-tu dire par gérer ce cas avec writeObject/readObject ? A moins que tu ne parles de l'écriture du Tbyte par la sérialization normale.
    - est-ce qu'il y a moyen d'améliorer la compression ?

    Merci encore pour ta réponse

  4. #4
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 586
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 586
    Par défaut
    Citation Envoyé par Sharcoux Voir le message
    J'enregistre ensuite le Tbyte comme attribut de ma classe Truc.

    J'ai bien compris ?
    Non. Le Tbyte sera un paramètre local de la méthode writeObject(), et le but est de balancer ça dans le ObjectOutputStream, à la place de ton ImageIcon non compressé.

    Citation Envoyé par Sharcoux Voir le message
    Si c'est ça, est-ce que tu peux éclaircir ces points pour moi ? :
    - comment je récupère mon image à partir du Tbyte ?
    Même chose en sens inverse. ByteArrayInputStream. ImageIO.read().

    Citation Envoyé par Sharcoux Voir le message
    - que veux-tu dire par gérer ce cas avec writeObject/readObject ? A moins que tu ne parles de l'écriture du Tbyte par la sérialization normale.
    Justement pas avec une sérialisation normale par défaut. Ca n'a pas de sens de se coltiner à la fois l'image non compressée et le tableau de bytes de l'image compressée. Il faut utiliser une sérialisation customisée pour ne sérialiser que ta String et le tableau compressé. La sérialisation customisée se fait avec writeObject(ObjectOutputStream) et readObject(ObjectInputStream), comme indiqué dans la JavaDoc de Serializable. S'il te plaît, lis-la et essaie-la avant de poser des questions.

    Citation Envoyé par Sharcoux Voir le message
    - est-ce qu'il y a moyen d'améliorer la compression ?
    Mieux que PNG ? Ça va être difficile. Les autres formats sont à perte, pas l'idéal pour une sérialisation.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  5. #5
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2011
    Messages
    442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2011
    Messages : 442
    Par défaut
    Ok. Pour les deux première questions, c'est ma faute, j'ai mal posé mes questions. On reprend avec juste une seule question qui résume le point qui reste flou pour moi :

    Quand tu dis
    Vu que pendant l'exécution un byte[] n'est pas très utile et qu'il vaut mieux un BufferedImage, il vaut mieux régler ce cas avec writeObject()/readObject().
    Est-ce que simplement écrire ma classe comme ceci (schématiquement) résoudrait le problème que tu soulève :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class Truc implements Serializable {
        String nom = "machin";
        transient Panel panel = new Panel();
        transient ImageIcon img = new ImageIcon(panel.capturerImage());//cette fois l'image n'est pas enregistrée
        byte[] Tbyte = monTableauDeBit //récupéré comme décrit dans mon second post
    }
    Ou alors est-ce que je passe à côté de quelque chose ?

    Quant à la dernière, je voulais savoir s'il y avait moyen de profiter du fait que je sais qu'une grande partie de l'image sera blanche pour améliorer encore la compression, mais peut-être que l'algorithme du png fera déjà le travail.

    En tout cas, merci pour tes réponses, merci pour ton temps, et désolé d'avoir posé mes questions un peu vite...

  6. #6
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 586
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 586
    Par défaut
    Citation Envoyé par Sharcoux Voir le message
    Ou alors est-ce que je passe à côté de quelque chose ?
    Tu passes à côté de quelque chose. Ce quelque chose s'appelle readObject(ObjectInputStream)/writeObject(ObjectOutputStream) que je t'ai demandé, trois fois, de lire dans la JavaDoc de Serializable. Relis, et ne reviens pas sans quelque chose qui implique readObject(ObjectInputStream)/writeObject(ObjectOutputStream)

    Citation Envoyé par Sharcoux Voir le message
    Quant à la dernière, je voulais savoir s'il y avait moyen de profiter du fait que je sais qu'une grande partie de l'image sera blanche pour améliorer encore la compression, mais peut-être que l'algorithme du png fera déjà le travail.
    C'est en principe le boulot de PNG, oui.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  7. #7
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2011
    Messages
    442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2011
    Messages : 442
    Par défaut
    Je ne vois pas bien où tu veux en venir. J'ai le choix entre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    class Truc implements Serializable {
        String nom = "machin";
        transient Panel panel = new Panel();
        transient ImageIcon img = new ImageIcon(panel.capturerImage());//cette fois l'image n'est pas enregistrée
        byte[] Tbyte = monTableauDeBit //récupéré comme décrit dans mon second post
    }
    //puis procédé normal de sérialization avec FileOutputStream et compagnie
     
    //********** ou **********
    class Truc implements Serializable {
        String nom = "machin";
        transient Panel panel = new Panel();
        transient BuffuredImage monImage = panel.capturerImage());//cette fois l'image n'est pas enregistrée
     
        private void writeObject(java.io.ObjectOutputStream output) throws IOException {
            OutputStream out = new ByteArrayOutputStream();
            ImageIO.write(monImage, "png", out);
            byte[] Tbyte = out.toByteArray();
            output.defaultWriteObject();
            output.writeInt(Tbyte.length);
            output.write(Tbyte);
        }
        private void readObject(java.io.ObjectInputStream input) throws IOException, ClassNotFoundException {
            input.defaultReadObject();
            byte[] Tbyte = new byte[input.readInt()];
            input.read(Tbyte);
            InputStream in = new ByteArrayInputStream(Tbyte);
            BuffuredImage monImage = ImageIO.read(in);
        }
    }
    Pour moi la seule différence entre les deux réside dans le fait que la première méthode force plus ou moins à conserver les deux objets en mémoire (le Tbyte et l'image). Mais à partir du moment où je supprime mon objet après la sérialization, je ne vois plus de différence. Quel est l'avantage de passer par les readObject et writeObject dans le cas présent ?

    Merci encore pour ton temps.

  8. #8
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 586
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 586
    Par défaut
    Citation Envoyé par Sharcoux Voir le message
    Je ne vois pas bien où tu veux en venir.
    Ton deuxième exemple semble pourtant montrer que tu as tout compris.

    Citation Envoyé par Sharcoux Voir le message
    Pour moi la seule différence entre les deux réside dans le fait que la première méthode force plus ou moins à conserver les deux objets en mémoire (le Tbyte et l'image). Mais à partir du moment où je supprime mon objet après la sérialization, je ne vois plus de différence. Quel est l'avantage de passer par les readObject et writeObject dans le cas présent ?
    Je dirais que l'avantage, c'est de ne pas se coltiner dans la classe une variable membre qui ne sert à rien pendant le cycle de vie de cette classe, et au lieu de ça de juste la laisser à sa place : dans les opérations de sérialisation/désérialisation, là où rien d'autre ne connaîtra son existence.

    De plus, cela évite de se charger d'opérations de sérialisation ailleurs que dans les méthodes chargées de la sérialisation.

    Mais effectivement, je ne voyais pas les choses comme ça, mais ça marcherait avec ta première méthode.
    Elle est juste à l'opposé de toute forme de bonne pratique, et donc plus difficile à lire, maintenir, et encourage de nouveaux bugs lors de l'écriture de nouveau code.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  9. #9
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2011
    Messages
    442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2011
    Messages : 442
    Par défaut
    Oui, je vois ce que tu veux dire. Je suis assez d'accord avec toi d'ailleurs. C'est juste que ma classe Truc est complètement inventée et que concrètement, lorsque je sauvegarde, je crée un objet

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class Machin {
        byte[] Tbyte;
        String autresDonnéesImportantes;
        ...
    }
    qui ne contient que les données, et que je sérialise. Ca me parait pas mal comme procédé parce qu'en isolant les informations qui sont enregistrées, je trouve qu'on voit mieux où on en est plutôt qu'en "cachant" une partie du processus dans le writeObject/readObject. (chacun ses goûts)

    Sinon, effectivement, ce serait débile de garder dans une même classe l'image et le Tbyte[] en mémoire. En même temps, une classe qui contient un champ Image, un champ JPanel, et des champs de données, je me doute que c'est pas trop dans l'esprit des Patterns POO actuels ! ^^

    En tout cas, mercie encore pour ton aide.

  10. #10
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 586
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 586
    Par défaut
    Citation Envoyé par Sharcoux Voir le message
    Oui, je vois ce que tu veux dire. Je suis assez d'accord avec toi d'ailleurs. C'est juste que ma classe Truc est complètement inventée et que concrètement, lorsque je sauvegarde, je crée un objet

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class Machin {
        byte[] Tbyte;
        String autresDonnéesImportantes;
        ...
    }
    qui ne contient que les données, et que je sérialise. Ca me parait pas mal comme procédé parce qu'en isolant les informations qui sont enregistrées, je trouve qu'on voit mieux où on en est plutôt qu'en "cachant" une partie du processus dans le writeObject/readObject. (chacun ses goûts)
    Ma foi, cela dépend de la situation réelle. Mais la manière dont la question était présentée semblait indiquer que tu avais déjà des objets sur lesquels tu utilises la sérialisation de manière classique, et que tu cherchais un moyen pour que l'image soit sérialisée compressée et non pas brute. Dans ce cas ça dépend de rien du tout, c'est la sérialisation qui doit se charger des problématiques de sérialisation, et le faire en ligne, point.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  11. #11
    Membre éclairé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2011
    Messages
    442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2011
    Messages : 442
    Par défaut
    Oui, en posant ma question à la base, je ne pensais pas que le contexte prendrait autant d'importance. Ta première réponse en fait était largement suffisante et on s'est mal compris par la suite.

    Quant à mon modèle (même si je doute que ça t'intéresse énormément), à la sauvegarde, je transmets les informations à sauvegarder à un objet sérializable. Lors de ce transfert, je peux donc me contenter d'envoyer le byte[] plutôt que d'envoyer l'image puis de la "bytiser" dans le writeObject. C'est pour ça que j'étais pas très chaud pour passer par les writeObject/readObject si c'était pas nécessaire. (encore une fois, question de goût)

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [JavaScript] [FileApi] Compresser une image AVANT d'uploader
    Par O-Zone dans le forum Contribuez
    Réponses: 0
    Dernier message: 15/01/2010, 13h47
  2. Interface JMAGICK ? Qui connait ? (compression image)
    Par ionix dans le forum Multimédia
    Réponses: 5
    Dernier message: 04/05/2006, 17h14
  3. [ImageMagick] Redimensionner une image avant de l'enregistrer
    Par julien.63 dans le forum Bibliothèques et frameworks
    Réponses: 3
    Dernier message: 03/05/2006, 22h01
  4. swing afficher une image avant la fenetre
    Par gripin dans le forum AWT/Swing
    Réponses: 1
    Dernier message: 26/04/2006, 00h21
  5. Preview d'une image avant upload
    Par nabbo dans le forum Général JavaScript
    Réponses: 20
    Dernier message: 21/12/2005, 02h02

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo