RE: [JAI] Improving speed of scaling (was Re: GIF image corrupt and Re: why is my performance...)
I think your difficulty stems from not understanding the JAI
"pull" model well enough. This is something a lot of people
including myself get confused by.
Your assumption that JAI.create("stream", ...) reads the image
into memory is not quite right, and that's at least the start
of your problems.
The key point to be made is this: When you perform a
image = JAI.create("operation", pb, ...);
you are generally NOT doing any computation. You are creating
a node in the operation chain, in which the input is hooked
up to a new instance of your operation, and the output is
returned to you as a RenderedOp.
The data in a RenderedOp is not computed until you request it.
So let's say you did
RenderedOp inputImage = JAI.create("stream", ...);
the inputImage will not contain any pixel data to start.
If you then pass this inputImage to another operation, the
same thing holds. If you construct a long chain of operations,
then call getTiles() on the final output image, the getTiles()
will result in a series of calls back through the chain to
retrieve whatever tiles are needed for the output image.
So, if you did
PlanarImage loadImage = JAI.create("stream", ...);
PlanarImage cropImage = JAI.create("crop", pb, ...);
PlanarImage scaleImage = JAI.create("scale", pb, ...);
Then no image data is computed. If you then called scaleImage.getTiles()
and timed this method, the total time would be the time needed to
the entire chain, not just the scale. In this case, crop is not really
a computational operator but a filter that adjusts the reported size
of its output image, so the timing info would account for both the time
needed to read the file and the time to scale the image.
Notice that all of this is based around tiles as the sort of "currency"
that is passed around between operations. If your source image is
i.e. has only one tile, then any subsequent operators in the chain will
eventually call getTile(0,0), which will read your entire image into
memory. Everything that you've described seems consistent with this.
Try to find out whether your TIFF is tiled by using tiffinfo (a quick
google search for tiffinfo will get you started in the right direction).
As for how to read tiles from an untiled image... well it MAY (only may)
be possible to pass a RenderingHints into the Stream operator that
specifies the tiling. And the TIFF reader may (just may) be able to
take advantage of this to retile your image from source. But I wouldn't
get too hopeful.
The way to do this would be something like
ImageLayout layout = new ImageLayout();
RenderingHints hints = new RenderingHints();
inputImage = JAI.create("stream", pb, hints);
I think that's how it works, it's been a while.
You might also try the JAI ImageIO tools TIFF reader. From what I
it's better than the JAI codec in several ways. And you may be able to
ImageRead operator and try something similar to what I showed above. Or
may not. worth a try though.
> -----Original Message-----
> From: Thomas Wolf [mailto:firstname.lastname@example.org]
> Sent: Tuesday, July 05, 2005 11:17 AM
> To: email@example.com
> Subject: [JAI] Improving speed of scaling (was Re: GIF image
> corrupt and Re: why is my performance...)
> Hi Mike,
> Nidel, Mike wrote:
> > So you are cropping, scaling, then writing a JPEG from a 200MB TIFF
> > file. This brings us to the perennial question: is your
> source image
> > tiled? If not, then as soon as you ask for a single pixel, you will
> > probably end up reading the entire image into memory. If
> this is the
> > case, then I would say that reading 200MB into main memory
> in 5 sec is
> > probably not slow at all.
> I don't think the source TIFFs are "tiled" - although I
> haven't got a clue on how to check that for sure (as you may
> have been able to tell from my posts, I'm not that versed in
> imaging :-) But I expected the whole image to be read into
> memory anyway - and I think the "reading into memory" is a
> one-time cost incurred with this call:
> workingImage = JAI.create("stream", stream);
> This method only gets called the first time the front-end
> requests a part of the image. After that, I retrieve a
> reference to this source image via the session and simply do
> the cropping and scaling operations and output as a JPEG.
> So, from my perspective, I can understand a 5sec initial
> delay as a 200mb image is read in. But subsequent operations
> should be much faster. But they aren't. I think I'm missing
> something important here :-(
> After getting James' suggestion regarding timing the operations,
> long t0 = System.currentTimeMillis();
> workingImage = JAI.create("scale", pb);
> workingImage.getTiles(); // force to compute
> long t1 = System.currentTimeMillis();
> System.out.println("Time spent in scale= "+(t1-t0));
> I now see that what I thought was a 5+ second encoding to
> JPEG is actually less than 500ms. Scaling seems to take
> 4.5seconds! But I'm a little
> confused: I thought - ok, if getTiles() forces computation
> for the scale operation, perhaps I will have to do the same
> for the crop operation to get an accurate timing of it! But
> when I do getTiles() after the crop operation, it turns into
> another 4.5sec operation (for a total time of
> 10+sec!) So, obviously doing a getTiles() on the crop
> operation to get
> more 'accurate' timing wasn't helping things. So I'm more
> than a bit confused as to timing things properly.
> Anyway, assuming that my 200mb TIFF source image is not tiled
> and that I always want to crop the image and output to a JPEG
> of size 700x"some- height-that-maintains-aspect-ratio", what
> is the optimal way to go? (The source images are
> high-resolution plant specimen images.)
> Thanks everyone for your patience in answering my questions. Tom
> To unsubscribe, e-mail: firstname.lastname@example.org
> For additional commands, e-mail: email@example.com