Matlab Grayscale Normalization - matlab

I am new to matlab and to image processing, and I am having some issues normalizing but I am not sure why.
In my code I store the image as a black and white image in lim3, then:
minvalue = min(min(min(lim3)));
maxvalue = max(max(max(lim3)));
normimg = (lim3-minvalue)*255/(maxvalue-minvalue);
Unfortunately, this gives a new image that is exactly the same as lim3 at all, but I am not sure why. Ideally, I don't want to use the histeq function, so if someone could explain how to fix this code to get it to work, I would appreciate it.

All of the people above in the comments have raised very good points, but if you want a tl;dr answer, here are the most important points:
If your minimum and maximum values are 0 and 255 respectively for all colour channels, this means that the minimum and maximum colour values are black and white respectively. This is the same situation if your image was a single channel image / grayscale. As such, if you try and normalize your output image, it will look the same as you would be multiplying and dividing by the same scale. However, just as a minor note, your above code will work if your image is grayscale. I would also get rid of the superfluous nested min/max calls.
You need to make sure that your image is cast to double before you do this scaling as you will most likely generate floating point numbers. Should your scale be < 1, this will inadvertently be truncated to 0. In general, you will lose precision when you're trying to normalize the intensities as the type of the image is most likely uint8. You also need to remember to cast back to uint8 when you're done, as that is what the original type of the image was before you cast. You can do this casting, or you can use im2double as this essentially does what you want under the hood, but normalizes the image's intensities to the range of [0,1].
As such, if you really really really really... really... want to use your code above, you'd have to do something like this:
lim3 = double(lim3); %// Cast to double
minvalue = min(lim3(:)); %// Note the change here
maxvalue = max(lim3(:)); %// Got rid of superfluous nested min/max calls
normimg = uint8((lim3-minvalue)*255/(maxvalue-minvalue)); %// Cast back to uint8
This code will work if the image you are reading in is grayscale.
Bonus - For Colour Images
However, if you want to apply the above for colour images, I don't recommend you use the above approach. The reason being is you will only see a difference if the minimum and maximum values for each colour plane are the same - 0 and 255 respectively. What I would recommend you do is normalize each colour plane separately so that you'll push each colour plane to the range of [0,1], not being bound to the minimum and maximum of just one colour plane.
As such, I would recommend you do something like this:
lim3 = double(lim3); %// Cast to double
normimg = uint8(zeros(size(lim3))); %// Allocate output image
for idx = 1 : 3
chan = lim3(:,:,idx);
minvalue = min(chan(:));
maxvalue = max(chan(:));
normimg(:,:,idx) = uint8((chan-minvalue)*255/(maxvalue-minvalue)); %// Cast back to uint8
end
The above code accesses each colour plane individually, normalizes the plane, then puts the result in the output image normimg. I would recommend you use the above approach instead if you want to see any contrast differences for colour images.

Related

Plot true color Sentinel-2A imagery in Matlab

Through a combination of non-matlab/non-native tools (GDAL) as well as native tools (geoimread) I can ingest Sentinel-2A data either a indiviual bands or as an RGB image having employed gdal merge. I'm stuck at a point where using
imshow(I, [])
Produces a black image, with apparently no signal. The range of intensity values in the image are 271 - 4349. I know that there is a good signal in the image because when I do:
bit_depth = 2^15;
I = swapbytes(I);
[I_indexed, color_map] = rgb2ind(I, bit_depth);
I_double = im2double(I_indexed, 'indexed');
ax1 = figure;
colormap(ax1, color_map);
image(I_double)
i.e. index the image, collect a colormap, set the colormap and then call the image function, I get a likeness of the region I'm exploring (albeit very strangely colored)
I'm currently considering whether I should try:
Find a low-level description of Sentinel-2A data, implement the scaling/correction
Use a toolbox, possibly this one.
Possibly adjust ouput settings in one of the earlier steps involving GDAL
Comments or suggestions are greatly appreciated.
A basic scaling scheme is:
% convert image to double
I_double = im2double(I);
% scaling
max_intensity = max(I_double(:));
min_intensity = min(I_double(:));
range_intensity = max_intensity - min_intensity;
I_scaled = 2^16.*((I_double - min_intensity) ./ range_intensity);
% display
imshow(uint16(I_scaled))
noting the importance of casting to uint16 from double for imshow.
A couple points...
You mention that I is an RGB image (i.e. N-by-M-by-3 data). If this is the case, the [] argument to imshow will have no effect. That only applies automatic scaling of the display for grayscale images.
Given the range of intensity values you list (271 to 4349), I'm guessing you are dealing with a uint16 data type. Since this data type has a maximum value of 65535, your image data only covers about the lower 16th of this range. This is why your image looks practically black. It also explains why you can see the signal with your given code: you apply swapbytes to I before displaying it with image, which in this case will shift values into the higher intensity ranges (e.g. swapbytes(uint16(4349)) gives a value of 64784).
In order to better visualize your data, you'll need to scale it. As a simple test, you'll probably be able to see something appear by just scaling it by 8 (to cover a little more than half of your dynamic range):
imshow(8.*I);

