Color Space Inversion for contrasting grid - colors

I have a randomly colored background that is split into solid colored rectangles. I want to draw a grid over the rectangles (this is not the problem). The issue is because of the random colors I cannot hard-code the grid color because it may not show up.
Another way to think about this is plotting a grid on a plot of a surface f(x,y). If the grid color happens to be the same color of the function (however it is defined) then it won't be visible.
I would like to take the background color and compute a new color (either grayscale or similar to the background color) that is contrasted with the color so it can easily be seen (but not distracting such as pure white on pure black).
I've tried using the luminance and weighted luminance but it doesn't work well for all colors. I've also tried gamma correcting the colors but it also does not work well.
I would also like the grid color to be as uniform as possible (I could possibly compute the adjacent grid colors to blend in). It is not that important but would be nice to have some uniformity.
The code I'm working with is based around
//byte I = (byte)(0.2*R + 0.7*G + 0.1*B);
//byte I = (byte)(R + G + B)/3.0);
byte I = (byte)(Math.Max(Bar.Background.R, Math.Max(Bar.Background.G, Bar.Background.B)));
if (I < 120)
I = (byte)(I + 30);
else
I = (byte)(I - 30);
//I = (byte)(Math.Pow(I/255.0, 1/2.0)*255);
I've also tried gamma correcting the rgb's first.
Anyone have any ideas?

The colors that offer the most contrast are colors that are fully saturated. This offers you a way to find color that may work(but not necessarily for many reasons). Essentially you pick the color the furthest away along the line connecting color and the fully saturated color.

Related

Exact border color and width of standard/system QLineEdit?

Does anyone know the border color of a standard/system QLineEdit? Black is/seems too dark. It looks like some shade of grey, but would like the exact color.
Also, is the border width 1px?
There is no "standard/system" border color nor border size. It depends on:
the OS;
the possible styling customization of the OS;
the Qt Style in use (which might or might not depend on the OS);
possible customization used with style sheets;
The color could be based on the colors of the current palette, but every style (and OS) use those colors in different ways, and in some cases they are even drawn using pixmaps.
The color roles that might be used for drawing borders are:
QPalette.WindowText
QPalette.Text
QPalette.Midlight
QPalette.Dark
QPalette.Mid
QPalette.Shadow
The frame width could be based on the PM_DefaultFrameWidth pixel metric, that can be queried with the following:
# within an existing QLineEdit instance
opt = QtWidgets.QStyleOptionFrame()
self.initStyleOption(opt)
border = opt.lineWidth
# without a QLineEdit, from any other widget
style = self.style()
# or from the app
opt = QtWidgets.QStyleOptionFrame()
style = QtWidgets.QApplication.style()
border = style.pixelMetric(QtWidgets.QStyle.PM_DefaultFrameWidth, opt)
But, as said above, the returned colors and values could not be consistent with the actual result, also because the style might do some ajdustments and add graphical effects that could increase/decrease the visible border width and change its appearance.

How to add border to an image, choosing color dynamically based on image edge color(s)?

