Java Forum / General / March 2007
confused about image colorspaces
LC's NoSpam Newsreading account - 05 Mar 2007 16:46 GMT I'd like to write a small applet to display astronomical images. As customary in astronomy, for us an image is an array img(x,y) of real values in physical units, and colour is a mere way of representing them. We usually scale the values img(x,y) according to user choice (linear scale, log scale, histogram equalization) to normalized values i(x,y) e.g. in the range [0,255] or [0.0,1.0] and use the latter as index into a lookup table (LUT) of R G B values (the LUT is also chosen by the user).
I have code which does that in Xlib, in Postscript (using [/Indexed /DeviceRGB 255 {LUT} ] setcolorspace) and in RSI IDL (using device,decomposed=0, and then setting tvlct,r,g,b where r,g,b are arrays of R G B levels). I'd like to do it in java as well in the same way. I am confused by the API documentation about images and color spaces which seems overshooting for my needs.
I tried therefore to define a small LUT of 10 colours (as a test) with one-byte depth and then to use this code to fill the image
IndexColorModel cm = new IndexColorModel(8,10, red,gre,blu) ; BufferedImage img = new BufferedImage(30,10,BufferedImage.TYPE_BYTE_INDEXED,cm) for (int jx=0; jx<30 ; jx++) { for (int jy=0; jy<10 ; jy++) { img.setRGB(jx,jy,jy) ; } } g2.drawImage(img,myx,myy,null) ;
With the assignment I do in setRGB I'd expect to get a 30x10 image with 10 horizontal stripes each one of a different colour as defined in my table. But what I get is always a gray rectangle, and the same occurs if I do img.setRGB(jx,jy,CONSTANT) ;
What am I doing wrong ?
As example this is the equivalent IDL codes (the Java R G B values will be scaled in the range -128 to 127 instead of 0-255)
IDL> red=[0,255,255, 0, 0,255,255, 0,128, 0] IDL> gre=[0,255, 0,255, 0,255, 0,255,128,128] IDL> blu=[0,255, 0, 0,255, 0,255,255,128, 0] IDL> tvlct,red,gre,blu IDL> img=fltarr(30,10) IDL> for i=0,29 do begin & for j=0,9 do begin & img(i,j)=j & endfor & endfor IDL> tv,img
 Signature ---------------------------------------------------------------------- nospam@mi.iasf.cnr.it is a newsreading account used by more persons to avoid unwanted spam. Any mail returning to this address will be rejected. Users can disclose their e-mail address in the article if they wish so.
