Opened 6 years ago

Closed 6 years ago

#3847 closed enhancement (fixed)

JPEG2000: Adding support for display resolution metadata

Reported by: Coatman Owned by: warmerdam
Priority: normal Milestone: 1.8.1
Component: GDAL_Raster Version: 1.7.3
Severity: normal Keywords: JP2KAK JPEG2000 resolution
Cc:

Description

The TIFF standard provides 3 tags to define how an image should be display or printed 282=XResolution, 283=YResolution, 296=ResolutionUnit? With tag 282=200, tag 283=200, and tag 296=3, a TIFF image will be printed with 200 pixels per centimeter in X, and 200 pixels per centimeter in Y.

JPEG2000 provides a similar capability, but uses the concept of JP2 boxes, instead of TIFF tags. I find the clearest explanation of the JPEG2000 Display Resolution Box is in the book JPEG2000 Image Compression Fundamentals, Standards, and Practice by David Taubman, Chapter 14, page 589, under resd = Display Resolution Box

The differences between display resolution TIFF tags and display resolution JPEG2000 boxes are

  1. While TIFF allows the tag 296=ResolutionUnit? to be of one 3 values: 1 = none, 2 = inch, 3 = centimeter, in JPEG2000 what would be the "JP2ResolutionUnit" is always meters (not centimeters), and hence "JP2ResolutionUnit" is not needed, and does not exist.
  1. JPEG2000 specifies the X and Y display resolution values via the equation (resolution numerator / resolution denominator) x resolution exponent. For example, with resolution numerator = 2, resolution denominator = 1, and resolution exponent = 4, the equation is (2 / 1) * (10*10*10*10) = 2 * 10000 = 20000 pixels/meter = 200 pixels/centimeter.

In Kakadu version 6.4.1, I see in

  1. apps/compressed_io/jpx.h, on line 5801, a reference to resd = Display Resolution Box
  1. apps/image/image_in.cpp (that reads in a TIFF and writes out a JP2), beginning at line 2163, 14 lines of code related to TIFF/JP2 display resolution
  1. apps/image/image_out.cpp (that reads in a JP2 and writes out a TIFF), beginning at line 1866, 14 lines of code related to JP2/TIFF display resolution

Beginning with Kakadu version 5.1.0, released 2006/11/10, kdu_compress and kdu_expand automatically support display resolution metadata using TIFF tags and JPEG2000 boxes.

I notice that using GDAL 1.7.3, released 2010/11/10, with gdal_translate -of GTiff in.tif out.tif, when in.tif includes TIFF tags 282, 283, and 296, then out.tif also includes tags 282, 283, and 296. This is good.

However, when gdal_translate reads in a TIFF, writes out a lossless or reversibly compressed JP2, using JP2KAK and JPEG2000, and then gdal_translate reads in that JP2, and writes out a TIFF, then tags 282, 283, and 296 are missing from the final .tif file, and I suspect the .jp2 file written by gdal_translate does not contain a JP2 Display Resolution Box.

gdal_translate -of JP2KAK -co QUALITY=100 in.tif out_gdal_173_JP2KAK.jp2

gdal_translate -of GTiff out_gdal_173_JP2KAK.jp2 out_gdal_173_JP2KAK.tif

gdal_translate -of JPEG2000 -co QUALITY=100 in.tif out_gdal_173_JPEG2000.jp2

gdal_translate -of GTiff out_gdal_173_JPEG2000.jp2 out_gdal_173_JPEG2000.tif

I suggest consideration be given to adding in gdal_translate support for display resolution metadata

  1. when reading from .tif and writing to .jp2
  1. when reading from .jp2 and writing to .tif

Attachments (1)

dcua_reduce10_200ppcm_kdu_641.jp2 (75.2 KB) - added by Coatman 6 years ago.
dcua_reduce10_200ppcm_kdu_641.jp2 includes a JP2 resd box

Download all attachments as: .zip

Change History (14)

Changed 6 years ago by Coatman

dcua_reduce10_200ppcm_kdu_641.jp2 includes a JP2 resd box

comment:1 Changed 6 years ago by Coatman

The sample file dcua_reduce10_200ppcm_kdu_641.jp2, created with Kakadu version 6.4.1 kdu_compress, is a JPEG2000 image that contains a JP2 resd Display Resolution Box that is set for 20000 pixels per meter = 200 pixels per centimeter, and a JP2 georeferencing metadata box. Below is an in depth analysis of the 10 data bytes in the JP2 resd box.

JP2 resd box, 10 bytes
19 9a 80 00 19 9a 80 00 05 05
Vertical   Numerator   (2 bytes) = 19 9a =  6554
Vertical   Denominator (2 bytes) = 80 00 = 32768
Horizontal Numerator   (2 bytes) = 19 9a =  6554
Horizontal Denominator (2 bytes) = 80 00 = 32768
Vertical   Exponent    (1 byte)  = 05    =  10^5 = 100000
Horizontal Exponent    (1 byte)  = 05    =  10^5 = 100000
(6554 / 32768)     * (10^5) = 
0.2000122070312500 * 100000 =
  20001.2207031250 pixels per meter

comment:2 Changed 6 years ago by Coatman

The 36 lines of code that Kakadu 6.4.1 uses to create the 6 values (Ver Num, Ver Den, Hor Num, Hor Den, Ver Exp, Hor Exp) for the 10 byte JP2 resd box are in apps/jp2/jp2.cpp, within j2_resolution::save_sub_box, that begins at line 4141.

comment:3 Changed 6 years ago by Coatman

In Kakadu 6.4.1, apps/jp2/jp2.cpp, within j2_resolution::save_sub_box, I found that changing two lines

from
old line 4153  v_den = 1<<15;
old line 4158  h_den = 1<<15;
to
new line 4153  v_den = (1<<16)-1;
new line 4158  h_den = (1<<16)-1;

