2

I am a newbie to graphics. I've been using this code to make thumbnails of image files. When i use small files(~100KB) like wall papers, it works fine but when i use an image file(a photo) of size ~5MB, it produces just a few bytes(~1-8KB) of file which shows up as black image. It does not matter what Width and Height i give it. What could be going wrong here? Is it a difference between image types or the camera that produces the images? I'm sure the problem images are from a different camera than the non problematic ones. I am giving quality param as 100 to not miss out any detail that way...

        ByteArrayOutputStream out = new ByteArrayOutputStream();
    try {
        int dx = thumbWidth, dy = thumbHeight; 

        Image image = Toolkit.getDefaultToolkit().createImage(file);
        MediaTracker mediaTracker = new MediaTracker(new Container());
        mediaTracker.addImage(image, 0);
        mediaTracker.waitForID(0);

        double thumbRatio = (double)thumbWidth / (double)thumbHeight;

        int imageWidth = image.getWidth(null);
        int imageHeight = image.getHeight(null);
        double imageRatio = (double)imageWidth / (double)imageHeight;

        if (thumbRatio < imageRatio) {
          thumbHeight = (int)(thumbWidth / imageRatio);
        } else {
          thumbWidth = (int)(thumbHeight * imageRatio);
        }

        if(thumbWidth > dx) {
            thumbWidth = dx;
            thumbHeight = (int)(thumbWidth / imageRatio);
        }
        if(thumbHeight > dy)
        {
            thumbHeight = dy;
            thumbWidth = (int) (thumbHeight*imageRatio);
        }

        log.debug("X="+thumbWidth+" Y="+thumbHeight);

        BufferedImage thumbImage = new BufferedImage(thumbWidth, thumbHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics2D = thumbImage.createGraphics();
        graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);

        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
        JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(thumbImage);

        quality = Math.max(0, Math.min(quality, 100));
        param.setQuality((float)quality / 100.0f, false);

        encoder.setJPEGEncodeParam(param);
        encoder.encode(thumbImage);
        log.debug("ThumbLength"+out.toByteArray().length);

        FileOutputStream fos = new FileOutputStream("/root/testx.jpg");
        fos.write(out.toByteArray());
        fos.close();

    } catch(Exception e) { log.debug(e.getMessage());}

    return out.toByteArray(); 
Ram
  • 325
  • 4
  • 22

1 Answers1

2

You might try BufferedImage.TYPE_INT_ARGB, as shown here.

Also, your MediaTracker is waiting on the same thread; ImageIO.read() might be simpler.

Addendum: Also consider AffineTransformOp, although the src and dst must be different.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • @trashgod Thanks for your reply. I tried it and probably i have to do a little more than simply change to what you've said but i am now getting images either black in color or orange in color. But i found something new... there is a memory usage issue in the code... – Ram Apr 24 '11 at 23:37
  • cannot post all of the log... I'm using tomcat 6.0.18 with java 1.5. I've used this code on OS X as well as fedora 8. Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space at java.awt.image.DataBufferInt.(DataBufferInt.java:41) at java.awt.image.Raster.createPackedRaster(Raster.java:458) at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1015) at sun.awt.image.ImageRepresentation.createBufferedImage(ImageRepresentation.java:230) at sun.awt.image.ImageRepresentation.setPixels(ImageRepresentation.java:535) – Ram Apr 24 '11 at 23:44
  • 1
    Yes, `TYPE_BICUBIC` is memory intensive. You'll have to determine a suitable limit empirically and reject images over the corresponding size. In a batch of 100 MiB images, I needed over 1 GiB of memory—a 10:1 ratio. As an alternative, you might try [`AffineTransformOp`](http://download.oracle.com/javase/6/docs/api/java/awt/image/AffineTransformOp.html). – trashgod Apr 25 '11 at 00:02
  • Correction: They were 100 megapixel images having 16 bits/pixel. A single 200 MiB image needed 1024 MiB of memory—a 5:1 ratio. – trashgod Apr 25 '11 at 00:51
  • In the worst case, I simply looped in a shell script, starting a new JVM for each image. – trashgod Apr 27 '11 at 15:04
  • One other thing: If you look into `AffineTransformOp`, the `filter()` method lets you specify a reusable `dst`. That might ease memory pressure. – trashgod Apr 27 '11 at 17:35
  • FWIW... I've tried to see memory usage at each command by using Runtime()'s totalMemory() and freeMemory() methods. Apparently, when i flush a buffered image or dispose() of the graphics, there is no noticeable memory reclamation. Only when i do a runFinalization() and, don't laugh at me, gc(), do i get at least 10-15MB of memory reclaimed immediately. I've tried AffineTransformOp but am getting black images and could not solve the problem in this time. Other than that, i've syncronized the method and made it static and that helps for the time being. Thanks for your valuable posts, trashgod! – Ram May 02 '11 at 22:48
  • 1
    @Ram: You're welcome. Looking closer, I see you invoke `createGraphics()`; except in `paint()`, this needs a corresponding `dispose()`. More [here](http://stackoverflow.com/questions/2486136/what-method-in-java-is-used-to-destroy-your-objects/2486200#2486200). – trashgod May 03 '11 at 01:23