imageio - IIOMetadata.setFromTree cripples data

M

Marian Schedenig

Hi!

I've posted the same question on the imageio-interest mailing list, but
since that seem pretty much defunct, and our client needs a quick solution
to this problem, I'm trying my luck here as well.

I'm trying to change the DPI settings of an image (png, jpg or tiff -
using the jar from JAI for tiff decoding). I'm doing this by getting the
metadata, changing the entries in the DOM tree, and re-setting it to the
metadata. However, I don't get the expected results, so I reduced my
code to the basic part which fails, and apparently I'm having problems
with getAsTree and setFromTree. Basically, what I'm doing now is this:

// ImageReader reader = something;
IIOMetadata metaData = reader.getImageMetadata(reader.getMinIndex());
// output the old metadata to System.out (contains correct values)
Node root = metaData.getAsTree("javax_imageio_1.0"); // (still contains
correct values)
metaData.setFromTree("javax_imageio_1.0", root); // (root contains
correct values)
// output the new metadata to System.out (this has the wrong values)

For all three formats (png, jpg, tiff), I get different results.in the
Dimension nodes. The most problematic is JPEG, which can't seem to read
the node's values at all. PNG changes my pixel size values. Only TIFF
seems to keep the correct values.

Here are the contents of the Dimension nodes for the various formats:

JPEG before:

<Dimension>
<PixelAspectRatio value="1.0"/>
<ImageOrientation value="normal"/>
<HorizontalPixelSize value="0.35277778"/>
<VerticalPixelSize value="0.35277778"/>
</Dimension>

JPEG after:

<Dimension>
<PixelAspectRatio value="NaN"/>
<ImageOrientation value="normal"/>
<HorizontalPixelSize value="Infinity"/>
<VerticalPixelSize value="Infinity"/>
</Dimension>

PNG before:

<Dimension>
<PixelAspectRatio value="1.0"/>
<ImageOrientation value="Normal"/>
<HorizontalPixelSize value="0.35273367"/>
<VerticalPixelSize value="0.35273367"/>
</Dimension>

PNG after:

<Dimension>
<PixelAspectRatio value="1.0"/>
<ImageOrientation value="Normal"/>
<HorizontalPixelSize value="2.8328612"/>
<VerticalPixelSize value="2.8328612"/>
</Dimension>

TIFF before:

<Dimension>
<PixelAspectRatio value="1.0"/>
<HorizontalPixelSize value="0.35277778"/>
<VerticalPixelSize value="0.35277778"/>
<ImageOrientation value="Normal"/>
</Dimension>

TIFF after:

<Dimension>
<PixelAspectRatio value="1.0"/>
<HorizontalPixelSize value="0.3527785"/>
<VerticalPixelSize value="0.3527785"/>
<ImageOrientation value="Normal"/>
</Dimension>

Any hints on what I'm doing wrong?

Thx,
Marian.
 
A

Andrey Kuznetsov

I'm trying to change the DPI settings of an image (png, jpg or tiff -
using the jar from JAI for tiff decoding). I'm doing this by getting the
metadata, changing the entries in the DOM tree, and re-setting it to the
metadata. However, I don't get the expected results
....

PNG before:

<Dimension>
<PixelAspectRatio value="1.0"/>
<ImageOrientation value="Normal"/>
<HorizontalPixelSize value="0.35273367"/>
<VerticalPixelSize value="0.35273367"/>
</Dimension>

PNG after:

<Dimension>
<PixelAspectRatio value="1.0"/>
<ImageOrientation value="Normal"/>
<HorizontalPixelSize value="2.8328612"/>
<VerticalPixelSize value="2.8328612"/>
</Dimension>

the interest thing is that 2.8328612 x 0.35273367 = 0.9999...

May be important point: I don't see where do you specify resolution units.

note that for png you can specify only meters or undefined as units.
for jpeg and tiff - cm, inch or undefined.

The general weakness of ImageIO is that it does not allow direct access to
metadata.
So if you want just to change resolution you have to decode / encode image.
For JPEG it may cause quality lost.

For such things you may use Imagero - it allows to change any metadata
"in-place".

Andrey
 
D

dsjoblom

Marian said:
Hi!

I've posted the same question on the imageio-interest mailing list, but
since that seem pretty much defunct, and our client needs a quick solution
to this problem, I'm trying my luck here as well.

I'm trying to change the DPI settings of an image (png, jpg or tiff -
using the jar from JAI for tiff decoding). I'm doing this by getting the
metadata, changing the entries in the DOM tree, and re-setting it to the
metadata. However, I don't get the expected results, so I reduced my
code to the basic part which fails, and apparently I'm having problems
with getAsTree and setFromTree. Basically, what I'm doing now is this:

// ImageReader reader = something;
IIOMetadata metaData = reader.getImageMetadata(reader.getMinIndex());
// output the old metadata to System.out (contains correct values)
Node root = metaData.getAsTree("javax_imageio_1.0"); // (still contains
correct values)
metaData.setFromTree("javax_imageio_1.0", root); // (root contains
correct values)
// output the new metadata to System.out (this has the wrong values)

