Though I have found a lot of topics on color tint and temperature, but till now I have not seen any definite solution, which is the reason I am creating this post..My apologies for that.
I am interested in adjusting color temp and tint in images from RGB values, somewhat similar to the iPhoto application found in iOS where it can be adjusted with a slider bar from left to right.
Whatever I have found, temp and tint are orthogonal properties, where temp adjustment is along the blue (left; cool colors)--yellow(right; warm colors) and tint along the green (left) -- magenta (right) axis.
How do I adjust them using formulas from RGB values i.e., uderlying implementation of the color temp and tint slider bars.
I can convert them to HSV space and then I can rotate the hue wheel channel towards those (blue, yello, green, magenta) angles, but how to do them in a systematic fashion similar to the slider bar implementation by changing gradually from low level (middle of the slider bar) to high level (right/left ends of the slider bar).
Thanks!
You should try using HSL instead of HSV. HSL saturation separates itself from the hue and luminosity has very definitive range when it comes to mathematical calculation.
In HSL, to add tint you move the L factor between 50-100 and to add shade the L factor varies between 0-50. Also saturation for HSL controls the tone directly unlike HSV.
For temperature, you have to devise your own stratagy changing the color between red and blue but one golden hint that I can give you is "every pure RGB color has one of 3 color values as zero, second fixed to 255 and 3rd varies with the factor of 255/60.
Hope this helps-
Whereas color temparature is a physical value, its expression
in terms of RGB values
not
trivial. If all you need is a pair of orthogonal axes in the RGB colorspace for the visual adjustment of white balance, they can be defined with relative ease in such a way as to resemble the true color temperature and its derivative the tint.
Let us name our RGB temperature BY—for the balance between blue and yellow, and our RGB tint GR—for the balance balance between green and red. Now, these functions must satisfy the following obvious requirements:
They shall not depend on brightness, or be invariant to multiplication of all the RGB components by the same factor:
BY(r,g,b) = BY(kr, kg, kb),
GR(r,g,b) = GR(kr, kg, kb).
They shall be zero for neutral gray:
BY(0,0,0) = 0,
GR(0,0,0) = 0.
They shall belong the to same range, symmetrical around zero point. I will use [-1..+1]
Any combination of BY and GR shall define a valid color.
Now, one of the ways to define them could be:
BY = (r + g - 2b)/(r + g + 2b),
GR = (r - g )/(r + g) .
so that each pair of BY and GR determines a specific proportion
r:g:b = (1 + BY)(1 + GR)
(1 + BY)(1 - GR)
1 - BY
The following image shows the colors of maximum brightness on our BY-GR plane. BY is directed right, GR down, and the neutral point (0,0) is at the center:
Proper
adjustment of white balance consists of multiplication of the linear RGB values by individual factors:
r_new = wb_r * r_old
g_new = wb_g * g_old
b_new = wb_b * b_old
It happens to work on gamma-compressed RGB too, but not so well on sRGB, because of a
piece-wise
definition of its transfer function, but the distortion will be small and often unnoticeable. If you want a perfect adjustment, however, make sure to work in linear RGB.
Once a BY-GR pair is chosen and the corresponding RGB proportion calculated, only one degree of freedom remains—the overall multiplier (see req. 1). Choose it so that no pixels become clipped.
Related
I need to develop an algorithm that assigns a probability to each pixel of a picture according to its RGB color code. If the pixel is completely red the probability is one. The probability lowers the less red the pixel gets. The blue pixels should be assigned with the lowest probability. I work with python. Thanks a lot for your help!
Color pattern of interest
RGB has 3 components: red, green, blue.
You could do it so it's: p = Math.max(0, red - (green + blue)/2)
If you want to discard green, p = Math.max(0, red - blue).
If values are within [0..255] (0x0..0xFF) you can normalize that to the interval [0..1]
I have a 2D vector field, and I would like to color code the vector field so that each vector direction shows up in a different color. Say my data are DataX and DataY. Currently, I am doing (in MATLAB):
R = DataX.^2 + DataY.^2;
theta1 = acos( DataX ./ R );
theta2 = asin( DataY ./ R );
surf(x,y,theta1); colormap jet; shading interp
figure; surf(x,y,theta2); colormap jet; shading interp
The issue I am having is that I cannot distinguish between vectors where either the x or y component is zero. For example, the color bar scale looks like (left, using arcsin, and right using arcos). When using arcsin, when DataY is zero (horizontal vector), arcsin(0) gives 0 regardless if the vector is pointing left or right.
Using the arctangent or arccotangent definitions of polar coordinates gives even worse results due to divide by zero errors. I am looking for an algorithm that would let me distinguish between the seemingly degenerate vectors. I have tried combining the arccos and arcsin results, but I have not been able to find a good way to do so.
I am also wondering how to extend this concept to 3D.
Thanks!
For 2D, you can use atan2(DataY, DataX) to get an angle, and then map the angle to your color. Use a cyclic colormap like 'hsv'
For 3D, you can normalize the vector to unit length and map the 3 components to red, green, and blue.
You know how every colour eventually turns white in an image if it's bright enough or sufficiently over-exposed? I'm trying to figure out a function to do this to apply to generated HDR images, in a realistic and pleasing looking way (using idealised camera performance as a reference I guess).
The problem the algorithm/function I want to obtain should solve is, let's say you have an orange pixel with the (linear RGB) values {1.0, 0.2, 0.0}. Everything is fine if you multiply each value by a factor of 1.0 or less, but let's say you multiply that pixel by 6, now you get {6.0, 1.2, 0.0}, what do you do with your out of range red and green value of 6.0 and 1.2? You could clip them which would give you {1.0, 1.0, 0.0}, which sadly is what Photoshop and 3DS Max seem to do, but it looks so very wrong as now your formerly orange pixel is yellow (so if you start with any saturated hue (meaning at least one channel is 0.0) you always end up with either magenta, yellow or cyan) and it will never become white.
I considered taking half of the excess of one channel and splitting it equally between the other channels, so for example {1.6, 0.5, 0.1} would become {1.0, 0.8, 0.4} but it's too simplistic and not very realistic. I strongly doubt that an acceptable solution could be anywhere near this trivial.
I'm sure there must have been research done on the topic, but I cannot find any relevant literature and sensitometry doesn't seem to be quite what I'm looking for.
Modifying the Python code I left in an answer on another question to work in the range [0.0-1.0]:
def redistribute_rgb(r, g, b):
threshold = 1.0
m = max(r, g, b)
if m <= threshold:
return r, g, b
total = r + g + b
if total >= 3 * threshold:
return threshold, threshold, threshold
x = (3 * threshold - total) / (3 * m - total)
gray = threshold - x * m
return gray + x * r, gray + x * g, gray + x * b
This should return acceptable results in either a linear or gamma-corrected color space, although linear will be better.
Multiplying each r,g,b value by the same amount retains their original proportions and thus the hue, up to the point where x=0 and you've achieved white. You've expressed interest in a non-linear response once clipping starts, but I'm not entirely sure how to work that in. The math was carefully chosen so that at least one of the returned values will be at the threshold, and none will be above.
Running this on your example of (1.6, 0.5, 0.1) returns (1.0, 0.6615, 0.5385).
I've found a way to do it based on Mark Ransom's suggestion with a twist. When the colour is out of gamut we compute the grey colour of equivalent perceptual luminosity then we linearly interpolate between the out-of-gamut input colour and that grey value to find the first in-gamut colour. Weighting each RGB channel to get the perceptual luminosity part is the tricky part seeing as the most commonly used formula from CIELab L = 0.2126*red + 0.7152*green + 0.0722*blue is quite blatantly wrong as it makes the blue way too bright. Instead I did some tests and chose the weights which looked the most correct to me, though these are not definite and you might want to tweak them, although for this particular problem this is perhaps not too crucial.
Or in fewer words the solution is to desaturate the out-of-gamut colour just enough that it might be in-gamut.
Here is my solution in C code. All variables are in floating point format.
Wr=0.125; Wg=0.68; Wb=0.195; // these are the weights for each colour
max = MAXN(MAXN(red, grn), blu); // max is the maximum value of the 3 colours
if (max > 1.) // if the colour is out of gamut
{
L = Wr*red + Wg*grn + Wb*blu; // Luminosity of the colour's grey point
if (L < 1.) // if the grey point is no brighter than white
{
// t represents the ratio on the line between the input colour
// and its corresponding grey point. t is between 0 and 1,
// a lower t meaning closer to the grey point and a
// higher t meaning closer to the input colour
t = (1.-L) / (max-L);
// a simple linear interpolation between the
// input colour and its grey point
red = red*t + L*(1.-t);
grn = grn*t + L*(1.-t);
blu = blu*t + L*(1.-t);
}
else // if it's too bright regardless of saturation
{
red = grn = blu = 1.;
}
}
Here's what it looks like with a linear orange gradient:
It does not use anything like arbitrary gamma which is good, the only mostly arbitrary thing has to do with the Luminosity weights, but I guess those are quite necessary.
You have to map it to some non-linear scale. For example: http://en.wikipedia.org/wiki/Gamma_correction .
Ex: Let y = f(x) = log(1+x) - log(1-x) define the "actual" luminescence.
The reverse function is x = g(y) = (e^y-1)/(e^y+1).
now, you have values x=1 and x=0.2. For the first case the corresponding y is infinity. Six times the infinity is still infinity. If you use function g, you get new x_new = 1.
For x=0.2, y = 0.4054651. After multiplying by 6, y_new = 2.432791 . The corresponding x_new = 0.8385876.
For x=0, x_new will still be 0 (I will leave the calculations to you).
So starting from (1.0, 0.2, 0.0) your new set of points are (1.0, 0.8385876, 0.0).
This is one example of mapping function. There are infinite number of them. Choose one that looks best to you.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
The new iTunes 11 has a very nice view for the song list of an album, picking the colors for the fonts and background in function of album cover. Anyone figured out how the algorithm works?
I approximated the iTunes 11 color algorithm in Mathematica given the album cover as input:
How I did it
Through trial and error, I came up with an algorithm that works on ~80% of the albums with which I've tested it.
Color Differences
The bulk of the algorithm deals with finding the dominant color of an image. A prerequisite to finding dominant colors, however, is calculating a quantifiable difference between two colors. One way to calculate the difference between two colors is to calculate their Euclidean distance in the RGB color space. However, human color perception doesn't match up very well with distance in the RGB color space.
Therefore, I wrote a function to convert RGB colors (in the form {1,1,1}) to YUV, a color space which is much better at approximating color perception:
(EDIT: #cormullion and #Drake pointed out that Mathematica's built-in CIELAB and CIELUV color spaces would be just as suitable... looks like I reinvented the wheel a bit here)
convertToYUV[rawRGB_] :=
Module[{yuv},
yuv = {{0.299, 0.587, 0.114}, {-0.14713, -0.28886, 0.436},
{0.615, -0.51499, -0.10001}};
yuv . rawRGB
]
Next, I wrote a function to calculate color distance with the above conversion:
ColorDistance[rawRGB1_, rawRGB2_] :=
EuclideanDistance[convertToYUV # rawRGB1, convertToYUV # rawRGB2]
Dominant Colors
I quickly discovered that the built-in Mathematica function DominantColors doesn't allow enough fine-grained control to approximate the algorithm that iTunes uses. I wrote my own function instead...
A simple method to calculate the dominant color in a group of pixels is to collect all pixels into buckets of similar colors and then find the largest bucket.
DominantColorSimple[pixelArray_] :=
Module[{buckets},
buckets = Gather[pixelArray, ColorDistance[#1,#2] < .1 &];
buckets = Sort[buckets, Length[#1] > Length[#2] &];
RGBColor ## Mean # First # buckets
]
Note that .1 is the tolerance for how different colors must be to be considered separate. Also note that although the input is an array of pixels in raw triplet form ({{1,1,1},{0,0,0}}), I return a Mathematica RGBColor element to better approximate the built-in DominantColors function.
My actual function DominantColorsNew adds the option of returning up to n dominant colors after filtering out a given other color. It also exposes tolerances for each color comparison:
DominantColorsNew[pixelArray_, threshold_: .1, n_: 1,
numThreshold_: .2, filterColor_: 0, filterThreshold_: .5] :=
Module[
{buckets, color, previous, output},
buckets = Gather[pixelArray, ColorDistance[#1, #2] < threshold &];
If[filterColor =!= 0,
buckets =
Select[buckets,
ColorDistance[ Mean[#1], filterColor] > filterThreshold &]];
buckets = Sort[buckets, Length[#1] > Length[#2] &];
If[Length # buckets == 0, Return[{}]];
color = Mean # First # buckets;
buckets = Drop[buckets, 1];
output = List[RGBColor ## color];
previous = color;
Do[
If[Length # buckets == 0, Return[output]];
While[
ColorDistance[(color = Mean # First # buckets), previous] <
numThreshold,
If[Length # buckets != 0, buckets = Drop[buckets, 1],
Return[output]]
];
output = Append[output, RGBColor ## color];
previous = color,
{i, n - 1}
];
output
]
The Rest of the Algorithm
First I resized the album cover (36px, 36px) & reduced detail with a bilateral filter
image = Import["http://i.imgur.com/z2t8y.jpg"]
thumb = ImageResize[ image, 36, Resampling -> "Nearest"];
thumb = BilateralFilter[thumb, 1, .2, MaxIterations -> 2];
iTunes picks the background color by finding the dominant color along the edges of the album. However, it ignores narrow album cover borders by cropping the image.
thumb = ImageCrop[thumb, 34];
Next, I found the dominant color (with the new function above) along the outermost edge of the image with a default tolerance of .1.
border = Flatten[
Join[ImageData[thumb][[1 ;; 34 ;; 33]] ,
Transpose # ImageData[thumb][[All, 1 ;; 34 ;; 33]]], 1];
background = DominantColorsNew[border][[1]];
Lastly, I returned 2 dominant colors in the image as a whole, telling the function to filter out the background color as well.
highlights = DominantColorsNew[Flatten[ImageData[thumb], 1], .1, 2, .2,
List ## background, .5];
title = highlights[[1]];
songs = highlights[[2]];
The tolerance values above are as follows: .1 is the minimum difference between "separate" colors; .2 is the minimum difference between numerous dominant colors (A lower value might return black and dark gray, while a higher value ensures more diversity in the dominant colors); .5 is the minimum difference between dominant colors and the background (A higher value will yield higher-contrast color combinations)
Voila!
Graphics[{background, Disk[]}]
Graphics[{title, Disk[]}]
Graphics[{songs, Disk[]}]
Notes
The algorithm can be applied very generally. I tweaked the above settings and tolerance values to the point where they work to produce generally correct colors for ~80% of the album covers I tested. A few edge cases occur when DominantColorsNew doesn't find two colors to return for the highlights (i.e. when the album cover is monochrome). My algorithm doesn't address these cases, but it would be trivial to duplicate iTunes' functionality: when the album yields less than two highlights, the title becomes white or black depending on the best contrast with the background. Then the songs become the one highlight color if there is one, or the title color faded into the background a bit.
More Examples
With the answer of #Seth-thompson and the comment of #bluedog, I build a little Objective-C (Cocoa-Touch) project to generate color schemes in function of an image.
You can check the project at :
https://github.com/luisespinoza/LEColorPicker
For now, LEColorPicker is doing:
Image is scaled to 36x36 px (this reduce the compute time).
It generates a pixel array from the image.
Converts the pixel array to YUV space.
Gather colors as Seth Thompson's code does it.
The color's sets are sorted by count.
The algorithm select the three most dominant colors.
The most dominant is asigned as Background.
The second and third most dominants are tested using the w3c color contrast formula, to check if the colors has enought contrast with the background.
If one of the text colors don't pass the test, then is asigned to white or black, depending of the Y component.
That is for now, I will be checking the ColorTunes project (https://github.com/Dannvix/ColorTunes) and the Wade Cosgrove project for new features. Also I have some new ideas for improve the color scheme result.
Wade Cosgrove of Panic wrote a nice blog post describing his implementation of an algorithm that approximates the one in iTunes. It includes a sample implementation in Objective-C.
You might also checkout ColorTunes which is a HTML implementation of the Itunes album view which is using the MMCQ (median cut color quantization) algorithm.
I just wrote a JS library implementing roughly the same algorithm that the one described by #Seth. It is freely available on github.com/arcanis/colibrijs, and on NPM as colibrijs.
With #Seth's answer I implemented the algorithm to get the dominant color in the two lateral borders of a picture using PHP and Imagick.
https://gist.github.com/philix/5688064#file-simpleimage-php-L81
It's being used to fill the background of cover photos in http://festea.com.br
I asked the same question in a different context and was pointed over to http://charlesleifer.com/blog/using-python-and-k-means-to-find-the-dominant-colors-in-images/ for a learning algorithm (k Means) that rougly does the same thing using random starting points in the image. That way, the algorithm finds dominant colors by itself.
I have an image whose pixel colors I want to change to match a particular color (though not completely).
As an example, I want to tint the image of a red car so that it appears blue. I can do this with the GIMP and with ImageMagick, but I would like to know which algorithm they are using to do this so I can implement it in my own program.
I have tried to do this with simple addition of the difference between the colors but it doesn't work very well.
As just a shot in the dark, untested suggestion from someone who's getting into image processing fairly recently... maybe you could just scale the channels?
For example:
RGB_Pixel.r = RGB_Pixel.r * 0.75;
RGB_Pixel.g = RGB_Pixel.g * 0.75;
RGB_Pixel.b = RGB_Pixel.b * 1.25;
If you loop through your image pixel-by-pixel with those three changes, I'd expect you to see the image shift towards blue, and the numbers of course can be trial-and-error'd.
EDIT:
Now if you want to ONLY change the color of pixels that are a certain color to begin with, say, you want to turn a blue car red without doing anything to the rest of the picture, you'll need to run a check on each pixel to see what color it looks like. One way to do this is to use a Euclidean distance:
int* R = RGB_Pixel.r;
int* G = RGB_Pixel.g;
int* B = RGB_Pixel.b;
// You are looking for Blue, which is [0 0 255];
// this variable D is the distance of your current pixel from the desired color.
float D = sqrt( (R-0)*(R-0) + (G-0)*(G-0) + (B-255)*(B-255) );
if(D < threshold)
{
R = R * 0.75;
G = G * 0.75;
B = B * 1.25;
}
The threshold variable is a number between 1 and 255 that represents the maximum distance a color can be from the color you're looking for and still be considered "close enough". This is because you don't want to only look for [0 0 255], very rarely will you find perfect blue (or perfect anything) in an image.
You want to use the lowest threshold you can get away with so that you don't end up coloring other things that aren't part of the object you're looking for, but you want to make sure your threshold is high enough that it covers your entire image. One way to do this is to set up multiple D variables, each with a different target color, so you can capture a few separate types of "blue" without using a really high threshold. For instance, to the human eye, [102 102 200] looks like blue, but might require a pretty high threshold to catch if [0 0 255] is your target color.
I suggest playing with this calculator to get a feel for which colors you want to search for specifically.