How to determine inverse color?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

How to determine inverse color?

Filip Rooms
Dear list,

Does anybody have an idea how to determine an inverse color? I don't
know if this is the appropriate term, but the problem is this: if you
want to draw markers in an image, we want these markers to have
optimal contrast with the background.

For black and white, this is easy. But what for example with an RGB
image, 8 bits per channel, where an area has values R=128 G=128 B=128
(i.e., exactly half way the dynamic range of the intensities per
band).

So, is there some kind of algorithm that, given a certain color (i.e.,
a set of RGB values), produces automatically in all cases the color
that provides optimal contrast on screen?

Many thanks in advance,

Kind regards,

Filip Rooms
Reply | Threaded
Open this post in threaded view
|

Re: How to determine inverse color?

Gabriel Landini
> the problem is this: if you
> want to draw markers in an image, we want these markers to have
> optimal contrast with the background.

One can use the bitwise XOR of the intensity. One advantage is that applying
it twice restores the original intensity. One problem is that near the middle
of the intensity space the contrast vanishes.

What about shifting the colour space by half a cycle? Although the contrast
for a greyscale image is never the maximum (it is half of it), the contrast
jump is maintained constant for all intensity values.
 
If "i" is the intensity and there are 256 possible values, then the shifted
value should be ((i+128)%256) where "%" is the "remainder" operator.

This would be ideal for a circular colour space, like the Hue channel in HSB
(since the result is the furthest away). However for RGB it has the
disadvantage of jumping from one extreme to the other for 2 consecutive
values at the middle of the colour space.

Wayne once posted a macro called DrawXORLine.txt to show how to draw XOR
lines.
I have changed only 1 line to draw a contrasted line with the suggestion
above.
I hope it helps.

Gabriel

//--------------8<-----------------
   if (nImages==0)
       newImage("Test","8-bit Ramp",512,512,1);
   r = minOf(getWidth, getHeight)/2;
   x1=getWidth/2; y1=getHeight/2;
   for (a=0; a<4*PI; a+=PI/100) {
       x2 = r*sin(a) + x1;
       y2 = r*cos(a) + y1;
       drawXORLine(x1,y1,x2,y2);
       wait(35);
       drawXORLine(x1,y1,x2,y2);
   }

   function drawXORLine(x1, y1, x2, y2) {
       if (bitDepth==8)
           value = 255;
       else if (bitDepth==16)
           value = 65535;
       else
           value = -1;
       autoUpdate(false);
       dx = x2-x1;
       dy = y2-y1;
       if (abs(dy)>abs(dx))
           n = abs(dy); else n = abs(dx);
       n++;
       xinc = dx/n;
       yinc = dy/n;
       do {
           //putPixel(x1, y1, value^getPixel(x1, y1)); // XOR line
           putPixel(x1, y1, ((getPixel(x1, y1)+128)%256)); // new line
           x1 += xinc;
           y1 += yinc;
           n--;
       } while (n>0);
       updateDisplay();
   }
//--------------8<-----------------
Reply | Threaded
Open this post in threaded view
|

Re: How to determine inverse color?

Robert Dougherty
If the problem is to maximize the contrast, and the contrast is defined as
the Euclidean distance between the background color and the new color in RGB
space, then there is a unique solution: choose the farthest corner of the
RGB cube from the background color.  Let the background color be (rIn, gIn,
bIn), and compute the new color, (rOut, gOut, bOut) according to

int rOut = 0;
int gOut = 0;
int bOut = 0;
if(rIn < 128) rOut = 255;
if(gIn < 128) gOut = 255;
if(bIn < 128) bOut = 255;

I haven't actually tested it....

Bob

Robert P. Dougherty, Ph.D.
President, OptiNav, Inc.
Phone (425) 467-1118
Fax (425) 467-1119
www.optinav.com
 
Reply | Threaded
Open this post in threaded view
|

Re: How to determine inverse color?

Robert Baer
In reply to this post by Gabriel Landini
Hi Gabriel,

Very interesting solution.  I tried it with your greyscale internal image
and with the sample image mandrill (baboon.jpg).  I don't quite understand
why in the latter case the lines persist but in the former they do not.

Do we need a bit-shifted RGB substitute for the following code line for your
XOR algorhithm to work with RGB images?  (Sorry, I don't quite have enough
insight into color representation to figure this out on my own.)
putPixel(x1, y1, ((getPixel(x1, y1)+128)%256)); // new line

Thanks,

Rob

____________________________
Robert W. Baer, Ph.D.
Associate Professor
Department of Physiology
A. T. Still University of Health Science
800 W. Jefferson St.
Kirksville, MO 63501-1497 USA


----- Original Message -----
From: "Gabriel Landini" <[hidden email]>
To: <[hidden email]>
Sent: Saturday, January 07, 2006 11:09 AM
Subject: Re: How to determine inverse color?


