Detailed Calculation of RGB to Graylevel by Mathematica - wolfram-mathematica

I tried
ColorConvert[img, "Grayscale"]
to convert the RGB to Graylevel.
I am wondering the detailed calculation by mathematica..
Gray level= square(R^2+G^2+B^2)?
or something else?

We can obtain the exact values used by mathematica by making up a 3 pixel image with pure red,green,blue and converting it:
lvec = First#
ImageData[
ColorConvert[Image[{{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}}],
"GrayScale"]]
{0.299, 0.587, 0.114}
Note these are the "Rec. 601 luna coefficients" per http://en.wikipedia.org/wiki/Luma_%28video%29
Test this on a real image:
lena = ExampleData[{"TestImage", "Lena"}];
lenag = ColorConvert[lena, "GrayScale"];
ImageData#ImageApply[ lvec.# & , lena ] == ImageData#lenag
True

See How can I convert colors to grayscale? for insight into how the calculation might be being done.
The two images produced by the commands below are a close match, but you could figure out the exact scaling vector with a short program making multiple comparisons. A further test on a different image would ascertain whether ColorConvert uses the same vector all the time, or whether the image is analysed before conversion for an optimal grayscale appearance.
ColorConvert[img, "GrayScale"]
ImageApply[{0.35, 0.45, 0.2}.# &, img]

Related

How to convert cv2.addWeighted and cv2.GaussianBlur into MATLAB?

I have this Python code:
cv2.addWeighted(src1, 4, cv2.GaussianBlur(src1, (0, 0), 10), src2, -4, 128)
How can I convert it to Matlab? So far I got this:
f = imread0('X.jpg');
g = imfilter(f, fspecial('gaussian',[size(f,1),size(f,2)],10));
alpha = 4;
beta = -4;
f1 = f*alpha+g*beta+128;
I want to subtract local mean color image.
Input image:
Blending output from OpenCV:
The documentation for cv2.addWeighted has the definition such that:
cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]]) → dst
Also, the operations performed on the output image is such that:
(source: opencv.org)
Therefore, what your code is doing is exactly correct... at least for cv2.addWeighted. You take alpha, multiply this by the first image, then beta, multiply this by the second image, then add gamma on top of this. The only intricacy left to deal with is saturate, which means that any values that are beyond the dynamic range of the data type you are dealing with, you cap it at that much. Because there is a potential for negatives to occur in the result, the saturate option simply means to make any values that are negative 0 and any values that are greater than the maximum expected to that max. In this case, you'll want to make any values larger than 1 equal to 1. As such, it'll be a good idea to convert your image to double through im2double because you want to allow the addition and subtraction of values beyond the dynamic range to happen first, then you saturate after. By using the default image precision of the image (which is uint8), the saturation will happen even before the saturate operation occurs, and that'll give you the wrong results. Because you're doing this double conversion, you'll want to convert the addition of 128 for your gamma to 0.5 to compensate.
Now, the only slight problem is your Gaussian Blur. Looking at the documentation, by doing cv2.GaussianBlur(src1, (0, 0), 10), you are telling OpenCV to infer on the mask size while the standard deviation is 10. MATLAB does not infer the size of the mask for you, so you need to do this yourself. A common practice is to simply find six-times the standard deviation, take the floor and add 1. This is for both the horizontal and vertical dimensions of the mask. You can see my post here on the justification as to why this is common practice: By which measures should I set the size of my Gaussian filter in MATLAB?
Therefore, in MATLAB, you would do this with your Gaussian blur instead. BTW, it's simply imread, not imread0:
f = im2double(imread('http://i.stack.imgur.com/kl3Md.jpg')); %// Change - Reading image directly from StackOverflow
sigma = 10; %// Change
sz = 1 + floor(6*sigma); %// Change
g = imfilter(f, fspecial('gaussian', sz, sigma)); %// Change
%// Rest of the code is the same
alpha = 4;
beta = -4;
f1 = f*alpha+g*beta+0.5; %// Change
%// Saturate
f1(f1 > 1) = 1;
f1(f1 < 0) = 0;
I get this image:
Take a note that there is a slight difference in the way this appears between OpenCV in MATLAB... especially the hallowing around the eye. This is because OpenCV does something different when inferring the mask size for the Gaussian blur. This I'm not sure what is going on, but how I specified the mask size by looking at the standard deviation is one of the most common heuristics for it. Play around with the standard deviation until you get something you like.

Generate an HSV image from an `M` by `N` by 3 array in Julia