changes the 2-byte resolution display denominator from 32768 to 65535, and as a result the values Kakadu computes and then stores in the JP2 resd box are more accurate. For example, when reading in a TIFF image with X display resolution = Y display resolution = 200.000 pixles/cm, using 32768 as the denominator, kdu_compress creates a JP2 resd box with the value 200.012 pixels/cm, but when using 65535 as the denominator, kdu_compress creates a JP2 resd box with the value 200.000 pixels/cm. I do not see any disadvantage to changing the vertical and horizontal denominator values from 32768 to 65535.

comment:4 Changed 6 years ago by Coatman

In Kakadu 6.4.1, the 36 lines of code for the jp2_resolution::save_sub_box are in apps/jp2/jp2.cpp, beginning at line 4141.

comment:5 Changed 6 years ago by warmerdam

I spent 40 minutes on this, but it was still pretty confusing. I'm setting this aside though I have no particular objection to the jp2kak driver return TIFFTAG_ resolution metadata and using it to set the resolution box.

comment:6 Changed 6 years ago by Coatman

To define how an image should be displayed when printed, TIFF provides 3 metadata tags 282=XResolution, 283=YResolution, 296=ResolutionUnit?. Similar in functionality to those TIFF metadata tags, JPEG2000 provides a resd (RESolution Display) metadata box, which is always 10 bytes, and organized like this.

JP2 resd box, 10 bytes
2 bytes for Vertical   Numerator
2 bytes for Vertical   Denominator
2 bytes for Horizontal Numerator
2 bytes for Horizontal Denominator
1 byte  for Vertical   Exponent
1 byte  for Horizontal Exponent

JP2 resd boxes are a JPEG2000 thing, not a Kakadu only thing. Beginning with Kakadu version 5.1.0, released 2006/11/10, kdu_compress and kdu_expand have automatically supported RESolution Display metadata using TIFF tags and JPEG2000 resd boxes. Using GDAL, there is no support for JP2 resd boxes, and this RESolution Display metadata is lost. It would be nice for GDAL to include support for the RESolution Display metadata through TIFF tags and JP2 resd boxes.

comment:7 Changed 6 years ago by Coatman

Because JPEG2000 supports reversible (or lossless) compression, it is convenient to read in a TIFF image, write out a reversibly compressed JPEG2000 image, and then read in that JPEG2000 and write out a TIFF with pixel values identical to the original TIFF image. Using GDAL gdal_translate for this preserves the pixels, the basic metadata (for example, number of columns and rows), the georeferencing metadata (for example, ground resolution). Not preserved, and lost, is the display resolution metadata that determines how the final TIFF image is printed. Perhaps the original TIFF was to be displayed / printed at 200 pixels per centimeter in X and Y, or maybe at 600 pixels per inch in X and Y? This resolution display metadata has been lost.

Beginning with Kakadu version 5.1.0, released 2006/11/10 (> 4 years ago), kdu_compress and kdu_expand automatically support reading and writing display resolution metadata using the 3 TIFF resolution tags, and a JPEG200 JP2 resd (RESolution Display) box. Note that JPEG2000 JP2 resd (RESolution Display) metadata boxes are part of the JPEG2000 standard, and are not a Kakadu only thing. Kakadu's implementation is shown in the 36 lines of code that Kakadu 6.4.1 uses to create the 6 values (Ver Num, Ver Den, Hor Num, Hor Den, Ver Exp, Hor Exp) for the 10 byte JP2 resd box, in apps/jp2/jp2.cpp, within j2_resolution::save_sub_box, that begins at line 4141. I encourage GDAL to add support for preserving resolution display metadata by reading and writing JP2 resd boxes.

comment:8 Changed 6 years ago by warmerdam

Keywords: JP2KAK added; display metadata removed
Milestone: 1.8.11.9.0
Resolution: fixed
Status: newclosed

Implemented in trunk (r22402).

Notes:

  • If resd and resc boxes exist it will use the first regardless of which it is.
  • I haven't tested properly with the case of an aspect ratio between x and y resolution.
  • inputs in pixels/inch are automatically converted to pixels/cm on the fly when writing.
  • it is only implemented for the JP2KAK driver for now, though the reading aspect is part of GDALJP2Metadata and could be easy utilized in other drivers.

I am hesitant to apply this to a stable branch since it is a new feature, not a bug fix.

comment:9 Changed 6 years ago by warmerdam

There haven't been any apparent problems with this in trunk, so I have back ported it to the 1.8 branch in time for 1.8.1.

comment:10 Changed 6 years ago by warmerdam

Milestone: 1.9.01.8.1

comment:11 Changed 6 years ago by Even Rouault

Hum, I see that this introduces a new member (papszMetadata) in GDALJP2Metadata class which is declared as CPL_DLL, so technically this looks like an ABI change, although I doubt that any external code of GDAL would use this class, so this is probably OK

comment:12 Changed 6 years ago by warmerdam

Resolution: fixed
Status: closedreopened

Even,

Yes, this was my primary concern with introducing this change into 1.8. It won't affect application but it could cause serious problems for plugin drivers (jp2ecw, jp2kak, etc) that use the GDALJP2Metadata class. I'm going to take a look and see if I can come up with a safer way of handling this in 1.8. I don't want to break plugin compatability.

comment:13 Changed 6 years ago by warmerdam

Resolution: fixed
Status: reopenedclosed

I have adjusted the approach in 1.8 (r22650) so that papszGMLMetadata is used to hold the TIFFTAG metadata. This is somewhat impure but should be fine in this context.

I have also added a test for the resolution metadata (r22649).

I don't want to admit how long it took me to do this!

Note: See TracTickets for help on using tickets.