> > the problem is this: if you
> > want to draw markers in an image, we want these markers to have
> > optimal contrast with the background.
>
> One can use the bitwise XOR of the intensity. One advantage is that
applying
> it twice restores the original intensity. One problem is that near the
middle
> of the intensity space the contrast vanishes.
>
> What about shifting the colour space by half a cycle? Although the
contrast
> for a greyscale image is never the maximum (it is half of it), the
contrast
> jump is maintained constant for all intensity values.
>
> If "i" is the intensity and there are 256 possible values, then the
shifted
> value should be ((i+128)%256) where "%" is the "remainder" operator.
>
> This would be ideal for a circular colour space, like the Hue channel in
HSB

> (since the result is the furthest away). However for RGB it has the
> disadvantage of jumping from one extreme to the other for 2 consecutive
> values at the middle of the colour space.
>
> Wayne once posted a macro called DrawXORLine.txt to show how to draw XOR
> lines.
> I have changed only 1 line to draw a contrasted line with the suggestion
> above.
> I hope it helps.
>
> Gabriel
>
> //--------------8<-----------------
>    if (nImages==0)
>        newImage("Test","8-bit Ramp",512,512,1);
>    r = minOf(getWidth, getHeight)/2;
>    x1=getWidth/2; y1=getHeight/2;
>    for (a=0; a<4*PI; a+=PI/100) {
>        x2 = r*sin(a) + x1;
>        y2 = r*cos(a) + y1;
>        drawXORLine(x1,y1,x2,y2);
>        wait(35);
>        drawXORLine(x1,y1,x2,y2);
>    }
>
>    function drawXORLine(x1, y1, x2, y2) {
>        if (bitDepth==8)
>            value = 255;
>        else if (bitDepth==16)
>            value = 65535;
>        else
>            value = -1;
>        autoUpdate(false);
>        dx = x2-x1;
>        dy = y2-y1;
>        if (abs(dy)>abs(dx))
>            n = abs(dy); else n = abs(dx);
>        n++;
>        xinc = dx/n;
>        yinc = dy/n;
>        do {
>            //putPixel(x1, y1, value^getPixel(x1, y1)); // XOR line
>            putPixel(x1, y1, ((getPixel(x1, y1)+128)%256)); // new line
>            x1 += xinc;
>            y1 += yinc;
>            n--;
>        } while (n>0);
>        updateDisplay();
>    }
> //--------------8<-----------------
>
Reply | Threaded
Open this post in threaded view
|

Re: How to determine inverse color?

Jeff Brandenburg
In reply to this post by Filip Rooms
On Jan 7, 2006, at 2:12 PM, Robert Dougherty wrote:

> If the problem is to maximize the contrast, and the contrast is
> defined as
> the Euclidean distance between the background color and the new color
> in RGB
> space, then there is a unique solution: choose the farthest corner of
> the
> RGB cube from the background color.  Let the background color be (rIn,
> gIn,
> bIn), and compute the new color, (rOut, gOut, bOut) according to
>
> int rOut = 0;
> int gOut = 0;
> int bOut = 0;
> if(rIn < 128) rOut = 255;
> if(gIn < 128) gOut = 255;
> if(bIn < 128) bOut = 255;

While this optimizes per-color contrast, it doesn't optimize luminance
contrast, and luminance contrast is what makes fine detail (like
markers) visible.  For example, on a green (r0gFFb0) background, either
black or white would be more legible than cyan (rFFg0bFF).

I faced a similar problem for captioning grayscale movies, and the
solution I adopted was shadowed or beveled text.  This provided
contrast against black, white, and anything in between, because each
letter feature was portrayed by both light and dark levels.  This could
still fail if the background image had texture similar to that of the
caption letters; the obvious worst case is captioning a background
image that consists entirely of caption letters. :-)

Filip, can you use markers that include texture, instead of a single
color -- perhaps something as simple as a white dot in a black ring?  
Is your background image amenable to this sort of treatment?
--
        -jeffB (Jeff Brandenburg, Duke Center for In-Vivo Microscopy)
Reply | Threaded
Open this post in threaded view
|

Re: How to determine inverse color?

Gabriel Landini
In reply to this post by Robert Baer
On Tuesday 10 January 2006 15:19, Robert Baer wrote:
> Very interesting solution.  I tried it with your greyscale internal image
> and with the sample image mandrill (baboon.jpg).  I don't quite understand
> why in the latter case the lines persist but in the former they do not.

The problem is that on an RGB image we should get the separate RGB pixel
values, calculate the contrasted colour for each plane with the same formula:

>  putPixel(x1, y1, ((getPixel(x1, y1)+128)%256)); // new line

