Skip to main content

[JAVA2D] Strategies to manage BufferedImage size

4 replies [Last post]
Anonymous

Guys,

First of all, hello. I'm new to the list. First post.

I'm working on an application that reads a BufferedImage from a variety
of sources and allows a user to either scale the image with
AffineTransformOp or brighten/darken it with RescaleOp.

I keep two copies of the image in memory...the original and the modified
version. I keep the original because I was never really able to get an
image to revert to it's normal state after being reduced in size with
the AffineTransform. I suspect this is because of some matrix algebra
that I don't understand fully, but I simply found it easier to just
reset to the original image before trying another operation.

Anyway, I'm running into problems when I zoom around 5 levels because
I'm overrunning the allocated memory for the stack. I've written one of
my test images to file at its largest size and it's around 9.3 mb. I'm
very interested in keeping my applications footprint as small as
possible, so this is simply not good.

So, I have a few questions.

1. Am I correct in assuming I need to keep the original around after
zooming/shrinking to provide accurate contrasting operations?
2. Are there any strategies for reducing the size of a BufferedImage
that I might not be aware of?

Thanks for any information you provide, whether links, pointers to
books, or first-hand experience. It will be very much appreciated.

John

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Andrew M. Sheridan

John, hi.

Instead of performing an AffineTransformOp every time the user scales the
image, you may be able to take advantage of the Graphics2D
drawRenderedImage( RenderedImage, AffineTransform ) method. This method
applies the AffineTransform to the image when it is drawn. Using this
method may not reduce the amount of work or memory that your app uses, but
it may simplify your code: you will only have to hang on to the original
or contrast adjusted image and not the scaled image.

One note for performance. For an image as large as 9.3mg, you might want
to perform contrast adjustment using a lookuptable.

To (try) and answer you questions:

1) Yes. An image that has been scaled will not be the same as the
original. The degree to which the original and scaled images are the same
is a function of the interpolation algorithm (e.g. Bilinear, Bicubic,
Nearest Neighbor) and the degree of scaling. Performing any operation on
an image that has been scaled will yield less "accurate" results than
performing the operation on the original image itself.

2) Yes. Instead of scaling via an AffineTransformOp, just draw it scaled.

Do you have a stack trace showing the StackOverflowError?

Hope this helps.

--
Andrew M. Sheridan
LumenIQ, Inc.
asheridan@lumeniq.com

On Mon, 26 Jul 2004 21:16:24 -0400, John Wells
wrote:

> Guys,
>
> First of all, hello. I'm new to the list. First post.
>
> I'm working on an application that reads a BufferedImage from a variety
> of sources and allows a user to either scale the image with
> AffineTransformOp or brighten/darken it with RescaleOp.
>
> I keep two copies of the image in memory...the original and the modified
> version. I keep the original because I was never really able to get an
> image to revert to it's normal state after being reduced in size with
> the AffineTransform. I suspect this is because of some matrix algebra
> that I don't understand fully, but I simply found it easier to just
> reset to the original image before trying another operation.
>
> Anyway, I'm running into problems when I zoom around 5 levels because
> I'm overrunning the allocated memory for the stack. I've written one of
> my test images to file at its largest size and it's around 9.3 mb. I'm
> very interested in keeping my applications footprint as small as
> possible, so this is simply not good.
>
> So, I have a few questions.
>
> 1. Am I correct in assuming I need to keep the original around after
> zooming/shrinking to provide accurate contrasting operations?
> 2. Are there any strategies for reducing the size of a BufferedImage
> that I might not be aware of?
>
> Thanks for any information you provide, whether links, pointers to
> books, or first-hand experience. It will be very much appreciated.
>
> John
>
> ===========================================================================
> To unsubscribe, send email to listserv@java.sun.com and include in the
> body
> of the message "signoff JAVA2D-INTEREST". For general help, send email
> to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

John Wells

Andrew,

Thanks very much for the reply! Comments below:

Andrew M. Sheridan wrote:

> John, hi.
>
> Instead of performing an AffineTransformOp every time the user scales the
> image, you may be able to take advantage of the Graphics2D
> drawRenderedImage( RenderedImage, AffineTransform ) method. This method
> applies the AffineTransform to the image when it is drawn. Using this
> method may not reduce the amount of work or memory that your app uses,
> but
> it may simplify your code: you will only have to hang on to the original
> or contrast adjusted image and not the scaled image.

Hmmm...a worthy point. I'm actually combining both Rescale and
AffineTransform ops each time the user clicks an "Apply" button, and the
levels each are set to are controlled by two JSliders. I'd probably
have to make two calls to drawRenderedImage, but it'd still work. Thanks!

> One note for performance. For an image as large as 9.3mg, you might want
> to perform contrast adjustment using a lookuptable.

I'm new the the imaging world...will have to research this. Thanks for
the pointer.

> 2) Yes. Instead of scaling via an AffineTransformOp, just draw it
> scaled.