For all three formats (png, jpg, tiff), I get different results.in the
Dimension nodes. The most problematic is JPEG, which can't seem to read
the node's values at all. PNG changes my pixel size values. Only TIFF
seems to keep the correct values.

Here are the contents of the Dimension nodes for the various formats:

PNG before:

<Dimension>
<PixelAspectRatio value="1.0"/>
<ImageOrientation value="Normal"/>
<HorizontalPixelSize value="0.35273367"/>
<VerticalPixelSize value="0.35273367"/>
</Dimension>

PNG after:

<Dimension>
<PixelAspectRatio value="1.0"/>
<ImageOrientation value="Normal"/>
<HorizontalPixelSize value="2.8328612"/>
<VerticalPixelSize value="2.8328612"/>
</Dimension>

Any hints on what I'm doing wrong?

You aren't doing anything wrong. You've found a couple of bugs in
ImageIO. I have located and identified the PNG one. Here's what happens
(you can verify this by checking out the source code for
com.sun.imageio.plugins.png.PNGMetadata):

The basic issue is a conversion error between the native PNG pHYs
metadata elements and the standard XML format. Every time you get an
XML element, the pHYs metadata is used as a source and converted to a
node, and every time you merge a node, the contents of the node are
stored internally as pHYs metadata. Ok, so I'll step through the
different stages for the horizontal pixel size case, the vertical case
is analoguous.

Step 1. You read the metadata with reader.getImageMetaData. The
metadata is stored internally in the PNGMetadata class as fields,
including fields for the pHYs elements.

Step 2. You create a standard XML tree out of the metadata using
getAsTree. The pHYs_pixelsPerUnitXAxis field is converted to a
HorizontalPixelSize node in the Dimension subtree of the XML metadata.
The HPS value is set by

node.setAttribute("value",
Float.toString(1000.0F/pHYs_pixelsPerUnitXAxis));

in method getStandardDimensionNode. So the first time you print the xML
metadata, you get "0.35273367" as HorizontalPixelSize . So, originally,
pHYs_pixelsPerUnitXAxis was about 2835.

Step 3. You merge the metadata tree using setFromTree. This time, the
values in the nodes are converted back to the pHYs_pixelsPerUnitXXX
fields. This is achieved through

pHYs_pixelsPerUnitXAxis = (int)(width*1000 + 0.5F);

in function mergeStandardTree, where width is the width read from the
node (in this case "0.35273367"). Ok. So this is where things go wrong.
Obviously, the programmer was trying to do the inverse operation of the
conversion in step 2, but the correct inverse operation is not
width*1000, but rather 1000 / width.

Step 4. You read back the merged tree again, like in step 2. But now,
because of the wrong conversion in step 3, pHYs_pixelsPerUnitXAxis
doesn't contain the same value used in step 2.

I suspect the JPEG metadata format implementation may have some similar
error, but I'm not too familiar with JPEG (the reason I happened to
find the PNG bug with relatively little effort is that I was inspecting
the PNG metadata code just last week when I found another bug which
affected me personally).

HTH,
Daniel Sjöblom

PS. You should consider reporting this bug to Sun.
 
M

Marian Schedenig

Andrey said:
The general weakness of ImageIO is that it does not allow direct access to
metadata.
So if you want just to change resolution you have to decode / encode
image. For JPEG it may cause quality lost.

For such things you may use Imagero - it allows to change any metadata
"in-place".

Thanks for the hints. The problem was that this is a last-minute fix for our
client. I managed to get it running with JMagick now; I just hope we won't
get into too much trouble when deploying the native libs on the target
server. :)

(Regarding quality loss: I agree; however, I was the one who insisted on
changing the DPI instead of resizing the entire image on a pixel basis, so
we can live with that)

Thx,
Marian.
 
M

Marian Schedenig

You aren't doing anything wrong. You've found a couple of bugs in
ImageIO. I have located and identified the PNG one.

Interesting. That was my first thought, but I didn't believe that both JPEG
and PNG would have so obvious problems. So the thin documentation led me to
assume I was just doing something the wrong way.
I suspect the JPEG metadata format implementation may have some similar
error, but I'm not too familiar with JPEG (the reason I happened to
find the PNG bug with relatively little effort is that I was inspecting
the PNG metadata code just last week when I found another bug which
affected me personally).

I suspect the JPEG bit is a bug converting floating point Strings back to
float or double values. It might even be related to my locale, since the
German format switches dots and commas in floating point numbers.
PS. You should consider reporting this bug to Sun.

I'll try to do that at work tomorrow. Thanks for your detailed info (I
didn't have time to look into the sources myself). As I already mentioned
in my other post, I switched to JMagick, which now works fine. I'll still
see if I can report that bug though.

Cheers,
Marian.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top