and then pack the RGB triplet and plot.
One advantage of this method is that it seems to be reversible too (so one
does not need to store the original colours, just re-apply the algorithm and
you've got them back).
I'll see to make it run in colour too.

> Do we need a bit-shifted RGB substitute for the following code line for
> your XOR algorhithm to work with RGB images?  (Sorry, I don't quite have
> enough insight into color representation to figure this out on my own.)

The colour representation is very simple, imagine that the lut is circular (so
255 is next to 0. For any LUT value, the "contrasted" value is 180 degrees
away from the current (i.e. each colour is 128 values to the right of it).

Cheers,

Gabriel
Reply | Threaded
Open this post in threaded view
|

Re: How to determine inverse color?

Robert Dougherty
In reply to this post by Jeff Brandenburg
Jeff,

As you suggest, the algorithm for maximum luminance contrast for one
background color would be to compute the luminance of the background from
something like Y = 0.299R + 0.587G + 0.114B and choose white if Y < 128 or
black otherwise.

Bob
 

> -----Original Message-----
> From: ImageJ Interest Group [mailto:[hidden email]] On Behalf Of Jeff
> Brandenburg
> Sent: Tuesday, January 10, 2006 7:20 AM
> To: [hidden email]
> Subject: Re: How to determine inverse color?
>
> On Jan 7, 2006, at 2:12 PM, Robert Dougherty wrote:
> > If the problem is to maximize the contrast, and the contrast is
> > defined as
> > the Euclidean distance between the background color and the new color
> > in RGB
> > space, then there is a unique solution: choose the farthest corner of
> > the
> > RGB cube from the background color.  Let the background color be (rIn,
> > gIn,
> > bIn), and compute the new color, (rOut, gOut, bOut) according to
> >
> > int rOut = 0;
> > int gOut = 0;
> > int bOut = 0;
> > if(rIn < 128) rOut = 255;
> > if(gIn < 128) gOut = 255;
> > if(bIn < 128) bOut = 255;
>
> While this optimizes per-color contrast, it doesn't optimize luminance
> contrast, and luminance contrast is what makes fine detail (like
> markers) visible.  For example, on a green (r0gFFb0) background, either
> black or white would be more legible than cyan (rFFg0bFF).
>
> I faced a similar problem for captioning grayscale movies, and the
> solution I adopted was shadowed or beveled text.  This provided
> contrast against black, white, and anything in between, because each
> letter feature was portrayed by both light and dark levels.  This could
> still fail if the background image had texture similar to that of the
> caption letters; the obvious worst case is captioning a background
> image that consists entirely of caption letters. :-)
>
> Filip, can you use markers that include texture, instead of a single
> color -- perhaps something as simple as a white dot in a black ring?
> Is your background image amenable to this sort of treatment?
> --
> -jeffB (Jeff Brandenburg, Duke Center for In-Vivo Microscopy)
Reply | Threaded
Open this post in threaded view
|

Re: How to determine inverse color?

Gabriel Landini
Hi,
The macro to draw the contrasted line supporting rgb images is below (mind the
line breaks).
Perhaps the function "drawXORLine" should be renamed as it is not a XOR
function.

Cheers.

Gabriel


-------------------------
   if (nImages==0)
       newImage("Test","8-bit Ramp",512,512,1);
   r = minOf(getWidth, getHeight)/2;
   x1=getWidth/2; y1=getHeight/2;
   for (a=0; a<4*PI; a+=PI/100) {
       x2 = r*sin(a) + x1;
       y2 = r*cos(a) + y1;
       drawXORLine(x1,y1,x2,y2);
       wait(35);
       drawXORLine(x1,y1,x2,y2);
   }

   function drawXORLine(x1, y1, x2, y2) {
       if (bitDepth==8)
           value = 255;
       else if (bitDepth==16)
           value = 65535;
       else
           value = -1;
       autoUpdate(false);
       dx = x2-x1;
       dy = y2-y1;
       if (abs(dy)>abs(dx))
           n = abs(dy); else n = abs(dx);
       n++;
       xinc = dx/n;
       yinc = dy/n;
       if (bitDepth()==24){
         do {
             p=getPixel(x1,y1);
             red=((p&0xff0000>>16)+128) % 256;
             green=((p&0x00ff00>>8) +128) % 256;
             blue= (p&0x0000ff+128) % 256;
             putPixel(x1, y1, ((red & 0xff) <<16)+ ((green & 0xff) << 8) +
(blue & 0xff));
             x1 += xinc;
             y1 += yinc;
             n--;
         } while (n>0);
       }
       else{
        do {
             putPixel(x1, y1, ((getPixel(x1, y1)+128)%256));
             x1 += xinc;
             y1 += yinc;
             n--;
         } while (n>0);
       }
       updateDisplay();
   }

-----------------