Skip to main content

Problem saving 12-bit ushort jpeg2000

5 replies [Last post]
elliot
Offline
Joined: 2004-04-11

Hi,

I'm having some trouble with the jpeg2000 ImageIO codec.

In a nutshell, I do this:

1) create a BufferedImage with ComponentColorModel,
PixelInterleavedSampleModel, single band, UShort data, with
12 bit values (0..4095)

2) display the image (looks fine)

3) save using ImageIO jpeg2000

4) reload with ImageIO

5) display (looks bad (12 bit data interpreted as 16))

If I print header info before saving and after re-loading,
I see that the bits-per-pixel
(color_model.getComponentSize( band=0 )
is 12 originally,
but 16 after re-loading..

Is there something I'm missing to have the bit depth
information preserved ??

Thanks in advance,

Elliot

The following program should demonstrate the issue...
(sorry, looks like the tabs got lost along the way)
=======================================================
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;

import java.awt.image.*;
import java.awt.Transparency;
import java.awt.Point;
import java.awt.color.ColorSpace;
import java.util.Hashtable;
import java.io.File;
import javax.imageio.ImageIO;

import javax.swing.*;
import javax.media.jai.*;
import javax.media.jai.widget.*;

/**
* Create 12 bit (in ushort) image data, and save to jpeg 2000,
* reload and see 16 bit conversion problem.
*/
public class Create12BitJ2k
{
//
// make a flat field 50% gray
//
public static void createPixelData( short[] pixels,
int bits_per_pixel,
int rows, int cols )
{
int max_value = (1 << bits_per_pixel) - 1;
short value = (short)(max_value/2);
for (int i=0; i< pixels.length; i++)
{
pixels[i] = value;
}
}

//
// create a ComponentColorModel, and then a pixel interleaved single
// band image.
//
public static RenderedImage createImage( int bits_per_pixel )
throws Exception
{
//
// create space & color model
//
int[] component_bpp = { bits_per_pixel };

ColorModel cm = new ComponentColorModel(
ColorSpace.getInstance( ColorSpace.CS_GRAY ),
component_bpp,
false,
false,
Transparency.OPAQUE,
DataBuffer.TYPE_USHORT );

//
// create image data
//
int rows=480;
int cols=640;
short[] pixels = new short[ rows*cols ];
createPixelData( pixels, bits_per_pixel, rows, cols );

//
// create a writable raster
//
DataBuffer buf = new DataBufferUShort( pixels, pixels.length );

int[] bankIndices = { 0 };
int[] bandOffsets = { 0 };

WritableRaster raster = Raster.createInterleavedRaster( buf,
cols,
rows,
cols,
1,
bandOffsets,
new Point(0,0) );

//
// create RenderedImage from ColorMap and Raster
//
RenderedImage image = new BufferedImage(cm,
raster,
false,
new Hashtable() );

return image;
}

//
// display selected header fields for a single band image
//
public static void printImageHeaderInfo( RenderedImage image )
throws Exception
{
System.err.println("rows = " + image.getHeight() );
System.err.println("cols = " + image.getWidth() );

SampleModel sm = image.getSampleModel();

System.err.println("sm class = " + sm.getClass().getName() );
System.err.println("sm sample size (band 0)= " + sm.getSampleSize(0) );
System.err.println("sm num bands = " + sm.getNumBands() );
System.err.println("sm data type = " + sm.getDataType() );

ColorModel cm = image.getColorModel();
System.err.println("cm class = " + cm.getClass().getName() );

System.err.println("cm num components = " + cm.getNumComponents() );
System.err.println("cm component 0 size = " + cm.getComponentSize(0) );
System.err.println("cm pixel size = " + cm.getPixelSize() );

ColorSpace cs = cm.getColorSpace();
System.err.println("cs class = " + cs.getClass().getName() );

System.err.println("cs num components = " + cs.getNumComponents() );
System.err.println("cs min value comp 0 = " + cs.getMinValue(0) );
System.err.println("cs max value comp 0 = " + cs.getMaxValue(0) );
System.err.println("cs name comp 0 = " + cs.getName(0) );
System.err.println("cs type = " + cs.getType() );
}

public static void main( String args[] )
{
try
{
//String output_type = args[0];
//String output_filename = args[1];
//int bits_per_pixel = Integer.parseInt( args[2] );

String output_type = "JPEG2000";
String output_filename = "test.j2k";
int bits_per_pixel = 12;

// create image
RenderedImage image = createImage( bits_per_pixel );

// display JAI header info before viewing and saving
printImageHeaderInfo( image );

// display image
JFrame window1;
ScrollingImagePanel panel1;
window1 = new JFrame("original image");
window1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel1 = new ScrollingImagePanel(image, 640, 480);
window1.getContentPane().add(panel1);
window1.pack();
window1.show();

// save to file
FileOutputStream ostream = new FileOutputStream( output_filename );
ImageIO.write( image, output_type, ostream);

// re-load from file into new image
FileInputStream istream = new FileInputStream( output_filename );
BufferedImage image2 = ImageIO.read( istream );

// print info
printImageHeaderInfo( image2 );

// display the image
JFrame window2;
ScrollingImagePanel panel2;
window2 = new JFrame("loaded from file");
window2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel2 = new ScrollingImagePanel( image2, 640, 480);
window2.getContentPane().add(panel2);
window2.pack();
window2.show();
}
catch ( Exception e )
{
System.err.println("error: " + e + "\n");
e.printStackTrace( System.err );
}
}
}

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
elliot
Offline
Joined: 2004-04-11

Thanks for responding!

The attached program creates an image which is
simply a flat field of value 2048, i.e.
~50% max brightness for a 12-bit image (0..4095)
it then uses that to create a BufferedImage which
is then saved with the j2k ImageIO writer.
So its pretty self-contained.

When the resulting file is read back, the data is
the same, but it's interpreted as 16 bit, so it displays
16x darker. Printing ColorModel.getComponentSize( 0 )
for the first and only band shows 12 initially,
but 16 after saving and re-loading.

I thought I'd screwed up the code when I first attached it,
but the copy I re-posted has the same issue.
For some reason, in addition to the tabs getting lost,
when I pasted the code into the form, the line
in the function createPixelData()
that says "pixels = value" should read "pixels[i] = value"
the "[i]" was somehow lost by something in the
clipboard/browser/httpd/cgi chain.

Thanks again.

Elliot

Brian Burkhalter

It looks as if both (Java and native) JPEG 2000 writer plug-ins set the bit
depth based on the sample size of the SampleModel which is determined by the
data type and therefore for ComponentSampleModels is always a multiple of 8.
This should probably use the ColorModel component size as you are doing. I
would suggest filing an issue (enhancement request) at

https://jai-imageio.dev.java.net/servlets/ProjectIssues

so that we don't forget about it. ;-) I am pretty sure that the JPEG 2000 file
format supports arbitrary bit depths up to its limit. I think it even supports
different bit depths for different components.

Brian

On Fri, 1 Jul 2005 jai-interest@javadesktop.org wrote:

> Thanks for responding!
>
> The attached program creates an image which is
> simply a flat field of value 2048, i.e.
> ~50% max brightness for a 12-bit image (0..4095)
> it then uses that to create a BufferedImage which
> is then saved with the j2k ImageIO writer.
> So its pretty self-contained.
>
> When the resulting file is read back, the data is
> the same, but it's interpreted as 16 bit, so it displays
> 16x darker. Printing ColorModel.getComponentSize( 0 )
> for the first and only band shows 12 initially,
> but 16 after saving and re-loading.
>
> I thought I'd screwed up the code when I first attached it,
> but the copy I re-posted has the same issue.
> For some reason, in addition to the tabs getting lost,
> when I pasted the code into the form, the line
> in the function createPixelData()
> that says "pixels = value" should read "pixels[i] = value"
> the "[i]" was somehow lost by something in the
> clipboard/browser/httpd/cgi chain.
>
> Thanks again.
>
> Elliot
> ---
> [Message sent by forum member 'elliot' (Elliot Lord)]
>
> http://www.javadesktop.org/forums/thread.jspa?messageID=96708&#96708
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: interest-unsubscribe@jai.dev.java.net
> For additional commands, e-mail: interest-help@jai.dev.java.net
>
>

----------------
Brian Burkhalter
Advanced Development/Media & Entertainment
Market Development Engineering
Sun Microsystems, Inc.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This email message is for the sole use of the intended recipient(s)
and may contain confidential and privileged information. Any
unauthorized review, use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai.dev.java.net
For additional commands, e-mail: interest-help@jai.dev.java.net

James Cheng

Please file it on the following page instead:

https://jai-imageio-core.dev.java.net/servlets/ProjectIssues

Thanks,
-James

On 07/01/05 04:42 PM, Brian Burkhalter wrote:
> It looks as if both (Java and native) JPEG 2000 writer plug-ins set the bit
> depth based on the sample size of the SampleModel which is determined by the
> data type and therefore for ComponentSampleModels is always a multiple of 8.
> This should probably use the ColorModel component size as you are doing. I
> would suggest filing an issue (enhancement request) at
>
> https://jai-imageio.dev.java.net/servlets/ProjectIssues
>
> so that we don't forget about it. ;-) I am pretty sure that the JPEG 2000 file
> format supports arbitrary bit depths up to its limit. I think it even supports
> different bit depths for different components.
>
> Brian
>
> On Fri, 1 Jul 2005 jai-interest@javadesktop.org wrote:
>
>
>>Thanks for responding!
>>
>>The attached program creates an image which is
>>simply a flat field of value 2048, i.e.
>>~50% max brightness for a 12-bit image (0..4095)
>>it then uses that to create a BufferedImage which
>>is then saved with the j2k ImageIO writer.
>>So its pretty self-contained.
>>
>>When the resulting file is read back, the data is
>>the same, but it's interpreted as 16 bit, so it displays
>>16x darker. Printing ColorModel.getComponentSize( 0 )
>>for the first and only band shows 12 initially,
>>but 16 after saving and re-loading.
>>
>>I thought I'd screwed up the code when I first attached it,
>>but the copy I re-posted has the same issue.
>>For some reason, in addition to the tabs getting lost,
>>when I pasted the code into the form, the line
>>in the function createPixelData()
>>that says "pixels = value" should read "pixels[i] = value"
>>the "[i]" was somehow lost by something in the
>>clipboard/browser/httpd/cgi chain.
>>
>>Thanks again.
>>
>>Elliot
>>---
>>[Message sent by forum member 'elliot' (Elliot Lord)]
>>
>>http://www.javadesktop.org/forums/thread.jspa?messageID=96708𗧄
>>
>>---------------------------------------------------------------------
>>To unsubscribe, e-mail: interest-unsubscribe@jai.dev.java.net
>>For additional commands, e-mail: interest-help@jai.dev.java.net
>>
>>
>
>
> ----------------
> Brian Burkhalter
> Advanced Development/Media & Entertainment
> Market Development Engineering
> Sun Microsystems, Inc.
>
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> This email message is for the sole use of the intended recipient(s)
> and may contain confidential and privileged information. Any
> unauthorized review, use, disclosure or distribution is prohibited.
> If you are not the intended recipient, please contact the sender by
> reply email and destroy all copies of the original message.
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: interest-unsubscribe@jai.dev.java.net
> For additional commands, e-mail: interest-help@jai.dev.java.net
>

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai.dev.java.net
For additional commands, e-mail: interest-help@jai.dev.java.net

elliot
Offline
Joined: 2004-04-11

Sorry, there was a typo in one line of the originally
attached program. This one should compile and run with:
javac Create12BitJ2k.java
java Create12BitJ2k

Thanks again if someone can help..

Again, jpeg2000 write of 12 bit grayscale image
appears to change bits-per-pixel to 16.

import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;

import java.awt.image.*;
import java.awt.Transparency;
import java.awt.Point;
import java.awt.color.ColorSpace;
import java.util.Hashtable;
import java.io.File;
import javax.imageio.ImageIO;

import javax.swing.*;
import javax.media.jai.*;
import javax.media.jai.widget.*;

/**
* Create 12 bit (in ushort) image data, and save to jpeg 2000,
* reload and see 16 bit conversion problem.
*/
public class Create12BitJ2k
{
//
// make a flat field 50% gray
//
public static void createPixelData( short[] pixels,
int bits_per_pixel,
int rows, int cols )
{
int max_value = (1 << bits_per_pixel) - 1;
short value = (short)(max_value/2);
for (int i=0; i< pixels.length; i++)
{
pixels[i] = value;
}
}

//
// create a ComponentColorModel, and then a pixel interleaved single
// band image.
//
public static RenderedImage createImage( int bits_per_pixel )
throws Exception
{
//
// create space & color model
//
int[] component_bpp = { bits_per_pixel };

ColorModel cm = new ComponentColorModel(
ColorSpace.getInstance( ColorSpace.CS_GRAY ),
component_bpp,
false,
false,
Transparency.OPAQUE,
DataBuffer.TYPE_USHORT );

//
// create image data
//
int rows=480;
int cols=640;
short[] pixels = new short[ rows*cols ];
createPixelData( pixels, bits_per_pixel, rows, cols );

//
// create a writable raster
//
DataBuffer buf = new DataBufferUShort( pixels, pixels.length );

int[] bankIndices = { 0 };
int[] bandOffsets = { 0 };

WritableRaster raster = Raster.createInterleavedRaster( buf,
cols,
rows,
cols,
1,
bandOffsets,
new Point(0,0) );

//
// create RenderedImage from ColorMap and Raster
//
RenderedImage image = new BufferedImage(cm,
raster,
false,
new Hashtable() );

return image;
}

//
// display selected header fields for a single band image
//
public static void printImageHeaderInfo( RenderedImage image )
throws Exception
{
System.err.println("rows = " + image.getHeight() );
System.err.println("cols = " + image.getWidth() );

SampleModel sm = image.getSampleModel();

System.err.println("sm class = " + sm.getClass().getName() );
System.err.println("sm sample size (band 0)= " + sm.getSampleSize(0) );
System.err.println("sm num bands = " + sm.getNumBands() );
System.err.println("sm data type = " + sm.getDataType() );

ColorModel cm = image.getColorModel();
System.err.println("cm class = " + cm.getClass().getName() );

System.err.println("cm num components = " + cm.getNumComponents() );
System.err.println("cm component 0 size = " + cm.getComponentSize(0) );
System.err.println("cm pixel size = " + cm.getPixelSize() );

ColorSpace cs = cm.getColorSpace();
System.err.println("cs class = " + cs.getClass().getName() );

System.err.println("cs num components = " + cs.getNumComponents() );
System.err.println("cs min value comp 0 = " + cs.getMinValue(0) );
System.err.println("cs max value comp 0 = " + cs.getMaxValue(0) );
System.err.println("cs name comp 0 = " + cs.getName(0) );
System.err.println("cs type = " + cs.getType() );
}

public static void main( String args[] )
{
try
{
//String output_type = args[0];
//String output_filename = args[1];
//int bits_per_pixel = Integer.parseInt( args[2] );

String output_type = "JPEG2000";
String output_filename = "test.j2k";
int bits_per_pixel = 12;

// create image
RenderedImage image = createImage( bits_per_pixel );

// display JAI header info before viewing and saving
printImageHeaderInfo( image );

// display image
JFrame window1;
ScrollingImagePanel panel1;
window1 = new JFrame("original image");
window1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel1 = new ScrollingImagePanel(image, 640, 480);
window1.getContentPane().add(panel1);
window1.pack();
window1.show();

// save to file
FileOutputStream ostream = new FileOutputStream( output_filename );
ImageIO.write( image, output_type, ostream);

// re-load from file into new image
FileInputStream istream = new FileInputStream( output_filename );
BufferedImage image2 = ImageIO.read( istream );

// print info
printImageHeaderInfo( image2 );

// display the image
JFrame window2;
ScrollingImagePanel panel2;
window2 = new JFrame("loaded from file");
window2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel2 = new ScrollingImagePanel( image2, 640, 480);
window2.getContentPane().add(panel2);
window2.pack();
window2.show();
}
catch ( Exception e )
{
System.err.println("error: " + e + "\n");
e.printStackTrace( System.err );
}
}
}

Brian Burkhalter

Do you have a sample data set available for testing?

Brian

On Wed, 29 Jun 2005 jai-interest@javadesktop.org wrote:

> Sorry, there was a typo in one line of the originally
> attached program. This one should compile and run with:
> javac Create12BitJ2k.java
> java Create12BitJ2k
>
> Thanks again if someone can help..
>
> Again, jpeg2000 write of 12 bit grayscale image
> appears to change bits-per-pixel to 16.
>
>
>
>
> import java.io.InputStream;
> import java.io.FileOutputStream;
> import java.io.FileInputStream;
>
> import java.awt.image.*;
> import java.awt.Transparency;
> import java.awt.Point;
> import java.awt.color.ColorSpace;
> import java.util.Hashtable;
> import java.io.File;
> import javax.imageio.ImageIO;
>
> import javax.swing.*;
> import javax.media.jai.*;
> import javax.media.jai.widget.*;
>
> /**
> * Create 12 bit (in ushort) image data, and save to jpeg 2000,
> * reload and see 16 bit conversion problem.
> */
> public class Create12BitJ2k
> {
> //
> // make a flat field 50% gray
> //
> public static void createPixelData( short[] pixels,
> int bits_per_pixel,
> int rows, int cols )
> {
> int max_value = (1 << bits_per_pixel) - 1;
> short value = (short)(max_value/2);
> for (int i=0; i< pixels.length; i++)
> {
> pixels[i] = value;
> }
> }
>
> //
> // create a ComponentColorModel, and then a pixel interleaved single
> // band image.
> //
> public static RenderedImage createImage( int bits_per_pixel )
> throws Exception
> {
> //
> // create space & color model
> //
> int[] component_bpp = { bits_per_pixel };
>
> ColorModel cm = new ComponentColorModel(
> ColorSpace.getInstance( ColorSpace.CS_GRAY ),
> component_bpp,
> false,
> false,
> Transparency.OPAQUE,
> DataBuffer.TYPE_USHORT );
>
> //
> // create image data
> //
> int rows=480;
> int cols=640;
> short[] pixels = new short[ rows*cols ];
> createPixelData( pixels, bits_per_pixel, rows, cols );
>
> //
> // create a writable raster
> //
> DataBuffer buf = new DataBufferUShort( pixels, pixels.length );
>
> int[] bankIndices = { 0 };
> int[] bandOffsets = { 0 };
>
> WritableRaster raster = Raster.createInterleavedRaster( buf,
> cols,
> rows,
> cols,
> 1,
> bandOffsets,
> new Point(0,0) );
>
> //
> // create RenderedImage from ColorMap and Raster
> //
> RenderedImage image = new BufferedImage(cm,
> raster,
> false,
> new Hashtable() );
>
> return image;
> }
>
>
> //
> // display selected header fields for a single band image
> //
> public static void printImageHeaderInfo( RenderedImage image )
> throws Exception
> {
> System.err.println("rows = " + image.getHeight() );
> System.err.println("cols = " + image.getWidth() );
>
> SampleModel sm = image.getSampleModel();
>
> System.err.println("sm class = " + sm.getClass().getName() );
> System.err.println("sm sample size (band 0)= " + sm.getSampleSize(0) );
> System.err.println("sm num bands = " + sm.getNumBands() );
> System.err.println("sm data type = " + sm.getDataType() );
>
> ColorModel cm = image.getColorModel();
> System.err.println("cm class = " + cm.getClass().getName() );
>
> System.err.println("cm num components = " + cm.getNumComponents() );
> System.err.println("cm component 0 size = " + cm.getComponentSize(0) );
> System.err.println("cm pixel size = " + cm.getPixelSize() );
>
> ColorSpace cs = cm.getColorSpace();
> System.err.println("cs class = " + cs.getClass().getName() );
>
> System.err.println("cs num components = " + cs.getNumComponents() );
> System.err.println("cs min value comp 0 = " + cs.getMinValue(0) );
> System.err.println("cs max value comp 0 = " + cs.getMaxValue(0) );
> System.err.println("cs name comp 0 = " + cs.getName(0) );
> System.err.println("cs type = " + cs.getType() );
> }
>
>
> public static void main( String args[] )
> {
> try
> {
> //String output_type = args[0];
> //String output_filename = args[1];
> //int bits_per_pixel = Integer.parseInt( args[2] );
>
> String output_type = "JPEG2000";
> String output_filename = "test.j2k";
> int bits_per_pixel = 12;
>
> // create image
> RenderedImage image = createImage( bits_per_pixel );
>
> // display JAI header info before viewing and saving
> printImageHeaderInfo( image );
>
> // display image
> JFrame window1;
> ScrollingImagePanel panel1;
> window1 = new JFrame("original image");
> window1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
> panel1 = new ScrollingImagePanel(image, 640, 480);
> window1.getContentPane().add(panel1);
> window1.pack();
> window1.show();
>
>
> // save to file
> FileOutputStream ostream = new FileOutputStream( output_filename );
> ImageIO.write( image, output_type, ostream);
>
>
> // re-load from file into new image
> FileInputStream istream = new FileInputStream( output_filename );
> BufferedImage image2 = ImageIO.read( istream );
>
> // print info
> printImageHeaderInfo( image2 );
>
> // display the image
> JFrame window2;
> ScrollingImagePanel panel2;
> window2 = new JFrame("loaded from file");
> window2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
> panel2 = new ScrollingImagePanel( image2, 640, 480);
> window2.getContentPane().add(panel2);
> window2.pack();
> window2.show();
> }
> catch ( Exception e )
> {
> System.err.println("error: " + e + "\n");
> e.printStackTrace( System.err );
> }
> }
> }
> ---
> [Message sent by forum member 'elliot' (Elliot Lord)]
>
> http://www.javadesktop.org/forums/thread.jspa?messageID=95981&#95981
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: interest-unsubscribe@jai.dev.java.net
> For additional commands, e-mail: interest-help@jai.dev.java.net
>
>

----------------
Brian Burkhalter
Advanced Development/Media & Entertainment
Market Development Engineering
Sun Microsystems, Inc.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This email message is for the sole use of the intended recipient(s)
and may contain confidential and privileged information. Any
unauthorized review, use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

---------------------------------------------------------------------
To unsubscribe, e-mail: interest-unsubscribe@jai.dev.java.net
For additional commands, e-mail: interest-help@jai.dev.java.net