Office Open XML satMod results in more than 100% saturation - excel

I'm trying to calculate the satMod (saturation modulation) for something like the following:
<a:srgbClr val="58CAFF">
<a:satMod="300000"/>
</a:srgbClr>
Section 20.1.2.3.27 of the EMCA-376 spec says of the <satMod> element: "This element specifies the input color with its saturation modulated by the given percentage. A 50% saturation modulate reduces the saturation by half. A 200% saturation modulate doubles the saturation."
The problem I'm having is that many colors are already saturated enough that increasing the saturation by 300% (the 300000 in there corresponds to 300%) puts it way out of the 0-100% range. I have been simply capping the saturation at 100% but my results are pretty different from what Excel does.
It seems there is some special magic happening here in cases where the saturation should overflow. Anyone know what Office/Excel does in this case?
I found essentially the same question here: http://social.msdn.microsoft.com/Forums/en-US/oxmlsdk/thread/040e0a1f-dbfe-4ce5-826b-38b4b6f6d3f7
The answer indicated that the srgb color should be converted to linear rgb first and then to hsl before the saturation is modified. For me that hasn't solved the problem.

That was me that asked that original question. I have since figured it out. With ever single color transformations (satMod, redMod, lumMod, etc.), you have to clamp the value to within sRGB 0,0,0 or 255,255,255 (or 1.0,1.0,1.0). Meaning if your satMod modifies your color by 300% and the result is a color value above 255, clamp it to 255 (or 1.0). With that resulting color, you can then apply other color transforms if they are in your color srgbClr or other color spaces.
This is what I do in an example like yours.
Convert color to HSL space (these kinds of RGB->HSL routines are common on Bing/Google in a look up).
Send in that color and the satMod to a routine like this:
Public Sub modulateHSL(ByVal c As HSL, ByVal val As System.Double)
Select Case c
Case HSL.Hue
Hue = Hue * val
If Hue = 0.0 Then
If val >= 1.0 Then
Hue = val - Fix(val)
End If
Else
Hue = Hue - Fix(Hue)
End If
Case HSL.Saturation
Saturation = Saturation * val
Case HSL.Luminance
Luminance = Luminance * val
End Select
HSL_To_sRGB(Hue, Saturation, Luminance)
Clamp_sARGB()
End Sub
At the end of this routine, you'll notice two calls 1) HSL_To_sRGB(Hue, Saturation, Luminance) and 2) Clamp_sARGB(). The first one converts back to sRGB space and the second one clamps the RGB values, like this:
Public Sub Clamp_sARGB()
If Red <= 0.0 Then Red = 0.0 Else If Red >= 1.0 Then Red = 1.0
If Green <= 0.0 Then Green = 0.0 Else If Green >= 1.0 Then Green = 1.0
If Blue <= 0.0 Then Blue = 0.0 Else If Blue >= 1.0 Then Blue = 1.0
If Alpha <= 0.0 Then Alpha = 0.0 Else If Alpha >= 1.0 Then Alpha = 1.0
sRGB_To_HSL(Red, Green, Blue)
End Sub
Note there is no need to use Linear RGB in the case where you're only modifying the saturation. I maintain both RBG and HSL spaces in class level fields (RGB in 0-1 space), so that's why you see sRGB_To_HSL(Red, Green, Blue) at the end of that routine.
Now this is for DrawingML as it appears in PowerPoint. Excel may be different (there is a long drawn out thread here that deals with charts that may also have your answer). Keep in mind that modifying saturation can also modify luminance depending on how you coded your routine. If that's the case, you'll want to use the original luminance when converting back from HSL to RGB.
If none of this is working for you, can you put an example XLSX on a
DropBox point somewhere with what is going on, what you're expecting, etc.?

Related

wordcloud2 drops long words, which gets the colors mismatched