What do you mean by this? I'm not clear on the difference...

> Do you have a stack trace showing the StackOverflowError?

Sorry if I was unclear. It's the standard java.lang.OutOfMemory error.

What's odd is that a JPEG that is 114k increases the program's memory
footprint immediately after loading (without applying transforms), by
approximately 10 megs in RAM and roughly 18 megs in Windows XP reported
VM Size:

Before loading: Mem Usage: 18700K -- VM Size: 16856K
After loading: Mem Usage: 28656K -- VM Size: 34304K All that it does
is read the image in as a ImageIcon, convert the ImageIcon's getImage
result to a BufferedImage, store a reference to the BufferedImage for
later use in manipulations, and then set a JLabel's displayed icon to
this BufferedImage. My assumption was this increase in size was due to
the image being represented or converted on the fly to a raster/bmp
image and not able to take advantage of JPEG's compression capabilities,
but the size difference is making my doubt myself.

Does this sound like a reasonable footprint to you? Seems very heavy to
me. I've re-explored my code and haven't idenitified a memory hole yet,
but I'm very concerned their may be one.

Thanks very much for your help!

John

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Jim Graham

> Before loading: Mem Usage: 18700K -- VM Size: 16856K
> After loading: Mem Usage: 28656K -- VM Size: 34304K
>
> All that it does
> is read the image in as a ImageIcon, convert the ImageIcon's getImage
> result to a BufferedImage, store a reference to the BufferedImage for
> later use in manipulations, and then set a JLabel's displayed icon to
> this BufferedImage.

On the input end, instead of the ImageIcon=>BufferedImage conversion,
use the Image I/O package to load the image directly as a
BufferedImage. Check out the javax.imageio.ImageIO class and its
various static read methods for more information.

Then on the output end, instead of using a JLabel with an ImageIcon to
display the image, create your own JComponent and override its
paintComponent(Graphics g) method to render the image directly. This
Graphics parameter that you receive can be cast to a Graphics2D object
and between the two classes you have access to several methods which
let you render the image in more dynamic ways. For example, there is
the Graphics.drawImage(x, y, w, h) method which lets you draw the image
scaled to a new size on the fly. There is also the
Graphics2D.drawImage(img, transform) method which lets you perform more
complex (rotation, shearing) transforms on the fly. There is also a
Graphics2D.drawImage(img, bufimgop) which lets you apply an arbitrary
BufferedImageOp to the image as it is rendered to the screen. At the
very least, avoiding the ImageIcon for use in displaying a
BufferedImage avoids a nasty side effect of having the ImageIcon treat
the BufferedImage as if it is an asynchronous image and having it go
through an unnecessary "load your data" cycle which could potentially
bloat memory as well.

The reason this wasn't more obvious in the first place is that you are
falling victim here to the fact that our system provides several
different mechanisms for loading, manipulating, and displaying images
that all store their data differently, but since we provide lots of
convenience APIs to transfer the data around between them then it
doesn't appear that way to the casual developer.

The ImageIcon class you are using in 2 places uses the original image
APIs that have been around since the 1.0 versions and which were
targetted at Applets. These APIs were designed to manage modest images
that might be appropriate for web page design elements and to do so
asynchronously so that your applet is not necessarily stuck waiting for
image data to arrive over the network while it is trying to get
initialized. The image data for these APIs is stored very opaquely and
is very difficult to manipulate and needs to be converted when moving
to or from a BufferedImage.

...jim

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

John Wells

Jim Graham wrote:

> The ImageIcon class you are using in 2 places uses the original image
> APIs that have been around since the 1.0 versions and which were
> targetted at Applets. These APIs were designed to manage modest images
> that might be appropriate for web page design elements and to do so
> asynchronously so that your applet is not necessarily stuck waiting for
> image data to arrive over the network while it is trying to get
> initialized. The image data for these APIs is stored very opaquely and
> is very difficult to manipulate and needs to be converted when moving
> to or from a BufferedImage.

Wow....TREMENDOUS improvement. I haven't gotten the refactoring
completely finished, but so far I've created the custom component,
loaded the image and magnified it 10x and am hovering at around 28m ram
usage....it was around 72m at 10x under the previous design.

Thanks very, very much guys. I'm a newbie when it comes to Java imaging
(and imaging at all, for that matter) and feeling my way through the dark.

Are there any good sources of information, targeted at non-enlightened
java developers like myself, that you would recommend? I have purchased
the book "Building Imaging Applications with Java Technology", and while
it's been somewhat helpful, the examples are iterative and I haven't had
the time to read cover-to-cover. I'm looking for more of a reference
with best practice information....how to do this and that while not
aiming gun at foot. For instance, some of the examples in Sun's
tutorial demonstrate loading images via an ImageIcon, iirc. I would've
never known it was causing such a negative effect had you not mentioned it.

Thanks again for your time.

John

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".