I need to add a border to each image in a set of tightly cropped logos. The border color should either match the most commonly used color along the edge, OR, if there are too many different edge colors, it should choose some sort of average of the edge colors.
Here's an example. In this case, I would want the added border to be the same color as the image "background" (using that term in the lay sense). A significant majority of the pixels along the edges are that color, and there are only two other colors, so the decision algorithm would be able to select that rather drecky greenish tan for the added border (not saying anything bad about the organization behind the logo, mind you).
Does Pillow have any functions to simplify this task?
I found answers that show how to use Pillow to add borders and how to determine the average color of an entire image. But I couldn't find any code that looks only at the edges of an image and finds the predominant color, which color could then be used in the border-adding routine. Just in case someone has already done that work, please point me to it. ('Edges' meaning bands of pixels along the top/bottom/left/right margins of the image, whose height or width would be specified as a percentage of the image's total size.)
Short of pointing me to a gist that solves my whole problem, are there Pillow routines that look at edges and/or that count the colors in a pixel range and put them into an array or what not?
I see here that OpenCV can add a border the duplicates the color of the each outermost pixel along all four edges, but that looks funky—I want a solid-color border. And I'd prefer to stick with Pillow—unless another library can do the whole edge-color-analysis-and-add-border procedure in one step, more or less, in which case, please point it out.
Overwrite the center part of the image with some fixed color, that – most likely – won't be present within the edge. For that, maybe use a color with a certain alpha value. Then, there's a function getcolors, which exactly does, what you're looking for. Sort the resulting list, and get the color with the highest count. (That, often, will be the color we used to overwrite the center part. So check for that, and take the second entry, if needed.) Finally, use ImageOps.expand to add the actual border.
That'd be the whole code:
from PIL import Image, ImageDraw, ImageOps
# Open image, enforce RGB with alpha channel
img = Image.open('path/to/your/image.png').convert('RGBA')
w, h = img.size
# Set up edge margin to look for dominant color
me = 3
# Set up border margin to be added in dominant color
mb = 30
# On an image copy, set non edge pixels to (0, 0, 0, 0)
img_copy = img.copy()
draw = ImageDraw.Draw(img_copy)
draw.rectangle((me, me, w - (me + 1), h - (me + 1)), (0, 0, 0, 0))
# Count colors, first entry most likely is color used to overwrite pixels
n_colors = sorted(img_copy.getcolors(2 * me * (w + h) + 1), reverse=True)
dom_color = n_colors[0][1] if n_colors[0][1] != (0, 0, 0, 0) else n_colors[1][1]
# Add border
img = ImageOps.expand(img, mb, dom_color).convert('RGB')
# Save image
img.save('with_border.png')
That'd be the result for your example:
And, that's some output for another image:
It's up to you to decide, whether there are several dominant colors, which you want to mix or average. You'd need to inspect the n_colors appropriately on the several counts for that. That's quite a lot of work, which is left out here.
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.9.1
PyCharm: 2021.1
Pillow: 8.2.0
----------------------------------------

color blending with GDI+

I am refering to a older question saying color blending with GDI+
Using GDI+ with Windows Forms, I want to be able to draw with a pen and blend color based on the destination pixel color.
For example, if I draw a line and it passes over black pixels, I want it to be a lighter color (like white for example) so that it's visible. When that same line passes over white pixels, it should be a darker color (black for example) so that it's still clearly visible.
the answers says to use a color matrix for transformation
so i started implementing it..
My image is present in raw data format in rgb48
Gdiplus::Bitmap image(input.width,input.height,input.width*6,PixelFormat48bppRGB,(unsigned char*)rgb48);
Gdiplus::Image *images= image.GetThumbnailImage(input.width,input.height);
Gdiplus::TextureBrush brush(images);
Gdiplus::Pen pen(&brush);
Gdiplus::ColorMatrix matrix={
-1.0f,0.0f,0.0f,0.0f,0.0f,
0.0f,-1.0f,0.0f,0.0f,0.0f,
0.0f,0.0f,-1.0f,0.0f,0.0f,
0.0f,0.0f,0.0f,1.0f,0.0f,
1.0f,1.0f,1.0f,0.0f,1.0f,
};
Gdiplus::Graphics gfx(&image1);
Gdiplus::ImageAttributes imageAttr;
imageAttr.SetColorMatrix(&matrix);
gfx.DrawImage(images,Gdiplus::Rect(0,0,input.width,input.height),0,0,1024,1024,Gdiplus::UnitPixel,&imageAttr);
I am not getting what i expect..Can some one help me in finding the mistake i m doing.
You can use the alpha component of a color to specify transparency, so that colors can be combined. However, if you want the combination to be something other than the alpha blend, you must draw the pixels yourself. You could first draw into a transparent bitmap, then render that onto the destination pixel by pixel.

