What range are L*a*b* values restricted to for a given reference white point? - matlab

I've got some images I want to do things in CIE L*a*b* with. What range can I expect the values to be in, given the initial sRGB values are in the range [0,1]?
I get my images like the following:
im_rgb = im2double(imread('/my/file/path/image.jpg'));
% ...do some muddling about with im_rgb, keeping range [0,1]
xform = makecform('srgb2lab');
im_lab = applycform(im_rgb, xform);
For starters, I'm reasonably sure that L* will be 0-100. However, I found this thread, which notes that "... a* and b* are not restricted to lie in the range [-100,100]."
Edit:
Matlab's default whitepoint is evaulated by whitepoint('ICC'), which returns 0.9642, 1, 0.8249. I'm using this value, as I'm not sure what else to use.
As I'm always using the same (default) transformation and the input colors are always real colors (as [0,1] for each of R, G, and B), Their equivalent L*a*b* representations are also real colors. Will these L*a*b* values also be bounded? If so, what are they bounded by, or how can I determine the boundaries?

You are basically asking how to find the boundary of the sRGB space in LAB, right?
So starting with L*, yes it will be bound between 0 and 100. This is by definition. If you look at the formula for conversion from XYZ to LAB you'll see that L = 116*(Y/Ywhitepoint)-16. When you are at sRGB white that Y ratio turns to 1 which makes the equation 116-16 = 100. A similar thing happens as back where the formula basically collapses to 4/29 * 116 -16 = 0.
Things are a little more complicated with the a and b. Since the XYZ -> LAB conversion is not linear the conversion doesn't make a easily described shape. But the outer surface of the sRGB cube will be become the outer boundary of the LAB space. What this means is you can take the extremes such as blue primary sRGB[0, 0, 1], convert to LAB and find what should be the furthest extent on the b axis: approximately -108. When you do that for all the corners of the sRGB cube you'll have a good idea about the extent of the sRGB space in LAB.
Most applications (notably Photoshop), clamp the encoding of the a and b channels between -128 and 127. This works fine in practice, but some large RGB spaces like ProPhoto RGB actually extent beyond this. Generally this doesn't have much practical consequence because most of these colors are imaginary, i.e. they sit outside the spectral locus.

Related

How to make a heat map on top of worldmap using hist3 in MATLAB?