so i have a dynamic list of text that i want to visualize in a wordcloud. sometimes some of the words are too long and they get dropped from the display. i can scale down the size of everything, but it's not always clear what scale i should get down to in order to prevent things from being dropped. i'd rather not fiddle with this and just accept some things are dropped. also, everything is in a shiny app and i have a slider to control scale if really needed. the problem though is i want my text colored by properties of the words in my dataset. in this example below you can see how each "word" has a color associated with it...
wc <- data.frame(
word = c("too big to fit this one is","red","green","blue"),
freq = c(2,1,2,3),
col = c("black","red","green","blue"),
stringsAsFactors = FALSE
)
>wc
word freq col
1 too big to fit this one is 2 black
2 red 1 red
3 green 2 green
4 blue 3 blue
wordcloud2(wc, color = wc$col)
this then draws the wordcloud but the first element is dropped and the colors don't drop too ("red" is colored black, "green" is colored red, and "blue" is colored green). i can't do wordcloud2(wc, color = col) like an aes style call in ggplot, the wordcloud does draw but all the text is clear... i can hover over it but not see any of it. anyone else work through this issue? thanks!!!

Interior.Color Property inverts colors?

I have written a piece of code that allows me to retrieve the shading color of a specific cell inside a sheet of an excel workbook. I have successfully retrieved the RGB integer value by starting a COM server using MATLAB's actxserver, and then accessing the Color Property of the Interior Object of that particular Cell Object. Then I obtain the equivalent RGB triplet of that integer, so I can use it later for plotting in MATLAB.
In order to test that my code works properly I designed the following test: I created an Excel workbook called colorTest.xlsx with 8 different colors:
Then I run my MATLAB code, which extracts the color information on each cell of the B column. I should get a plot with the colors on the same vertical order and a table with the int value and the RGB triplet of each color.
However something unexpected happens! Look at the results:
Notice that the integer value that is obtained from the Color Property does not always match the color of the original cell, for black, white, green and magenta the integer values are correct, but this is not true for all the other colors. You can see, for example, that for red color on the Excel, the output int and RGB triplet correspond to blue color.
I have added the following table with the correct results I should get, for reference:
Color Int R G B
-------- -------- -----
Black 0 0 0 0
White 16777215 1 1 1
Red 16711680 1 0 0
Green 65280 0 1 0
Blue 255 0 0 1
Cyan 65535 0 1 1
Magenta 16711935 1 0 1
Yellow 16776960 1 1 0
I obtained the correct integer values for each color using this RGB Int Calculator.
If we compare the two tables, we can deduce that the Red and Blue channels are inverted.
The code:
The function that I execute to run the test is called getCellColor. Have a look at the code:
function getCellColor()
clear all;
clc;
% Excel
filename = 'colorTest.xlsx';
% Start Excel as ActiveX server process on local host
Excel = actxserver('Excel.Application');
% Handle requested Excel workbook filename
path = validpath(filename);
% Cleanup tasks upon function completion
cleanUp = onCleanup(#()xlsCleanup(Excel, path));
% Open Excel workbook.
readOnly = true;
[~, workbookHandle] = openExcelWorkbook (Excel, path, readOnly);
% Initialise worksheets object
workSheets = workbookHandle.Worksheets;
% Get the sheet object (sheet #1)
sheet = get(workSheets,'item',1);
% Print table headers
fprintf('Color \t Int \t R G B\n');
fprintf('--------\t --------\t -----\n');
% Create figure
figure;
hold on;
% Loop through every color on the Excel file
for row = 1:8
% Get the cell object with name of color
cell = get(sheet, 'Cells', row, 1);
cName = cell.value;
% Get the cell object with colored background
cell = get(sheet, 'Cells', row, 2);
% Get the interior object
interior = cell.Interior;
% Get the color integer property
cInt = get(interior, 'Color'); % <-- Pay special attention here(*)
% Get the RGB triplet from its integer value
cRGB = int2rgb(cInt);
% Plot the color
patch([0 0 1 1], [8-row 9-row 9-row 8-row], cRGB);
% Print row with color data
fprintf('%-8s\t %8d\t %d %d %d\n', cName, cInt, cRGB);
end
% Turn off axes
set(findobj(gcf, 'type','axes'), 'Visible','off')
end
(*) This instruction is responsible of recovering the color integer.
Note: The functions described next, do not cause the problem since they do not take part in the obtaining of the color integer (they are only used for secondary tasks). I have included this information only for completeness.
During this process I use three private functions from the MATLAB's iofun folder, which are: validpath, xlsCleanup and openExcelWorkbook. I simply copied them into a folder called private inside the project folder.
Finally, to obtain the RGB triplet from the color integer, I use a function which I adapted from this other function that I found on the net.
Here is the code for my int2rgb function:
function[RGB] = int2rgb(colorInt)
% Returns RGB triplet of an RGB integer.
if colorInt > 16777215 || colorInt < 0
error ('Invalid int value. Valid range: 0 <= value <= 16777215')
end
R = floor(colorInt / (256*256));
G = floor((colorInt - R*256*256)/256);
B = colorInt - R*256*256 - G*256;
RGB = [R, G, B]/255;
end
I am trying to make some sense out of this, but I really have no idea of what is happening. I have done some research, without much luck, but this post and this other post caught my attention. Maybe it has something to do with my problem.
So does the Interior.Color Property really inverts colors?
If this is the case, should I consider this as normal behavior or is this a bug?
Link to download:
I have packed the entire project on a .zip file and uploaded it, so you can run this test on your machine straight away. Download the file and unpack.
getCellColor.zip
There is no "right" or "wrong" here, Matlab and Excel just encode color differently. You need to account for that in your code.
The closest I can find to an official source is this MSDN article, about half way down see the example of encoding of "blue"
MSDN article
The following examples set the interior of a selection of cells to the color blue.
Selection.Interior.Color = 16711680
Selection.Interior.Color = &HFF0000
Selection.Interior.Color = &O77600000
Selection.Interior.Color = RGB(0, 0, 255)
My first thought is check the channels order RGB vs. BGR.
You could probably simplify that int2rgb function by using typecast instead. Here's an example using the values you posted:
clrs = [0; 16777215; 16711680; 65280; 255; 65535; 16711935; 16776960]
for i=1:numel(clrs)
bgra = typecast(int32(clrs(i)), 'uint8')
end
The output:
clrs =
0
16777215
16711680
65280
255
65535
16711935
16776960
bgra =
0 0 0 0
bgra =
255 255 255 0
bgra =
0 0 255 0
bgra =
0 255 0 0
bgra =
255 0 0 0
bgra =
255 255 0 0
bgra =
255 0 255 0
bgra =
0 255 255 0
Your int2rgb method inverts R and B. Replace them and you'll get the right conversion. The Interior.Color property uses the convention where R is the least significant, while the FileExchange function you used uses the opposite convention.
To convert from int to RGB:
B = floor(colorInt / (256*256));
G = floor((colorInt - B*256*256)/256);
R = colorInt - B*256*256 - G*256;
colorRGB = [R G B];
To convert from RGB to int:
colorInt = colorRGB * [1 256 256*256]';
From the MSDN article on RGB Color Model:
The RGB color model is used for specifying colors. This model
specifies the intensity of red, green, and blue on a scale of 0 to
255, with 0 (zero) indicating the minimum intensity. The settings of
the three colors are converted to a single integer value by using this
formula:
RGB value = Red + (Green*256) + (Blue*256*256)
As it was suggested in chris neilsen answer, there is no "right" or "wrong" in terms of color encoding. Microsoft has chosen this particular way to encode colors for reasons only they know, and I should stick to it.
So the RGB values that I get are totally correct.
In the following table, I have included the RGB values provided in the MSDN article next to the ones that I get in MATLAB, and they are a perfect match.
Color Int RGB values from MSDN
-------- -------- --------
Black 0 0
White 16777215 16777215
Red 255 255
Green 65280 65280
Blue 16711680 16711680
Cyan 16776960 16776960
Magenta 16711935 16711935
Yellow 65535 65535

Calculate distance between colors in HSV space

I intend to find a distance metric between two colours in HSV space.
Suppose that each colour element has 3 components: hue, saturation, and value. Hue is ranged between 0 to 360, saturation is ranged between 0 to 1, and value is ranged between 0 to 255.
Also hue has a circular property, for example, 359 in hue is closer to 0 in hue value than 10 in hue.
Can anyone provide a good metric to calculate the distance between 2 colour element in HSV space here?
First a short warning: Computing the distance of colors does not make sense (in most cases). Without considering the results of 50 years of research in Colorimetry, things like the CIECAM02 Color Space or perceptual linearity of distance measures, the result of such a distance measure will be counterintuitive. Colors that are "similar" according to your distance measure will appear "very different" to a viewer, and other colors, that have a large "distance" will be undistinguishable by viewers. However...
The actual question seems to aim mainly at the "Hue" part, which is a value between 0 and 360. And in fact, the values of 0 and 360 are the same - they both represent "red", as shown in this image:
Now, computing the difference of two of these values boils down to computing the distance of two points on a circle with a circumference of 360. You already know that the values are strictly in the range [0,360). If you did not know that, you would have to use the Floating-Point Modulo Operation to bring them into this range.
Then, you can compute the distance between these hue values, h0 and h1, as
hueDistance = min(abs(h1-h0), 360-abs(h1-h0));
Imagine this as painting both points on a circle, and picking the smaller "piece of the cake" that they describe - that is, the distance between them either in clockwise or in counterclockwise order.
EDIT Extended for the comment:
The "Hue" elements are in the range [0,360]. With the above formula, you can compute a distance between two hues. This distance is in the range [0,180]. Dividing the distance by 180.0 will result in a value in [0,1]
The "Saturation" elements are in the range [0,1]. The (absolute) difference between two saturations will also be in the range [0,1].
The "Value" elements are in the range [0,255]. The absolute difference between two values will thus be in the range [0,255] as well. Dividing this difference by 255.0 will result in a value in [0,1].
So imagine you have two HSV tuples. Call them (h0,s0,v0) and (h1,s1,v1). Then you can compute the distances as follows:
dh = min(abs(h1-h0), 360-abs(h1-h0)) / 180.0
ds = abs(s1-s0)
dv = abs(v1-v0) / 255.0
Each of these values will be in the range [0,1]. You can compute the length of this tuple:
distance = sqrt(dh*dh+ds*ds+dv*dv)
and this distance will be a metric for the HSV space.
Given hsv values, normalized to be in the ranges [0, 2pi), [0, 1], [0, 1], this formula will project the colors into the HSV cone and give you the squared (cartesian) distance in that cone:
( sin(h1)*s1*v1 - sin(h2)*s2*v2 )^2
+ ( cos(h1)*s1*v1 - cos(h2)*s2*v2 )^2
+ ( v1 - v2 )^2
In case you are looking for checking just the hue, Marco's answer will do it. However, for a more accurate comparison considering hue, saturation and value, Sean's answer is the right one.
You can't simply check the distance from hue, saturation and value equally, because hue is a circle, not a normal vector. It's not like RGB where red, green and blue are vectors
PS: I know I am not giving any new solutions with this post, but Sean's answer really saved me and I wanted to acknowledge it besides upvoting since it is not the top answer here.
Lets start with:
c0 = HSV( h0, s0, v0 )
c1 = HSV( h1, s1, v1 )
Here are two more solutions:
(Helix Length)Find the length of curve in euclidean space:
x = ( s0+t*(s1-s0) ) * cos( h0+t*( h1-h0 ) )
y = ( s0+t*(s1-s0) ) * sin( h0+t*( h1-h0 ) )
z = ( v0+t*(v1-v0) )
t goes from 0 to 1.
Note: h1-h0 is not just subtraction it is modulus subtraction.
This can be optimized by rotation and then use: h0=0, and h1 = min(abs(h1-h0), 360-abs(h1-h0))
(Helix Length over RGB)Same as above but convert above curve in to RGB instead to euclidean space then calculate arc length.
And again convex combination by coordinate of HSV colors, each point on HSV-line convert to RGB.
Calculate the length of that line in RGB space with euclidean norm.
helix_rgb( t ) = RGB( HSV( h0+t*( h1-h0 ), s0+t*(s1-s0), v0+t*(v1-v0) ) )
t goes from 0 to 1.
Note: h1-h0 is not just subtraction it is (more than) modulus subtraction e.g.
D(HSV(300,50,50),HSV(10,50,50)) = D(HSV(300,50,50),HSV( 0,50,50)) + D(HSV( 0,50,50), HSV(10,50,50))
Comparison of metrics:
RGB(0,1,0) is referent point and calculate distance to color in right-down corner image.
Color image is generated by rule HSL([0-360], 100, [1-100] ).
EM is short from Euclid with Modulo as Marco13 propose with Sean Gerrish's scale.
Comparison of solutions over HSI, HSL and HSV, there are also distance in RGB and CIE76(LAB).
Comparing EM to other solutions like Helix len, RGB2RGB, CIE76 appears that EM give acceptable result at very low cost.
In https://github.com/dmilos/color.git it is implemented EM with arbitrary scaling.
Example:
typedef ::color::hsv<double> color_t; // or HSI, HSL
color_t A = ::color::constant::orange_t{}; \
color_t B = ::color::constant::lime_t{}; \
auto distance = ::color::operation::distance<::color::constant::distance::hue_euclid_entity>( A, B, 3.1415926/* pi is default */ );

Programatically step through 24 bit color spectrum, get x amount of average colors

Yes, I AM trying to re-invent the wheel. :-) . I am doing my own image compression, (testing some ideas for transmitting parts of images over tcp). Anyway... I am trying to step through 24 bit rgb color, get a complete linear range, and (step through) that range at x intervals.
I am trying to get the average 99 colors over the complete spectrum. (24bit / 99) = 167488.6363636364 , so at 16K interval I want to pic a color for my 99 color palette.
I am having trouble understanding how RGB really works... It seems the is NO linear range..., or is there...?
I am currently doing the following:
var_interval = (255 * 255 * 255) / 99
For r = 0 To 255
For g = 0 To 255
For b = 0 To 255
If var_counter = var_interval Then
objWriter.Write(r & "," & g & "," & b)
End If
var_counter += 1
Next
Next
Next
I am getting my colors, but this step does not generate "scaling" colors if you will.
Any ideas?
There certainly is a way to iterate through the color spectrum, but whether you go this approach is your own choice :). You can make use of the HSL color space (Hue, saturation, and lightness) instead of RGB (red, green, blue). The hue represents which color (ranging from 0 to 360), the saturation is how much of that color your want (0 to 100), and the lightness is how bright you want it.
So, to answer your question, you can take 360/99 as your sampling rate from the hue, choose a consistent value and intensity that you'd want and then convert that to RGB space to display it. For the conversion see:
http://web2.clarkson.edu/class/image_process/RGB_to_HSI.pdf
and for information on HSL color space see:
http://en.wikipedia.org/wiki/HSL_and_HSV

