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 |
> 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<----------------- |
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 |
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<----------------- > |
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) |
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 |
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) |
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(); } ----------------- |
Free forum by Nabble | Edit this page |