Saving kinect depth frame (uint16) using MATLAB but why is it too dark?

Recently I work on kinect using MATLAB. I take depth frame which is in uint16 format. But when I display it or save it using MATLAB command like: imshow & imwrite respectively, it shows too dark image. But when set the display range or convert it in uint8 format it becomes brighter. But I want to save it as a brighter format without converting in uint8 format like scaling the range between 0 to 4500.
vid = videoinput('kinect',1);
vid2 = videoinput('kinect',2);
vid.FramesPerTrigger = 1;
vid2.FramesPerTrigger = 1;
% % Set the trigger repeat for both devices to 200, in order to acquire 201 frames from both the color sensor and the depth sensor.
vid.TriggerRepeat = 200;
vid2.TriggerRepeat = 200;
% % Configure the camera for manual triggering for both sensors.
triggerconfig([vid vid2],'manual');
% % Start both video objects.
start([vid vid2]);
trigger([vid vid2])
[imgDepth, ts_depth, metaData_Depth] = getdata(vid2);
f=imgDepth;
figure,imshow(f);
figure,imshow(f,[0 4500]);
imwrite(f,'C:\Users\sufi\Desktop\matlab_kinect\Data_image\output\depth\fo.tiff');
stop([vid vid2]);
When I set the display range:
Without setting the display range:
The values in a 16bit image range from 0 to 65535.
If we take a look at the histogram of your image:
We see that the max value is 7995. But that's just a few outliers. Most information is somewhere between 700 and 4300.
So all our values are in 5-10% of our value range. That makes it look very dark.
In order to make it look better for humans we have to normalize it. (Some image viewer do this automatically).
So in order to get a nicer image into your power point presentation you have two options.
a) display it in an image viewer that can display it nicely and take a screenshot
b) normalize the image in matlab and save it to a file.
You can further improve the image by removing those outliers befor normalization.
One simple way can be scaling the image based on following formula:
Pixel_value=Pixel_value/4500*65535
If you want see the exact image that you get from uint8 ; I guess the following steps will work for you.
Probably while casting the image to uint8 matlab firstly clip the values above some threshold lets say 4095=2**12-1 (i'm not sure about value) and then it makes right shifts (4 shifts in our case) to make it inside the range of 0-255.
So i guess multiplying the value of uint8 with 256 and casting it as uint16 will help you get the same image
Pixel_uint16_value= Pixel_uint8_value*256 //or Pixel_uint16_value= Pixel_uint8_value<<8
//dont forget to cast the result as uint16

Gaussian kernel isn't showing up [duplicate]

I have imported an image. I have parsed it to double precision and performed some filtering on it.
When I plot the result with imshow, the double image is too dark. But when I use imshowpair to plot the original and the final image, both images are correctly displayed.
I have tried to use uint8, im2uint8, multiply by 255 and then use those functions, but the only way to obtain the correct image is using imshowpair.
What can I do?
It sounds like a problem where the majority of your intensities / colour data are outside the dynamic range of what is accepted for imshow when showing double data.
I also see that you're using im2double, but im2double simply converts the image to double and if the image is already double, nothing happens. It's probably because of the way you are filtering the images. Are you doing some sort of edge detection? The reason why you're getting dark images is probably because the majority of your intensities are negative, or are hovering around 0. imshow whe displaying double type images assumes that the dynamic range of intensities is [0,1].
Therefore, one way to resolve your problem is to do:
imshow(im,[]);
This shifts the display so that range so the smallest value is mapped to 0, and the largest to 1.
If you'd like a more permanent solution, consider creating a new output variable that does this for you:
out = (im - min(im(:))) / (max(im(:)) - min(im(:)));
This will perform the same shifting that imshow does when displaying data for you. You can now just do:
imshow(out);

Manually turn RGB image into Grayscale Matlab

Let me preface this by saying, I understand there are functions that would do this for me, but I wanted to do it manually so I could understand what exactly is happening here.
So my goal is to read in a RGB image and turn it into a grayscale. All of my image processing work up to this point has been solely grayscale based, so I am a little bit lost.
I have tried to first read in the image by doing
fid = fopen('color.raw');
myimage = imread(fid, [512 384], 'uint8');
fclose(fid);
but myimage ends up as an empty 0 x 0 matrix. I think I need to assign an "R" "G" and "B" value to each pixel, thus giving each pixel three values for the three colors, but I am not sure if thats correct, and even how to attempt that.
my question is the following: How would I read in a RGB image and then turn into a grayscale one.
EDIT: So I understand how I would go about turning the RGB into the grayscale after getting the R G and B values, but I cannot seem to make Matlab read in the image, can anyone offer any assistance? using imread seems to make the most sense, but
[pathname] = ...
uigetfile({'*.raw';'*.mdl';'*.mat';'*.*'},'File Selector');
fid = fopen(pathname);
myimage = imread(fid);
fclose(fid);
Is not working, im getting an error of invalid filename for fopen, and I really do not understand why.
Conceptually you just average the red, green, and blue components and then providing that average as the output for the red, green, and blue components. You can do this using a simple arithmetic mean
(r+g+b)/3.
Or for kicks, you can use a geometric mean to get a slightly different contrast..
(r*g*b)^(1/3).
imread - read image from graphics file (it's in the documentation)
http://www.mathworks.com/help/matlab/ref/imread.html
For RGB to gray scale use the luminosity method. http://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/
The luminosity method is a more sophisticated version of the average
method. It also averages the values, but it forms a weighted average
to account for human perception. We’re more sensitive to green than
other colors, so green is weighted most heavily. The formula for
luminosity is 0.21 R + 0.71 G + 0.07 B.
The problem with averaging all 3 values as other users have indicated is that that is not how our eyes work at all.
When you average your images, you get the result below (on the built in peppers.png image)
You are effectively doing grayImg=.33*R+.33*G+.33*B when you find the average, now compare that to how MATLAB calculates grayscale values (with consideration into how humans view images)
0.2989 * R + 0.5870 * G + 0.1140 * B
See the stark contrast in values?
To get better looking data, stay closer to the coefficients MATLAB uses :)
The way MATLAB renders it is like this:

Matlab: why is the input wrong?

I have an image that named 'binary3.tiff'.
I am asked:
"In the following function (is called func) the images are given as matrices of doubles. In those images 1 represents the object and 0 (black) represents the background."
what should the input be?
I tried:
img = imread('binary3.tiff');
img2 = double(img)/255;
newimg = func(img2);
but it doesn't work.
please help me.
Without more details about what func does and the nature of the error you're getting, I can't help you much, but you can do this instead:
img2 = double(img > 0);
to ensure that the values in the input image are binary, and give it another go. Note that instead of 0 you can, of course, put any threshold number below which it is considered "background".
The problem may be due to the fact that imread assumes tiff images use the CMYK color space instead of the RGB color space, thus making img=imread('image.tiff') a matrix whose thrid dimension has size 4, instead of 3, due to this, some functions don't work properly on img, for instance, image(img) will throw an error, this is probably why you interpret the input as being incorrect.
The format of img=imread('image.tiff') is uint8, that means every value is an integer between 0 and 255, if you want to conver them to doubles between 0 and 1 it is correct to do img2=double(img)/255 as dividing a matrix by a scalar is the same as dividing each element by that scalar.
Finally, if you are sure your image is in the RGB color space you can simply discard the 4th color layer of the matrix by doing img=imread('image.tif') and then img=img(:,:,1:3) if you do this, AND the image is indeed in RGB, commands such as image(img) will work fine.