Mark Space - 05 Mar 2007 20:06 GMT > IndexColorModel cm = new IndexColorModel(8,10, red,gre,blu) ; > BufferedImage img = new [quoted text clipped - 10 lines] > table. But what I get is always a gray rectangle, and the same occurs if > I do img.setRGB(jx,jy,CONSTANT) ; I'm not an expert on this, but i don't see where you set up the index or the color LUT. Is it just in a different portion of the code?
Java images mostly mimic the historical graphics modes of PCs. Trying to set a byte depth with only ten colors when the system is expecting 256 may be an issue. I'd try setting all of them (256 colors) and see if that helps.
a24900@googlemail.com - 05 Mar 2007 22:29 GMT On Mar 5, 5:46 pm, LC's NoSpam Newsreading account <nos...@mi.iasf.cnr.it> wrote:
> IndexColorModel cm = new IndexColorModel(8,10, red,gre,blu) ; > BufferedImage img = new [quoted text clipped - 3 lines] > img.setRGB(jx,jy,jy) ; > }}
> What am I doing wrong ? Each component in setRGB() has a value range from 0...255. You generate colors from (0,0,0) to (29,9,9) in the loop. The values are mapped to the nearest color in the LUT. Unfortunately, you neglected to show us the LUT. If you use the same LUT as in your IDL example, then the nearest color in the LUT is likely (0,0,0), even for your largest value (29,9,9). You asked for black, you got black.
LC's NoSpam Newsreading account - 06 Mar 2007 09:21 GMT > > IndexColorModel cm = new IndexColorModel(8,10, red,gre,blu) ; > > BufferedImage(30,10,BufferedImage.TYPE_BYTE_INDEXED,cm)
> > img.setRGB(jx,jy,jy) ;
> Each component in setRGB() has a value range from 0...255. You > generate colors from (0,0,0) to (29,9,9) in the loop. The values are > mapped to the nearest color in the LUT. Unfortunately, you neglected > to show us the LUT. If you use the same LUT as in your IDL example, > then the nearest color in the LUT is likely (0,0,0), even for your > largest value (29,9,9). You asked for black, you got black. Hmm ... there should be a basic misunderstanding here ... the API doc says "setRGB(int x, int y, int rgb) Sets a pixel in this BufferedImage to the specified RGB value". So I assumed that the first two values were SPATIAL coordinates not R and G values ... the API doc does not say how to code an "int rgb", so I assumed that was just an INDEX in the LUT i.e. a number from 0 to n-1
What I wanted to achieve was an image with 10 rows of 30 pixels
a row of 30 0's a row of 30 1's ... a row of 30 9's
You and also Mark Space say "I don't see where you set up the index or the color LUT, is it just in a different portion of the code?"
Well, to me the equivalent of the IDL tvlct,red,gre,blu was
> > IndexColorModel cm = new IndexColorModel(8,10, red,gre,blu) ; I assumed "10" meant the LUT in this test has only 10 colours (which should be possible, why not ?) and "8" meant red, gre and blu were ranging -128,127.
Did I not understand how java LUTs work ?
I did not show the actual LUT (or what I intended to be it) because the actual numbers were not interesting, and the code was slightly long (or clumsy ?). Essentially I start with three integer arrays (IDL-style) in range 0-255
int[] red1={0,255,255, 0, 0,255,255, 0,128, 0} ; int[] gre1={0,255, 0,255, 0,255, 0,255,128,128} ; int[] blu1={0,255, 0, 0,255, 0,255,255,128, 0} ;
then cast them into byte arrays red,gre,blu which I use in the call to IndexColorModel. I'm slightly upset by the fact that java byte values are signed in the range -128,127 ... my clumsy conversion code is in a loop and similar to this
int ti=red1[i]-128 ; Integer t=new Integer(ti) ; red[i]=t.byteValue() ;
Anyhow I've found a different way of doing it in an example : This uses my integer LUT above, and generates the horizontal stripe test image I want.
for (int jy=0; jy<10 ; jy++) { for (int jx=0; jx<30 ; jx++) { pix[index++] = (255<<24) | (red1[jy]<<16) | (gre1[jy]<<8) | blu1[jy] } } Image ima = createImage(new MemoryImageSource(30,10,pix,0,30)); g2.drawImage(ima,myx,myy,null) ;
Replacing red1[jy] gre1[jy] blu1[jy] with red1(scaleddata[jx,jy]) and alike where my original data are scaled via an ITT to values 0 to n-1 where n is the size of the red1,gre1,blu1 arrays will implement the LUT.
But I hoped to find a ready-made solution for that (a bit like my Postscript level 2 solution does it in the Postscript language and sends an array of scaled data, while my Postscript level 1 solution does it in my code and sends three arrays of R G B.
On Mon, 5 Mar 2007, Mark Space wrote:
> Java images mostly mimic the historical graphics modes of PCs. > Trying to set a byte depth with only ten colors when the system is > expecting 256 may be an issue. I'd try setting all of them (256 > colors) and see if that helps. More familiar with oldtimer things like Ramtek, Sigma ARGS, DeAnza :-) Or Xlib and IDL. I've always used LUTs of less than 256 colours when I needed only few (mainly for vector graphics than image display), taking care I did not address colours beyond the LUT size.
 Signature ---------------------------------------------------------------------- nospam@mi.iasf.cnr.it is a newsreading account used by more persons to avoid unwanted spam. Any mail returning to this address will be rejected. Users can disclose their e-mail address in the article if they wish so.
Chris Uppal - 06 Mar 2007 17:59 GMT > the API doc > says "setRGB(int x, int y, int rgb) Sets a pixel in this BufferedImage > to the specified RGB value". So I assumed that the first two values > were SPATIAL coordinates not R and G values ... the API doc does not say > how to code an "int rgb", so I assumed that was just an INDEX in the LUT > i.e. a number from 0 to n-1 The doc says:
Sets a pixel in this BufferedImage to the specified RGB value. The pixel is assumed to be in the default RGB color model, TYPE_INT_ARGB, and default sRGB color space. For images with an IndexColorModel, the index with the nearest color is chosen.
I'm no expert on the Image stuff, but as far as I can tell, you have two options (given that you are starting with input data which is already organised assuming a lookup table).
One is to use an "ordinary" BufferedImage -- not trying to specify a LUT colour model -- and then write RBG values into it, using your own colour-mapping logic to convert raw data into whatever RBG values. Thus the colour-mapping happens as the BufferedImage is written to.
The other is to start with your "raw" image data and lookup table, and then wrap suitable instances of ColorModel and Raster around them (using custom ColorModel and Raster subclasses if necessary), and then use them to create a BufferedImage. Thus the colour mapping happens when the BufferedImage is read from.
You seem to be trying to do both at once, and I can't see what the point is.
FWIW, the first option seems a lot simpler to me, but that's not to say that the second option may not have significant advantages which I don't know about.
-- chris
a24900@googlemail.com - 06 Mar 2007 20:27 GMT On Mar 6, 10:21 am, LC's NoSpam Newsreading account <nos...@mi.iasf.cnr.it> wrote:
> Hmm ... there should be a basic misunderstanding here ... the API doc > says "setRGB(int x, int y, int rgb) Sets a pixel in this BufferedImage > to the specified RGB value". So I assumed that the first two values > were SPATIAL coordinates not R and G values ... Yes that's right. But it doesn't change the principle issue.
> the API doc does not say > how to code an "int rgb", so I assumed that was just an INDEX in the LUT > i.e. a number from 0 to n-1 The documentation gives a hint: TYPE_INT_ARGB. That is 8 bit/ component, packed in the order alpha, red, green, blue packed into an int.
You actually asked for the nearest color in the LUT to alpha = 0, red = 0, green = 0, blue = 0 ... 9 That is even more close to black than my misinterpreted (29,9,9).
> You and also Mark Space say "I don't see where you set up the index or > the color LUT, is it just in a different portion of the code?" > > Well, to me the equivalent of the IDL tvlct,red,gre,blu was > > > > IndexColorModel cm = new IndexColorModel(8,10, red,gre,blu) ; What we both wanted to know is the contents of the red, gre, and blu arrays. The contents, byte for byte. Each one should be a byte[], with at least 10 elements.
> I assumed "10" meant the LUT in this test has only 10 colours (which > should be possible, why not ?) Yes, but we wanted to know how your particular LUT looks like. If you put in ten times black it is a LUT with ten entries, but don't expect to see very much.
> ranging -128,127. You seem to be obsessed with this range, which is entirely wrong. The 8 bit color component range is 0 ... 255. When encoded into a signed byte the range becomes 0 ... 127, -128 ... -1 In that particular order, not -128 ... 127!
> I did not show the actual LUT (or what I intended to be it) because the > actual numbers were not interesting, They are. If you happen to put ten times black into the LUT ...
> int[] red1={0,255,255, 0, 0,255,255, 0,128, 0} ; > int[] gre1={0,255, 0,255, 0,255, 0,255,128,128} ; > int[] blu1={0,255, 0, 0,255, 0,255,255,128, 0} ; > > then cast them into byte arrays You can't cast arrays.
> red,gre,blu which I use in the call to > IndexColorModel. I'm slightly upset by the fact that java byte values [quoted text clipped - 4 lines] > Integer t=new Integer(ti) ; > red[i]=t.byteValue() ; That is not casting. This is casting:
red[i] = (byte)red1[i];
And that casting would have work for the intended purpose. See why we want to see how you build the LUT arrays? It would anyhow be simpler to just define the byte arrays:
int[] red = { 0, (byte)255, (byte)255, 0, 0, (byte)255, (byte)255, 0, (byte)128, 0} ;
Sometimes the casting goes on people's nerves, so people also do things like
static final byte eo = (byte)128; static final byte ff = (byte)255; int[] red = { 0, ff, ff, 0, 0, ff, ff, 0, eo, 0} ;
LC's NoSpam Newsreading account - 07 Mar 2007 11:30 GMT > > the API doc does not say > > how to code an "int rgb", so I assumed that was just an INDEX in the LUT [quoted text clipped - 3 lines] > component, packed in the order alpha, red, green, blue packed into an > int. So that will be analogous to my second (working) example using MemoryImageSource(30,10,pix,0,30) with pix built e.g. as
pix[index++] = (255<<24) | (red1[jy]<<16) | (gre1[jy]<<8) | blu1[jy]
I thought the rgb value in setRGB was the index in the LUT. This looks not to be the case.
> > ranging -128,127. > > You seem to be obsessed with this range, which is entirely wrong. The > 8 bit color component range is 0 ... 255. When encoded into a signed > byte the range becomes 0 ... 127, -128 ... -1 In that particular > order, not -128 ... 127! http://java.sun.com/docs/books/tutorial/java/nutsandbolts/datatypes.html says "byte: The byte data type [...]. It has a minimum value of -128 and a maximum value of 127 (inclusive)." This misled me. The above masking-with-shifting into pix[] works with actual 0-255 values.
> > int[] red1={0,255,255, 0, 0,255,255, 0,128, 0} ; > > int[] gre1={0,255, 0,255, 0,255, 0,255,128,128} ; > > int[] blu1={0,255, 0, 0,255, 0,255,255,128, 0} ; > > > > then cast them into byte arrays > You can't cast arrays. I may have used the wrong expression ("effective content casting" ? "casting of array elements" ?) but I suppose it was clear what I wanted to achieve (despite I might have coded it wrongly). Three arrays defining : black, white, red, green, blue, yellow, magenta. cyan, gray and dark green.
> > int ti=red1[i]-128 ; > > Integer t=new Integer(ti) ; [quoted text clipped - 3 lines] > > red[i] = (byte)red1[i]; This is what I tried originally, but I got compilation errors about "possible loss of precision", which I attributed to the issue of 0-255 vs -128-127, and that's why I used the clumsier code. Sorry but I'm used to languages like Fortran and IDL with implicit type conversion.
> int[] red = { 0, (byte)255, (byte)255, 0, 0, (byte)255, (byte)255, > 0, (byte)128, 0} ; > > Sometimes the casting goes on people's nerves Yes that form seemed excessively verbose to me ... especially since I wanted to create a byte array and
byte[] test={0,255,255, 0, 0,255,255, 0,128, 0} gave me the "possible loss of precision" error.
In IDL I'd use either one of "lighter" form.
b=byte([0,255,255, 0, 0,255,255, 0,128, 0]) b=[0B,255B,255B, 0B, 0B,255B,255B, 0B,128B, 0B]
Let's now assume that I'd got the correct byte arrays red,gre,blu Then I'd do
IndexColorModel cm = new IndexColorModel(8,10, red,gre,blu) ; BufferedImage img = new BufferedImage(30,10,BufferedImage.TYPE_BYTE_INDEXED,cm) ;
How do I then fill the content of img (or a random pixel region of it) with values which are indexes in my LUT (numbers 0-9 in the example) if setRGB assumes TYPE_INT_ARGB and default sRGB color space i.e. is inconsistent with the definition of img ?
 Signature ---------------------------------------------------------------------- nospam@mi.iasf.cnr.it is a newsreading account used by more persons to avoid unwanted spam. Any mail returning to this address will be rejected. Users can disclose their e-mail address in the article if they wish so.
a24900@googlemail.com - 07 Mar 2007 18:35 GMT On Mar 7, 12:30 pm, LC's NoSpam Newsreading account <nos...@mi.iasf.cnr.it> wrote:
> How do I then fill the content of img (or a random pixel region of it) > with values which are indexes in my LUT (numbers 0-9 in the example) if > setRGB assumes TYPE_INT_ARGB and default sRGB color space i.e. is > inconsistent with the definition of img ? getRaster().setSample(x, y, 0, value)
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|