confused about image colorspaces

G

Guest

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
 
M

Mark Space

LC's NoSpam Newsreading account said:
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) ;

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.
 
A

a24900

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) ;
}}
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.
 
G

Guest

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

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-128 ;
Integer t=new Integer(ti) ;
red=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.

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.
 
C

Chris Uppal

LC's NoSpam Newsreading account said:
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
 
A

a24900

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

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
are signed in the range -128,127 ... my clumsy conversion code is in a
loop and similar to this

int ti=red1-128 ;
Integer t=new Integer(ti) ;
red=t.byteValue() ;


That is not casting. This is casting:

red = (byte)red1;

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} ;
 
G

Guest

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.

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.

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-128 ;
Integer t=new Integer(ti) ;
red=t.byteValue() ;


That is not casting. This is casting:

red = (byte)red1;


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 ?
 
A

a24900

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)
 

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,983
Messages
2,570,187
Members
46,747
Latest member
jojoBizaroo

Latest Threads

Top