Alpha blending a red, blue, and green image to produce an image tinted to any rgb value?

Basically, I have a context where I can't programatically tint an image, though I can change it's alpha value. With some experimentation, I've found that I can layer a red, blue, and green version of the image, with specific alpha values, to produce a wide range of colors. However, I am wondering if it's possible to achieve a true RGB representation through this method? If so, what is the formula for converting an RGB value into different alpha values for the red, blue, and green layers.
The basic "equation" of alpha combination is:
alpha * (R1,G1,B1) + (1-alpha) * (R2,G2,B2)
When you have three layers with alpha you are actually combining 4 layers (the 4th one is black) so the final color is:
alpha1 * (R1,G1,B1) + (1-alpha1) * (
alpha2 * (R2,G2,B2) + (1-alpha2) * (
alpha3 * (R3,G3,B3) + (1-alpha2) * (0,0,0) ) )
Provided you have the same image on each layer and layer1 is the red channel (G1=B1=0) and layer2 is green and layer3 is blue you get:
(alpha1 * R, (1-alpha1)*alpha2 * G, (1-alpha1)*(1-alpha2)*alpha3 * B)
For a white pixel you can do any possible color. For a black pixel you cannot do anything but black. For any other pixel, you are restricted by the values of R, G and B.
Say you wanted to achieve (Rd, Gd, Bd) at a pixel where the current color is (R, G, B) then you would have to choose:
alpha1 = Rd/R
alpha2 = Gd/(G*(1-alpha1))
alpha3 = Bd/(B*(1-alpha1)*(1-alpha2))
The problem is that alpha can typically only be between 0 and 1. So, for example, if Rd > R there is nothing you can do.
You can do better if you can control the blending function (for example, in Photoshop).
I don't think that's possible, if I understand you correctly.
If, for a certain pixel, your image's red value is, say, 0.5, you can combine that with an alpha in the (typical) range [0,1] to form any value up to and including 0.5, but you can't go above, to get e.g. 0.6 or so as the output value.
If you're looking to create 3 layers that blended together add up to the original image, it's quite possible. Here's a link to a Python script that calls functions in Paint Shop Pro to do the hard parts.
http://pixelnook.home.comcast.net/~pixelnook/SplitToRGB.htm
If you need more detail than that, leave a comment and I'll try to fill it in later.

Resources