0

An application I have reads in quite large images (jpegs) but only needs to work with smaller images so I subsample them with something like

ImageReadParam param = reader.getDefaultReadParam();
param.setSourceSubsampling(4, 4, 0, 0);
img = reader.read(0);

However, due to a bug in the jpeg reader that does not handle some meta data that I have fall back to other methods, one is using JAI to read the image and then resize (code is below, not I have to use reflection as some deployment environments don't have JAI available, I know I could design around this better but that's how it is).

try {
Class<?> c = ImageUtil.class.getClassLoader().loadClass("javax.media.jai.JAI");

if (c != null) { 
    URL url = new URL("file://" + file.getAbsolutePath());
    Method m = c.getMethod("create", String.class, Object.class);
    Object pi = m.invoke(null, "url", url);
    img = (BufferedImage) pi.getClass().getMethod("getAsBufferedImage").invoke(pi);
}
} catch (Throwable tt) {
// ...
}

However some of the images are really large an now and again I get out of memory exceptions, is there anyway I can get JAI to subsample the image when it is read in the manner i read the images using an ImageReader?

vickirk
  • 3,979
  • 2
  • 22
  • 37

2 Answers2

2
RenderedOp rop = JAI.create("fileload", file.getAbsolutePath());

ParameterBlock pb = new ParameterBlock();
pb.addSource(rop);
pb.add(0.5f);
pb.add(0.5f);
rop = JAI.create("scale", pb);

// For better looking results, but slower:
// rop = JAI.create("SubsampleAverage", pb);

BufferedImage bufImg = rop.getAsBufferedImage();   
Jordan
  • 33
  • 6
0

I'm assuming the out of memory exception is thrown when you attempt to do the conversion to a BufferedImage?

If that's the case then perhaps considering sticking to the RenderedOp that's returned by JAI's create method and instead use a ParameterBlock and a further JAI create to produce the scaled image?

ParameterBlock paramBlock = new ParameterBlock();
paramBlock.addSource(((RenderedOp) pi).createInstance()); // Updated this
paramBlock.add(0.5f); // x Scale (Change these two Scale values!)
paramBlock.add(0.5f); // y Scale
paramBlock.add(0.0f); // x Translate
paramBlock.add(0.0f); // y Translate
paramBlock.add(new InterpolationBilinear()); // I think this Interpolation should work...)
RenderedOp resized = JAI.create("scale", paramBlock, null);

The above code comes fully untested as standard I'm afraid, but should get you started!

Once you've got that resized RenderedOp you should be able to safely convert it to a BufferedImage if you have to.

Jasoon
  • 432
  • 2
  • 9
  • I tried something similar, using "SubsampleAverage" but got the same error as I got when I tried yours, i.e. Caused by: java.lang.IllegalArgumentException: operation "Scale" requires 1 source object(s). at javax.media.jai.JAI.createNS(JAI.java:1091) at javax.media.jai.JAI.create(JAI.java:977) at javax.media.jai.JAI.create(JAI.java:1412) ... 31 more – vickirk Nov 01 '09 at 19:51
  • From what I can make out the source needs to be a PlanarImage, is that correct? If I try calling getRendering() it (obviously) throws an out of memory exception :-( – vickirk Nov 01 '09 at 20:37
  • Ah ha, so it is. The Object pi that you get from calling the first create should be a RenderedOp, if you call createInstance() that should return a PlanarImage that you can work with. – Jasoon Nov 02 '09 at 08:44
  • Didn't work :-( createInstance gives me a NullOpImage which isn't liked by the create so I still get "operation "Scale" requires 1 source object(s)." – vickirk Nov 04 '09 at 20:06