I have an M by N by 3 array of floating-point reals which I would like to interpret as the HSV channels of an image. I would like to generate and export the image.
According to the function documentation,
colorim(A, [colorspace])
Creates a 2d color image from an AbstractArray, auto-detecting which
of the first or last dimension encodes the color and choosing between
"horizontal-" and "vertical-major" accordingly. colorspace defaults to
"RGB" but could also be e.g. "Lab" or "HSV".
I first tested the RGB case:
using Images
imwrite(colorim(rand(Float64, 200, 200, 3), "RGB"), "Image.PNG")
which produces the following:
However, when I replace "RGB" with "HSV" as per the documentation, I get an error message.
What is the correct syntax to interpret and export an array as HSV data?
using Images, Color
B = permutedims(A, [3,1,2]) # put color first
C = reinterpret(HSV{Float64}, B)
Tim Holy gave part of the answer, namely that the data must first be converted into an Array{HSV{Float64}, 2} using reinterpret. The missing piece is that imwrite does not appear to play nicely with arrays with HSV elements, and conversion into an Array{RGB{Float64}, 2} is necessary using convert, as Tim pointed out in this thread.
Putting this all together:
A = ones(Float64, 3, 200, 200);
A[1, :, :] = 180.0;
B = reinterpret(HSV{Float64}, A);
C = convert(Array{RGB{Float64}}, B);
imwrite(C, "test.png")
giving

Converting array into grayscale in matlab

I am trying to plot a set of data in grayscale. However, the image i get seems to be always blue.
I have a set of data, albedo that ranges from [0, 0.068], which is a 1X1 double.
My code is:
for all px,py
albedoMax = 0.0679; albedoMin = 0;
out_im(px,py) = 1/(albedoMax-albedoMin)*(albedo - albedoMin);
imshow(out_im);
drawnow;
end
Basically px,py are the image coordinates that i have to iterate over, and the formula is trying to map the input range of [0, 0.068] to [0 1]. However, by running this code, i notice that the output is always blueish. I was wondering what went wrong.
Thanks for the help.
Can't you make use of the rgb2gray function?
What you are making is one layer of the RGB image.
If you are creating a homogeneous blue image with constant color then the normalization is wrong. But if it is just the matter of being blue instead of being gray then just convert it using :
ImGray = rgb2gray(Im);
Do not forget to distribute the pixels like a grid/mesh, to fill all the image not just a part of it.

How does the algorithm to color the song list in iTunes 11 work? [closed]

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.

What is the minimal difference in RGB color values which Mathematica renders and exports as different colors?

I was amazed when I found that Mathematica gives True for the following code (on 32 bit Windows XP with Mathematica 8.0.1):
Rasterize[Graphics[{RGBColor[0, 0, 0], Disk[]}]] ===
Rasterize[Graphics[{RGBColor[0, 0, 1/257], Disk[]}]]
What is the minimal difference in RGB color values which Mathematica renders and exports as different colors? Is it machine-dependent?
I believe this behaviour is machine dependent, but I do not know how exactly it depends on the OS. On my machine, it evaluates to True only when the denominator is 511.
n = 257;
While[(Rasterize[Graphics[{RGBColor[0, 0, 0], Disk[]}]] ===
Rasterize[Graphics[{RGBColor[0, 0, 1/n], Disk[]}]]) != True,
n++];
Print#n
Out[1]=511
There is a difference between the two images for n<511
p1 = ImageData#Rasterize[Graphics[{RGBColor[0, 0, 0], Disk[]}]];
p2 = ImageData#Rasterize[Graphics[{RGBColor[0, 0, 1/257], Disk[]}]];
ArrayPlot[p1 - p2]
This difference is constant all the way through n=510 and is equal to 1/255.
Max[p2 - p1] === N[1/255]
Out[1]=True
Looks like Rasterize rounds each pixel's R G B channels to the closest 8bit value (to the closest 1/256).
image = Image[{{{0, 0, .2/256}, {0, 0, .7/256}, {0, 0, 1.2/256}, {0,
0, 1.7/256}}}, ImageSize -> 4]
ImageData#image
Rasterize#image
ImageData#Rasterize#image
So the minimal difference, rasterizing into different colors should be around 0.000000000000000000000000000...
Guilty party here is Rasterize, which chops off color precision. Get help on ImageType[] to see that Mathematica actually recognizes other bit depths, but Rasterize[] vandalizes anything beyond Byte.

Resources