How computers draw transparency?

In real life, transparency (or opacity) can be explained in a "simple" way by how much an object can reflect light, or how much of it pass through. So if an object is transparent light pass trough it, reflect on whatever is behind it and the light get back to us.
How computers simulate this behavior? I mean, we as developers, have many abstractions and APIs to set alpha levels and opacities of our pixels but how computers translates this into a bitmap to the screen?
What I think is happening: Both back and front colors are "combined" to result in a third color and this is then draw to screen. Eg: transparent white over back red on screen will be painted as pink!
Yes, you have it right. The "back" color is combined with the "front" color in proportion to the opacity of the front color.
For a single color channel, e.g. red, with opacity from 0 to 1:
new = old * (1 - opacity) + front * opacity

Find most readable colour of text that is drawn on a coloured surface

I'm not sure how to ask this but here goes.
I draw a filled coloured rectangle on screen. The colour is in form of R,G,B
I then want to draw text on top of the rectangle, however the colour of the text has to be such that it provides the best contrast, meaning it's readable.
Example:
If I draw a black rectangle, the obvious colour for text would be white.
What I tried right now is this. I pass this function the colour of the rectangle and it returns an inverted colour that I then use for my text.
It works, but it's not the best way.
Any suggestions?
// eg. usage: Color textColor = GetInverseLuminance(rectColor);
private Color GetInverseLuminance(Color color)
{
int greyscale = (int)(255 - ((color.R * 0.30f) + (color.G * 0.59f) + (color.B * 0.11f)));
return Color.FromArgb(greyscale, greyscale, greyscale);
}
One simple approach that is guaranteed to give a significantly different color is to toggle the top bit of each component of the RGB triple.
Color inverse(Color c)
{
return new Color(c.R ^ 0x80, c.G ^ 0x80, c.B ^ 0x80);
}
If the original color was #1AF283, the "inverse" will be #9A7203.
The contrast will be significant. I make no guarantees about the aesthetics.
Update, 2009/4/3: I experimented with this and other schemes. Results at my blog.
The most readable color is going to be either white or black. The most 'soothing' color will be something that is not white nor black, it will be a color that lightly contrasts your background color. There is no way to programmatically do this because it is subjective. You will not find the most readable color for everyone because everyone sees things differently.
Some tips about color, particularly concerning foreground and background juxtaposition, such as with text.
The human eye is essentially a simple lens, and therefore can only effectively focus on one color at a time. The lenses used in most modern cameras work around this problem by using multiple lenses of different refractive indexes (chromatic lenses) so that all colors are in focus at one time, but the human eye is not that advanced.
For that reason, your users should only have to focus on one color at a time to read the text. This means that either the foreground is in color, or the background, but never both. This leads to a condition typically called vibration, in which the eye rapidly shifts focus between foreground and background colors, trying to resolve the shape, but it never resolves, the shape is never in focus, and it leads to eyestrain.
Your function won't work if you supply it with RGB(127,127,127), because it will return the exact same colour. (modifying your function to return either black or white would slightly improve things)
The best way to always have things readable is to have white text with black around it, or the other way around.
It's oftenly achieved by first drawing black text at (x-1,y-1),(x+1,y-1),(x+1,y-1),(x+1,x+1), and then white text at (x,y).
Alternatively, you could first draw a semi-transparent black block, and then non-transparent white text over it. That ensures that there will always be a certain amount of contrast between your background and your text.
why grey? either black or white would be best. white on dark colors, black on light colors. just see if luminance is above a threshold and pick one or the other
(you don't need the .net, c# or asp.net tags, by the way)
You need to study some color theory. A program called "Color Wheel Pro" is fun to play around with and will give you the general idea.
Essentially, you're looking for complimentary colors for a given color.
That said, I think you will find that while color theory helps, you still need a human eye to fine tune.

Resources