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

2D Java Discussion :

Heap Space lors d'un redimensionnement de BufferedImage à partir d'une image TIFF


Sujet :

2D Java

  1. #1
    Candidat au Club
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Octobre 2007
    Messages : 7
    Points : 3
    Points
    3
    Par défaut Heap Space lors d'un redimensionnement de BufferedImage à partir d'une image TIFF
    Bonjour,

    Voilà mon problème :
    Dans le cadre d'un module de scan (acquisition de documents par un scanner), je souhaite afficher une série de vignettes (aperçus d'image) dans un panel... Pour ce faire, je redimensionne l'image de base (acquise en bufferedImage) grace à la méthode drawimage de Graphics2D.
    Jusqu'ici, tout se passe bien avec tous les types d'images (jpeg, png,...), sauf les images TIFF (simple ou multi-page, peu importe, le problème est le même) !

    Voici le cheminement qui amène l'erreur :

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
     
    /**
    * Méthode de création de la vignette
    */
    private void initVignette(File file) {
     
    		BufferedImage image = null;
    		try {
    			if (FileUtils.getExtension(file).equalsIgnoreCase("tif") || FileUtils.getExtension(file).equalsIgnoreCase("tiff")) {
    				// -- Cas d'un fichier TIFF
    				image = ScanUtils.readTIFF(file, m_pageNumber - 1);
    			} else {
    				image = ImageIO.read(file);
    			}
     
    	        final int imgWidth = image.getWidth();
    	        final int imgHeight = image.getHeight();
    	        if (imgWidth == -1 || imgHeight == -1) {
    	            // -- l'image n'a pas fini de charger
    	            return;
    	        }
     
    			// -- Format Portrait ou paysage
    			if (imgWidth < imgHeight) {
    				int tmp = m_defaultWidth;
    				setDefaultWidth(m_defaultHeight);
    				setDefaultHeight(tmp);
    			}
     
    			m_vignette = new BufferedImage(m_defaultWidth, m_defaultHeight + 15, image.getType());
    			Graphics2D g2 = m_vignette.createGraphics();
    			g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
    					RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
    			g2.setRenderingHint(RenderingHints.KEY_RENDERING,
    					RenderingHints.VALUE_RENDER_SPEED);
    			g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
    					RenderingHints.VALUE_COLOR_RENDER_SPEED);
    			g2.setRenderingHint(RenderingHints.KEY_DITHERING,
    					RenderingHints.VALUE_DITHER_DISABLE);
     			// -- Redimensionnement
    			g2.drawImage(image, 0, 0, m_defaultWidth, m_defaultHeight, null);
    			setVignetteText(g2);
    			g2.dispose();
    		} catch (IOException e) {
    			s_logger.fatal(e.getMessage(), e);
    		}
     
    /**
    * Méthode de ScanUtils pour lire une image à partir d'un fichier TIFF et de son numéro de page
    */
    public static BufferedImage readTIFF(File file, int numPage) {
     
    		BufferedImage result = null;
    		try {
    			FileSeekableStream stream = new FileSeekableStream(file);
    			TIFFDecodeParam paramd = new TIFFDecodeParam();
    			ImageDecoder decoder = ImageCodec.createImageDecoder("TIFF", stream, paramd);
    			RenderedImageAdapter ria = new RenderedImageAdapter(decoder.decodeAsRenderedImage(numPage));
    			result = ria.getAsBufferedImage();
    		} catch (IOException e) {
    			//TODO : MC : I18n
    			s_logger.error(e.getMessage(), e);
    		}
    		return result;
    	}
    m_defaultWidth et m_defaultHeight sont des int respectivement fixés à 160 et 120.
    L'erreur se produit systématiquement à la ligne (de la méthode setVignette()) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    g2.drawImage(image, 0, 0, m_defaultWidth, m_defaultHeight, null);
    Avec un Java Heap Space...

    Quelqu'un aurait-il une idée lumineuse ?

  2. #2
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2007
    Messages
    572
    Détails du profil
    Informations personnelles :
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations forums :
    Inscription : Février 2007
    Messages : 572
    Points : 675
    Points
    675
    Par défaut
    Citation Envoyé par Elatan Voir le message
    Avec un Java Heap Space...
    Tu as plus d'info à donner sur l'erreur ?

    A priori, je dirais qu'il n'y a plus de memoire. Le tiff est non compressé, donc le fichier utilise plus de memoire.
    Peux tu essayer avec un tiff de petite taille ?

  3. #3
    Candidat au Club
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Octobre 2007
    Messages : 7
    Points : 3
    Points
    3
    Par défaut
    Merci de ta réponse Sanguko !

    Pour les infos supplémentaires sur l'erreur, voici le log :

    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
    32
    33
    34
     
    Caused by: java.lang.OutOfMemoryError: Java heap space
    	at java.awt.image.DataBufferInt.<init>(DataBufferInt.java:41)
    	at java.awt.image.Raster.createPackedRaster(Raster.java:458)
    	at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1015)
    	at java.awt.image.BufferedImage.<init>(BufferedImage.java:321)
    	at sun.java2d.loops.GraphicsPrimitive.convertFrom(GraphicsPrimitive.java:537)
    	at sun.java2d.loops.GraphicsPrimitive.convertFrom(GraphicsPrimitive.java:521)
    	at sun.java2d.loops.MaskBlit$General.MaskBlit(MaskBlit.java:171)
    	at sun.java2d.loops.Blit$GeneralMaskBlit.Blit(Blit.java:186)
    	at sun.java2d.pipe.DrawImage.blitSurfaceData(DrawImage.java:927)
    	at sun.java2d.pipe.DrawImage.renderImageCopy(DrawImage.java:550)
    	at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:54)
    	at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:982)
    	at sun.java2d.pipe.ValidatePipe.copyImage(ValidatePipe.java:168)
    	at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:2979)
    	at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:2964)
    	at sun.java2d.pipe.DrawImage.makeBufferedImage(DrawImage.java:317)
    	at sun.java2d.pipe.DrawImage.renderImageXform(DrawImage.java:372)
    	at sun.java2d.pipe.DrawImage.transformImage(DrawImage.java:251)
    	at sun.java2d.pipe.DrawImage.scaleImage(DrawImage.java:111)
    	at sun.java2d.pipe.DrawImage.scaleImage(DrawImage.java:1018)
    	at sun.java2d.pipe.ValidatePipe.scaleImage(ValidatePipe.java:189)
    	at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:2944)
    	at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:2895)
    	at presentation.swing.frontend.workspace.detail.dossiers.scan.ScanImage.initVignette(ScanImage.java:204)
    	at presentation.swing.frontend.workspace.detail.dossiers.scan.ScanImage.<init>(ScanImage.java:137)
    	at presentation.swing.frontend.workspace.detail.dossiers.scan.ImageLoader.doInBackground(ImageLoader.java:135)
    	at presentation.swing.frontend.workspace.detail.dossiers.scan.ImageLoader.doInBackground(ImageLoader.java:1)
    	at javax.swing.SwingWorker$1.call(SwingWorker.java:278)
    	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    	at javax.swing.SwingWorker.run(SwingWorker.java:317)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:885)
    Sinon, en ce qui concerne la compression, mes fichiers TIFF de test sont compressés (compression TIFF Group3 ou Group4, je ne sais plus). Pour être plus précis, mon fichier de test fait 162Ko, pour 7 images, et c'est un cas que l'appli doit pouvoir traiter. L'appli fonctionne avec des fichiers contenant moins de pages, mais bon, le but est de pouvoir traiter d'assez gros fichiers...

    Donc, a priori, tu as vu juste, c'est bien un problème de mémoire. J'aimerais donc savoir comment je pourrais éviter cette consommation de mémoire, qui est pour le moins handicapante, dans le traitement des images TIFF...

    Merci d'avance !

    EDIT : Une nouvelle piste : j'ai essayé avec un TIFF "mono-image", mais de dimension 2478 * 3504, qui me renvoie la même erreur... Pas cool du tout ca...

  4. #4
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2007
    Messages
    572
    Détails du profil
    Informations personnelles :
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations forums :
    Inscription : Février 2007
    Messages : 572
    Points : 675
    Points
    675
    Par défaut
    2478 * 3504, c'est super gourmand en mémoire.

    Une image pour pouvoir être affichée est forcement decompressée. Pour connaitre la taille en memoire d'une image décompressée, il faut utiliser la formule suivante :
    largeur*hauteur*nb_octets_par_pixel

    En général, nb_octets_par_pixel vaut 4 (les pc travailllent en true color, à 32bits par pixel,soit 4 octets)

    Donc pour ton image, 2478*3504*4, ca donne environ 34 Mo.

    Avec de telles images, on arrive rapidement à saturer la mémoire.

    Pour avancer dans la résolution de ton problème, à mon avis, il faut que tu prennes conscience de ce qui consomme de la mémoire dans ton appli. Voici une question de la faq (ici) qui devrait t'aider.
    Sinon, pour la manipulation des images, regarde aussi du coté de jai

  5. #5
    Candidat au Club
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Octobre 2007
    Messages : 7
    Points : 3
    Points
    3
    Par défaut
    Merci beaucoup pour ton aide ! Je crois que mon problème est résolu ! Et tout cela grâce à toi et JAI

    En fait J'avais déjà survolé JAI en l'utilisant pour lire les images TIFF (notamment avec la classe ImageCodec), mais dorénavant, je l'utilise aussi pour redimensionner l'image, et depuis, pplus de problème de mémoire !

    Je vous poste les changements opérés !
    Partie modifiée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    m_vignette = new BufferedImage(m_defaultWidth, m_defaultHeight + 15, image.getType());
    			Graphics2D g2 = m_vignette.createGraphics();
    			g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
    					RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
    			g2.setRenderingHint(RenderingHints.KEY_RENDERING,
    					RenderingHints.VALUE_RENDER_SPEED);
    			g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
    					RenderingHints.VALUE_COLOR_RENDER_SPEED);
    			g2.setRenderingHint(RenderingHints.KEY_DITHERING,
    					RenderingHints.VALUE_DITHER_DISABLE);
     			// -- Redimensionnement
    			g2.drawImage(image, 0, 0, m_defaultWidth, m_defaultHeight, null);
    Remplacée par :
    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
    Map<Key, Object> map = new HashMap<Key, Object>();
    			map.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
    			map.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
    			map.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
    			map.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
    			RenderingHints rh = new RenderingHints(map);
     
    			// -- Redimensionnement
    			ParameterBlock pb = new ParameterBlock();
    			pb.addSource(image);
    			pb.add((float) m_defaultWidth / image.getWidth());
    			pb.add((float) m_defaultHeight / image.getHeight());
    			pb.add(0.0F);
    			pb.add(0.0F);
    			pb.add(new InterpolationNearest());
    			m_vignette = JAI.create("scale", pb, rh).getAsBufferedImage();
    			Graphics2D g2 = (Graphics2D) m_vignette.getGraphics();

  6. #6
    Expert éminent sénior
    Avatar de sinok
    Profil pro
    Inscrit en
    Août 2004
    Messages
    8 765
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2004
    Messages : 8 765
    Points : 12 977
    Points
    12 977
    Par défaut
    Juste une chose, les valeurs de RenderingHints que tu utilises pour le redimensionnement ne sont de loin pas les meilleures au niveau de la qualité d'image obtenue en sortie, pour plus d'infos:

    http://today.java.net/pub/a/today/20...dinstance.html
    Hey, this is mine. That's mine. All this is mine. I'm claiming all this as mine. Except that bit. I don't want that bit. But all the rest of this is mine. Hey, this has been a really good day. I've eaten five times, I've slept six times, and I've made a lot of things mine. Tomorrow, I'm gonna see if I can't have sex with something.

  7. #7
    Candidat au Club
    Profil pro
    Inscrit en
    Octobre 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Octobre 2007
    Messages : 7
    Points : 3
    Points
    3
    Par défaut
    Citation Envoyé par sinok Voir le message
    Juste une chose, les valeurs de RenderingHints que tu utilises pour le redimensionnement ne sont de loin pas les meilleures au niveau de la qualité d'image obtenue en sortie
    Je sais, je sais ! C'était fait exprès justement, à cause de mon problème de mémore, je voulais que les traitements soient minimaux afin de consommer le moins de mémoire possible... Mais maintenant que c'est réglé, je vais changer tout ça !

    Merci !

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

Discussions similaires

  1. Réponses: 7
    Dernier message: 12/09/2011, 19h20
  2. Réponses: 3
    Dernier message: 24/04/2008, 12h09
  3. Réponses: 4
    Dernier message: 02/10/2007, 19h17
  4. Redimensionnement fenêtre en fonction d'une image
    Par skyjoe dans le forum Général JavaScript
    Réponses: 8
    Dernier message: 17/12/2006, 13h54
  5. Réponses: 6
    Dernier message: 30/09/2004, 12h21

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