My x-axis is latitudes, y-axis is longitudes, and z-axis is the hist3 of the two. It is given by: z=hist3(location(:,1:2),[180,360]), where location(:,1) is the latitude column, and location(:,2) is the longitude column.
What I now want is, instead of plotting on a self-created XY plane, I want to plot the same on a worldmap. And instead of representing the frequency of each latitude-longitude pair with the height of the bars of hist3, I want to represent the frequency of each location by a heat map on top of the world map, corresponding to each latitude-longitude pair's frequency on the dataset. I have been searching a lot for this, but have not found much help. How to do this? I could only plot the skeleton of the worldmap like this:
worldmap world
load geoid
geoshow(geoid, geoidrefvec, 'DisplayType', 'texturemap');
load coast
geoshow(lat, long)
I don't know what the colour is being produced based on.
Additionally, if possible, I would also like to know how to plot the hist3 on a 3D map of the world (or globe), where each bar of the hist3 would correspond to the frequency of each location (i.e., each latitude-longitude pair). Thank you.
The hist3 documentation, which you can find here hist3, says:
Color the bars based on the frequency of the observations, i.e. according to the height of the bars. set(get(gca,'child'),'FaceColor','interp','CDataMode','auto');
If that's not what you need, you might wanna try it with colormap. More info about it here colormap. I haven't tried using colormap on histograms directly, so If colormap doesn't help, then you can try creating a new matrix manually which will have values in colors instead of the Z values the histogram originally had.
To do that, you need to first calculate the maximum Z value with:
maxZ=max(Z);
Then, you need to calculate how much of the colors should overlap. For example, if you use RGB system and you assign Blue for the lowest values of the histogram, then Green for the middle and Red for the High, and the green starts after the Blue with no overlap, than it will look artificial. So, if you decide that you will have, for example overlapping of 10 values, than, having in mind that every R, G and B component of the RGB color images have 255 values (8 bits) and 10 of each overlap with the former, that means that you will have 255 values (from the Blue) + 245 values (From the Green, which is 255 - 10 since 10 of the Green overlap with those of the Blue) + 245 (From the Red, with the same comment as for the Green), which is total amount of 745 values that you can assign to the new colored Histogram.
If 745 > maxZ there is no logic for you to map the new Z with more than maxZ values. Then you can calculate the number of overlaping values in this manner:
if 745 > maxZ
overlap=floor(255- (maxZ-255)/2)
end
At this point you have 10 overlapping values (or more if you still think that it doesn't looks good) if the maximum value of the Z is bigger than the total amount of values you are trying to assign to the new Z, or overlap overlapping values, if the maximum of Z is smaller.
When you have this two numbers (i.e. 745 and maxZ), you can write the following code so you can create the newZ.
First you need to specify that newZ is of the same size as Z. You can achieve that by creating a zero matrix with the same size as Z, but having in mind that in order to be in color, it has to have an additional dimension, which will specify the three color components (if you are working with RGB).
This can be achieved in the following manner:
newZ=zeros(size(Z),3)
The number 3 is here, as I said, so you would be able to give color to the new histogram.
Now you need to calculate the step (this is needed only if maxZ > The number of colors you wish to assign). The step can be calculated as:
stepZ=maxZ/Total_Number_of_Colors
If maxZ is, for example 2000 and Total_Number_of_Colors is (With 10 overlaping colours) 745, then stepZ=2.6845637583892617449664429530201. You will also need a counter so you would know what color you would assign to the new matrix. You can initialize it here:
count=0;
Now, finally the assignment is as follows:
For i=1:stepZ:maxZ
count=count+1;
If count>245
NewZ(Z==stepz,3)=count;
elseif count>245 && count<256
NewZ(Z==stepz,3)=count;
NewZ(Z==stepz,2)=count-245;
elseif count>255
NewZ(Z==stepz,2)=count-245;
elseif count>500 && count<511
NewZ(Z==stepz,2)=count-245;
NewZ(Z==stepz,1)=count-500;
else
NewZ(Z==stepz,1)=count-500;
end
end
At this point you have colored your histogram. Note that you can manually color it in different colors than red, green and blue (even if you are working in RGB), but it would be a bit harder, so if you don't like the colors you can experiment with the last bit of code (the one with the for loops), or check the internet of some other automatic way to color your newZ matrix.
Now, how do you think to superimpose this matrix (histogram) over your map? Do you want only the black lines to be shown over the colored histogram? If that's the case, than it can be achieved by resampling the NewZ matrix (the colored histogram) with the same precision as the map. For example, if the map is of size MxN, then the histogram needs to be adjusted to that size. If, on the other hand, their sizes are the same, then you can directly continue to the next part.
Your job is to find all pixels that have black in the map. Since the map is not binary (blacks and whites), it will be a bit more harder, but still achievable. You need to find a satisfactory threshold for the three components. All the lines under this threshold should be the black lines that are shown on the map. You can test these values with imshow(worldmap) and checking the values of the black lines you wish to preserve (borders and land edges, for example) by pointing the cross tool on the top of the figure, in the tools bar on every pixel which is of interest.
You don't need to test all black lines that you wish to preserve. You just need to have some basic info about what values the threshold should have. Then you continue with the rest of the code and if you don't like the result so much, you just adjust the threshold in some trial and error manner. When you have figured that this threshold is, for example, (40, 30, 60) for all of the RGB values of the map that you wish to preserve (have in mind that only values that are between (0,0,0) and (40,30,60) will be kept this way, all others will be erased), then you can add the black lines with the following few commands:
for i = 1:size(worldmap,1)
for j = 1:size(worldmap,2)
if worldmap(i,j,1)<40 && worldmap(i,j,2)<30 && worldmap(i,j,3)<60
newZ(i,j,:)=worldmap(i,j,:)
end
end
I want to note that I haven't tested this code, since I don't have Matlab near me atm, so It can have few errors, but those should be easily debugable.
Hopes this is what you need,
Cheers!

Otsu method (graythresh function in matlab) produces a scaled result on which scale? 0:255, 0:max(px intensity), min:max?

Just clarifying a point about the Otsu thresholding method that lacks definition in the documentation & wikipedia articles. If you apply the Otsu method (in matlab the function graythresh) it returns a threshold value between 0 and 1.
Given 2 hypothetical grayscale images:
dark (with pixel intensities in the range of 0 to 100) and
light (with pixel intensities in the range of 155 to 255)
If I got an Otsu threshold of 0.75 for both dark and light images respectively, what grayscale pixel intensity would it map to in each case?
dark -> 75 and light -> 231 E.g. relative to the range of values in each image
dark -> 75 and light -> 191 E.g. relative to the range 0 to max pixel value
dark -> 191 and light -> 191 E.g. relative to the full range of grayscale pixel values (0-255)?
The accepted answer by #Ratbert makes the incorrect claims that
The correct answer is the first one
and
graythresh uses the min and max values in the image as boundaries,
which is the most logical behavior.
and rayryeng appears to agree with it. David Parks appears to have empirically verified it.
The correct answer is given by Anand which strangely seems to have a negative vote. He explains very convincingly that
full range of grayscale pixel values' depends on the data type of the input image
As he explains,
this is the third option
except for the fact that the dark image could not possibly get a threshold of 0.75.
First let us clarify the difference between the claims for the simplest case, in clear MATLAB, so there is no confusion. For an image with values ranging from min to max, the question poses three possibilities which, when translated to an equation are:
threshold = min + (max-min) * graythresh
threshold = max * graythresh
threshold = 255 * graythresh
Suppose the image consists of just two points one with an intensity of 0, and the other with 100. This means dark = uint8([0 100]);. A second image light = dark+155;. When we compute 255*graythresh(dark) we get exactly 49.5. When we compute 255*graythresh(light) we get exactly 204.5. These answers make it patently obvious that the third option is the only possibility.
There is one further fine point. If you try 255*graythresh(uint8(1:2)) the answer is 1, and not 1.5. So it appears that if you are using greythresh to threshold an image, you should use image <= 255*graythesh(image) with a less-than-or-equal-to, rather than a plain less-than.
Your third answer seems most right to me, with the clarification that 'full range of grayscale pixel values' depends on the data type of the input image. For example, for a uint8 image, an Otsu threshold of 0.75 corresponds to around 191. For a uint16 image, this would correspond to 49151.
Well, for posterity sake I did a comparison of the approaches mentioned before. I took a typical image with a full range of grayscale pixel intensities, then took a dark and light version of the same image and got the graythresh value for each. I Applied the Otsu threshold using each of the above mappings.
The light image pretty clearly demonstrates that the algorithm is producing a threshold over the range of the images' pixel intensities. Were we to assume a full range of pixel intensities, the Otsu algorithm would be producing a threshold below any actually present in the image, which doesn't make much sense, at least presuming the existing black background is transparent/irrelevant.
I suppose someone could make an argument for assuming the full range of pixel intensities if you assume the existing black part of the image were relevant darkness. I would certainly welcome comments there.
Full size images seen below
Amending my words above: When I blacken all but the top half of the light image and take the Otsu threshold again I get the same threshold value of 0.3020. Were the dark parts of the image relevant to the Otsu threshold being produced the extra darkness would have affected the value, therefore, Ratbert's answer is empirically demonstrated as correct.
The correct answer is the first one :
dark = 75 and light = 230, relative to the range of values in each image
graythresh uses the min and max values in the image as boundaries, which is the most logical behavior.

Region merging by using superpixels

I implemented SLIC algorithm to find labels and I obtained the labels. I would like to compute a color feature vector that contains the average of the color features for each region. For each pair of neighboring regions, if the Euclidean distance between their feature vectors is less than a threshold, I will merge the two regions. I will do this for all pairs of neighboring regions. Then, I will repeat steps until no pair of regions can be merged. However, I don't know how to implement those steps.
there are a few choices for your color features, and they really depend on your colorspace, and what information you are looking for.
If your objective is to find objects that are the same color (invariant to lighting) I would strongly suggest the hsv colorspace you convert your regular rgb image using rgb2hsv the HSV colorspace has three channels (just like RGB) which are channel 1 = H = Hue = the color, channel 2 = S = Saturation = how vivid the color is, channel 3 = V = brightness Value = how bright a color is. all values are between 0 and 1. again if you wanted to find colors invariant of lighting, your feature would simply be the Hue channel. One thing to not about the hue channel is it is actually cyclic so 0 and 1 are actually the same (red). so your distance would have to wrap around. for instance pixel A has h=.7 pixel B has H=.3 pixel C has H=.01. which is closer to pixel A? you would immediately guess pixel B since delta_H=.4 but actually delta_H for a and c is only 0.31
If you are interested in more than just the simplistic color model by hue other choices are YCbCr, YUV (most people just use YCbCr since there is no TRUE YUV in matlab), CIE (also not completely native to matlab but it is supported as in this example). each of these represent the image brightness in the first channel. and the colors are represented by the last 2 channels. Using the last two channels, you could easily plot colors on a 2d cartesian plane, with one axis being channel2 and the other being channel 3 something like this (this example is specifically YCbCr colorspace)
and the similarity measure could be the euclidean distance between two colors
Im guessing your overall goal is some kind of compression. So what I would do is simply replace pixel values. So if pixel A and B are similar, then make the value of pixel B = pixel A. This means that every iteration you are reducing the total number of different colors in the image. Whereas with averages, you are still maintaining lots of different colors. think of it this way
replace
1. iteration 1, pixel A=x B=x+delta, and they are close enough so you say A=B=x
2. iteration 2, pixel B=x, C=x-delta, they are close so you say B=C=x
3. at this point you have A=B=C=x so there is a reduction in the number of colors from 3 to 1
average
1. iteration 1, pixel A=x B=x+delta, they are close so now A=B=x+.5delta
2. iteration 2, pixel B=x+.5delta, C=x-delta, they are so now B
3. at this point you have A=B=C=x so there is a reduction in the number of colors from 3 to 1

Direct conversion from YCbCr to CIE L* a* b*

I would like to convert a pixel value in YUV (YCbCr) to CIE L* a* b* color space. Do I have to go through RGB and CIEXYZ color space or do anyone know a formula for direct conversion?
You need to go through each step. YCbCr is often encoded over video range (16-235/240 for 8 bit), and that needs to be converted to a XYZ using a particular Video RGB space definition(ie. Rec709 for High Def) which involves undoing the per-channel non-linearity of the RGB and then multiplying by the RGB->XYZ primary matrix. You then need to supply a white point (typically D65, the one in the RGB space definition), apply a different non-linearity and then another matrix to produce L*a*b*. I doubt there is much efficiency to be gained by combining all these into one transform.

Inpainting pixels between regions with nearest color in MATLAB

Is there an efficient way to fill in pixels with a value of zero between pixels with non-zero values with the nearest non-zero value, while leaving the rest of pixels at zero untouched?
To clarify, I am looking to inpaint those pixels whose closest distance to a non-zero pixel is lower than a given value (e.g. 4 pixels).
The image is initially represented as a matrix of uint32 integers.
In the example above, all the thin cracks between the colored regions should be filled with the surrounding color, while large black regions should remain the same (i.e. the routine should inpaint the pixels between the colored regions).
I imagine there is a way to do this via interpolation. In either case, I am looking for a relatively efficient solution.
Given an input matrix A:
b = imclose(A==0,ones(3,3)) %only the big zero regions
c = imdilate(A,ones(3,3)) %inpainting all neighboring pixels
d = zeros(size(A));
d(b==0) = c(b==0); %copy the inpainting only in places where there are no big regions
I haven't tested it, so there may be some problems with the code. (if you made changes to the code to make it work please edit my answer)